diff --git a/CGI.pl b/CGI.pl
index 062d023482ba2ac79927fcefee495bc2cb14f8b3..abf46a9d5a66020214b73bd5262f2e21ff706764 100644
--- a/CGI.pl
+++ b/CGI.pl
@@ -192,7 +192,8 @@ sub value_quote {
 }
 
 sub navigation_header {
-    if (defined $::COOKIE{"BUGLIST"} && $::COOKIE{"BUGLIST"} ne "") {
+    if (defined $::COOKIE{"BUGLIST"} && $::COOKIE{"BUGLIST"} ne "" &&
+        defined $::FORM{'id'}) {
 	my @bugs = split(/:/, $::COOKIE{"BUGLIST"});
 	my $cur = lsearch(\@bugs, $::FORM{"id"});
 	print "<B>Bug List:</B> (@{[$cur + 1]} of @{[$#bugs + 1]})\n";
@@ -220,6 +221,7 @@ sub make_options {
     my $last = "";
     my $popup = "";
     my $found = 0;
+    $default = "" if !defined $default;
 
     if ($src) {
         foreach my $item (@$src) {
@@ -281,7 +283,11 @@ sub quietly_check_login() {
     if (defined $::COOKIE{"Bugzilla_login"} &&
 	defined $::COOKIE{"Bugzilla_logincookie"}) {
         ConnectToDatabase();
-        SendSQL("select profiles.groupset, profiles.login_name = " .
+        if (!defined $ENV{'REMOTE_HOST'}) {
+            $ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
+        }
+        SendSQL("select profiles.groupset, profiles.login_name, " .
+                "profiles.login_name = " .
 		SqlQuote($::COOKIE{"Bugzilla_login"}) .
 		" and profiles.cryptpassword = logincookies.cryptpassword " .
 		"and logincookies.hostname = " .
@@ -291,9 +297,11 @@ sub quietly_check_login() {
 		" and profiles.userid = logincookies.userid");
         my @row;
         if (@row = FetchSQLData()) {
-            $loginok = $row[1];
+            $loginok = $row[2];
             if ($loginok) {
                 $::usergroupset = $row[0];
+                $::COOKIE{"Bugzilla_login"} = $row[1]; # Makes sure case is in
+                                                       # canonical form.
             }
         }
     }
@@ -308,16 +316,15 @@ sub quietly_check_login() {
 
 sub CheckEmailSyntax {
     my ($addr) = (@_);
-    if ($addr !~ /^[^@, ]*@[^@, ]*\.[^@, ]*$/) {
+    my $match = Param('emailregexp');
+    if ($addr !~ /$match/) {
         print "Content-type: text/html\n\n";
 
         print "<H1>Invalid e-mail address entered.</H1>\n";
         print "The e-mail address you entered\n";
         print "(<b>$addr</b>) didn't match our minimal\n";
-        print "syntax checking for a legal email address.  A legal\n";
-        print "address must contain exactly one '\@', and at least one\n";
-        print "'.' after the \@, and may not contain any commas or.\n";
-        print "spaces.\n";
+        print "syntax checking for a legal email address.\n";
+        print Param('emailregexpdesc');
         print "<p>Please click <b>back</b> and try again.\n";
         exit;
     }
@@ -325,6 +332,40 @@ sub CheckEmailSyntax {
 
 
 
+sub MailPassword {
+    my ($login, $password) = (@_);
+    my $urlbase = Param("urlbase");
+    my $template = "From: bugzilla-daemon
+To: %s
+Subject: Your bugzilla password.
+
+To use the wonders of bugzilla, you can use the following:
+
+ E-mail address: %s
+       Password: %s
+
+ To change your password, go to:
+ ${urlbase}changepassword.cgi
+
+ (Your bugzilla and CVS password, if any, are not currently synchronized.
+ Top hackers are working around the clock to fix this, as you read this.)
+";
+    my $msg = sprintf($template, $login . Param('emailsuffix'),
+                      $login, $password);
+
+    open SENDMAIL, "|/usr/lib/sendmail -t";
+    print SENDMAIL $msg;
+    close SENDMAIL;
+
+    print "Content-type: text/html\n\n";
+    print "<H1>Password has been emailed.</H1>\n";
+    print "The password for the e-mail address\n";
+    print "$login has been e-mailed to that address.\n";
+    print "<p>When the e-mail arrives, you can click <b>Back</b>\n";
+    print "and enter your password in the form there.\n";
+}
+
+
 sub confirm_login {
     my ($nexturl) = (@_);
 
@@ -344,41 +385,13 @@ sub confirm_login {
         if (defined $::FORM{"PleaseMailAPassword"}) {
 	    my $realpwd;
             if ($realcryptpwd eq "") {
-		$realpwd = InsertNewUser($enteredlogin);
+		$realpwd = InsertNewUser($enteredlogin, "");
             } else {
                 SendSQL("select password from profiles where login_name = " .
 			SqlQuote($enteredlogin));
 		$realpwd = FetchOneColumn();
             }
-            my $urlbase = Param("urlbase");
-            my $template = "From: bugzilla-daemon
-To: %s
-Subject: Your bugzilla password.
-
-To use the wonders of bugzilla, you can use the following:
-
- E-mail address: %s
-       Password: %s
-
- To change your password, go to:
- ${urlbase}changepassword.cgi
-
- (Your bugzilla and CVS password, if any, are not currently synchronized.
- Top hackers are working around the clock to fix this, as you read this.)
-";
-            my $msg = sprintf($template, $enteredlogin, $enteredlogin,
-			      $realpwd);
-            
-	    open SENDMAIL, "|/usr/lib/sendmail -t";
-	    print SENDMAIL $msg;
-	    close SENDMAIL;
-
-            print "Content-type: text/html\n\n";
-            print "<H1>Password has been emailed.</H1>\n";
-            print "The password for the e-mail address\n";
-            print "$enteredlogin has been e-mailed to that address.\n";
-            print "<p>When the e-mail arrives, you can click <b>Back</b>\n";
-            print "and enter your password in the form there.\n";
+            MailPassword($enteredlogin, $realpwd);
             exit;
         }
 
@@ -391,6 +404,9 @@ To use the wonders of bugzilla, you can use the following:
             exit;
         }
         $::COOKIE{"Bugzilla_login"} = $enteredlogin;
+        if (!defined $ENV{'REMOTE_HOST'}) {
+            $ENV{'REMOTE_HOST'} = $ENV{'REMOTE_ADDR'};
+        }
 	SendSQL("insert into logincookies (userid,cryptpassword,hostname) values (@{[DBNameToIdAndCheck($enteredlogin)]}, @{[SqlQuote($realcryptpwd)]}, @{[SqlQuote($ENV{'REMOTE_HOST'})]})");
         SendSQL("select LAST_INSERT_ID()");
         my $logincookie = FetchOneColumn();
@@ -420,7 +436,7 @@ To use the wonders of bugzilla, you can use the following:
 	    $nexturl = $&;
         }
         my $method = "POST";
-        if (defined $ENV{"REQUEST_METHOD"}) {
+        if (defined $ENV{"REQUEST_METHOD"} && length($::buffer) > 1) {
             $method = $ENV{"REQUEST_METHOD"};
         }
         print "
@@ -465,7 +481,7 @@ name=PleaseMailAPassword>
 
 
 sub PutHeader {
-    my ($title, $h1, $h2) = (@_);
+    my ($title, $h1, $h2, $extra) = (@_);
 
     if (!defined $h1) {
 	$h1 = $title;
@@ -473,11 +489,14 @@ sub PutHeader {
     if (!defined $h2) {
 	$h2 = "";
     }
+    if (!defined $extra) {
+	$extra = "";
+    }
 
     print "<HTML><HEAD>\n<TITLE>$title</TITLE>\n";
     print Param("headerhtml") . "\n</HEAD>\n";
     print "<BODY   BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\"\n";
-    print "LINK=\"#0000EE\" VLINK=\"#551A8B\" ALINK=\"#FF0000\">\n";
+    print "LINK=\"#0000EE\" VLINK=\"#551A8B\" ALINK=\"#FF0000\" $extra>\n";
 
     print PerformSubsts(Param("bannerhtml"), undef);
 
@@ -500,6 +519,52 @@ sub PutHeader {
 }
 
 
+sub DumpBugActivity {
+    my ($id, $starttime) = (@_);
+    my $datepart = "";
+    if (defined $starttime) {
+        $datepart = "and bugs_activity.when >= $starttime";
+    }
+    my $query = "
+        select bugs_activity.field, bugs_activity.when,
+                bugs_activity.oldvalue, bugs_activity.newvalue,
+                profiles.login_name
+        from bugs_activity,profiles
+        where bugs_activity.bug_id = $id $datepart
+        and profiles.userid = bugs_activity.who
+        order by bugs_activity.when";
+
+    SendSQL($query);
+    
+    print "<table border cellpadding=4>\n";
+    print "<tr>\n";
+    print "    <th>Who</th><th>What</th><th>Old value</th><th>New value</th><th>When</th>\n";
+    print "</tr>\n";
+    
+    my @row;
+    while (@row = FetchSQLData()) {
+        my ($field,$when,$old,$new,$who) = (@row);
+        $old = value_quote($old);
+        $new = value_quote($new);
+        if ($old eq "") {
+            $old = "&nbsp;";
+        }
+        if ($new eq "") {
+            $new = "&nbsp;";
+        }
+        print "<tr>\n";
+        print "<td>$who</td>\n";
+        print "<td>$field</td>\n";
+        print "<td>$old</td>\n";
+        print "<td>$new</td>\n";
+        print "<td>$when</td>\n";
+        print "</tr>\n";
+    }
+    print "</table>\n";
+}
+
+
+
 
 
 
diff --git a/CHANGES b/CHANGES
index 5f36c4369956a0f18fc48fa3c05944a6f06158d6..527e472d9c6dc6682b3123d4c63f6171219137b7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -10,6 +10,87 @@ query the CVS tree.  For example,
 will tell you what has been changed in the last week.
 
 
+8/16/99 Added "OpenVMS" to the list of OS's. Feed this to mysql:
+
+	alter table bugs change column op_sys op_sys enum("All", "Windows 3.1", "Windows 95", "Windows 98", "Windows NT", "Mac System 7", "Mac System 7.5", "Mac System 7.6.1", "Mac System 8.0", "Mac System 8.5", "Mac System 8.6", "AIX", "BSDI", "HP-UX", "IRIX", "Linux", "FreeBSD", "OSF/1", "Solaris", "SunOS", "Neutrino", "OS/2", "BeOS", "OpenVMS", "other") not null;
+
+6/22/99 Added an entry to the attachments table to record who the submitter
+was.  Nothing uses this yet, but it still should be recorded.
+
+	alter table attachments add column submitter_id mediumint not null;
+
+You should also run this script to populate the new field:
+
+#!/usr/bonsaitools/bin/perl -w
+use diagnostics;
+use strict;
+require "globals.pl";
+$|=1;
+ConnectToDatabase();
+SendSQL("select bug_id, attach_id from attachments order by bug_id");
+my @list;
+while (MoreSQLData()) {
+    my @row = FetchSQLData();
+    push(@list, \@row);
+}
+foreach my $ref (@list) {
+    my ($bug, $attach) = (@$ref);
+    SendSQL("select long_desc from bugs where bug_id = $bug");
+    my $comment = FetchOneColumn() . "Created an attachment (id=$attach)";
+
+    if ($comment =~ m@-* Additional Comments From ([^ ]*)[- 0-9/:]*\nCreated an attachment \(id=$attach\)@) {
+	print "Found $1\n";
+	SendSQL("select userid from profiles where login_name=" .
+		SqlQuote($1));
+	my $userid = FetchOneColumn();
+	if (defined $userid && $userid > 0) {
+	    SendSQL("update attachments set submitter_id=$userid where attach_id = $attach");
+	}
+    } else {
+	print "Bug $bug can't find comment for attachment $attach\n";
+    }
+}
+
+
+
+
+
+
+6/14/99 Added "BeOS" to the list of OS's. Feed this to mysql:
+
+	alter table bugs change column op_sys op_sys enum("All", "Windows 3.1", "Windows 95", "Windows 98", "Windows NT", "Mac System 7", "Mac System 7.5", "Mac System 7.6.1", "Mac System 8.0", "Mac System 8.5", "Mac System 8.6", "AIX", "BSDI", "HP-UX", "IRIX", "Linux", "FreeBSD", "OSF/1", "Solaris", "SunOS", "Neutrino", "OS/2", "BeOS", "other") not null;
+
+
+5/27/99 Added support for dependency information.  You must run the new
+"makedependenciestable.sh" script.  You can turn off dependencies with the new
+"usedependencies" param, but it defaults to being on.  Also, read very
+carefully the description for the new "webdotbase" param; you will almost
+certainly need to tweak it.
+
+
+5/24/99 Added "Mac System 8.6" and "Neutrino" to the list of OS's.
+Feed this to mysql:
+
+	alter table bugs change column op_sys op_sys enum("All", "Windows 3.1", "Windows 95", "Windows 98", "Windows NT", "Mac System 7", "Mac System 7.5", "Mac System 7.6.1", "Mac System 8.0", "Mac System 8.5", "Mac System 8.6", "AIX", "BSDI", "HP-UX", "IRIX", "Linux", "FreeBSD", "OSF/1", "Solaris", "SunOS", "Neutrino", "OS/2", "other") not null;
+
+
+5/12/99 Added a pref to control how much email you get.  This needs a new
+column in the profiles table, so feed the following to mysql:
+
+	alter table profiles add column emailnotification enum("ExcludeSelfChanges", "CConly", "All") not null default "ExcludeSelfChanges";
+
+5/5/99 Added the ability to search by creation date.  To make this perform
+well, you ought to do the following:
+
+	alter table bugs change column creation_ts creation_ts datetime not null, add index (creation_ts);
+
+
+4/30/99 Added a new severity, "blocker".  To get this into your running
+Bugzilla, do the following:
+
+	alter table bugs change column bug_severity bug_severity enum("blocker", "critical", "major", "normal", "minor", "trivial", "enhancement") not null;
+
+
 4/22/99 There was a bug where the long descriptions of bugs had a variety of
 newline characters at the end, depending on the operating system of the browser
 that submitted the text.  This bug has been fixed, so that no further changes
diff --git a/CVS/Entries b/CVS/Entries
index 48efc1f1c294075140453bafd01f61c19d90d1c1..073a8ed2d5ccb1aad0cd30f75d0edaffd234fec6 100644
--- a/CVS/Entries
+++ b/CVS/Entries
@@ -1,56 +1,60 @@
-/1x1.gif/1.1/Fri Apr 30 19:42:19 1999/-kb/
-/CGI.pl/1.12/Fri Apr 30 19:42:19 1999//
-/CHANGES/1.24/Fri Apr 30 19:42:20 1999//
-/README/1.20/Fri Apr 30 19:42:20 1999//
-/addcomponent.cgi/1.1/Fri Apr 30 19:42:20 1999//
-/ant.jpg/1.2/Fri Apr 30 19:42:20 1999/-kb/
-/backdoor.cgi/1.7/Fri Apr 30 19:42:20 1999//
-/bug_form.pl/1.15/Fri Apr 30 19:42:20 1999//
-/bug_status.html/1.4/Fri Apr 30 19:42:20 1999//
-/buglist.cgi/1.28/Fri Apr 30 19:42:20 1999//
-/changepassword.cgi/1.6/Fri Apr 30 19:42:20 1999//
-/colchange.cgi/1.7/Fri Apr 30 19:42:20 1999//
-/collectstats.pl/1.5/Fri Apr 30 19:42:20 1999//
-/createaccount.cgi/1.1/Fri Apr 30 19:42:20 1999//
-/createattachment.cgi/1.2/Fri Apr 30 19:42:20 1999//
-/defparams.pl/1.8/Fri Apr 30 19:42:20 1999//
-/describecomponents.cgi/1.1/Fri Apr 30 19:42:20 1999//
-/doaddcomponent.cgi/1.1/Fri Apr 30 19:42:21 1999//
-/doeditcomponents.cgi/1.1/Fri Apr 30 19:42:21 1999//
-/doeditowners.cgi/1.4/Fri Apr 30 19:42:21 1999//
-/doeditparams.cgi/1.6/Fri Apr 30 19:42:21 1999//
-/editcomponents.cgi/1.2/Fri Apr 30 19:42:21 1999//
-/editowners.cgi/1.4/Fri Apr 30 19:42:21 1999//
-/editparams.cgi/1.8/Fri Apr 30 19:42:21 1999//
-/enter_bug.cgi/1.17/Fri Apr 30 19:42:21 1999//
-/help.html/1.1/Fri Apr 30 19:42:21 1999//
-/helpemailquery.html/1.1/Fri Apr 30 19:42:21 1999//
-/how_to_mail.html/1.1/Fri Apr 30 19:42:21 1999//
-/index.html/1.4/Fri Apr 30 19:42:21 1999//
-/long_list.cgi/1.6/Fri Apr 30 19:42:21 1999//
-/makeactivitytable.sh/1.2/Fri Apr 30 19:42:22 1999//
-/makeattachmenttable.sh/1.1/Fri Apr 30 19:42:22 1999//
-/makebugtable.sh/1.9/Fri Apr 30 19:42:22 1999//
-/makecctable.sh/1.2/Fri Apr 30 19:42:22 1999//
-/makecomponenttable.sh/1.59/Fri Apr 30 19:42:22 1999//
-/makegroupstable.sh/1.1/Fri Apr 30 19:42:22 1999//
-/makelogincookiestable.sh/1.1/Fri Apr 30 19:42:22 1999//
-/makeproducttable.sh/1.12/Fri Apr 30 19:42:22 1999//
-/makeprofilestable.sh/1.3/Fri Apr 30 19:42:22 1999//
-/makeversiontable.sh/1.13/Fri Apr 30 19:42:22 1999//
-/new_comment.cgi/1.2/Fri Apr 30 19:42:23 1999//
-/newquip.html/1.2/Fri Apr 30 19:42:23 1999//
-/notargetmilestone.html/1.1/Fri Apr 30 19:42:23 1999//
-/post_bug.cgi/1.6/Fri Apr 30 19:42:23 1999//
-/process_bug.cgi/1.14/Fri Apr 30 19:42:23 1999//
-/processmail/1.13/Fri Apr 30 19:42:23 1999//
-/query.cgi/1.33/Fri Apr 30 19:42:23 1999//
-/relogin.cgi/1.5/Fri Apr 30 19:42:23 1999//
-/reports.cgi/1.10/Fri Apr 30 19:42:23 1999//
-/sanitycheck.cgi/1.8/Fri Apr 30 19:42:23 1999//
-/show_activity.cgi/1.2/Fri Apr 30 19:42:23 1999//
-/show_bug.cgi/1.5/Fri Apr 30 19:42:23 1999//
-/showattachment.cgi/1.1/Fri Apr 30 19:42:23 1999//
-/whineatnews.pl/1.1/Fri Apr 30 19:42:23 1999//
-/globals.pl/1.20/Fri Apr 30 19:42:25 1999//
+/1x1.gif/1.1/Wed Aug 26 06:14:15 1998/-kb/
+/CGI.pl/1.21/Fri Aug 27 17:17:29 1999//
+/CHANGES/1.33/Mon Aug 16 23:54:50 1999//
+/README/1.26/Tue Jun 29 00:24:40 1999//
+/addcomponent.cgi/1.1/Wed Apr 28 22:23:04 1999//
+/ant.jpg/1.2/Wed Aug 26 22:36:05 1998/-kb/
+/backdoor.cgi/1.10/Wed May 12 05:22:34 1999//
+/bug_form.pl/1.22/Mon Aug 30 14:26:09 1999//
+/bug_status.html/1.6/Wed May 12 04:40:02 1999//
+/buglist.cgi/1.41/Fri Aug  6 17:00:33 1999//
+/changepassword.cgi/1.11/Fri Aug 27 17:17:43 1999//
+/colchange.cgi/1.7/Wed Jan 27 21:17:07 1999//
+/collectstats.pl/1.6/Tue May  4 00:01:57 1999//
+/createaccount.cgi/1.3/Thu Aug 19 00:06:00 1999//
+/createattachment.cgi/1.7/Wed Aug  4 17:36:43 1999//
+/defparams.pl/1.16/Thu Aug  5 20:02:20 1999//
+/describecomponents.cgi/1.2/Fri Jun 11 17:39:38 1999//
+/doaddcomponent.cgi/1.2/Fri Jun 11 17:37:43 1999//
+/doeditcomponents.cgi/1.1/Thu Mar 11 22:48:41 1999//
+/doeditowners.cgi/1.4/Thu Mar 11 16:30:52 1999//
+/doeditparams.cgi/1.6/Thu Mar 11 16:30:52 1999//
+/editcomponents.cgi/1.2/Wed Apr 28 22:23:04 1999//
+/editowners.cgi/1.4/Thu Mar 11 16:30:52 1999//
+/editparams.cgi/1.8/Thu Mar 11 16:30:53 1999//
+/enter_bug.cgi/1.23/Thu Jul  8 23:55:32 1999//
+/help.html/1.2/Tue Jun 15 04:29:05 1999//
+/helpemailquery.html/1.1/Tue Jan 19 00:07:45 1999//
+/how_to_mail.html/1.1/Wed Aug 26 06:14:18 1998//
+/index.html/1.6/Sun Jun 27 02:09:45 1999//
+/long_list.cgi/1.7/Wed May 12 04:31:35 1999//
+/makeactivitytable.sh/1.2/Tue Mar 23 22:32:21 1999//
+/makeattachmenttable.sh/1.2/Tue Jun 22 23:44:22 1999//
+/makebugtable.sh/1.15/Mon Aug 16 23:54:50 1999//
+/makecctable.sh/1.2/Tue Jan 19 00:07:46 1999//
+/makecomponenttable.sh/1.59/Fri Apr 30 19:40:51 1999//
+/makedependenciestable.sh/1.1/Thu May 27 14:13:40 1999//
+/makegroupstable.sh/1.1/Thu Mar 11 16:30:53 1999//
+/makelogincookiestable.sh/1.1/Wed Sep  2 18:52:48 1998//
+/makeproducttable.sh/1.12/Fri Apr 30 19:40:52 1999//
+/makeprofilestable.sh/1.5/Tue May 25 17:59:39 1999//
+/makeversiontable.sh/1.13/Fri Apr 30 19:40:53 1999//
+/new_comment.cgi/1.2/Tue Sep 15 21:49:25 1998//
+/newquip.html/1.2/Tue Sep 15 21:49:26 1998//
+/notargetmilestone.html/1.1/Wed Feb  3 02:46:51 1999//
+/post_bug.cgi/1.9/Thu Jul  8 00:21:33 1999//
+/process_bug.cgi/1.21/Thu Jul  8 23:06:47 1999//
+/processmail/1.22/Wed Aug  4 15:07:28 1999//
+/query.cgi/1.42/Tue Aug 24 15:24:50 1999//
+/relogin.cgi/1.6/Tue Jun 15 04:29:05 1999//
+/reports.cgi/1.21/Sun Aug 29 15:42:12 1999//
+/sanitycheck.cgi/1.9/Tue Jun  1 20:52:15 1999//
+/show_activity.cgi/1.3/Tue May 25 19:22:31 1999//
+/show_bug.cgi/1.8/Tue Jun 15 04:25:29 1999//
+/showattachment.cgi/1.3/Mon May 24 19:08:59 1999//
+/showdependencygraph.cgi/1.5/Thu May 27 20:20:57 1999//
+/showdependencytree.cgi/1.3/Fri Aug 27 17:31:21 1999//
+/showowners.cgi/1.2/Mon Jul 26 20:41:54 1999//
+/whineatnews.pl/1.2/Fri Aug 13 17:10:01 1999//
+/globals.pl/1.26/Mon Aug 30 23:02:02 1999//
 D
diff --git a/CVS/Repository b/CVS/Repository
index f662091b79eb6d901671bd12bad068ef4f8fdaff..65700f2a8f40f27d319b5d660fa06aac61193b7d 100644
--- a/CVS/Repository
+++ b/CVS/Repository
@@ -1 +1 @@
-/cvsroot/mozilla/webtools/bugzilla
+mozilla/webtools/bugzilla
diff --git a/README b/README
index 61364777c3688cf38bf772b7ebd4421dd1ed5eb2..a335dfd8960767a36aa5fff4e3f68f86dbb3adc1 100644
--- a/README
+++ b/README
@@ -52,13 +52,20 @@ build from sources you can easily set the dataDir as an option to configure.
 you'll want to make sure to add mysqld to your init scripts so the server
 daemon will come back up whenever your machine reboots.
 
+  You also may want to edit those init scripts, to make sure that mysqld will
+accept large packets.  By default, mysqld is set up to only accept packets up
+to 64K long.  This limits the size of attachments you may put on bugs.  If you
+add something like "-O max_allowed_packet=1M" to the command that starts mysqld
+(or safe_mysqld), then you will be able to have attachments up to about 1
+megabyte.
+
 1.2. Perl (5.004 or greater)
 
   Any machine that doesn't have perl on it is a sad machine indeed.  Perl
 for *nix systems can be gotten in source form from http://www.perl.com.
 
   Perl is now a far cry from the the single compiler/interpreter binary it
-once. It now includes a great many required modules and quite a few other
+once was. It now includes a great many required modules and quite a few other
 support files.  If you're not up to or not inclined to build perl from source,
 you'll want to install it on your machine using some sort of packaging system
 (be it RPM, deb, or what have you) to ensure a sane install.  In the subsequent
@@ -125,11 +132,11 @@ provide backwards compatibility with the older MySQL packages; you must
 answer YES to this question.  The default will be no, and if you select it
 things won't work later.
 
-   A host of 'localhost' should be fine and a testing user of 'test'
-should find itself with sufficient access to run tests on the 'test'
-database which MySQL created upon installation.  If 'make test' and 'make
-install' go through without errors you should be ready to go as far as
-database connectivity is concerned.
+   A host of 'localhost' should be fine and a testing user of 'test' and
+a null password should find itself with sufficient access to run tests on 
+the 'test' database which MySQL created upon installation.  If 'make test' 
+and 'make install' go through without errors you should be ready to go as 
+far as database connectivity is concerned.
 
 1.6. TimeDate Perl module collection
 
@@ -164,10 +171,8 @@ listed in Appendix A.
 1.9. HTTP server
 
    You have a freedom of choice here - Apache, Netscape or any other server on
-UNIX would do. The only thing - to make configuration easier you'd better run
-HTTP daemon on the same machine that you run MySQL server on.  (Theoretically,
-it's possible to always use MySQL in a remote manner, but we don't know of
-anyone who has done that with Bugzilla yet.)
+UNIX would do. You can easily run the web server on a different machine than
+MySQL, but that makes MySQL permissions harder to manage.
 
    You'll want to make sure that your web server will run any file with the
 .cgi extension as a cgi and not just display it.  If you're using apache that
@@ -199,12 +204,13 @@ directory writable by your webserver's user (which may require just
 making it world writable).  Inside this main bugzilla directory issue the
 following commands:
 
+	mkdir data
+	cd data
 	touch comments
 	touch nomail
 	touch mail
 	
-   Make sure the comments, nomail, and mail files are writable by the
-webserver too.
+   Make sure the data directory and files are writable by the webserver.
 
    Lastly, you'll need to set up a symbolic link from /usr/bonsaitools/bin
 to the correct location of your perl executable (probably /usr/bin/perl).  Or,
@@ -223,20 +229,26 @@ that, too, then the magic is to do run "mysql mysql", and feed it commands like
 this (replace all instances of HOSTNAME with the name of the machine mysql is
 running on):
 
-	DELETE * FROM host;
-	DELETE * FROM user;
-	INSERT INTO host VALUES ('localhost','%','Y','Y','Y','Y','Y','Y');
-	INSERT INTO host VALUES (HOSTNAME,'%','Y','Y','Y','Y','Y','Y');
-	INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
-	INSERT INTO user VALUES (HOSTNAME,'','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
-	INSERT INTO user VALUES (HOSTNAME,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
-	INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+	DELETE FROM host;
+	DELETE FROM user;
+	INSERT INTO host VALUES ('localhost','%','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+	INSERT INTO host VALUES (HOSTNAME,'%','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+	INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+	INSERT INTO user VALUES (HOSTNAME,'','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+	INSERT INTO user VALUES (HOSTNAME,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+	INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
+
+The number of 'Y' entries to use varies with the version of MySQL; they keep
+adding columns.  The list here should work with version 3.22.23b.
 
 This run of "mysql mysql" may need some extra parameters to deal with whatever
 database permissions were set up previously.  In particular, you might have to say "mysql -uroot mysql", and give it an appropriate password.
 
 For much more information about MySQL permissions, see the MySQL documentation.
 
+After you've tweaked the permissions, run "mysqladmin reload" to make sure that
+the database server knows to look at your new permission list.
+
    Next we'll create the bugs database in MySQL.  This is done using the
 'mysql' command line client.  This client allows one to funnel SQL
 statements into the MySQL server directly.  It's usage summary is
@@ -253,29 +265,37 @@ should enter:
 	quit  
 
 
-
    To create the tables necessary for bug tracking and to minimally
 populate the bug tracking system you'll need to run the eight shell
 scripts found in your bugzilla directory that begin with 'make'.  These
 scripts load data into the database by piping input into the mysql
-command.
-
-
-   When calling the eight scripts order doesn't matter, but this one is
-fine:
+command.  Order does not matter, but this one is fine:
 
 	./makeactivitytable.sh
+	./makeattachmenttable.sh
 	./makebugtable.sh
 	./makecctable.sh
 	./makecomponenttable.sh
+	./makedependenciestable.sh
+	./makegroupstable.sh
 	./makelogincookiestable.sh
 	./makeproducttable.sh
 	./makeprofilestable.sh
 	./makeversiontable.sh
-	./makegroupstable.sh
 
-   After running those you've got a nearly empty copy of the mozilla bug
-tracking setup.
+You may want to edit the scripts; once bugs are entered it gets very hard to
+make changes. Think carefully about how you want database users to describe bugs.  Here's one
+suggested alternative:
+
+	priority enum("P1", "P2", "P3", "P4", "defer") not null,
+	bug_severity enum("critical", "normal", "low", "---",
+                          "enhancement", "requirement", "polish") not null,
+	op_sys enum("Unspecified", "Windows 3.1", "Windows 95", "Windows 98",
+                    "Windows NT", "Mac System 7", "Mac System 8", "Linux",
+                    "Solaris", "FreeBSD", "Other Unix", "other") not null,
+	rep_platform enum("Unspecified", "Apple", "PC Clone", "Sun", "other"),
+ 
+After running the scripts you've got a nearly empty copy of the bug tracking setup.
 
 4. Tweaking the Bugzilla->MySQL Connection Data
 
@@ -286,7 +306,7 @@ code to connect appropriately.
    In order for bugzilla to be able to connect to the MySQL database
 you'll have to tell bugzilla where the database server is, what database
 you're connecting to, and whom to connect as.  Simply open up the
-global.pl file in the bugzilla directory and find the line that begins
+globals.pl file in the bugzilla directory and find the line that begins
 like:
 
 	$::db = Mysql->Connect("
@@ -300,7 +320,7 @@ takes four parameters which are (with appropriate values):
 		probably "nobody"
 	4. Password for the MySQL account in item 3.
 
-Just fill in those values and close up global.pl
+Just fill in those values and close up globals.pl
 
 5. Setting up yourself as Maintainer
 
@@ -313,9 +333,9 @@ mail, log in with it.  Don't finish entering that new bug.
     Now, bring up MySQL, and add yourself to every group.  This will
 effectively make you 'superuser'.  The SQL to type is:
 
-	update profiles set groupset=0x7fffffffffffffff where login_name = XXX;
+	update profiles set groupset=0x7fffffffffffffff where login_name = 'XXX';
 
-replacing XXX with your email address in quotes.
+replacing XXX with your BugZilla email address.
 
 Now, if you go to the query page (off of the bugzilla main menu) where you'll
 now find a 'edit parameters' option which is filled with editable treats.
@@ -341,6 +361,63 @@ command:
 as a nightly entry to your crontab and after two days have passed you'll
 be able to view bug graphs from the Bug Reports page.
 
+8. Real security for MySQL
+
+MySQL has "interesting" default security parameters:
+        mysqld defaults to running as root
+        it defaults to allowing external network connections
+        it has a known port number, and is easy to detect
+        it defaults to no passwords whatsoever
+        it defaults to allowing "File_Priv"
+This means anyone from anywhere on the internet can not only drop the database
+with one SQL command, and they can write as root to the system. 
+
+To see your permissions do:
+        > mysql -u root -p
+        use mysql;
+        show tables;
+        select * from user;
+        select * from db;
+
+To fix the gaping holes:
+        DELETE FROM user WHERE User='';
+        UPDATE user SET Password=PASSWORD('new_password') WHERE user='root';
+        FLUSH PRIVILEGES;
+
+If you're not running "mit-pthreads" you can use:
+        GRANT USAGE ON *.* TO bugs@localhost;
+        GRANT ALL ON bugs.* TO bugs@localhost;
+        REVOKE DROP ON bugs.* FROM bugs@localhost;
+        FLUSH PRIVILEGES;
+
+With "mit-pthreads" you'll need to modify the "globals.pl" Mysql->Connect line
+to specify a specific host name instead of "localhost", and accept external
+connections:
+        GRANT USAGE ON *.* TO bugs@bounce.hop.com;
+        GRANT ALL ON bugs.* TO bugs@bounce.hop.com;
+        REVOKE DROP ON bugs.* FROM bugs@bounce.hop.com;
+        FLUSH PRIVILEGES;
+
+Consider also:
+	o Turning off external networking with "--skip-networking",
+	unless you have "mit-pthreads", in which case you can't.  Without
+	networking, MySQL connects with a Unix domain socket.
+
+	o using the --user= option to mysqld to run it as an unprivileged user.
+
+        o starting MySQL in a chroot jail
+
+        o running the httpd in a jail
+
+        o making sure the MySQL passwords are different from the OS
+        passwords (MySQL "root" has nothing to do with system "root").
+
+        o running MySQL on a separate untrusted machine
+
+        o making backups ;-)
+
+
+
 ---------[ Appendices ]-----------------------
 
 Appendix A. Required Software Download Links
@@ -390,10 +467,31 @@ hour old, so Bugzilla will eventually notice your changes by itself, but
 generally you want it to notice right away, so that you can test things.
 
 
-Appendix C. History
+Appendix C. Upgrading from previous versions of BugZilla
+
+The developers of BugZilla are constantly adding new tables, columns and fields.
+You'll get SQL errors if you just update the code.  The strategy to update is
+to read the CHANGES file backwards, and apply all of the instructions given,
+typically things like:
+
+	./makeattachmenttable.sh
+	./makegroupstable.sh
+	alter table products add column disallownew tinyint not null;
+
+Someday someone should write a script that queries the existing database
+for all the required columns, and if any are missing, adds them.
+
+
+Appendix D. History
 
    This document was originally adapted from the Bonsai installation
 instructions by Terry Weissman <terry@mozilla.org>.
 
-   The February 25, 1999 re-write was done by Ry4an Brase
-<ry4an@ry4an.org>, with some edits by Terry Weissman.
+   The February 25, 1999 re-write of this page was done by Ry4an Brase
+<ry4an@ry4an.org>, with some edits by Terry Weissman, Bryce Nesbitt,
+& Martin Pool (But don't send bug reports to them!  Report them using bugzilla,
+at http://bugzilla.mozilla.org/enter_bug.cgi , project Webtools, component
+Bugzilla).
+
+   Comments from people using this document for the first time are especially
+welcomed.
diff --git a/backdoor.cgi b/backdoor.cgi
index 0c4a013be920baf06a424278ab7cf94d0402005f..dd96b63164925a32f32e6446053aa3d9eae687da 100755
--- a/backdoor.cgi
+++ b/backdoor.cgi
@@ -160,4 +160,4 @@ foreach my $cc (split(/,/, $::FORM{'cc'})) {
 }
 
 print "Created bugzilla bug $zillaid\n";
-system("./processmail $zillaid < /dev/null > /dev/null 2> /dev/null &");
+system("./processmail $zillaid");
diff --git a/bug_form.pl b/bug_form.pl
index 1d7cae19de38d5e3a8a0ee25e8d40dd311b473ea..c563daf06c4b085c7c50ad735c2b14d06e71f871 100644
--- a/bug_form.pl
+++ b/bug_form.pl
@@ -52,7 +52,7 @@ sub quoteUrls {
     # which can happen if you do multiple s///g operations.
 
     my @things;
-    while ($text =~ s%((mailto:)?([\w\.\-\+\=]+\@\w+(?:\.\w+)+)\b|
+    while ($text =~ s%((mailto:)?([\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+)\b|
                        (\b((?:$protocol):\S+[\w/])))%"##$count##"%exo) {
         my $item = $&;
 
@@ -92,6 +92,7 @@ sub quoteUrls {
     }
 
     $text = value_quote($text);
+    $text =~ s/\&#010;/\n/g;
 
     # Stuff everything back from the array.
     for (my $i=0 ; $i<$count ; $i++) {
@@ -104,7 +105,9 @@ sub quoteUrls {
     return $text;
 }
 
-quietly_check_login();
+my $loginok = quietly_check_login();
+
+my $id = $::FORM{'id'};
 
 my $query = "
 select
@@ -126,9 +129,10 @@ select
 	qa_contact,
 	status_whiteboard,
         date_format(creation_ts,'Y-m-d'),
-        groupset
+        groupset,
+	delta_ts
 from bugs
-where bug_id = $::FORM{'id'}
+where bug_id = $id
 and bugs.groupset & $::usergroupset = bugs.groupset";
 
 SendSQL($query);
@@ -141,7 +145,7 @@ if (@row = FetchSQLData()) {
 		       "bug_severity", "component", "assigned_to", "reporter",
 		       "bug_file_loc", "short_desc", "target_milestone",
                        "qa_contact", "status_whiteboard", "creation_ts",
-                       "groupset") {
+                       "groupset", "delta_ts") {
 	$bug{$field} = shift @row;
 	if (!defined $bug{$field}) {
 	    $bug{$field} = "";
@@ -149,18 +153,30 @@ if (@row = FetchSQLData()) {
 	$count++;
     }
 } else {
-    my $maintainer = Param("maintainer");
-    print "<TITLE>Bug Splat Error</TITLE>\n";
-    print "<H1>Query Error</H1>Somehow something went wrong.  Possibly if\n";
-    print "you mail this page to $maintainer, he will be able to fix\n";
-    print "things.<HR>\n";
-    print "Bug $::FORM{'id'} not found<H2>Query Text</H2><PRE>$query<PRE>\n";
-    exit 0
+    SendSQL("select groupset from bugs where bug_id = $id");
+    if (@row = FetchSQLData()) {
+        print "<H1>Permission denied.</H1>\n";
+        if ($loginok) {
+            print "Sorry; you do not have the permissions necessary to see\n";
+            print "bug $id.\n";
+        } else {
+            print "Sorry; bug $id can only be viewed when logged\n";
+            print "into an account with the appropriate permissions.  To\n";
+            print "see this bug, you must first\n";
+            print "<a href=\"show_bug.cgi?id=$id&GoAheadAndLogIn=1\">";
+            print "log in</a>.";
+        }
+    } else {
+        print "<H1>Bug not found</H1>\n";
+        print "There does not seem to be a bug numbered $id.\n";
+    }
+    exit;
 }
 
 $bug{'assigned_to'} = DBID_to_name($bug{'assigned_to'});
 $bug{'reporter'} = DBID_to_name($bug{'reporter'});
-$bug{'long_desc'} = GetLongDescription($::FORM{'id'});
+$bug{'long_desc'} = GetLongDescription($id);
+my $longdesclength = length($bug{'long_desc'});
 
 
 GetVersionTable();
@@ -180,7 +196,7 @@ my $component_popup = make_options($::components{$bug{'product'}},
 				   $bug{'component'});
 
 my $cc_element = '<INPUT NAME=cc SIZE=30 VALUE="' .
-    ShowCcList($::FORM{'id'}) . '">';
+    ShowCcList($id) . '">';
 
 
 my $URL = $bug{'bug_file_loc'};
@@ -192,10 +208,10 @@ if (defined $URL && $URL ne "none" && $URL ne "NULL" && $URL ne "") {
 }
 
 print "
-<HEAD><TITLE>Bug $::FORM{'id'} -- " . html_quote($bug{'short_desc'}) .
-    "</TITLE></HEAD><BODY>
 <FORM NAME=changeform METHOD=POST ACTION=\"process_bug.cgi\">
-<INPUT TYPE=HIDDEN NAME=\"id\" VALUE=$::FORM{'id'}>
+<INPUT TYPE=HIDDEN NAME=\"delta_ts\" VALUE=\"$bug{'delta_ts'}\">
+<INPUT TYPE=HIDDEN NAME=\"longdesclength\" VALUE=\"$longdesclength\">
+<INPUT TYPE=HIDDEN NAME=\"id\" VALUE=$id>
 <INPUT TYPE=HIDDEN NAME=\"was_assigned_to\" VALUE=\"$bug{'assigned_to'}\">
   <TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0><TR>
     <TD ALIGN=RIGHT><B>Bug#:</B></TD><TD><A HREF=\"show_bug.cgi?id=$bug{'bug_id'}\">$bug{'bug_id'}</A></TD>
@@ -295,22 +311,64 @@ if (Param("usestatuswhiteboard")) {
 }
 
 print "<tr><td align=right><B>Attachments:</b></td>\n";
-SendSQL("select attach_id, creation_ts, description from attachments where bug_id = $::FORM{'id'}");
+SendSQL("select attach_id, creation_ts, description from attachments where bug_id = $id");
 while (MoreSQLData()) {
-    my ($id, $date, $desc) = (FetchSQLData());
+    my ($attachid, $date, $desc) = (FetchSQLData());
     if ($date =~ /^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/) {
         $date = "$3/$4/$2 $5:$6";
     }
-    my $link = "showattachment.cgi?attach_id=$id";
+    my $link = "showattachment.cgi?attach_id=$attachid";
     $desc = value_quote($desc);
     print qq{<td><a href="$link">$date</a></td><td colspan=4>$desc</td></tr><tr><td></td>};
-    $knownattachments{$id} = 1;
+    $knownattachments{$attachid} = 1;
+}
+print "<td colspan=6><a href=\"createattachment.cgi?id=$id\">Create a new attachment</a> (proposed patch, testcase, etc.)</td></tr></table>\n";
+
+
+sub EmitDependList {
+    my ($desc, $myfield, $targetfield) = (@_);
+    print "<th align=right>$desc:</th><td>";
+    my @list;
+    SendSQL("select dependencies.$targetfield, bugs.bug_status
+ from dependencies, bugs
+ where dependencies.$myfield = $id
+   and bugs.bug_id = dependencies.$targetfield
+ order by dependencies.$targetfield");
+    while (MoreSQLData()) {
+        my ($i, $stat) = (FetchSQLData());
+        push(@list, $i);
+        my $opened = ($stat eq "NEW" || $stat eq "ASSIGNED" ||
+                      $stat eq "REOPENED");
+        if (!$opened) {
+            print "<strike>";
+        }
+        print qq{<a href="show_bug.cgi?id=$i">$i</a>};
+        if (!$opened) {
+            print "</strike>";
+        }
+        print " ";
+    }
+    print "</td><td><input name=$targetfield value=\"" .
+        join(',', @list) . "\"></td>\n";
 }
-print "<td colspan=6><a href=createattachment.cgi?id=$::FORM{'id'}>Create a new attachment</a> (proposed patch, testcase, etc.)</td></tr>\n";
 
+if (Param("usedependencies")) {
+    print "<table><tr>\n";
+    EmitDependList("Bugs that bug $id depends on", "blocked", "dependson");
+    print qq{
+<td rowspan=2><a href="showdependencytree.cgi?id=$id">Show dependency tree</a>
+};
+    if (Param("webdotbase") ne "") {
+        print qq{
+<br><a href="showdependencygraph.cgi?id=$id">Show dependency graph</a>
+};
+    }
+    print "</td></tr><tr>";
+    EmitDependList("Bugs depending on bug $id", "dependson", "blocked");
+    print "</tr></table>\n";
+}
 
 print "
-</TABLE>
 <br>
 <B>Additional Comments:</B>
 <BR>
@@ -364,7 +422,7 @@ if ($status eq "NEW" || $status eq "ASSIGNED" || $status eq "REOPENED") {
         Resolve bug, mark it as duplicate of bug # 
         <INPUT NAME=dup_id SIZE=6 ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\"><br>\n";
     $knum++;
-    my $assign_element = "<INPUT NAME=assigned_to SIZE=32 ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\" VALUE=$bug{'assigned_to'}>";
+    my $assign_element = "<INPUT NAME=\"assigned_to\" SIZE=32 ONCHANGE=\"document.changeform.knob\[$knum\].checked=true\" VALUE=\"$bug{'assigned_to'}\">";
 
     print "<INPUT TYPE=radio NAME=knob VALUE=reassign> 
           <A HREF=\"bug_status.html#assigned_to\">Reassign</A> bug to
@@ -395,11 +453,11 @@ print "
 <INPUT TYPE=hidden name=form_name VALUE=process_bug>
 <BR>
 <FONT size=\"+1\"><B>
- <A HREF=\"show_activity.cgi?id=$::FORM{'id'}\">View Bug Activity</A>
- <A HREF=\"long_list.cgi?buglist=$::FORM{'id'}\">Format For Printing</A>
+ <A HREF=\"show_activity.cgi?id=$id\">View Bug Activity</A>
+ <A HREF=\"long_list.cgi?buglist=$id\">Format For Printing</A>
 </B></FONT><BR>
 </FORM>
-<table><tr><td align=left><B>Description:</B></td><td width=100%>&nbsp;</td>
+<table><tr><td align=left><B>Description:</B></td><td width=\"100%\">&nbsp;</td>
 <td align=right>Opened:&nbsp;$bug{'creation_ts'}</td></tr></table>
 <HR>
 <PRE>
@@ -411,11 +469,8 @@ print "
 
 # To add back option of editing the long description, insert after the above
 # long_list.cgi line:
-#  <A HREF=\"edit_desc.cgi?id=$::FORM{'id'}\">Edit Long Description</A>
-
+#  <A HREF=\"edit_desc.cgi?id=$id\">Edit Long Description</A>
 
 navigation_header();
 
-print "</BODY>\n";
-
 1;
diff --git a/bug_status.html b/bug_status.html
index a3706dffcdb430ad4d09639e5e3a7511eba800c8..3fd38c60e735e24b704ac5f5826104116e9b4a64 100755
--- a/bug_status.html
+++ b/bug_status.html
@@ -117,6 +117,7 @@ This field describes the impact of a bug.
 <p>
  
 <table>
+<tr><th>Blocker</th><td>Blocks development and/or testing work
 <tr><th>Critical</th><td>crashes, loss of data, severe memory leak 
 <tr><th>Major</th><td>major loss of function 
 <tr><th>Minor</th><td>minor loss of function, or other problem where easy workaround is present 
@@ -144,21 +145,6 @@ prioritized their work to be done.  The available priorities are:
 </table>
 </tr></table>
 
-
-<a name="area"><h2>Area</h2></a>
-This is the general area which is covered by the bug report.  This allows
-bugs to migrate over to testing, but not show up on the "daily bug list".
-Most bugs should have area set to <B>CODE</B>.  Legal values include:
-<UL>
-<LI> CODE
-<LI> JAVA
-<LI> TEST
-<LI> UI
-<LI> BUILD
-<LI> PERF
-<LI> i18n <i>(internationalization)</i>
-<LI> l10n <i>(localization)</i>
-</UL>
 <a name="rep_platform"><h2>Platform</h2></a>
 This is the hardware platform against which the bug was reported.  Legal
 platforms include:
@@ -194,10 +180,6 @@ This is the person in charge of resolving the bug.  Every time this
 field changes, the status changes to <B>NEW</B> to make it easy to see
 which new bugs have appeared on a person's list.
 
-<p><A HREF="http://www.mozilla.org/owners.html">List of module owners.</a>
-<p>
-
-
 The default status for queries is set to NEW, ASSIGNED and REOPENED. When
 searching for bugs that have been resolved or verified, remember to set the
 status field appropriately. 
@@ -205,6 +187,6 @@ status field appropriately.
 <hr>
 <address><a href="http://home.netscape.com/people/terry/">Terry Weissman &lt;terry@netscape.com&gt;</a></address>
 <!-- hhmts start -->
-Last modified: Mon Mar  8 18:31:07 1999
+Last modified: Tue May 11 21:34:56 1999
 <!-- hhmts end -->
 </body> </html>
diff --git a/buglist.cgi b/buglist.cgi
index a8c10d1c29a9c52a980404c247d4bc2d2eb5f074..07e05900fa68ef3f25260b377b9906106bfae242 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -25,21 +25,6 @@ use strict;
 require "CGI.pl";
 use Date::Parse;
 
-my $serverpush = 1;
-
-if ($ENV{'HTTP_USER_AGENT'} =~ /MSIE/) {
-    # Internet explorer doesn't seem to understand server push.  What fun.
-    $serverpush = 0;
-}
-
-if ($serverpush) {
-    print "Content-type: multipart/x-mixed-replace;boundary=thisrandomstring\n";
-    print "\n";
-    print "--thisrandomstring\n";
-}
-
-# Shut up misguided -w warnings about "used only once":
-
 use vars @::legal_platform,
     @::versions,
     @::legal_product,
@@ -55,12 +40,12 @@ use vars @::legal_platform,
 
 ConnectToDatabase();
 
+
 if (!defined $::FORM{'cmdtype'}) {
     # This can happen if there's an old bookmark to a query...
     $::FORM{'cmdtype'} = 'doit';
 }
 
-
 CMD: for ($::FORM{'cmdtype'}) {
     /^runnamed$/ && do {
         $::buffer = $::COOKIE{"QUERY_" . $::FORM{"namedcmd"}};
@@ -73,7 +58,8 @@ CMD: for ($::FORM{'cmdtype'}) {
 Refresh: 0; URL=$url
 
 <TITLE>What a hack.</TITLE>
-Loading your query named <B>$::FORM{'namedcmd'}</B>...";
+Loading your query named <B>$::FORM{'namedcmd'}</B>...
+";
         exit;
     };
     /^forgetnamed$/ && do {
@@ -84,7 +70,8 @@ Content-type: text/html
 <TITLE>Forget what?</TITLE>
 OK, the <B>$::FORM{'namedcmd'}</B> query is gone.
 <P>
-<A HREF=query.cgi>Go back to the query page.</A>";
+<A HREF=query.cgi>Go back to the query page.</A>
+";
         exit;
     };
     /^asnamed$/ && do {
@@ -98,7 +85,8 @@ OK, you now have a new query named <B>$::FORM{'newqueryname'}</B>.
 
 <P>
 
-<A HREF=query.cgi>Go back to the query page.</A>";
+<A HREF=query.cgi>Go back to the query page.</A>
+";
         } else {
             print "Content-type: text/html
 
@@ -107,7 +95,8 @@ OK, you now have a new query named <B>$::FORM{'newqueryname'}</B>.
 Query names can only have letters, digits, spaces, or underbars.  You entered 
 \"<B>$::FORM{'newqueryname'}</B>\", which doesn't cut it.
 <P>
-Click the <B>Back</B> button and type in a valid name for this query.";
+Click the <B>Back</B> button and type in a valid name for this query.
+";
         }
         exit;
     };
@@ -117,16 +106,34 @@ Content-type: text/html
 
 <HTML>
 <TITLE>OK, default is set.</TITLE>
-OK, you now have a new default query.
+OK, you now have a new default query.  You may also bookmark the result of any
+individual query.
 
-<P>
-
-<A HREF=query.cgi>Go back to the query page, using the new default.</A>";
+<P><A HREF=query.cgi>Go back to the query page, using the new default.</A>
+";
         exit;
     };
 }
 
 
+my $serverpush = 0;
+if ($ENV{'HTTP_USER_AGENT'} =~ /Mozilla.[3-9]/ && $ENV{'HTTP_USER_AGENT'} !~ /[Cc]ompatible/ ) {
+    # Search for real Netscape 3 and up.  http://www.browsercaps.org used as source of
+    # browsers compatbile with server-push.  It's a Netscape hack, incompatbile
+    # with MSIE and Lynx (at least).  Even Communicator 4.51 has bugs with it,
+    # especially during page reload.
+    $serverpush = 1;
+
+    print "Content-type: multipart/x-mixed-replace;boundary=thisrandomstring\n\n";
+    print "--thisrandomstring\n";
+    print "Content-type: text/html\n\n";
+    print "<p>Please stand by ... <p>\n";
+    # Note! HTML header is complete!
+} else {
+    print "Content-type: text/html\n";
+    # Note! Don't finish HTML header yet!  Only one newline so far!
+}
+
 sub DefCol {
     my ($name, $k, $t, $s, $q) = (@_);
     
@@ -183,11 +190,8 @@ if ($dotweak) {
 }
 
 
-print "Content-type: text/html\n\n";
-
 my $query = "select bugs.bug_id, bugs.groupset";
 
-
 foreach my $c (@collist) {
     if (exists $::needquote{$c}) {
         $query .= ",
@@ -226,12 +230,12 @@ if ((defined $::FORM{'emailcc1'} && $::FORM{'emailcc1'}) ||
 }
 
 if (defined $::FORM{'sql'}) {
-  $query .= "and (\n$::FORM('sql')\n)"
+  $query .= "and (\n$::FORM{'sql'}\n)"
 } else {
   my @legal_fields = ("bug_id", "product", "version", "rep_platform", "op_sys",
                       "bug_status", "resolution", "priority", "bug_severity",
                       "assigned_to", "reporter", "component",
-                      "target_milestone");
+                      "target_milestone", "groupset");
 
   foreach my $field (keys %::FORM) {
       my $or = "";
@@ -314,7 +318,8 @@ foreach my $id ("1", "2") {
         $lead = " or ";
     }
     if (!$foundone) {
-        print "You must specify one or more fields in which to search for <tt>$email</tt>.\n";
+        print "\n\n<P>You must specify one or more fields in which to search for <tt>$email</tt>.\n";
+        print "<P>Please click the <B>Back</B> button and try again.\n";
         exit;
     }
     if ($lead eq " or ") {
@@ -330,12 +335,10 @@ if (defined $::FORM{'changedin'}) {
     my $c = trim($::FORM{'changedin'});
     if ($c ne "") {
         if ($c !~ /^[0-9]*$/) {
-            print "
-The 'changed in last ___ days' field must be a simple number.  You entered 
-\"$c\", which doesn't cut it.
-<P>
-Click the <B>Back</B> button and try again.";
-              exit;
+            print "\n\n<P>The 'changed in last ___ days' field must be a simple ";
+            print "number. You entered \"$c\", which doesn't cut it.";
+            print "<P>Please click the <B>Back</B> button and try again.\n";
+            exit;
         }
         $query .= "and to_days(now()) - to_days(bugs.delta_ts) <= $c ";
     }
@@ -351,7 +354,7 @@ sub SqlifyDate {
     }
     my $date = str2time($str);
     if (!defined $date) {
-        print "The string '<tt>$str</tt>' is not a legal date.\n";
+        print "\n\n<P>The string '<tt>$str</tt>' is not a legal date.\n";
         print "<P>Please click the <B>Back</B> button and try again.\n";
         exit;
     }
@@ -359,7 +362,23 @@ sub SqlifyDate {
 }
 
 
-        
+if (defined $ref) {
+    my $which = lsearch($ref, "[Bug creation]");
+    if ($which >= 0) {
+        splice(@$ref, $which, 1);
+        $query .= "and bugs.creation_ts >= " .
+            SqlifyDate($::FORM{'chfieldfrom'}) . "\n";
+        my $to = $::FORM{'chfieldto'};
+        if (defined $to) {
+            $to = trim($to);
+            if ($to ne "" && $to !~ /^now$/i) {
+                $query .= "and bugs.creation_ts <= " .
+                    SqlifyDate($to) . "\n";
+            }
+        }
+    }
+}        
+
 
 
 if (defined $ref && 0 < @$ref) {
@@ -400,8 +419,12 @@ foreach my $f ("short_desc", "long_desc", "bug_file_loc",
             $s = SqlQuote($s);
             if ($::FORM{$f . "_type"} eq "regexp") {
                 $query .= "and $f regexp $s\n";
-            } else {
+            } elsif ($::FORM{$f . "_type"} eq "notregexp") {
+                $query .= "and $f not regexp $s\n";
+            } elsif ($::FORM{$f . "_type"} eq "casesubstring") {
                 $query .= "and instr($f, $s)\n";
+            } else {
+                $query .= "and instr(lower($f), lower($s))\n";
             }
         }
     }
@@ -433,13 +456,10 @@ if (defined $::FORM{'order'} && $::FORM{'order'} ne "") {
     $query .= $::FORM{'order'};
 }
 
-if ($serverpush) {
-    print "Please stand by ... <p>\n";
-    if (defined $::FORM{'debug'}) {
-        print "<pre>$query</pre>\n";
-    }
-}
 
+if (Param('expectbigqueries')) {
+    SendSQL("set option SQL_BIG_TABLES=1");
+}
 SendSQL($query);
 
 my $count = 0;
@@ -544,30 +564,42 @@ while (@row = FetchSQLData()) {
         pnl "\n";
     }
 }
-
-
 my $buglist = join(":", @bugarray);
 
 
+# This is stupid.  We really really need to move the quip list into the DB!
+my $quip;
+if (open (COMMENTS, "<data/comments")) {
+    my @cdata;
+    while (<COMMENTS>) {
+        push @cdata, $_;
+    }
+    close COMMENTS;
+    $quip = $cdata[int(rand($#cdata + 1))];
+}
+if (!defined $quip) {
+    $quip = "Bugzilla would like to put a random quip here, but nobody has entered any.";
+}
+
+ 
+# We've done all we can without any output.  If we can server push it is time
+# take down the waiting page and put up the real one.       
 if ($serverpush) {
     print "\n";
     print "--thisrandomstring\n";
+    print "Content-type: text/html\n";
+    # Note! HTML header not yet closed
 }
-
-
 my $toolong = 0;
-print "Content-type: text/html\n";
 if (length($buglist) < 4000) {
-    print "Set-Cookie: BUGLIST=$buglist\n";
+    print "Set-Cookie: BUGLIST=$buglist\n\n";
 } else {
-    print "Set-Cookie: BUGLIST=\n";
+    print "Set-Cookie: BUGLIST=\n\n";
     $toolong = 1;
 }
-
-print "\n";
-
 PutHeader("Bug List");
 
+
 print "
 <CENTER>
 <B>" .  time2str("%a %b %e %T %Z %Y", time()) . "</B>";
@@ -581,29 +613,25 @@ if ($toolong) {
     print "Next/Prev/First/Last buttons won't appear.</h2>\n";
 }
 
-# This is stupid.  We really really need to move the quip list into the DB!
-
-my $quip;
-if (open (COMMENTS, "<data/comments")) {
-    my @cdata;
-    while (<COMMENTS>) {
-        push @cdata, $_;
-    }
-    close COMMENTS;
-    $quip = $cdata[int(rand($#cdata + 1))];
-} else {
-    $quip = "Bugzilla would like to put a random quip here, but nobody has entered any.";
-}
-        
-
-print "<HR><I><A HREF=newquip.html>$quip\n";
-print "</I></A></CENTER>\n";
+print "<HR><A HREF=newquip.html><I>$quip</I></A></CENTER>\n";
 print "<HR SIZE=10>$tablestart\n";
 print $::bugl;
 print "</TABLE>\n";
 
 if ($count == 0) {
     print "Zarro Boogs found.\n";
+    # I've been asked to explain this ... way back when, when Netscape released
+    # version 4.0 of its browser, we had a release party.  Naturally, there
+    # had been a big push to try and fix every known bug before the release.
+    # Naturally, that hadn't actually happened.  (This is not unique to
+    # Netscape or to 4.0; the same thing has happened with every software
+    # project I've ever seen.)  Anyway, at the release party, T-shirts were
+    # handed out that said something like "Netscape 4.0: Zarro Boogs".
+    # Just like the software, the T-shirt had no known bugs.  Uh-huh.
+    #
+    # So, when you query for a list of bugs, and it gets no results, you
+    # can think of this as a friendly reminder.  Of *course* there are bugs
+    # matching your query, they just aren't in the bugsystem yet...
 } elsif ($count == 1) {
     print "One bug found.\n";
 } else {
@@ -743,7 +771,7 @@ if ($::usergroupset ne '0' && $buggroupset =~ /^\d*$/) {
         $knum++;
     }
     my @statuskeys = keys %statushash;
-    if ($#statuskeys == 1) {
+    if (1 == @statuskeys) {
         if (defined $statushash{'RESOLVED'}) {
             print "
 <INPUT TYPE=radio NAME=knob VALUE=verify>
@@ -788,12 +816,13 @@ if ($count > 0) {
 <INPUT TYPE=HIDDEN NAME=buglist VALUE=$buglist>
 <INPUT TYPE=SUBMIT VALUE=\"Long Format\">
 <A HREF=\"query.cgi\">Query Page</A>
-&nbsp;&nbsp;&nbsp;<A HREF=\"enter_bug.cgi\">Enter New Bug</A>
-&nbsp;&nbsp;&nbsp;<A HREF=\"colchange.cgi?$::buffer\">Change columns</A>
-</FORM>";
+&nbsp;&nbsp;<A HREF=\"enter_bug.cgi\">Enter New Bug</A>
+&nbsp;&nbsp;<A HREF=\"colchange.cgi?$::buffer\">Change columns</A>";
     if (!$dotweak && $count > 1) {
-        print "<A HREF=\"buglist.cgi?$fields&tweak=1\">Make changes to several of these bugs at once.</A>\n";
+        print "&nbsp;&nbsp;<A HREF=\"buglist.cgi?$fields&tweak=1\">";
+        print "Change several bugs at once</A>\n";
     }
+    print "</FORM>\n";
 }
 if ($serverpush) {
     print "\n--thisrandomstring--\n";
diff --git a/changepassword.cgi b/changepassword.cgi
index a3a17e39a3d54b47bd5fb449f1b658950f850b18..068180fbddd8b71254c299ceb9fad36fd4c55d39 100755
--- a/changepassword.cgi
+++ b/changepassword.cgi
@@ -23,28 +23,73 @@ require "CGI.pl";
 
 confirm_login();
 
-if (! defined $::FORM{'pwd1'}) {
-    print "Content-type: text/html
+print "Content-type: text/html\n\n";
 
-<H1>Change your password</H1>
+if (! defined $::FORM{'pwd1'}) {
+    PutHeader("Preferences", "Change your password and<br>other preferences",
+              $::COOKIE{'Bugzilla_login'});
+
+    my $qacontactpart = "";
+    if (Param('useqacontact')) {
+        $qacontactpart = ", the current QA Contact";
+    }
+    my $loginname = SqlQuote($::COOKIE{'Bugzilla_login'});
+    SendSQL("select emailnotification,realname from profiles where login_name = " .
+            $loginname);
+    my ($emailnotification, $realname) = (FetchSQLData());
+    $realname = value_quote($realname);
+    print qq{
 <form method=post>
+<hr>
 <table>
 <tr>
 <td align=right>Please enter the new password for <b>$::COOKIE{'Bugzilla_login'}</b>:</td>
-<td><input type=password name=pwd1></td>
+<td><input type=password name="pwd1"></td>
 </tr>
 <tr>
 <td align=right>Re-enter your new password:</td>
-<td><input type=password name=pwd2></td>
+<td><input type=password name="pwd2"></td>
+</tr>
+<tr>
+<td align=right>Your real name (optional):</td>
+<td><input size=35 name=realname value="$realname"></td>
+</tr>
+</table>
+<hr>
+<table>
+<tr>
+<td align=right>Bugzilla will send out email notification of changed bugs to 
+the current owner, the submitter of the bug$qacontactpart, and anyone on the
+CC list.  However, you can suppress some of those email notifications.
+On which of these bugs would you like email notification of changes?</td>
+<td><SELECT NAME="emailnotification">
+};
+    foreach my $i (["ExcludeSelfChanges", "All qualifying bugs except those which I change"],
+                   ["CConly", "Only those bugs which I am listed on the CC line"],
+                   ["All", "All qualifying bugs"]) {
+        my ($tag, $desc) = (@$i);
+        my $selectpart = "";
+        if ($tag eq $emailnotification) {
+            $selectpart = " SELECTED";
+        }
+        print qq{<OPTION$selectpart VALUE="$tag">$desc\n};
+    }
+    print "
+</SELECT>
+</td>
+</tr>
 </table>
-<input type=submit value=Submit>\n";
+<hr>
+<input type=submit value=Submit>
+</form>
+<hr>
+";
+    navigation_header();
     exit;
 }
 
 if ($::FORM{'pwd1'} ne $::FORM{'pwd2'}) {
-    print "Content-type: text/html
-
-<H1>Try again.</H1>
+    print "<H1>Try again.</H1>
 The two passwords you entered did not match.  Please click <b>Back</b> and try again.\n";
     exit;
 }
@@ -53,36 +98,49 @@ The two passwords you entered did not match.  Please click <b>Back</b> and try a
 my $pwd = $::FORM{'pwd1'};
 
 
-if ($pwd !~ /^[a-zA-Z0-9-_]*$/ || length($pwd) < 3 || length($pwd) > 15) {
-    print "Content-type: text/html
+sub x {
+    my $sc="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
+    return substr($sc, int (rand () * 100000) % (length ($sc) + 1), 1);
+}
 
-<H1>Sorry; we're picky.</H1>
+if ($pwd ne "") {
+    if ($pwd !~ /^[a-zA-Z0-9-_]*$/ || length($pwd) < 3 || length($pwd) > 15) {
+        print "<H1>Sorry; we're picky.</H1>
 Please choose a password that is between 3 and 15 characters long, and that
 contains only numbers, letters, hyphens, or underlines.
 <p>
 Please click <b>Back</b> and try again.\n";
-    exit;
+        exit;
+    }
+    
+    
+# Generate a random salt.
+    
+    my $salt  = x() . x();
+    
+    my $encrypted = crypt($pwd, $salt);
+    
+    SendSQL("update profiles set password='$pwd',cryptpassword='$encrypted' where login_name=" .
+            SqlQuote($::COOKIE{'Bugzilla_login'}));
+    
+    SendSQL("update logincookies set cryptpassword = '$encrypted' where cookie = $::COOKIE{'Bugzilla_logincookie'}");
 }
 
 
-print "Content-type: text/html\n\n";
+SendSQL("update profiles set emailnotification='$::FORM{'emailnotification'}' where login_name = " .
+        SqlQuote($::COOKIE{'Bugzilla_login'}));
 
-# Generate a random salt.
+my $newrealname = $::FORM{'realname'};
 
-sub x {
-    my $sc="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
-    return substr($sc, int (rand () * 100000) % (length ($sc) + 1), 1);
+if ($newrealname ne "") {
+    $newrealname = SqlQuote($newrealname);
+    SendSQL("update profiles set realname=$newrealname where login_name = " .
+            SqlQuote($::COOKIE{'Bugzilla_login'}));
 }
-my $salt  = x() . x();
-
-my $encrypted = crypt($pwd, $salt);
 
-SendSQL("update profiles set password='$pwd',cryptpassword='$encrypted' where login_name=" .
-        SqlQuote($::COOKIE{'Bugzilla_login'}));
-
-SendSQL("update logincookies set cryptpassword = '$encrypted' where cookie = $::COOKIE{'Bugzilla_logincookie'}");
+PutHeader("Preferences updated.");
+print "
+Your preferences have been updated.
+<p>";
+navigation_header();
 
-print "<H1>OK, done.</H1>
-Your new password has been set.
-<p>
-<a href=query.cgi>Back to query page.</a>\n";
diff --git a/collectstats.pl b/collectstats.pl
index e35c7aa239749a865504b7ed2e3e0995c13a3595..dc8306715ba5226f51d926c0ab72d0ccd7a82469 100755
--- a/collectstats.pl
+++ b/collectstats.pl
@@ -31,7 +31,10 @@ require "globals.pl";
 ConnectToDatabase();
 GetVersionTable();
 
-foreach (@::legal_product) {
+my @myproducts;
+push( @myproducts, "-All-", @::legal_product );
+
+foreach (@myproducts) {
     my $dir = "data/mining";
 
     &check_data_dir ($dir);
@@ -61,7 +64,11 @@ sub collect_stats {
         push my @row, &today;
 
         foreach my $status ('NEW', 'ASSIGNED', 'REOPENED') {
-            SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'");
+	    if( $product eq "-All-" ) {
+                SendSQL("select count(bug_status) from bugs where bug_status='$status'");
+	    } else {
+                SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'");
+	    }
             push @row, FetchOneColumn();
         }
 
diff --git a/createaccount.cgi b/createaccount.cgi
index c6bef90cfe7803a89e5746214ad9b86243ab054a..cc9bdd018e892cd64e0f3655c63f6e57cb0c0a4c 100755
--- a/createaccount.cgi
+++ b/createaccount.cgi
@@ -44,6 +44,7 @@ Content-type: text/html
 PutHeader("Create a new bugzilla account");
 
 my $login = $::FORM{'login'};
+my $realname = $::FORM{'realname'};
 if (defined $login) {
     CheckEmailSyntax($login);
     if (DBname_to_id($login) != 0) {
@@ -53,7 +54,8 @@ if (defined $login) {
 	print "the <b>E-mail me a password</b> button.\n";
 	exit;
     }
-    DBNameToIdAndCheck($login, 1);
+    my $password = InsertNewUser($login, $realname);
+    MailPassword($login, $password);
     print "A bugzilla account for <tt>$login</tt> has been created.  The\n";
     print "password has been e-mailed to that address.  When it is\n";
     print "received, you may <a href=query.cgi?GoAheadAndLogIn>click\n";
@@ -65,7 +67,8 @@ if (defined $login) {
 print q{
 To create a bugzilla account, all that you need to do is to enter a
 legitimate e-mail address.  The account will be created, and its
-password will be mailed to you.
+password will be mailed to you. Optionally you may enter your real name 
+as well.
 
 <FORM method=get>
 <table>
@@ -73,6 +76,10 @@ password will be mailed to you.
 <td align=right><b>E-mail address:</b></td>
 <td><input size=35 name=login></td>
 </tr>
+<tr>
+<td align=right><b>Real name:</b></td>
+<td><input size=35 name=realname></td>
+</tr>
 </table>
 <input type=submit>
 };
diff --git a/createattachment.cgi b/createattachment.cgi
index 80dff3520b823ff3ef3c636a732b325964bf367c..b638277075673f8aca094780aa166e029b358ef3 100755
--- a/createattachment.cgi
+++ b/createattachment.cgi
@@ -58,6 +58,9 @@ What kind of file is this?
 <br><input type=radio name=type value=patch>Patch file (text/plain, diffs)
 <br><input type=radio name=type value="text/plain">Plain text (text/plain)
 <br><input type=radio name=type value="text/html">HTML source (text/html)
+<br><input type=radio name=type value="image/gif">GIF Image (image/gif)
+<br><input type=radio name=type value="image/jpeg">JPEG Image (image/jpeg)
+<br><input type=radio name=type value="image/png">PNG Image (image/png)
 <br><input type=radio name=type value="application/octet-stream">Binary file (application/octet-stream)
 <br><input type=radio name=type value="other">Other (enter mime type: <input name=othertype size=30>)
 <P>
@@ -89,24 +92,20 @@ What kind of file is this?
     if ($mimetype !~ m@^(\w|-)+/(\w|-)+$@) {
         Punt("You must select a legal mime type.  '<tt>$mimetype</tt>' simply will not do.");
     }
-    SendSQL("insert into attachments (bug_id, filename, description, mimetype, ispatch, thedata) values ($id," .
+    SendSQL("insert into attachments (bug_id, filename, description, mimetype, ispatch, submitter_id, thedata) values ($id," .
             SqlQuote($::FILENAME{'data'}) . ", " . SqlQuote($desc) . ", " .
             SqlQuote($mimetype) . ", $ispatch, " .
+            DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'}) . ", " .
             SqlQuote($::FORM{'data'}) . ")");
     SendSQL("select LAST_INSERT_ID()");
     my $attachid = FetchOneColumn();
     AppendComment($id, $::COOKIE{"Bugzilla_login"},
                   "Created an attachment (id=$attachid)\n$desc\n");
-    print "Your attachment has been created.";
-    system("./processmail $id < /dev/null > /dev/null 2> /dev/null &");
-
 
+    print "<TABLE BORDER=1><TD><H2>Attachment to bug $id created</H2>\n";
+    system("./processmail $id $::COOKIE{'Bugzilla_login'}");
+    print "<TD><A HREF=\"show_bug.cgi?id=$id\">Go Back to BUG# $id</A></TABLE>\n";
 }
 
+navigation_header();
 
-
-print qq{
-<P>
-<a href="show_bug.cgi?id=$id">Go back to bug $id</a><br>
-<a href="query.cgi">Go back to the query page</a><br>
-};
diff --git a/defparams.pl b/defparams.pl
index 9bcc9a56738cd330bcdef61d2a7f0dadcddbfc15..da8cf2afa4e61217dc102ab89bbf7251dbf8d057 100644
--- a/defparams.pl
+++ b/defparams.pl
@@ -17,6 +17,7 @@
 # Netscape Communications Corporation. All Rights Reserved.
 # 
 # Contributor(s): Terry Weissman <terry@mozilla.org>
+#                 Dawn Endico <endico@mozilla.org>
 
 
 # This file defines all the parameters that we have a GUI to edit within
@@ -154,8 +155,8 @@ DefParam("blurbhtml",
          "l",
          "This is <B>Bugzilla</B>: the Mozilla bug system.  For more 
 information about what Bugzilla is and what it can do, see 
-<A HREF=http://www.mozilla.org/>mozilla.org</A>'s
-<A HREF=http://www.mozilla.org/bugs/><B>bug pages</B></A>.");
+<A HREF=\"http://www.mozilla.org/\">mozilla.org</A>'s
+<A HREF=\"http://www.mozilla.org/bugs/\"><B>bug pages</B></A>.");
 
 
 
@@ -238,7 +239,7 @@ You will get this message once a day until you've dealt with these bugs!
 DefParam("defaultquery",
  	 "This is the default query that initially comes up when you submit a bug.  It's in URL parameter format, which makes it hard to read.  Sorry!",
 	 "t",
-	 "bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=Mozilla&order=%22Importance%22");
+	 "bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=%22Importance%22");
 
 
 DefParam("letsubmitterchoosepriority",
@@ -276,6 +277,12 @@ DefParam("nummilestones",
          10,
          \&check_numeric);
 
+DefParam("curmilestone",
+         "If using Target Milestone, Which milestone are we working toward right now?",
+         "t",
+         1,
+         \&check_numeric);
+
 DefParam("useqacontact",
 	 "Do you wish to use the QA Contact field?",
 	 "b",
@@ -286,7 +293,46 @@ DefParam("usestatuswhiteboard",
 	 "b",
 	 0);
 
-	 
+DefParam("usebrowserinfo",
+	 "Do you want bug reports to be assigned an OS & Platform based on the browser
+	  the user makes the report from?",
+	 "b",
+	 1);
+
+DefParam("usedependencies",
+         "Do you wish to use dependencies (allowing you to mark which bugs depend on which other ones)?",
+         "b",
+         1);
+
+DefParam("webdotbase",
+         "This is the URL prefix that is common to all requests for webdot.  The <a href=\"http://www.research.att.com/~north/cgi-bin/webdot.cgi\">webdot package</a> is a very swell thing that generates pictures of graphs.  If you have an installation of bugsplat that hides behind a firewall, then to get graphs to work, you will have to install a copy of webdot behind your firewall, and change this path to match.  Also, webdot has some trouble with software domain names, so you may have to play games and hack the %urlbase% part of this.  If this all seems like too much trouble, you can set this paramater to be the empty string, which will cause the graphing feature to be disabled entirely.",
+         "t",
+         "http://www.research.att.com/~north/cgi-bin/webdot.cgi/%urlbase%");
+
+DefParam("entryheaderhtml",
+         "This is a special header for the bug entry page. The text will be printed after the page header, before the bug entry form. It is meant to be a place to put pointers to intructions on how to enter bugs.",
+         "l",
+         '');
+
+DefParam("expectbigqueries",
+         "If this is on, then we will tell mysql to <tt>set option SQL_BIG_TABLES=1</tt> before doing queries on bugs.  This will be a little slower, but one will not get the error <tt>The table ### is full</tt> for big queries that require a big temporary table.",
+         "b",
+         0);
+
+DefParam("emailregexp",
+         'This defines the regexp to use for legal email addresses.  The default tries to match fully qualified email addresses.  Another popular value to put here is <tt>^[^@, ]$</tt>, which means "local usernames, no @ allowed.',
+         "t",
+         q:^[^@, ]*@[^@, ]*\\.[^@, ]*$:);
+
+DefParam("emailregexpdesc",
+         "This describes in english words what kinds of legal addresses are allowed by the <tt>emailregexp</tt> param.",
+         "l",
+         "A legal address must contain exactly one '\@', and at least one '.' after the \@, and may not contain any commas or spaces.");
+
+DefParam("emailsuffix",
+         "This is a string to append to any email addresses when actually sending mail to that address.  It is useful if you have changed the <tt>emailregexp</tt> param to only allow local usernames, but you want the mail to be delivered to username\@my.local.hostname.",
+         "t",
+         "");
 
 
 1;
diff --git a/describecomponents.cgi b/describecomponents.cgi
index bce7577d24ab76940f84aa54fdfcda33f7eab3c2..ad3467d62480b84ae6f6690c2d02b504e8001f80 100755
--- a/describecomponents.cgi
+++ b/describecomponents.cgi
@@ -74,7 +74,7 @@ my $colbut1 = $cols - 1;
 
 print "</tr>";
 
-SendSQL("select value, initialowner, initialqacontact, description from components where program = " . SqlQuote($product));
+SendSQL("select value, initialowner, initialqacontact, description from components where program = " . SqlQuote($product) . " order by value");
 
 while (MoreSQLData()) {
     my @row = FetchSQLData();
diff --git a/doaddcomponent.cgi b/doaddcomponent.cgi
index c7856fff1124eb87f2ba34be5419a8b0e6a41d9b..f6a9675962458caa66a7caea80446b9cdc26c536 100755
--- a/doaddcomponent.cgi
+++ b/doaddcomponent.cgi
@@ -53,6 +53,11 @@ my $component = trim($::FORM{"component"});
 my $product = trim($::FORM{"product"});
 my $description = trim($::FORM{"description"});
 my $initialowner = trim($::FORM{"initialowner"});
+
+if (!defined $::FORM{"initialqacontact"}) {
+    # May not be defined if we're not using this field.
+    $::FORM{'initialqacontact'} = "";
+}
 my $initialqacontact = trim($::FORM{"initialqacontact"});
 
 if ($component eq "") {
diff --git a/enter_bug.cgi b/enter_bug.cgi
index 83792e851aa22b1ea15dc562a30d3d087d357e4a..4459148fac0e7d8d4b78bb0372087ea7d5947986 100755
--- a/enter_bug.cgi
+++ b/enter_bug.cgi
@@ -26,7 +26,6 @@ require "CGI.pl";
 
 # Shut up misguided -w warnings about "used only once":
 use vars @::legal_platform,
-    @::buffer,
     @::legal_severity,
     @::legal_opsys,
     @::legal_priority;
@@ -49,7 +48,7 @@ if (!defined $::FORM{'product'}) {
                 # to allow people to specify that product here.
                 next;
             }
-            print "<tr><th align=right valign=top><a href=\"enter_bug.cgi?product=" . url_quote($p) . "\"&$::buffer>$p</a>:</th>\n";
+            print "<tr><th align=right valign=top><a href=\"enter_bug.cgi?product=" . url_quote($p) . "\">$p</a>:</th>\n";
             if (defined $::proddesc{$p}) {
                 print "<td valign=top>$::proddesc{$p}</td>\n";
             }
@@ -83,28 +82,33 @@ sub pickplatform {
     if ($value ne "") {
         return $value;
     }
-    for ($ENV{'HTTP_USER_AGENT'}) {
-        /Mozilla.*\(Windows/ && do {return "PC";};
-        /Mozilla.*\(Macintosh/ && do {return "Macintosh";};
-        /Mozilla.*\(Win/ && do {return "PC";};
-        /Mozilla.*Linux.*86/ && do {return "PC";};
-        /Mozilla.*Linux.*alpha/ && do {return "DEC";};
-        /Mozilla.*OSF/ && do {return "DEC";};
-        /Mozilla.*HP-UX/ && do {return "HP";};
-        /Mozilla.*IRIX/ && do {return "SGI";};
-        /Mozilla.*(SunOS|Solaris)/ && do {return "Sun";};
-        # default
-        return "Other";
+    if ( Param('usebrowserinfo') ) {
+        for ($ENV{'HTTP_USER_AGENT'}) {
+            /Mozilla.*\(Windows/ && do {return "PC";};
+            /Mozilla.*\(Macintosh/ && do {return "Macintosh";};
+            /Mozilla.*\(Win/ && do {return "PC";};
+            /Mozilla.*Linux.*86/ && do {return "PC";};
+            /Mozilla.*Linux.*alpha/ && do {return "DEC";};
+            /Mozilla.*OSF/ && do {return "DEC";};
+            /Mozilla.*HP-UX/ && do {return "HP";};
+            /Mozilla.*IRIX/ && do {return "SGI";};
+            /Mozilla.*(SunOS|Solaris)/ && do {return "Sun";};
+        }
     }
+    # default
+    return "Other";
 }
 
 
 
 sub pickversion {
     my $version = formvalue('version');
-    if ($version eq "") {
-        if ($ENV{'HTTP_USER_AGENT'} =~ m@Mozilla[ /]([^ ]*)@) {
-            $version = $1;
+
+    if ( Param('usebrowserinfo') ) {
+        if ($version eq "") {
+            if ($ENV{'HTTP_USER_AGENT'} =~ m@Mozilla[ /]([^ ]*)@) {
+                $version = $1;
+            }
         }
     }
     
@@ -135,24 +139,26 @@ sub pickos {
     if (formvalue('op_sys') ne "") {
         return formvalue('op_sys');
     }
-    for ($ENV{'HTTP_USER_AGENT'}) {
-        /Mozilla.*\(.*;.*; IRIX.*\)/    && do {return "IRIX";};
-        /Mozilla.*\(.*;.*; 32bit.*\)/   && do {return "Windows 95";};
-        /Mozilla.*\(.*;.*; 16bit.*\)/   && do {return "Windows 3.1";};
-        /Mozilla.*\(.*;.*; 68K.*\)/     && do {return "Mac System 8.5";};
-        /Mozilla.*\(.*;.*; PPC.*\)/     && do {return "Mac System 8.5";};
-        /Mozilla.*\(.*;.*; OSF.*\)/     && do {return "OSF/1";};
-        /Mozilla.*\(.*;.*; Linux.*\)/   && do {return "Linux";};
-        /Mozilla.*\(.*;.*; SunOS 5.*\)/ && do {return "Solaris";};
-        /Mozilla.*\(.*;.*; SunOS.*\)/   && do {return "SunOS";};
-        /Mozilla.*\(.*;.*; SunOS.*\)/   && do {return "SunOS";};
-        /Mozilla.*\(.*;.*; BSD\/OS.*\)/ && do {return "BSDI";};
-        /Mozilla.*\(Win16.*\)/          && do {return "Windows 3.1";};
-        /Mozilla.*\(Win95.*\)/          && do {return "Windows 95";};
-        /Mozilla.*\(WinNT.*\)/          && do {return "Windows NT";};
-        # default
-        return "other";
+    if ( Param('usebrowserinfo') ) {
+        for ($ENV{'HTTP_USER_AGENT'}) {
+            /Mozilla.*\(.*;.*; IRIX.*\)/    && do {return "IRIX";};
+            /Mozilla.*\(.*;.*; 32bit.*\)/   && do {return "Windows 95";};
+            /Mozilla.*\(.*;.*; 16bit.*\)/   && do {return "Windows 3.1";};
+            /Mozilla.*\(.*;.*; 68K.*\)/     && do {return "Mac System 8.5";};
+            /Mozilla.*\(.*;.*; PPC.*\)/     && do {return "Mac System 8.5";};
+            /Mozilla.*\(.*;.*; OSF.*\)/     && do {return "OSF/1";};
+            /Mozilla.*\(.*;.*; Linux.*\)/   && do {return "Linux";};
+            /Mozilla.*\(.*;.*; SunOS 5.*\)/ && do {return "Solaris";};
+            /Mozilla.*\(.*;.*; SunOS.*\)/   && do {return "SunOS";};
+            /Mozilla.*\(.*;.*; SunOS.*\)/   && do {return "SunOS";};
+            /Mozilla.*\(.*;.*; BSD\/OS.*\)/ && do {return "BSDI";};
+            /Mozilla.*\(Win16.*\)/          && do {return "Windows 3.1";};
+            /Mozilla.*\(Win95.*\)/          && do {return "Windows 95";};
+            /Mozilla.*\(WinNT.*\)/          && do {return "Windows NT";};
+        }
     }
+    # default
+    return "other";
 }
 
 
@@ -179,12 +185,27 @@ my $component_popup = make_popup('component', $::components{$product},
 PutHeader ("Enter Bug");
 
 print "
-<FORM NAME=enterForm METHOD=POST ACTION=\"post_bug.cgi\">
+<FORM METHOD=POST ACTION=\"post_bug.cgi\">
 <INPUT TYPE=HIDDEN NAME=bug_status VALUE=NEW>
-<INPUT TYPE=HIDDEN NAME=reporter VALUE=$::COOKIE{'Bugzilla_login'}>
+<INPUT TYPE=HIDDEN NAME=reporter VALUE=\"$::COOKIE{'Bugzilla_login'}\">
 <INPUT TYPE=HIDDEN NAME=product VALUE=\""  . value_quote($product) . "\">
-  <TABLE CELLSPACING=2 CELLPADDING=0 BORDER=0>
+  <TABLE CELLSPACING=2 CELLPADDING=0 BORDER=0>";
+
+if (Param("entryheaderhtml")){
+  print "
+  <TR>
+    <td></td>
+    <td colspan=3>" .
+  Param("entryheaderhtml") . "\n" .
+  " </td> 
+  </TR>
+  <TR><td><br></td></TR>";
+}
+
+print "
   <TR>
+    <td ALIGN=right valign=top><B>Reporter:</B></td>
+    <td valign=top>$::COOKIE{'Bugzilla_login'}</td>
     <td ALIGN=right valign=top><B>Product:</B></td>
     <td valign=top>$product</td>
   </TR>
@@ -192,12 +213,12 @@ print "
     <td ALIGN=right valign=top><B>Version:</B></td>
     <td>" . Version_element(pickversion(), $product) . "</td>
     <td align=right valign=top><b><a href=\"describecomponents.cgi?product=" .
-    url_quote($product) . "\">Component:</b></td>
+    url_quote($product) . "\">Component:</a></b></td>
     <td>$component_popup</td>
   </TR>
   <tr><td>&nbsp<td> <td> <td> <td> <td> </tr>
   <TR>
-    <td align=right><b><B><A HREF=\"bug_status.html#rep_platform\">Platform:</A></B></td>
+    <td align=right><B><A HREF=\"bug_status.html#rep_platform\">Platform:</A></B></td>
     <TD>$platform_popup</TD>
     <TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#op_sys\">OS:</A></B></TD>
     <TD>$opsys_popup</TD>
@@ -222,13 +243,12 @@ print "
   </TR>
   <tr><td>&nbsp<td> <td> <td> <td> <td> </tr>
   <tr>
-    <TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#assigned_to\">Assigned To:
-        </A></B></TD>
+    <TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#assigned_to\">Assigned To:</A></B></TD>
     <TD colspan=5>$assign_element
-    (Leave blank to assign to default owner for component)</td>
+    (Leave blank to assign to default component owner)</td>
   </tr>
   <tr>
-    <TD ALIGN=RIGHT ><B>Cc:</B></TD>
+    <TD ALIGN=RIGHT><B>Cc:</B></TD>
     <TD colspan=5>$cc_element</TD>
   </tr>
   <tr><td>&nbsp<td> <td> <td> <td> <td> </tr>
@@ -246,9 +266,8 @@ print "
     value_quote(formvalue('short_desc')) .
     "\"></TD>
   </TR>
-  <tr><td>&nbsp<td> <td> <td> <td> <td> </tr>
-  <tr>
-    <td aligh=right valign=top><B>Description:</b>
+  <tr><td align=right valign=top><B>Description:</b></td>
+<!--  </tr> <tr> -->
     <td colspan=5><TEXTAREA WRAP=HARD NAME=comment ROWS=10 COLS=80>" .
     value_quote(formvalue('comment')) .
     "</TEXTAREA><BR></td>
@@ -262,12 +281,24 @@ print "
        &nbsp;&nbsp;&nbsp;&nbsp;
        <INPUT TYPE=\"submit\" NAME=maketemplate VALUE=\"Remember values as bookmarkable template\">
     </td>
-  </tr>
+  </tr>";
+
+if ( Param('usebrowserinfo') ) {
+    print "
+  <tr>
+    <td></td>
+    <td colspan=3>
+     <br>
+     Some fields initialized from your user-agent, 
+     <b>$ENV{'HTTP_USER_AGENT'}</b>.  If you think it got it wrong, 
+     please tell " . Param('maintainer') . " what it should have been.
+    </td>
+  </tr>";
+}
+print "
   </TABLE>
   <INPUT TYPE=hidden name=form_name VALUE=enter_bug>
-</FORM>
+</FORM>\n";
 
-Some fields initialized from your user-agent, <b>$ENV{'HTTP_USER_AGENT'}</b>.
-If you think it got it wrong, please tell " . Param('maintainer') . " what it should have been.
+print "</BODY></HTML>\n";
 
-</BODY></HTML>";
diff --git a/globals.pl b/globals.pl
index ddf2a1f5f9084554089cc0342614cefe080f380e..48dde9d8b8df5dd37801f454ff06eff9262cc31c 100644
--- a/globals.pl
+++ b/globals.pl
@@ -28,7 +28,7 @@ use Date::Format;               # For time2str().
 # use Carp;                       # for confess
 
 # Contains the version string for the current running Bugzilla.
-$::param{'version'} = '2.4';
+$::param{'version'} = '2.6';
 
 $::dontchange = "--do_not_change--";
 $::chooseone = "--Choose_one:--";
@@ -78,13 +78,8 @@ sub FetchOneColumn {
 
 sub AppendComment {
     my ($bugid,$who,$comment) = (@_);
-    open(DEBUG, ">/tmp/debug");
-    print DEBUG "A $comment";
     $comment =~ s/\r\n/\n/g;     # Get rid of windows-style line endings.
-    print DEBUG "B $comment";
     $comment =~ s/\r/\n/g;       # Get rid of mac-style line endings.
-    print DEBUG "C $comment";
-    close DEBUG;
     if ($comment =~ /^\s*$/) {  # Nothin' but whitespace.
         return;
     }
@@ -227,7 +222,7 @@ sub GenerateVersionTable {
         push @{$::versions{$p1}}, $v;
         $varray{$v} = 1;
     }
-    SendSQL("select value, program from components");
+    SendSQL("select value, program from components order by value");
     while (@line = FetchSQLData()) {
         my ($c,$p) = (@line);
         if (!defined $::components{$p}) {
@@ -351,14 +346,14 @@ sub GetVersionTable {
         do 'data/versioncache';
 
         if (!defined %::versions) {
-            die "Can't generate version info; tell terry.";
+            die "Can't generate file data/versioncache";
         }
     }
 }
 
 
 sub InsertNewUser {
-    my ($username) = (@_);
+    my ($username, $realname) = (@_);
     my $password = "";
     for (my $i=0 ; $i<8 ; $i++) {
         $password .= substr("abcdefghijklmnopqrstuvwxyz", int(rand(26)), 1);
@@ -375,7 +370,9 @@ sub InsertNewUser {
         }
     }
             
-    SendSQL("insert into profiles (login_name, password, cryptpassword, groupset) values (@{[SqlQuote($username)]}, '$password', encrypt('$password'), $groupset)");
+    $username = SqlQuote($username);
+    $realname = SqlQuote($realname);
+    SendSQL("insert into profiles (login_name, realname, password, cryptpassword, groupset) values ($username, $realname, '$password', encrypt('$password'), $groupset)");
     return $password;
 }
 
@@ -411,7 +408,7 @@ sub DBNameToIdAndCheck {
         return $result;
     }
     if ($forceok) {
-        InsertNewUser($name);
+        InsertNewUser($name, "");
         $result = DBname_to_id($name);
         if ($result > 0) {
             return $result;
diff --git a/help.html b/help.html
index 0d7244df252ac642e6960e843a5b65df807cd102..4896fbd2a3cc38a18049c83d3ec3b3fc06c3fae9 100644
--- a/help.html
+++ b/help.html
@@ -53,3 +53,10 @@ reporter area
 bug_file_loc
 short_desc
 </PRE>
+<HR>
+<H1>Browser Notes</H1>
+<P>Bugzilla uses several non-standard Netscape extentions, but this does not seem
+to case any problem with other browsers.  The lynx browser does work, but lynx
+seems to cache results of a .cgi.  You'll sometimes need to press CONTROL-R to reload
+the screen to see an update.
+
diff --git a/index.html b/index.html
index 7cef3c1e2cc9e610d0eecfe553c4b688a2d4de3d..e136e93ad355cbe27769e1e1cc596130dcda78ea 100644
--- a/index.html
+++ b/index.html
@@ -1,3 +1,4 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
 <HTML>
 <!--
      The contents of this file are subject to the Mozilla Public License
@@ -55,8 +56,6 @@ LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
 </TD></TR></TABLE>
 
 
-<body>
-
 <img align=right width=329 height=220 src=ant.jpg border=2>
 
 
@@ -67,12 +66,15 @@ bugzilla.
 
 But it all boils down to a choice of:
 <br>
-<a href="query.cgi">Go to the query page to start.</a><br>
-<a href="enter_bug.cgi">Enter a new bug</a><br>
-<a href="reports.cgi">Bug reports</a><br>
+<a href="query.cgi">Query existing bug reports</a><br>
+<a href="enter_bug.cgi">Enter a new bug report</a><br>
+<a href="reports.cgi">Get summary reports</a><br>
+<p>
 <a href="createaccount.cgi">Open a new Bugzilla account</a><br>
-<FORM METHOD=GET ACTION=show_bug.cgi><INPUT TYPE=SUBMIT VALUE="Find"> bug
-# <INPUT NAME=id SIZE=6></FORM></TD>
+<a href="relogin.cgi">Forget the currently stored login</a><br>
+<a href="changepassword.cgi">Change password or user preferences</a><br>
+<FORM METHOD=GET ACTION="show_bug.cgi">
+<INPUT TYPE=SUBMIT VALUE="Find"> bug # <INPUT NAME=id SIZE=6></FORM>
 
 </BODY>
 </HTML>
diff --git a/long_list.cgi b/long_list.cgi
index 723aa2cf37770e2b56b04a5feacd7784682f1aac..69546119fadb18947f4e4ae9aab276556be292e2 100755
--- a/long_list.cgi
+++ b/long_list.cgi
@@ -93,7 +93,8 @@ foreach my $bug (split(/:/, $::FORM{'buglist'})) {
         if (Param("usetargetmilestone")) {
             print "<TD><B>Target milestone:</B>$target_milestone\n";
         }
-        print "<TR><TD COLSPAN=6><B>URL:</B> " . html_quote($url) . "\n";
+        print "<TR><TD COLSPAN=6><B>URL:</B>&nbsp;";
+	print "<A HREF=\"" . $url . "\">" .  html_quote($url) . "</A>\n"; 
         print "<TR><TD COLSPAN=6><B>Summary:</B> " . html_quote($shortdesc) . "\n";
         if (Param("usestatuswhiteboard")) {
             print "<TR><TD COLSPAN=6><B>Status Whiteboard:" .
diff --git a/makeattachmenttable.sh b/makeattachmenttable.sh
index b938626740acf24b0bb2c820f03aac521841b60a..ab6b04b71387384f0840e3ebce246d717d325c68 100755
--- a/makeattachmenttable.sh
+++ b/makeattachmenttable.sh
@@ -37,6 +37,7 @@ mimetype mediumtext not null,
 ispatch tinyint,
 filename mediumtext not null,
 thedata longblob not null,
+submitter_id mediumint not null,
 
 index(bug_id),
 index(creation_ts)
diff --git a/makebugtable.sh b/makebugtable.sh
index bad74010e015e2a19d634fb06f920b19d83eff9a..8c33612f779a7af3cf67f9701ba2397addbf0408 100755
--- a/makebugtable.sh
+++ b/makebugtable.sh
@@ -32,13 +32,13 @@ bug_id mediumint not null auto_increment primary key,
 groupset bigint not null,
 assigned_to mediumint not null, # This is a comment.
 bug_file_loc text,
-bug_severity enum("critical", "major", "normal", "minor", "trivial", "enhancement") not null,
+bug_severity enum("blocker", "critical", "major", "normal", "minor", "trivial", "enhancement") not null,
 bug_status enum("NEW", "ASSIGNED", "REOPENED", "RESOLVED", "VERIFIED", "CLOSED") not null,
-creation_ts datetime,
+creation_ts datetime not null,
 delta_ts timestamp,
 short_desc mediumtext,
 long_desc mediumtext,
-op_sys enum("All", "Windows 3.1", "Windows 95", "Windows 98", "Windows NT", "Mac System 7", "Mac System 7.5", "Mac System 7.6.1", "Mac System 8.0", "Mac System 8.5", "AIX", "BSDI", "HP-UX", "IRIX", "Linux", "FreeBSD", "OSF/1", "Solaris", "SunOS", "OS/2", "other") not null,
+op_sys enum("All", "Windows 3.1", "Windows 95", "Windows 98", "Windows NT", "Mac System 7", "Mac System 7.5", "Mac System 7.6.1", "Mac System 8.0", "Mac System 8.5", "Mac System 8.6", "AIX", "BSDI", "HP-UX", "IRIX", "Linux", "FreeBSD", "OSF/1", "Solaris", "SunOS", "Neutrino", "OS/2", "BeOS", "OpenVMS", "other") not null,
 priority enum("P1", "P2", "P3", "P4", "P5") not null,
 product varchar(16) not null,
 rep_platform enum("All", "DEC", "HP", "Macintosh", "PC", "SGI", "Sun", "Other"),
@@ -52,6 +52,7 @@ qa_contact mediumint not null,
 status_whiteboard mediumtext not null,
 
 index (assigned_to),
+index (creation_ts),
 index (delta_ts),
 index (bug_severity),
 index (bug_status),
diff --git a/makedependenciestable.sh b/makedependenciestable.sh
new file mode 100755
index 0000000000000000000000000000000000000000..80a6795b2e962f74d9b7c7331f7ae9a0522819af
--- /dev/null
+++ b/makedependenciestable.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Terry Weissman <terry@mozilla.org>
+
+mysql > /dev/null 2>/dev/null << OK_ALL_DONE
+
+use bugs;
+
+drop table dependencies
+OK_ALL_DONE
+
+mysql << OK_ALL_DONE
+use bugs;
+create table dependencies (
+    blocked mediumint not null,
+    dependson mediumint not null,
+
+    index(blocked),
+    index(dependson)
+);
+
+
+
+show columns from dependencies;
+show index from dependencies;
+
+OK_ALL_DONE
diff --git a/makeprofilestable.sh b/makeprofilestable.sh
index be4c03e4bf8fda8243e76f86a1b56bafeff38dbb..42f6f350c420bf569f05f4c6b27a4974f1cc6986 100755
--- a/makeprofilestable.sh
+++ b/makeprofilestable.sh
@@ -34,6 +34,7 @@ password varchar(16),
 cryptpassword varchar(64),
 realname varchar(255),
 groupset bigint not null,
+emailnotification enum("ExcludeSelfChanges", "CConly", "All") not null default "ExcludeSelfChanges",
 index(login_name)
 );
 
diff --git a/post_bug.cgi b/post_bug.cgi
index c0102fdbf23602fba65b47b9e480a1500a8aeca2..d31a73774cebb7d6e0c1b5abe08dfe5ca8a3ecd4 100755
--- a/post_bug.cgi
+++ b/post_bug.cgi
@@ -78,7 +78,8 @@ $::FORM{'reporter'} = DBNameToIdAndCheck($::FORM{'reporter'});
 
 my @bug_fields = ("reporter", "product", "version", "rep_platform",
                   "bug_severity", "priority", "op_sys", "assigned_to",
-                  "bug_status", "bug_file_loc", "short_desc", "component");
+                  "bug_status", "bug_file_loc", "short_desc", "component",
+                  "status_whiteboard", "target_milestone");
 
 if (Param("useqacontact")) {
     SendSQL("select initialqacontact from components where program=" .
@@ -93,15 +94,20 @@ if (Param("useqacontact")) {
 
 
 
+my @used_fields;
+foreach my $f (@bug_fields) {
+    if (exists $::FORM{$f}) {
+        push (@used_fields, $f);
+    }
+}
 
-
-my $query = "insert into bugs (\n" . join(",\n", @bug_fields) . ",
+my $query = "insert into bugs (\n" . join(",\n", @used_fields) . ",
 creation_ts, long_desc )
 values (
 ";
 
 
-foreach my $field (@bug_fields) {
+foreach my $field (@used_fields) {
     $query .= SqlQuote($::FORM{$field}) . ",\n";
 }
 
@@ -136,9 +142,12 @@ foreach my $person (keys %ccids) {
     SendSQL("insert into cc (bug_id, who) values ($id, $person)");
 }
 
-print "<H2>Changes Submitted</H2>\n";
-print "<A HREF=\"show_bug.cgi?id=$id\">Show BUG# $id</A>\n";
-print "<BR><A HREF=\"query.cgi\">Back To Query Page</A>\n";
+print "<TABLE BORDER=1><TD><H2>Bug $id posted</H2>\n";
+system("./processmail $id $::COOKIE{'Bugzilla_login'}");
+print "<TD><A HREF=\"show_bug.cgi?id=$id\">Back To BUG# $id</A></TABLE>\n";
+
+print "<BR><A HREF=\"createattachment.cgi?id=$id\">Attach a file to this bug</a>\n";
+
+navigation_header();
 
-system("./processmail $id < /dev/null > /dev/null 2> /dev/null &");
 exit;
diff --git a/process_bug.cgi b/process_bug.cgi
index fc425a199f64948256d24b8b546f47e27e3ab20d..32580cf9a448b94f90e90710d1c2bc2e04bbc521 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -68,7 +68,7 @@ if ($::FORM{'product'} ne $::dontchange) {
         print "<input type=submit value=Commit>\n";
         print "</form>\n";
         print "</hr>\n";
-        print "<a href=query.cgi>Cancel all this and go back to the query page.</a>\n";
+        print "<a href=query.cgi>Cancel all this and go to the query page.</a>\n";
         exit;
     }
 }
@@ -226,13 +226,17 @@ SWITCH: for ($::FORM{'knob'}) {
             exit;
         }
         if ($::FORM{'dup_id'} == $::FORM{'id'}) {
-            print "Nice try.  But it doesn't really make sense to mark a\n";
+            print "Nice try, $::FORM{'who'}.  But it doesn't really make sense to mark a\n";
             print "bug as a duplicate of itself, does it?\n";
             exit;
         }
         AppendComment($::FORM{'dup_id'}, $::FORM{'who'}, "*** Bug $::FORM{'id'} has been marked as a duplicate of this bug. ***");
         $::FORM{'comment'} .= "\n\n*** This bug has been marked as a duplicate of $::FORM{'dup_id'} ***";
-        system("./processmail $::FORM{'dup_id'} < /dev/null > /dev/null 2> /dev/null &");
+
+        print "<TABLE BORDER=1><TD><H2>Notation added to bug $::FORM{'dup_id'}</H2>\n";
+        system("./processmail $::FORM{'dup_id'} $::FORM{'who'}");
+        print "<TD><A HREF=\"show_bug.cgi?id=$::FORM{'dup_id'}\">Go To BUG# $::FORM{'dup_id'}</A></TABLE>\n";
+
         last SWITCH;
     };
     # default
@@ -256,19 +260,136 @@ if ($::comma eq "") {
 }
 
 my $basequery = $::query;
+my $delta_ts;
+
 
 sub SnapShotBug {
     my ($id) = (@_);
-    SendSQL("select " . join(',', @::log_columns) .
+    SendSQL("select delta_ts, " . join(',', @::log_columns) .
             " from bugs where bug_id = $id");
-    return FetchSQLData();
+    my @row = FetchSQLData();
+    $delta_ts = shift @row;
+
+    return @row;
+}
+
+
+sub SnapShotDeps {
+    my ($i, $target, $me) = (@_);
+    SendSQL("select $target from dependencies where $me = $i order by $target");
+    my @list;
+    while (MoreSQLData()) {
+        push(@list, FetchOneColumn());
+    }
+    return join(',', @list);
+}
+
+
+my $whoid = DBNameToIdAndCheck($::FORM{'who'});
+my $timestamp;
+
+sub LogDependencyActivity {
+    my ($i, $oldstr, $target, $me) = (@_);
+    my $newstr = SnapShotDeps($i, $target, $me);
+    if ($oldstr ne $newstr) {
+        SendSQL("insert into bugs_activity (bug_id,who,when,field,oldvalue,newvalue) values ($i,$whoid,$timestamp,'$target','$oldstr','$newstr')");
+        return 1;
+    }
+    return 0;
 }
 
 
 foreach my $id (@idlist) {
-    SendSQL("lock tables bugs write, bugs_activity write, cc write, profiles write");
+    my %dependencychanged;
+    SendSQL("lock tables bugs write, bugs_activity write, cc write, profiles write, dependencies write");
     my @oldvalues = SnapShotBug($id);
 
+    if (defined $::FORM{'delta_ts'} && $::FORM{'delta_ts'} ne $delta_ts) {
+        print "
+<H1>Mid-air collision detected!</H1>
+Someone else has made changes to this bug at the same time you were trying to.
+The changes made were:
+<p>
+";
+        DumpBugActivity($id, $delta_ts);
+        my $longdesc = GetLongDescription($id);
+        my $longchanged = 0;
+        if (length($longdesc) > $::FORM{'longdesclength'}) {
+            $longchanged = 1;
+            print "<P>Added text to the long description:<blockquote><pre>";
+            print html_quote(substr($longdesc, $::FORM{'longdesclength'}));
+            print "</pre></blockquote>\n";
+        }
+        SendSQL("unlock tables");
+        print "You have the following choices: <ul>\n";
+        $::FORM{'delta_ts'} = $delta_ts;
+        print "<li><form method=post>";
+        foreach my $i (keys %::FORM) {
+            my $value = value_quote($::FORM{$i});
+            print qq{<input type=hidden name="$i" value="$value">\n};
+        }
+        print qq{<input type=submit value="Submit my changes anyway">\n};
+        print " This will cause all of the above changes to be overwritten";
+        if ($longchanged) {
+            print ", except for the changes to the description";
+        }
+        print qq{.</form>\n<li><a href="show_bug.cgi?id=$id">Throw away my changes, and go revisit bug $id</a></ul>\n};
+        navigation_header();
+        exit;
+    }
+        
+    my %deps;
+    if (defined $::FORM{'dependson'}) {
+        my $me = "blocked";
+        my $target = "dependson";
+        for (1..2) {
+            $deps{$target} = [];
+            my %seen;
+            foreach my $i (split('[\s,]+', $::FORM{$target})) {
+                if ($i eq "") {
+                    next;
+
+                }
+                SendSQL("select bug_id from bugs where bug_id = " .
+                        SqlQuote($i));
+                my $comp = FetchOneColumn();
+                if ($comp ne $i) {
+                    print "<H1>$i is not a legal bug number</H1>\n";
+                    print "<p>Click <b>Back</b> and try again.\n";
+                    exit;
+                }
+                if (!exists $seen{$i}) {
+                    push(@{$deps{$target}}, $i);
+                    $seen{$i} = 1;
+                }
+            }
+            my @stack = @{$deps{$target}};
+            while (@stack) {
+                my $i = shift @stack;
+                SendSQL("select $target from dependencies where $me = $i");
+                while (MoreSQLData()) {
+                    my $t = FetchOneColumn();
+                    if ($t == $id) {
+                        print "<H1>Dependency loop detected!</H1>\n";
+                        print "The change you are making to dependencies\n";
+                        print "has caused a circular dependency chain.\n";
+                        print "<p>Click <b>Back</b> and try again.\n";
+                        exit;
+                    }
+                    if (!exists $seen{$t}) {
+                        push @stack, $t;
+                        $seen{$t} = 1;
+                    }
+                }
+            }
+                        
+
+            my $tmp = $me;
+            $me = $target;
+            $target = $tmp;
+        }
+    }
+
     my $query = "$basequery\nwhere bug_id = $id";
     
 # print "<PRE>$query</PRE>\n";
@@ -296,9 +417,63 @@ foreach my $id (@idlist) {
         }
     }
 
+    SendSQL("select delta_ts from bugs where bug_id = $id");
+    $timestamp = FetchOneColumn();
+
+    if (defined $::FORM{'dependson'}) {
+        my $me = "blocked";
+        my $target = "dependson";
+        for (1..2) {
+            SendSQL("select $target from dependencies where $me = $id order by $target");
+            my %snapshot;
+            my @oldlist;
+            while (MoreSQLData()) {
+                push(@oldlist, FetchOneColumn());
+            }
+            my @newlist = sort {$a <=> $b} @{$deps{$target}};
+            @dependencychanged{@oldlist} = 1;
+            @dependencychanged{@newlist} = 1;
+
+            while (0 < @oldlist || 0 < @newlist) {
+                if (@oldlist == 0 || (@newlist > 0 &&
+                                      $oldlist[0] > $newlist[0])) {
+                    $snapshot{$newlist[0]} = SnapShotDeps($newlist[0], $me,
+                                                          $target);
+                    shift @newlist;
+                } elsif (@newlist == 0 || (@oldlist > 0 &&
+                                           $newlist[0] > $oldlist[0])) {
+                    $snapshot{$oldlist[0]} = SnapShotDeps($oldlist[0], $me,
+                                                          $target);
+                    shift @oldlist;
+                } else {
+                    if ($oldlist[0] != $newlist[0]) {
+                        die "Error in list comparing code";
+                    }
+                    shift @oldlist;
+                    shift @newlist;
+                }
+            }
+            my @keys = keys(%snapshot);
+            if (@keys) {
+                my $oldsnap = SnapShotDeps($id, $target, $me);
+                SendSQL("delete from dependencies where $me = $id");
+                foreach my $i (@{$deps{$target}}) {
+                    SendSQL("insert into dependencies ($me, $target) values ($id, $i)");
+                }
+                foreach my $k (@keys) {
+                    LogDependencyActivity($k, $snapshot{$k}, $me, $target);
+                }
+                LogDependencyActivity($id, $oldsnap, $target, $me);
+            }
+
+            my $tmp = $me;
+            $me = $target;
+            $target = $tmp;
+        }
+    }
+
+
     my @newvalues = SnapShotBug($id);
-    my $whoid;
-    my $timestamp;
     foreach my $col (@::log_columns) {
         my $old = shift @oldvalues;
         my $new = shift @newvalues;
@@ -309,14 +484,9 @@ foreach my $id (@idlist) {
             $new = "";
         }
         if ($old ne $new) {
-            if (!defined $whoid) {
-                $whoid = DBNameToIdAndCheck($::FORM{'who'});
-                SendSQL("select delta_ts from bugs where bug_id = $id");
-                $timestamp = FetchOneColumn();
-            }
-            if ($col eq 'assigned_to') {
-                $old = DBID_to_name($old);
-                $new = DBID_to_name($new);
+            if ($col eq 'assigned_to' || $col eq 'qa_contact') {
+                $old = DBID_to_name($old) if $old != 0;
+                $new = DBID_to_name($new) if $new != 0;
             }
             $col = SqlQuote($col);
             $old = SqlQuote($old);
@@ -327,20 +497,26 @@ foreach my $id (@idlist) {
         }
     }
     
-    print "<TABLE BORDER=1><TD><H1>Changes Submitted</H1>\n";
+    print "<TABLE BORDER=1><TD><H2>Changes to bug $id submitted</H2>\n";
+    SendSQL("unlock tables");
+    system("./processmail $id $::FORM{'who'}");
     print "<TD><A HREF=\"show_bug.cgi?id=$id\">Back To BUG# $id</A></TABLE>\n";
 
-    SendSQL("unlock tables");
+    foreach my $k (keys(%dependencychanged)) {
+        print "<TABLE BORDER=1><TD><H2>Checking for dependency changes on bug $k</H2>\n";
+        system("./processmail $k $::FORM{'who'}");
+        print "<TD><A HREF=\"show_bug.cgi?id=$k\">Go To BUG# $k</A></TABLE>\n";
+    }
 
-    system("./processmail $id < /dev/null > /dev/null 2> /dev/null &");
 }
 
 if (defined $::next_bug) {
+    print("<P>The next bug in your list is:\n");
     $::FORM{'id'} = $::next_bug;
     print "<HR>\n";
 
     navigation_header();
     do "bug_form.pl";
 } else {
-    print "<BR><A HREF=\"query.cgi\">Back To Query Page</A>\n";
+    navigation_header();
 }
diff --git a/processmail b/processmail
index 3da6448e170ef3b3e32017153790a8152ac6b3f6..4c780d15361122bfc86db15d1da0a6ba27d7c6d6 100755
--- a/processmail
+++ b/processmail
@@ -17,8 +17,8 @@
 # Corporation. Portions created by Netscape are Copyright (C) 1998
 # Netscape Communications Corporation. All Rights Reserved.
 # 
-# Contributor(s): Terry Weissman <terry@mozilla.org>
-
+# Contributor(s): Terry Weissman <terry@mozilla.org>,
+# Bryce Nesbitt <bryce-mozilla@nextbus.com>
 
 # To recreate the shadow database,  run "processmail regenerate" .
 
@@ -32,6 +32,8 @@ $| = 1;
 umask(0);
 
 $::lockcount = 0;
+my $regenerate = 0;
+my $nametoexclude = "";
 
 sub Lock {
     if ($::lockcount <= 0) {
@@ -103,6 +105,43 @@ sub DescCC {
 }
 
 
+sub DescDependencies {
+    my ($id) = (@_);
+    if (!Param("usedependencies")) {
+        return "";
+    }
+    my $result = "";
+    my $me = "blocked";
+    my $target = "dependson";
+    my $title = "BugsThisDependsOn";
+    for (1..2) {
+        SendSQL("select $target from dependencies where $me = $id order by $target");
+        my @list;
+        while (MoreSQLData()) {
+            push(@list, FetchOneColumn());
+        }
+        if (@list) {
+            my @verbose;
+            foreach my $i (@list) {
+                SendSQL("select bug_status, resolution from bugs where bug_id = $i");
+                my ($bug_status, $resolution) = (FetchSQLData());
+                if ($resolution ne "") {
+                    $resolution = "/$resolution";
+                }
+                push(@verbose, $i . "[$bug_status$resolution]");
+            }
+            $result .= "$title: " . join(', ', @verbose) . "\n";
+        }
+        my $tmp = $me;
+        $me = $target;
+        $target = $tmp;
+        $title = "OtherBugsDependingOnThis";
+    }
+    return $result;
+}
+
+
+
 sub GetBugText {
     my ($id) = (@_);
     undef %::bug;
@@ -169,21 +208,38 @@ AssignedTo: $::bug{'assigned_to'}
 ReportedBy: $::bug{'reporter'}               
 $qa_contact$target_milestone${status_whiteboard}URL: $::bug{'bug_file_loc'}
 " . DescCC($::bug{'cclist'}) . "Summary: $::bug{'short_desc'}
-
+" . DescDependencies($id) . "
 $::bug{'long_desc'}
 ";
 
 }
 
 
-
+my $didexclude = 0;
+my %seen;
 sub fixaddresses {
-    my ($list) = (@_);
+    my ($field, $list) = (@_);
     my @result;
-    my %seen;
     foreach my $i (@$list) {
-        if ($i ne "" && !defined $::nomail{$i} && !defined $seen{$i}) {
-            push @result, $i;
+        if ($i eq "") {
+            next;
+        }
+        SendSQL("select emailnotification from profiles where login_name = " .
+                SqlQuote($i));
+        my $emailnotification = FetchOneColumn();
+        if ($emailnotification eq "CConly") {
+            if ($field ne "cc") {
+                next;
+            }
+        }
+        if ($emailnotification eq "ExcludeSelfChanges" &&
+           (lc($i) eq $nametoexclude)) {
+            $didexclude = 1;
+            next;
+        }
+        
+        if (!defined $::nomail{$i} && !defined $seen{$i}) {
+            push(@result, $i . Param('emailsuffix'));
             $seen{$i} = 1;
         }
     }
@@ -200,40 +256,8 @@ sub Log {
     Unlock();
 }
     
-
-ConnectToDatabase();
-
-
-Lock();
-
-# foreach i [split [read_file -nonewline "okmail"] "\n"] {
-#     set okmail($i) 1
-# }
-
-
-
-if (open(FID, "<data/nomail")) {
-    while (<FID>) {
-        $::nomail{trim($_)} = 1;
-    }
-    close FID;
-}
-
-
-my $regenerate = 0;
-
-if ($ARGV[0] eq "regenerate") {
-    $regenerate = 1;
-    shift @ARGV;
-    SendSQL("select bug_id from bugs order by bug_id");
-    my @row;
-    while (@row = FetchSQLData()) {
-        push @ARGV, $row[0];
-    }
-    print "$#ARGV bugs to be regenerated.\n";
-}
-
-foreach my $i (@ARGV) {
+sub ProcessOneBug {
+    my $i = $_[0];
     my $old = "shadow/$i";
     my $new = "shadow/$i.tmp.$$";
     my $diffs = "shadow/$i.diffs.$$";
@@ -254,10 +278,11 @@ foreach my $i (@ARGV) {
     close FID;
     if (Different($old, $new)) {
         system("diff -c -b $old $new > $diffs");
-        my $tolist = fixaddresses([$::bug{'assigned_to'}, $::bug{'reporter'},
+        my $tolist = fixaddresses("to",
+                                  [$::bug{'assigned_to'}, $::bug{'reporter'},
                                    $::bug{'qa_contact'}]);
-        my $cclist = fixaddresses($::bug{'cclist'});
-        my $logstr = "Bug $i changed";
+        my $cclist = fixaddresses("cc", $::bug{'cclist'});
+        my $logstr = "Bug $i $verb";
         if ($tolist ne "" || $cclist ne "") {
             my %substs;
 
@@ -275,11 +300,16 @@ foreach my $i (@ARGV) {
             my $msg = PerformSubsts(Param("changedmail"), \%substs);
 
             if (!$regenerate) {
+		# Note: fixaddresses may result in a Cc: only.  This seems harmless.
                 open(SENDMAIL, "|/usr/lib/sendmail -t") ||
                     die "Can't open sendmail";
                 print SENDMAIL $msg;
                 close SENDMAIL;
-                $logstr = "$logstr; mail sent to $tolist $cclist";
+                $logstr = "$logstr; mail sent to $tolist, $cclist";
+		print "<B>Email sent to:</B> $tolist $cclist\n";
+                if ($didexclude) {
+                    print "<B>Excluding:</B> $nametoexclude (<a href=changepassword.cgi>change your preferences</a> if you wish not to be excluded)\n";
+                }
             }
         }
         unlink($diffs);
@@ -290,6 +320,46 @@ foreach my $i (@ARGV) {
     if ($regenerate) {
         print "$i ";
     }
+    %seen = ();
 }
 
+# Code starts here
+
+ConnectToDatabase();
+Lock();
+
+if (open(FID, "<data/nomail")) {
+    while (<FID>) {
+        $::nomail{trim($_)} = 1;
+    }
+    close FID;
+}
+
+if (($#ARGV < 0) || ($#ARGV > 1)) {
+    print "Usage error: processmail {bugid} {nametoexclude}\nOr: processmail regenerate\n";
+    exit;
+}
+
+# To recreate the shadow database,  run "processmail regenerate" .
+if ($ARGV[0] eq "regenerate") {
+    $regenerate = 1;
+    shift @ARGV;
+    SendSQL("select bug_id from bugs order by bug_id");
+    my @regenerate_list;
+    while (my @row = FetchSQLData()) {
+        push @regenerate_list, $row[0];
+    }
+    foreach my $i (@regenerate_list) {
+        ProcessOneBug($i);
+    }
+    print("\n");
+    exit;
+}
+
+if ($#ARGV == 1) {
+    $nametoexclude = lc($ARGV[1]);
+}
+
+ProcessOneBug($ARGV[0]);
+
 exit;
diff --git a/query.cgi b/query.cgi
index 5ee513ec0e99c2c0ffd151f275afdb91d5271826..4408dbfbb74163ef4f2639a6ab285a7b4a0cce8d 100755
--- a/query.cgi
+++ b/query.cgi
@@ -67,7 +67,11 @@ foreach my $name ("bug_status", "resolution", "assigned_to", "rep_platform",
                   "email1", "emailtype1", "emailreporter1",
                   "emailassigned_to1", "emailcc1", "emailqa_contact1",
                   "email2", "emailtype2", "emailreporter2",
-                  "emailassigned_to2", "emailcc2", "emailqa_contact2") {
+                  "emailassigned_to2", "emailcc2", "emailqa_contact2",
+                  "changedin", "short_desc", "short_desc_type",
+                  "long_desc", "long_desc_type", "bug_file_loc",
+                  "bug_file_loc_type", "status_whiteboard",
+                  "status_whiteboard_type") {
     $default{$name} = "";
     $type{$name} = 0;
 }
@@ -154,7 +158,7 @@ sub GenerateEmailInput {
 <table cellspacing=0 cellpadding=0>
 <tr>
 <td rowspan=2 valign=top><a href="helpemailquery.html">Email:</a>
-<input name="email$id" size="30" value="">&nbsp;matching as
+<input name="email$id" size="30" value="$defstr">&nbsp;matching as
 <SELECT NAME=emailtype$id>
 <OPTION VALUE="regexp">regexp
 <OPTION VALUE="notregexp">not regexp
@@ -193,7 +197,7 @@ my $emailinput2 = GenerateEmailInput(2);
 # javascript
     
 my $jscript = << 'ENDSCRIPT';
-<script language="Javascript1.2">
+<script language="Javascript1.2" type="text/javascript">
 <!--
 var cpts = new Array();
 var vers = new Array();
@@ -242,6 +246,13 @@ function selectProduct(f) {
     // Apparently, IE4 chokes on the below, so do nothing if running that.
     var agt=navigator.userAgent.toLowerCase();
     if ((agt.indexOf("msie") != -1)) return;
+    // Netscape 4.04 and 4.05 also choke with an "undefined"
+    // error.  if someone can figure out how to "define" the
+    // whatever, we'll remove this hack.  in the mean time, we'll
+    // assume that v4.00-4.03 also die, so we'll disable the neat
+    // javascript stuff for Netscape 4.05 and earlier.
+    var agtver = parseFloat(navigator.appVersion);
+    if (agtver <= 4.05 ) return;
 
     var cnt = 0;
     var i;
@@ -338,15 +349,18 @@ function selectProduct(f) {
 #    set legal_product [concat $default{"product"} [lreplace $legal_product $w $w]]
 # }
 
-PutHeader("Bugzilla Query Page", "Query Page");
+PutHeader("Bugzilla Query Page", "Query Page", "",
+          q{onLoad="selectProduct(document.forms[0]);"});
 
 push @::legal_resolution, "---"; # Oy, what a hack.
 push @::legal_target_milestone, "---"; # Oy, what a hack.
 
 print $jscript;
 
+my @logfields = ("[Bug creation]", @::log_columns);
+
 print "
-<FORM NAME=queryForm METHOD=GET ACTION=\"buglist.cgi\">
+<FORM METHOD=GET ACTION=\"buglist.cgi\">
 
 <table>
 <tr>
@@ -403,7 +417,7 @@ Changed in the <NOBR>last <INPUT NAME=changedin SIZE=2 VALUE=\"$default{'changed
 <td rowspan=2 align=right>Where the field(s)
 </td><td rowspan=2>
 <SELECT NAME=\"chfield\" MULTIPLE SIZE=4>
-@{[make_options(\@::log_columns, $default{'chfield'}, $type{'chfield'})]}
+@{[make_options(\@logfields, $default{'chfield'}, $type{'chfield'})]}
 </SELECT>
 </td><td rowspan=2>
 changed.
@@ -424,7 +438,7 @@ changed.
 <tr>
 <TH ALIGN=LEFT VALIGN=BOTTOM>Program:</th>
 <TH ALIGN=LEFT VALIGN=BOTTOM>Version:</th>
-<TH ALIGN=LEFT VALIGN=BOTTOM><A HREF=describecomponents.cgi>Component:<a></th>
+<TH ALIGN=LEFT VALIGN=BOTTOM><A HREF=describecomponents.cgi>Component:</a></th>
 ";
 
 if (Param("usetargetmilestone")) {
@@ -462,38 +476,48 @@ if (Param("usetargetmilestone")) {
 </td>";
 }
 
+
+sub StringSearch {
+    my ($desc, $name) = (@_);
+    my $type = $name . "_type";
+    my $def = value_quote($default{$name});
+    print qq{<tr>
+<td align=right>$desc:</td>
+<td><input name=$name size=30 value="$def"></td>
+<td><SELECT NAME=$type>
+};
+    if ($default{$type} eq "") {
+        $default{$type} = "substring";
+    }
+    foreach my $i (["substring", "case-insensitive substring"],
+                   ["casesubstring", "case-sensitive substring"],
+                   ["regexp", "regular expression"],
+                   ["notregexp", "not ( regular expression )"]) {
+        my ($n, $d) = (@$i);
+        my $sel = "";
+        if ($default{$type} eq $n) {
+            $sel = " SELECTED";
+        }
+        print qq{<OPTION VALUE="$n"$sel>$d\n};
+    }
+    print "</SELECT></TD>
+</tr>
+";
+}
+
 print "
 </tr>
 </table>
 
 <table border=0>
-<tr>
-<td align=right>Summary:</td>
-<td><input name=short_desc size=30></td>
-<td><input type=radio name=short_desc_type value=substr checked>Substring</td>
-<td><input type=radio name=short_desc_type value=regexp>Regexp</td>
-</tr>
-<tr>
-<td align=right>Description:</td>
-<td><input name=long_desc size=30></td>
-<td><input type=radio name=long_desc_type value=substr checked>Substring</td>
-<td><input type=radio name=long_desc_type value=regexp>Regexp</td>
-</tr>
-<tr>
-<td align=right>URL:</td>
-<td><input name=bug_file_loc size=30></td>
-<td><input type=radio name=bug_file_loc_type value=substr checked>Substring</td>
-<td><input type=radio name=bug_file_loc_type value=regexp>Regexp</td>
-</tr>";
+";
+
+StringSearch("Summary", "short_desc");
+StringSearch("Description", "long_desc");
+StringSearch("URL", "bug_file_loc");
 
 if (Param("usestatuswhiteboard")) {
-    print "
-<tr>
-<td align=right>Status whiteboard:</td>
-<td><input name=status_whiteboard size=30></td>
-<td><input type=radio name=status_whiteboard_type value=substr checked>Substring</td>
-<td><input type=radio name=status_whiteboard_type value=regexp>Regexp</td>
-</tr>";
+    StringSearch("Status whiteboard", "status_whiteboard");
 }
 
 print "
@@ -536,7 +560,6 @@ print "
 <INPUT TYPE=\"reset\" VALUE=\"Reset back to the default query\">
 <INPUT TYPE=hidden name=form_name VALUE=query>
 <BR>Give me a <A HREF=\"help.html\">clue</A> about how to use this form.
-</CENTER>
 </FORM>
 
 ";
@@ -553,7 +576,7 @@ if (UserInGroup("editcomponents")) {
 if (defined $::COOKIE{"Bugzilla_login"}) {
     print "<a href=relogin.cgi>Log in as someone besides <b>$::COOKIE{'Bugzilla_login'}</b></a><br>\n";
 }
-print "<a href=changepassword.cgi>Change your password.</a><br>\n";
+print "<a href=changepassword.cgi>Change your password or preferences.</a><br>\n";
 print "<a href=\"enter_bug.cgi\">Create a new bug.</a><br>\n";
 print "<a href=\"createaccount.cgi\">Open a new Bugzilla account</a><br>\n";
 print "<a href=\"reports.cgi\">Bug reports</a><br>\n";
diff --git a/relogin.cgi b/relogin.cgi
index 5ef523945c21136bea8849d84ba8ddca4e4064ff..d1e2700aa71c5ab8e0f577b82e4e0f361a2556fe 100755
--- a/relogin.cgi
+++ b/relogin.cgi
@@ -34,20 +34,9 @@ Content-type: text/html
 The cookie that was remembering your login is now gone.  The next time you
 do an action that requires a login, you will be prompted for it.
 <p>
-<a href=query.cgi>Back to the query page.</a>
 ";
 
-exit;
-
-# The below was a different way, that prompted you for a login right then.
+navigation_header();
 
-# catch {unset COOKIE(Bugzilla_login)}
-# catch {unset COOKIE(Bugzilla_password)}
-# confirm_login
-
-# puts "Content-type: text/html\n"
-# puts "<H1>OK, logged in.</H1>"
-# puts "You are now logged in as <b>$COOKIE(Bugzilla_login)</b>."
-# puts "<p>"
-# puts "<a href=query.cgi>Back to the query page.</a>"
+exit;
 
diff --git a/reports.cgi b/reports.cgi
index f78fd643fcea5d9316e29fc0e099a9cd928cd832..c7a22fcd32d80a4bca67b4eb86464ba07bfcc7cd 100755
--- a/reports.cgi
+++ b/reports.cgi
@@ -18,7 +18,12 @@
 # Netscape Communications Corporation. All Rights Reserved.
 #
 # Contributor(s): Harrison Page <harrison@netscape.com>,
-# Terry Weissman <terry@mozilla.org>
+# Terry Weissman <terry@mozilla.org>,
+# Dawn Endico <endico@mozilla.org>
+# Bryce Nesbitt <bryce@nextbus.COM>,
+#    Added -All- report, change "nobanner" to "banner" (it is strange to have a
+#    list with 2 positive and 1 negative choice), default links on, add show
+#    sql comment.
 
 use diagnostics;
 use strict;
@@ -32,6 +37,7 @@ use vars @::legal_product;
 my $dir = "data/mining";
 my $week = 60 * 60 * 24 * 7;
 my @status = qw (NEW ASSIGNED REOPENED);
+my %bugsperperson;
 
 # while this looks odd/redundant, it allows us to name
 # functions differently than the value passed in
@@ -39,33 +45,29 @@ my @status = qw (NEW ASSIGNED REOPENED);
 my %reports = 
 	( 
 	"most_doomed" => \&most_doomed,
+	"most_doomed_for_milestone" => \&most_doomed_for_milestone,
+	"most_recently_doomed" => \&most_recently_doomed,
 	"show_chart" => \&show_chart,
 	);
 
-# patch from Sam Ziegler <sam@ziegler.org>:
-#
-# "reports.cgi currently has it's own idea of what 
-# the header should be. This patch sets it to the 
-# system wide header." 
-
 print "Content-type: text/html\n\n";
 
-if (defined $::FORM{'nobanner'})
-	{
-print <<FIN;
-<html>
-<head><title>Bug Reports</title></head>
-<body bgcolor="#FFFFFF">
-FIN
-	}
+# If we're here for the first time, give a banner.  Else respect the banner flag.
+if ( (!defined $::FORM{'product'}) || ($::FORM{'banner'})  )
+        {
+        PutHeader ("Bug Reports")
+        }
 else
-	{
-	PutHeader ("Bug Reports") unless (defined $::FORM{'nobanner'});
-	}
+        {
+	print("<html><head><title>Bug Reports</title></head><body bgcolor=\"#FFFFFF\">");
+        }
 
 ConnectToDatabase();
 GetVersionTable();
 
+my @myproducts;
+push( @myproducts, "-All-", @::legal_product );
+
 $::FORM{'output'} = $::FORM{'output'} || "most_doomed"; # a reasonable default
 
 if (! defined $::FORM{'product'})
@@ -111,7 +113,7 @@ FIN
 
 sub choose_product
 	{
-	my $product_popup = make_options (\@::legal_product, $::legal_product[0]);
+	my $product_popup = make_options (\@myproducts, $myproducts[0]);
 	my $charts = (-d $dir) ? "<option value=\"show_chart\">Bug Charts" : "";
 
 	print <<FIN;
@@ -132,14 +134,20 @@ $product_popup
 <td align=center>
 <select name="output">
 <option value="most_doomed">Bug Counts
+FIN
+        if (Param('usetargetmilestone')) {
+            print "<option value=\"most_doomed_for_milestone\">Most Doomed";
+            }
+        print "<option value=\"most_recently_doomed\">Most Recently Doomed";
+	print <<FIN;
 $charts
 </select>
 <tr>
 <td align=center><b>Switches:</b></td>
 <td align=left>
-<input type=checkbox name=links value=1>&nbsp;Links to Bugs<br>
-<input type=checkbox name=nobanner value=1>&nbsp;No Banner<br>
-<input type=checkbox name=quip value=1>&nbsp;Include Quip<br>
+<input type=checkbox name=links checked value=1>&nbsp;Links to Bugs<br>
+<input type=checkbox name=banner checked value=1>&nbsp;Banner<br>
+<input type=checkbox name=quip value=1>&nbsp;Quip<br>
 </td>
 </tr>
 <tr>
@@ -151,6 +159,8 @@ $charts
 </form>
 <p>
 FIN
+#Add this above to get a control for showing the SQL query:
+#<input type=checkbox name=showsql value=1>&nbsp;Show SQL<br>
 	}
 
 sub most_doomed
@@ -165,7 +175,9 @@ Bug Report for $::FORM{'product'}
 $when<p>
 FIN
 
-	my $query = <<FIN;
+# Build up $query string
+	my $query;
+	$query = <<FIN;
 select 
 	bugs.bug_id, bugs.assigned_to, bugs.bug_severity,
 	bugs.bug_status, bugs.product, 
@@ -179,7 +191,13 @@ from   bugs,
        versions projector
 where  bugs.assigned_to = assign.userid
 and    bugs.reporter = report.userid
-and    bugs.product='$::FORM{'product'}'
+FIN
+
+	if( $::FORM{'product'} ne "-All-" ) {
+		$query .= "and    bugs.product='$::FORM{'product'}'";
+	}
+
+	$query .= <<FIN;
 and 	 
 	( 
 	bugs.bug_status = 'NEW' or 
@@ -187,6 +205,7 @@ and
 	bugs.bug_status = 'REOPENED'
 	)
 FIN
+# End build up $query string
 
 	print "<font color=purple><tt>$query</tt></font><p>\n" 
 		unless (! exists $::FORM{'showsql'});
@@ -380,17 +399,7 @@ FIN
 sub is_legal_product
 	{
 	my $product = shift;
-	return grep { $_ eq $product} @::legal_product;
-	}
-
-sub header
-	{
-	print <<FIN;
-<TABLE BGCOLOR="#000000" WIDTH="100%" BORDER=0 CELLPADDING=0 CELLSPACING=0>
-<TR><TD><A HREF="http://www.mozilla.org/"><IMG
- SRC="http://www.mozilla.org/images/mozilla-banner.gif" ALT=""
- BORDER=0 WIDTH=600 HEIGHT=58></A></TD></TR></TABLE>
-FIN
+	return grep { $_ eq $product} @myproducts;
 	}
 
 sub show_chart
@@ -506,4 +515,244 @@ FIN
 	exit;
 	}
 
+sub bybugs {
+   $bugsperperson{$a} <=> $bugsperperson{$b}
+   }
+
+sub most_doomed_for_milestone
+	{
+	my $when = localtime (time);
+        my $ms = "M" . Param("curmilestone");
+        my $quip = "Summary";
+
+	print "<center>\n<h1>";
+        if( $::FORM{'product'} ne "-All-" ) {
+            print "Most Doomed for $ms ($::FORM{'product'})";
+        } else {
+            print "Most Doomed for $ms";
+            }
+        print "</h1>\n$when<p>\n";
+
+	#########################
+	# start painting report #
+	#########################
+
+	if ($::FORM{'quip'})
+                {
+                if (open (COMMENTS, "<data/comments"))
+                        {
+                        my @cdata;
+                        while (<COMMENTS>)
+                                {
+                                push @cdata, $_;
+                                }
+                        close COMMENTS;
+                        $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>";                        }
+                }
+
+
+        # Build up $query string
+	my $query;
+	$query = "select distinct assigned_to from bugs where target_milestone=\"$ms\"";
+	if( $::FORM{'product'} ne "-All-" ) {
+		$query .= "and    bugs.product='$::FORM{'product'}'";
+	}
+	$query .= <<FIN;
+and 	 
+	( 
+	bugs.bug_status = 'NEW' or 
+	bugs.bug_status = 'ASSIGNED' or 
+	bugs.bug_status = 'REOPENED'
+	)
+FIN
+# End build up $query string
+
+        SendSQL ($query);
+        my @people = ();
+        while (my ($person) = FetchSQLData())
+            {
+            push @people, $person;
+            }
+
+        #############################
+        # suck contents of database # 
+        #############################
+        my $person = "";
+        my $bugtotal = 0;
+        foreach $person (@people)
+                {
+                my $query = "select count(bug_id) from bugs,profiles where target_milestone=\"$ms\" and userid=assigned_to and userid=\"$person\"";
+	        if( $::FORM{'product'} ne "-All-" ) {
+                    $query .= "and    bugs.product='$::FORM{'product'}'";
+                    }
+	        $query .= <<FIN;
+and 	 
+	( 
+	bugs.bug_status = 'NEW' or 
+	bugs.bug_status = 'ASSIGNED' or 
+	bugs.bug_status = 'REOPENED'
+	)
+FIN
+                SendSQL ($query);
+	        my $bugcount = FetchSQLData();
+                $bugsperperson{$person} = $bugcount;
+                $bugtotal += $bugcount;
+                }
+
+#       sort people by the number of bugs they have assigned to this milestone
+        @people = sort bybugs @people;
+        my $totalpeople = @people;
+                
+        print "<TABLE>\n";
+        print "<TR><TD COLSPAN=2>\n";
+        print "$totalpeople engineers have $bugtotal $ms bugs and features.\n";
+        print "</TD></TR>\n";
+
+        while (@people)
+                {
+                $person = pop @people;
+                print "<TR><TD>\n";
+                SendSQL("select login_name from profiles where userid=$person");
+                my $login_name= FetchSQLData();
+                print("<A HREF=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&target_milestone=$ms&assigned_to=$login_name\">\n");
+                print("$bugsperperson{$person}  bugs and features");
+                print("</A>");
+                print(" for \n");
+                print("<A HREF=\"mailto:$login_name\">");
+                print("$login_name");
+                print("</A>\n");
+                print("</TD><TD>\n");
+
+                $person = pop @people;
+                if ($person) {
+                    SendSQL("select login_name from profiles where userid=$person");
+                    my $login_name= FetchSQLData();
+                    print("<A HREF=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&target_milestone=$ms&assigned_to=$login_name\">\n");
+                    print("$bugsperperson{$person}  bugs and features");
+                    print("</A>");
+                    print(" for \n");
+                    print("<A HREF=\"mailto:$login_name\">");
+                    print("$login_name");
+                    print("</A>\n");
+                    print("</TD></TR>\n\n");
+                    }
+                }
+        print "</TABLE>\n";
+
+        }
+
+
+
+sub most_recently_doomed
+	{
+	my $when = localtime (time);
+        my $ms = "M" . Param("curmilestone");
+        my $quip = "Summary";
+
+	print "<center>\n<h1>";
+        if( $::FORM{'product'} ne "-All-" ) {
+            print "Most Recently Doomed ($::FORM{'product'})";
+        } else {
+            print "Most Recently Doomed";
+            }
+        print "</h1>\n$when<p>\n";
+
+	#########################
+	# start painting report #
+	#########################
+
+	if ($::FORM{'quip'})
+                {
+                if (open (COMMENTS, "<data/comments"))
+                        {
+                        my @cdata;
+                        while (<COMMENTS>)
+                                {
+                                push @cdata, $_;
+                                }
+                        close COMMENTS;
+                        $quip = "<i>" . $cdata[int(rand($#cdata + 1))] . "</i>";                        }
+                }
+
+
+        # Build up $query string
+	my $query;
+        $query = "select distinct assigned_to from bugs where bugs.bug_status='NEW' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
+	if( $::FORM{'product'} ne "-All-" ) {
+		$query .= "and    bugs.product='$::FORM{'product'}'";
+	}
 
+# End build up $query string
+
+        SendSQL ($query);
+        my @people = ();
+        while (my ($person) = FetchSQLData())
+            {
+            push @people, $person;
+            }
+
+        #############################
+        # suck contents of database # 
+        #############################
+        my $person = "";
+        my $bugtotal = 0;
+        foreach $person (@people)
+                {
+                my $query = "select count(bug_id) from bugs,profiles where bugs.bug_status='NEW' and userid=assigned_to and userid='$person' and target_milestone='' and bug_severity!='enhancement' and status_whiteboard='' and (product='Browser' or product='MailNews')";
+	        if( $::FORM{'product'} ne "-All-" ) {
+                    $query .= "and    bugs.product='$::FORM{'product'}'";
+                    }
+                SendSQL ($query);
+	        my $bugcount = FetchSQLData();
+                $bugsperperson{$person} = $bugcount;
+                $bugtotal += $bugcount;
+                }
+
+#       sort people by the number of bugs they have assigned to this milestone
+        @people = sort bybugs @people;
+        my $totalpeople = @people;
+        
+        if ($totalpeople > 20) {
+            splice @people, 0, $totalpeople-20;
+            }
+                
+        print "<TABLE>\n";
+        print "<TR><TD COLSPAN=2>\n";
+        print "$totalpeople engineers have $bugtotal untouched new bugs.\n";
+        if ($totalpeople > 20) {
+            print "These are the 20 most doomed.";
+            }
+        print "</TD></TR>\n";
+
+        while (@people)
+                {
+                $person = pop @people;
+                print "<TR><TD>\n";
+                SendSQL("select login_name from profiles where userid=$person");
+                my $login_name= FetchSQLData();
+                print("<A HREF=\"buglist.cgi?bug_status=NEW&email1=$login_name&emailtype1=substring&emailassigned_to1=1&product=Browser&product=MailNews&target_milestone=---&status_whiteboard=.&status_whiteboard_type=notregexp&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=normal&bug_severity=minor&bug_severity=trivial\">\n"); 
+                print("$bugsperperson{$person}  bugs");
+                print("</A>");
+                print(" for \n");
+                print("<A HREF=\"mailto:$login_name\">");
+                print("$login_name");
+                print("</A>\n");
+                print("</TD><TD>\n");
+
+                $person = pop @people;
+                if ($person) {
+                    SendSQL("select login_name from profiles where userid=$person");
+                    my $login_name= FetchSQLData();
+                    print("<A HREF=\"buglist.cgi?bug_status=NEW&email1=$login_name&emailtype1=substring&emailassigned_to1=1&product=Browser&product=MailNews&target_milestone=---&status_whiteboard=.&status_whiteboard_type=notregexp&bug_severity=blocker&bug_severity=critical&bug_severity=major&bug_severity=normal&bug_severity=minor&bug_severity=trivial\">\n"); 
+                    print("$bugsperperson{$person}  bugs");
+                    print("</A>");
+                    print(" for \n");
+                    print("<A HREF=\"mailto:$login_name\">");
+                    print("$login_name");
+                    print("</A>\n");
+                    print("</TD></TR>\n\n");
+                    }
+                }
+        print "</TABLE>\n";
+
+        }
diff --git a/sanitycheck.cgi b/sanitycheck.cgi
index 969cfd3c448a747e38ca1a170e4a28f20a371a0b..f13fb4a1300e919d526778ae5dc2ddeabe73ac84 100755
--- a/sanitycheck.cgi
+++ b/sanitycheck.cgi
@@ -166,3 +166,21 @@ while (@row = FetchSQLData()) {
         Alert("Bad who $who in " . BugLink($id));
     }
 }
+
+
+Status("Checking dependency table");
+
+SendSQL("select blocked, dependson from dependencies");
+while (@row = FetchSQLData()) {
+    my ($blocked, $dependson) = (@row);
+    if (!defined $bugid{$blocked}) {
+        Alert("Bad blocked " . BugLink($blocked));
+    }
+    if (!defined $bugid{$dependson}) {
+        Alert("Bad dependson " . BugLink($dependson));
+    }
+}
+
+
+
+Status("Sanity check completed.");
diff --git a/show_activity.cgi b/show_activity.cgi
index 70f4c253ff87391701f71235eba666090d4893d5..fbcdcc6879cb92c69297a423fabe9b2a39be8395 100755
--- a/show_activity.cgi
+++ b/show_activity.cgi
@@ -29,35 +29,8 @@ print "Content-type: text/html\n\n";
 PutHeader("Changes made to bug $::FORM{'id'}", "Activity log",
           "Bug $::FORM{'id'}");
 
-my $query = "
-        select bugs_activity.field, bugs_activity.when,
-                bugs_activity.oldvalue, bugs_activity.newvalue,
-                profiles.login_name
-        from bugs_activity,profiles
-        where bugs_activity.bug_id = $::FORM{'id'}
-        and profiles.userid = bugs_activity.who
-        order by bugs_activity.when";
-
 ConnectToDatabase();
-SendSQL($query);
 
-print "<table border cellpadding=4>\n";
-print "<tr>\n";
-print "    <th>Who</th><th>What</th><th>Old value</th><th>New value</th><th>When</th>\n";
-print "</tr>\n";
+DumpBugActivity($::FORM{'id'});
 
-my @row;
-while (@row = FetchSQLData()) {
-    my ($field,$when,$old,$new,$who) = (@row);
-    $old = value_quote($old);
-    $new = value_quote($new);
-    print "<tr>\n";
-    print "<td>$who</td>\n";
-    print "<td>$field</td>\n";
-    print "<td>$old</td>\n";
-    print "<td>$new</td>\n";
-    print "<td>$when</td>\n";
-    print "</tr>\n";
-}
-print "</table>\n";
 print "<hr><a href=show_bug.cgi?id=$::FORM{'id'}>Back to bug $::FORM{'id'}</a>\n";
diff --git a/show_bug.cgi b/show_bug.cgi
index 75a3bb8e34ae2b95eedcd5289b6e7bdff32dd8d7..927d526886946876bc083e39af576c924bf9f642 100755
--- a/show_bug.cgi
+++ b/show_bug.cgi
@@ -22,12 +22,18 @@
 use diagnostics;
 use strict;
 
+require "CGI.pl";
+
+ConnectToDatabase();
+
+if ($::FORM{'GoAheadAndLogIn'}) {
+    confirm_login();
+}
+
 print "Content-type: text/html\n";
 print "\n";
 
-require "CGI.pl";
-
-if (!defined $::FORM{'id'}) {
+if (!defined $::FORM{'id'} || $::FORM{'id'} !~ /^\s*\d+\s*$/) {
     print "<H2>Search By Bug Number</H2>\n";
     print "<FORM METHOD=GET ACTION=\"show_bug.cgi\">\n";
     print "You may find a single bug by entering its bug id here: \n";
@@ -37,8 +43,6 @@ if (!defined $::FORM{'id'}) {
     exit;
 }
 
-ConnectToDatabase();
-
 GetVersionTable();
 
 PutHeader("Bugzilla bug $::FORM{'id'}", "Bugzilla Bug", $::FORM{'id'});
@@ -48,3 +52,6 @@ print "<HR>\n";
 
 $! = 0;
 do "bug_form.pl" || die "Error doing bug_form.pl: $!";
+print "</BODY>";
+print "</HTML>\n";
+
diff --git a/showattachment.cgi b/showattachment.cgi
index 147ce8fef6b244e8603e441ccb6ada8f4763b7e2..f4c3157e3341152c426f274dffbd42a695dc4a20 100755
--- a/showattachment.cgi
+++ b/showattachment.cgi
@@ -18,7 +18,6 @@
 # Netscape Communications Corporation. All Rights Reserved.
 # 
 # Contributor(s): Terry Weissman <terry@mozilla.org>
-#                 David Gardiner <david.gardiner@unisa.edu.au>
 
 use diagnostics;
 use strict;
@@ -29,7 +28,7 @@ ConnectToDatabase();
 
 my @row;
 if (defined $::FORM{'attach_id'}) {
-    SendSQL("select mimetype, filename, thedata from attachments where attach_id = $::FORM{'attach_id'}");
+    SendSQL("select mimetype, thedata from attachments where attach_id = $::FORM{'attach_id'}");
     @row = FetchSQLData();
 }
 if (!@row) {
@@ -38,6 +37,6 @@ if (!@row) {
     print "Please hit back and try again.\n";
     exit;
 }
-print qq{Content-type: $row[0]; name="$row[1]"; \n\n$row[2]};
+print qq{Content-type: $row[0]\n\n$row[1]};
 
     
diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..7e04259f84381a4e56cfdbc7a5331b87e77bd6d2
--- /dev/null
+++ b/showdependencygraph.cgi
@@ -0,0 +1,182 @@
+#!/usr/bonsaitools/bin/perl -w
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Terry Weissman <terry@mozilla.org>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+my $id = $::FORM{'id'};
+my $urlbase = Param("urlbase");
+
+my %seen;
+my %edgesdone;
+
+sub AddLink {
+    my ($blocked, $dependson) = (@_);
+    my $key = "$blocked,$dependson";
+    if (!exists $edgesdone{$key}) {
+        $edgesdone{$key} = 1;
+        print DOT "$blocked -> $dependson\n";
+        $seen{$blocked} = 1;
+        $seen{$dependson} = 1;
+    }
+}
+
+
+print "Content-type: text/html\n\n";
+PutHeader("Dependency graph", "Dependency graph", $id);
+
+$::FORM{'rankdir'} = "LR" if !defined $::FORM{'rankdir'};
+
+
+if (defined $id) {
+    ConnectToDatabase();
+    quietly_check_login();
+    $::usergroupset = $::usergroupset; # More warning suppression silliness.
+
+    mkdir("data/webdot", 0777);
+
+    my $filename = "data/webdot/$$.dot";
+    open(DOT, ">$filename") || die "Can't create $filename";
+    print DOT "digraph G {";
+    print DOT qq{
+graph [URL="${urlbase}query.cgi", rankdir=$::FORM{'rankdir'}]
+node [URL="${urlbase}show_bug.cgi?id=\\N", style=filled, color=lightgrey]
+};
+    my %baselist;
+
+    foreach my $i (split('[\s,]+', $::FORM{'id'})) {
+        $i = trim($i);
+        if ($i ne "") {
+            $baselist{$i} = 1;
+        }
+    }
+    my @basearray = keys(%baselist);
+
+    if ($::FORM{'doall'}) {
+        SendSQL("select blocked, dependson from dependencies");
+        
+        while (MoreSQLData()) {
+            my ($blocked, $dependson) = (FetchSQLData());
+            AddLink($blocked, $dependson);
+        }
+    } else {
+        my @stack = @basearray;
+        while (@stack) {
+            my $id = shift @stack;
+            SendSQL("select blocked, dependson from dependencies where blocked = $id or dependson = $id");
+            while (MoreSQLData()) {
+                my ($blocked, $dependson) = (FetchSQLData());
+                if ($blocked != $id && !exists $seen{$blocked}) {
+                    push @stack, $blocked;
+                }
+                if ($dependson != $id && !exists $seen{$dependson}) {
+                    push @stack, $dependson;
+                }
+                AddLink($blocked, $dependson);
+            }
+        }
+    }
+
+    foreach my $k (@basearray) {
+        $seen{$k} = 1;
+    }
+    foreach my $k (keys(%seen)) {
+        my $summary = "";
+        my $stat;
+        if ($::FORM{'showsummary'}) {
+            SendSQL("select bug_status, short_desc from bugs where bug_id = $k and bugs.groupset & $::usergroupset = bugs.groupset");
+            ($stat, $summary) = (FetchSQLData());
+            $stat = "NEW" if !defined $stat;
+            $summary = "" if !defined $summary;
+        } else {
+            SendSQL("select bug_status from bugs where bug_id = $k");
+            $stat = FetchOneColumn();
+        }
+        my @params;
+#        print DOT "$k [URL" . qq{="${urlbase}show_bug.cgi?id=$k"};
+        if ($summary ne "") {
+            $summary =~ s/([\\\"])/\\$1/g;
+            push(@params, qq{label="$k\\n$summary"});
+        }
+        if (exists $baselist{$k}) {
+            push(@params, "shape=box");
+        }
+        my $opened = ($stat eq "NEW" || $stat eq "ASSIGNED" ||
+                      $stat eq "REOPENED");
+        if ($opened) {
+            push(@params, "color=green");
+        }
+        if (@params) {
+            print DOT "$k [" . join(',', @params) . "]\n";
+        } else {
+            print DOT "$k\n";
+        }
+    }
+
+
+    print DOT "}\n";
+    close DOT;
+    chmod 0777, $filename;
+    
+    my $url = PerformSubsts(Param("webdotbase")) . $filename;
+
+    print qq{<a href="$url.map"> <img src="$url.gif" ismap> </a><hr>\n};
+
+    # Cleanup any old .dot files created from previous runs.
+    my $since = time() - 24 * 60 * 60;
+    foreach my $f (glob("data/webdot/*.dot")) {
+        if (ModTime($f) < $since) {
+            unlink $f;
+        }
+    }
+} else {
+    $::FORM{'id'} = "";
+    $::FORM{'doall'} = 0;
+    $::FORM{'showsummary'} = 0;
+}    
+
+print "
+<form>
+<table>
+<tr>
+<th align=right>Bug numbers:</th>
+<td><input name=id value=\"" . value_quote($::FORM{'id'}) . "\"></td>
+<td><input type=checkbox name=doall" . ($::FORM{'doall'} ? " checked" : "") .
+">Show <b>every</b> bug in the system with 
+dependencies</td>
+</tr>
+<tr><td colspan=3><input type=checkbox name=showsummary" .
+($::FORM{'showsummary'} ? " checked" : "") . ">Show the summary of all bugs
+</tr>
+<tr><td colspan=3><select name=rankdir>
+<option value=\"TB\"" . ($::FORM{'rankdir'} eq 'TB' ? 'selected' : '') .
+">Orient top-to-bottom
+<option value=\"LR\"" . ($::FORM{'rankdir'} eq 'LR' ? 'selected' : '') .
+">Orient left-to-right
+</select></td></tr>
+</table>
+<input type=submit value=\"Submit\">
+</form>
+ ";
+
+navigation_header();
diff --git a/showdependencytree.cgi b/showdependencytree.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..36694ba6e46198e372ad5b1aa873ac13660490ac
--- /dev/null
+++ b/showdependencytree.cgi
@@ -0,0 +1,88 @@
+#!/usr/bonsaitools/bin/perl -w
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Terry Weissman <terry@mozilla.org>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+# Shut up misguided -w warnings about "used only once":
+
+use vars %::FORM;
+
+
+my $id = $::FORM{'id'};
+my $linkedid = qq{<a href="show_bug.cgi?id=$id">$id</a>};
+
+print "Content-type: text/html\n\n";
+PutHeader("Dependency tree", "Dependency tree", "Bug $linkedid");
+
+ConnectToDatabase();
+
+quietly_check_login();
+
+$::usergroupset = $::usergroupset; # More warning suppression silliness.
+
+my %seen;
+
+sub DumpKids {
+    my ($i, $me, $target) = (@_);
+    if (exists $seen{$i}) {
+        return;
+    }
+    $seen{$i} = 1;
+    SendSQL("select $target from dependencies where $me = $i order by $target");
+    my @list;
+    while (MoreSQLData()) {
+        push(@list, FetchOneColumn());
+    }
+    if (@list) {
+        print "<ul>\n";
+        foreach my $kid (@list) {
+            SendSQL("select bug_id, bug_status, short_desc from bugs where bug_id = $kid and bugs.groupset & $::usergroupset = bugs.groupset");
+            my ($bugid, $stat, $short_desc) = (FetchSQLData());
+            if (!defined $bugid) {
+                next;
+            }
+            my $opened = ($stat eq "NEW" || $stat eq "ASSIGNED" ||
+                          $stat eq "REOPENED");
+            print "<li>";
+            if (!$opened) {
+                print "<strike>";
+            }
+            $short_desc = html_quote($short_desc);
+            print qq{<a href="show_bug.cgi?id=$kid">$kid $short_desc</a>};
+            if (!$opened) {
+                print "</strike>";
+            }
+            DumpKids($kid, $me, $target);
+        }
+        print "</ul>\n";
+    }
+}
+
+print "<h1>Bugs that bug $linkedid depends on</h1>";
+DumpKids($id, "blocked", "dependson");
+print "<h1>Bugs that depend on bug $linkedid</h1>";
+undef %seen;
+DumpKids($id, "dependson", "blocked");
+
+navigation_header();
diff --git a/showowners.cgi b/showowners.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..b0926e4735f62f216811060a280f7aa5c8c38cfc
--- /dev/null
+++ b/showowners.cgi
@@ -0,0 +1,145 @@
+#!/usr/bonsaitools/bin/perl -w
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public License
+# Version 1.0 (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+# License for the specific language governing rights and limitations
+# under the License.
+# 
+# The Original Code is the Bugzilla Bug Tracking System.
+# 
+# The Initial Developer of the Original Code is Netscape Communications
+# Corporation. Portions created by Netscape are Copyright (C) 1998
+# Netscape Communications Corporation. All Rights Reserved.
+# 
+# Contributor(s): Bryce Nesbitt <bryce@nextbus.com>
+#
+# This program lists all BugZilla users, and lists what modules they
+# either own or are default QA for.  It is very slow on large databases.
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+require "globals.pl";
+
+# Fetch, one row at a time, the product and module.
+# Build the contents of the table cell listing each unique
+# product just once, with all the modules.
+sub FetchAndFormat {
+	my $result = "";
+	my $temp = "";
+	my @row = "";
+
+	while (@row = FetchSQLData()) {
+		if( $temp ne $row[0] ) {
+			$result .= " " . $row[0] . ": ";
+		} else {
+			$result .= ", ";
+		}
+		$temp = $row[0];
+		$result .=  "<I>" . $row[1] .  "</I>";
+	}
+	return( $result );
+}
+
+
+# Start the resulting web page
+print "Content-type: text/html\n\n";
+print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">
+<html><head><title>BugZilla module owners list</title></head>\n";
+
+ConnectToDatabase();
+GetVersionTable();
+
+# Collect all BugZilla user names
+SendSQL("select login_name,userid from profiles order by login_name");
+my @list;
+my @row;
+while (@row = FetchSQLData()) {
+	push @list, $row[0];
+}
+
+print "<P>The following is a list of BugZilla users who are the default owner
+for at least one module.  BugZilla will only assign or Cc: a bug to the exact
+name stored in the database.  Click on a name to see bugs assigned to that person:</P>\n";
+print "<table border=1>\n";
+print "<tr><td><B>Login name</B></td>\n";
+print "<td><B>Default owner for</B></td><td><B>Default QA for</B></td>\n";
+
+# If a user is a initialowner or initialqacontact, list their modules
+my $person;
+my $nospamperson;
+my $firstcell;
+my $secondcell;
+my @nocell;
+foreach $person (@list) {
+
+	my $qperson = SqlQuote($person);
+
+	SendSQL("select program,value from components\
+		 where initialowner = $qperson order by program,value");
+	$firstcell = FetchAndFormat();
+
+	SendSQL("select program,value from components\
+		 where initialqacontact = $qperson order by program,value");
+	$secondcell = FetchAndFormat();
+
+	$_ = $person;		# Anti-spam
+	s/@/ @/;		# Mangle
+	$nospamperson = $_;	# Email 
+
+	if( $firstcell || $secondcell ) {
+		print "<tr>";
+
+		print "<td>\n";
+		print "<a href=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&email1=${person}&emailtype1=substring&emailassigned_to1=1&cmdtype=doit&newqueryname=&order=%22Importance%22&form_name=query\">${nospamperson}</a>\n";
+		print "</td>\n";
+
+		print "<td>";
+		print $firstcell;
+		print "</td>\n";
+
+		print "<td>";
+		print $secondcell;
+		print "</td>\n";
+
+		print "</tr>\n";
+	} else {
+		push @nocell, $person;
+	}
+}
+
+print "<tr>";
+print "<td colspan=3>";
+print "Other valid logins: ";
+foreach $person (@nocell) {
+	$_ = $person;		# Anti-spam
+	s/@/ @/;		# Mangle
+	$nospamperson = $_;	# Email 
+
+	print "<a href=\"buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&email1=${person}&emailtype1=substring&emailassigned_to1=1&cmdtype=doit&newqueryname=&order=%22Importance%22&form_name=query\">${nospamperson}</a>\n";
+	print ", ";
+}
+print "</td>";
+print "</tr>\n";
+
+print "</table>\n";
+
+# Enhancement ideas
+#	o Use just one table cell for each person.  The table gets unbalanced for installs
+#	  where just a few QA people handle lots of modules
+#	o Optimize for large systems.  Terry notes:
+# 	  The problem is that you go query the components table 10,000 times, 
+#	  twice for each of the 5,000 logins that we have.  Yow!
+#	 
+#	  It would be better to generate your initial list of logins by selecting 
+#	  for distinct initialqacontact and initialowner values from the 
+#	  components database.  Then when you generate the list of "other 
+#	  logins", you can query for the whole list of logins and subtract out 
+#	  things that were in the components database.
diff --git a/whineatnews.pl b/whineatnews.pl
index 1f3d19cf6e5217ebb4d7141897ef35e71f7b261b..6be5f895e2298a1389447edc199ab288b3bb45b0 100755
--- a/whineatnews.pl
+++ b/whineatnews.pl
@@ -50,10 +50,11 @@ while (@row = FetchSQLData()) {
 
 my $template = Param('whinemail');
 my $urlbase = Param('urlbase');
+my $emailsuffix = Param('emailsuffix');
 
 foreach my $email (sort (keys %bugs)) {
     my %substs;
-    $substs{'email'} = $email;
+    $substs{'email'} = $email . $emailsuffix;
     my $msg = PerformSubsts($template, \%substs);
 
     foreach my $i (@{$bugs{$email}}) {