From 44a0ae404a3681a16604a1c50d96b30b5d1abf55 Mon Sep 17 00:00:00 2001
From: Per Cederqvist <ceder@lysator.liu.se>
Date: Fri, 11 Jul 2014 09:33:33 +0200
Subject: [PATCH] Imported Bugzilla 2.4.

---
 CGI.pl                 | 184 ++++++++++++-----
 CHANGES                |  84 ++++++++
 CVS/Entries            | 101 +++++-----
 CVS/Root               |   2 +-
 README                 | 438 ++++++++++++++++++++++++++++++++---------
 addcomponent.cgi       |  99 ++++++++++
 backdoor.cgi           |   4 +-
 bug_form.pl            | 135 ++++++++++++-
 bug_status.html        |   5 +-
 buglist.cgi            |  94 ++++++++-
 collectstats.pl        | 101 ++++------
 createaccount.cgi      |  79 ++++++++
 createattachment.cgi   | 112 +++++++++++
 defparams.pl           |  37 +++-
 doaddcomponent.cgi     |  99 ++++++++++
 doeditcomponents.cgi   | 149 ++++++++++++++
 doeditowners.cgi       |  10 +-
 doeditparams.cgi       |   9 +-
 editcomponents.cgi     | 120 +++++++++++
 editowners.cgi         |   9 +-
 editparams.cgi         |   9 +-
 enter_bug.cgi          |  23 ++-
 globals.pl             |  50 ++++-
 index.html             |   3 +-
 long_list.cgi          |   7 +-
 makeactivitytable.sh   |   4 +-
 makeattachmenttable.sh |  45 +++++
 makebugtable.sh        |   1 +
 makecomponenttable.sh  | 129 +-----------
 makegroupstable.sh     |  69 +++++++
 makeproducttable.sh    |  14 +-
 makeprofilestable.sh   |   1 +
 makeversiontable.sh    |  15 +-
 post_bug.cgi           |   7 +-
 process_bug.cgi        |  16 ++
 processmail            |   7 +-
 query.cgi              | 190 ++++++++++++++++--
 sanitycheck.cgi        |  65 +++++-
 showattachment.cgi     |  43 ++++
 39 files changed, 2085 insertions(+), 484 deletions(-)
 create mode 100755 addcomponent.cgi
 create mode 100755 createaccount.cgi
 create mode 100755 createattachment.cgi
 create mode 100755 doaddcomponent.cgi
 create mode 100755 doeditcomponents.cgi
 create mode 100755 editcomponents.cgi
 create mode 100755 makeattachmenttable.sh
 create mode 100755 makegroupstable.sh
 create mode 100755 showattachment.cgi

diff --git a/CGI.pl b/CGI.pl
index f16640bc1..062d02348 100644
--- a/CGI.pl
+++ b/CGI.pl
@@ -119,6 +119,54 @@ sub ProcessFormFields {
 }
 
 
+sub ProcessMultipartFormFields {
+    my ($boundary) = (@_);
+    $boundary =~ s/^-*//;
+    my $remaining = $ENV{"CONTENT_LENGTH"};
+    my $inheader = 1;
+    my $itemname = "";
+#    open(DEBUG, ">debug") || die "Can't open debugging thing";
+#    print DEBUG "Boundary is '$boundary'\n";
+    while ($remaining > 0 && ($_ = <STDIN>)) {
+        $remaining -= length($_);
+#        print DEBUG "< $_";
+        if ($_ =~ m/^-*$boundary/) {
+#            print DEBUG "Entered header\n";
+            $inheader = 1;
+            $itemname = "";
+            next;
+        }
+
+        if ($inheader) {
+            if (m/^\s*$/) {
+                $inheader = 0;
+#                print DEBUG "left header\n";
+                $::FORM{$itemname} = "";
+            }
+            if (m/^Content-Disposition:\s*form-data\s*;\s*name\s*=\s*"([^\"]+)"/i) {
+                $itemname = $1;
+#                print DEBUG "Found itemname $itemname\n";
+                if (m/;\s*filename\s*=\s*"([^\"]+)"/i) {
+                    $::FILENAME{$itemname} = $1;
+                }
+            }
+            
+            next;
+        }
+        $::FORM{$itemname} .= $_;
+    }
+    delete $::FORM{""};
+    # Get rid of trailing newlines.
+    foreach my $i (keys %::FORM) {
+        chomp($::FORM{$i});
+        $::FORM{$i} =~ s/\r$//;
+    }
+}
+        
+
+
+
+
 sub FormData {
     my ($field) = (@_);
     return $::FORM{$field};
@@ -138,19 +186,11 @@ sub value_quote {
     $var =~ s/</\&lt;/g;
     $var =~ s/>/\&gt;/g;
     $var =~ s/"/\&quot;/g;
+    $var =~ s/\n/\&#010;/g;
+    $var =~ s/\r/\&#013;/g;
     return $var;
 }
 
-sub value_unquote {
-    my ($var) = (@_);
-    $var =~ s/\&quot/\"/g;
-    $var =~ s/\&lt/</g;
-    $var =~ s/\&gt/>/g;
-    $var =~ s/\&amp/\&/g;
-    return $var;
-}
-
-
 sub navigation_header {
     if (defined $::COOKIE{"BUGLIST"} && $::COOKIE{"BUGLIST"} ne "") {
 	my @bugs = split(/:/, $::COOKIE{"BUGLIST"});
@@ -180,17 +220,20 @@ sub make_options {
     my $last = "";
     my $popup = "";
     my $found = 0;
-    foreach my $item (@$src) {
-        if ($item eq "-blank-" || $item ne $last) {
-            if ($item eq "-blank-") {
-		$item = "";
-	    }
-            $last = $item;
-            if ($isregexp ? $item =~ $default : $default eq $item) {
-                $popup .= "<OPTION SELECTED VALUE=\"$item\">$item";
-                $found = 1;
-            } else {
-		$popup .= "<OPTION VALUE=\"$item\">$item";
+
+    if ($src) {
+        foreach my $item (@$src) {
+            if ($item eq "-blank-" || $item ne $last) {
+                if ($item eq "-blank-") {
+                    $item = "";
+                }
+                $last = $item;
+                if ($isregexp ? $item =~ $default : $default eq $item) {
+                    $popup .= "<OPTION SELECTED VALUE=\"$item\">$item";
+                    $found = 1;
+                } else {
+                    $popup .= "<OPTION VALUE=\"$item\">$item";
+                }
             }
         }
     }
@@ -231,6 +274,57 @@ sub PasswordForLogin {
     return $result;
 }
 
+
+sub quietly_check_login() {
+    $::usergroupset = '0';
+    my $loginok = 0;
+    if (defined $::COOKIE{"Bugzilla_login"} &&
+	defined $::COOKIE{"Bugzilla_logincookie"}) {
+        ConnectToDatabase();
+        SendSQL("select profiles.groupset, profiles.login_name = " .
+		SqlQuote($::COOKIE{"Bugzilla_login"}) .
+		" and profiles.cryptpassword = logincookies.cryptpassword " .
+		"and logincookies.hostname = " .
+		SqlQuote($ENV{"REMOTE_HOST"}) .
+		" from profiles,logincookies where logincookies.cookie = " .
+		SqlQuote($::COOKIE{"Bugzilla_logincookie"}) .
+		" and profiles.userid = logincookies.userid");
+        my @row;
+        if (@row = FetchSQLData()) {
+            $loginok = $row[1];
+            if ($loginok) {
+                $::usergroupset = $row[0];
+            }
+        }
+    }
+    if (!$loginok) {
+        delete $::COOKIE{"Bugzilla_login"};
+    }
+    return $loginok;
+}
+
+
+
+
+sub CheckEmailSyntax {
+    my ($addr) = (@_);
+    if ($addr !~ /^[^@, ]*@[^@, ]*\.[^@, ]*$/) {
+        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 "<p>Please click <b>back</b> and try again.\n";
+        exit;
+    }
+}
+
+
+
 sub confirm_login {
     my ($nexturl) = (@_);
 
@@ -243,19 +337,8 @@ sub confirm_login {
 
 	my $enteredlogin = $::FORM{"Bugzilla_login"};
         my $enteredpwd = $::FORM{"Bugzilla_password"};
-	if ($enteredlogin !~ /^[^@, ]*@[^@, ]*\.[^@, ]*$/) {
-            print "Content-type: text/html\n\n";
+        CheckEmailSyntax($enteredlogin);
 
-            print "<H1>Invalid e-mail address entered.</H1>\n";
-            print "The e-mail address you entered\n";
-            print "(<b>$enteredlogin</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 "<p>Please click <b>back</b> and try again.\n";
-            exit;
-        }
         my $realcryptpwd  = PasswordForLogin($::FORM{"Bugzilla_login"});
         
         if (defined $::FORM{"PleaseMailAPassword"}) {
@@ -324,25 +407,9 @@ To use the wonders of bugzilla, you can use the following:
     }
 
 
-    my $loginok = 0;
-
-    if (defined $::COOKIE{"Bugzilla_login"} &&
-	defined $::COOKIE{"Bugzilla_logincookie"}) {
-        SendSQL("select profiles.login_name = " .
-		SqlQuote($::COOKIE{"Bugzilla_login"}) .
-		" and profiles.cryptpassword = logincookies.cryptpassword " .
-		"and logincookies.hostname = " .
-		SqlQuote($ENV{"REMOTE_HOST"}) .
-		" from profiles,logincookies where logincookies.cookie = " .
-		$::COOKIE{"Bugzilla_logincookie"} .
-		" and profiles.userid = logincookies.userid");
-        $loginok = FetchOneColumn();
-        if (!defined $loginok) {
-            $loginok = 0;
-        }
-    }
+    my $loginok = quietly_check_login();
 
-    if ($loginok ne "1") {
+    if ($loginok != 1) {
         print "Content-type: text/html\n\n";
         print "<H1>Please log in.</H1>\n";
         print "I need a legitimate e-mail address and password to continue.\n";
@@ -407,7 +474,8 @@ sub PutHeader {
 	$h2 = "";
     }
 
-    print "<HTML><HEAD><TITLE>$title</TITLE></HEAD>\n";
+    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";
 
@@ -454,10 +522,18 @@ if (defined $ENV{"REQUEST_METHOD"}) {
         } else {
             $::buffer = "";
         }
+        ProcessFormFields $::buffer;
     } else {
-	read STDIN, $::buffer, $ENV{"CONTENT_LENGTH"} || die "Couldn't get form data";
+        if ($ENV{"CONTENT_TYPE"} =~
+            m@multipart/form-data; boundary=\s*([^; ]+)@) {
+            ProcessMultipartFormFields($1);
+            $::buffer = "";
+        } else {
+            read STDIN, $::buffer, $ENV{"CONTENT_LENGTH"} ||
+                die "Couldn't get form data";
+            ProcessFormFields $::buffer;
+        }
     }
-    ProcessFormFields $::buffer;
 }
 
 
diff --git a/CHANGES b/CHANGES
index cba77d560..5f36c4369 100644
--- a/CHANGES
+++ b/CHANGES
@@ -10,6 +10,90 @@ query the CVS tree.  For example,
 will tell you what has been changed in the last week.
 
 
+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
+like that will happen.  But to fix problems that have already crept into your
+database, you can run the following perl script (which is slow and ugly, but
+does work:)
+#!/usr/bonsaitools/bin/perl -w
+use diagnostics;
+use strict;
+require "globals.pl";
+$|=1;
+ConnectToDatabase();
+SendSQL("select bug_id from bugs order by bug_id");
+my @list;
+while (MoreSQLData()) {
+    push(@list, FetchOneColumn());
+}
+foreach my $id (@list) {
+    if ($id % 50 == 0) {
+	print "\n$id ";
+    }
+    SendSQL("select long_desc from bugs where bug_id = $id");
+    my $comment = FetchOneColumn();
+    my $orig = $comment;
+    $comment =~ s/\r\n/\n/g;     # Get rid of windows-style line endings.
+    $comment =~ s/\r/\n/g;       # Get rid of mac-style line endings.
+    if ($comment ne $orig) {
+	SendSQL("update bugs set long_desc = " . SqlQuote($comment) .
+		" where bug_id = $id");
+	print ".";
+    } else {
+	print "-";
+    }
+}
+
+
+
+4/8/99 Added ability to store patches with bugs.  This requires a new table
+to store the data, so you will need to run the "makeattachmenttable.sh" script.
+
+3/25/99 Unfortunately, the HTML::FromText CPAN module had too many bugs, and
+so I had to roll my own.  We no longer use the HTML::FromText CPAN module.
+
+3/24/99 (This entry has been removed.  It used to say that we required the
+HTML::FromText CPAN module, but that's no longer true.)
+
+3/22/99 Added the ability to query by fields which have changed within a date
+range.  To make this perform a bit better, we need a new index:
+
+	alter table bugs_activity add index (field);
+
+3/10/99 Added 'groups' stuff, where we have different group bits that we can
+put on a person or on a bug.  Some of the group bits control access to bugzilla
+features.  And a person can't access a bug unless he has every group bit set
+that is also set on the bug.  See the comments in makegroupstable.sh for a bit
+more info.
+
+The 'maintainer' param is now used only as an email address for people to send
+complaints to.  The groups table is what is now used to determine permissions.
+
+You will need to run the new script "makegroupstable.sh".  And then you need to
+feed the following lines to MySQL (replace XXX with the login name of the
+maintainer, the person you wish to be all-powerful).
+
+	alter table bugs add column groupset bigint not null;
+	alter table profiles add column groupset bigint not null;
+	update profiles set groupset=0x7fffffffffffffff where login_name = XXX;
+
+
+
+3/8/99 Added params to control how priorities are set in a new bug.  You can
+now choose whether to let submitters of new bugs choose a priority, or whether
+they should just accept the default priority (which is now no longer hardcoded
+to "P2", but is instead a param.)  The default value of the params will cause
+the same behavior as before.
+
+3/3/99 Added a "disallownew" field to the products table.  If non-zero, then
+don't let people file new bugs against this product.  (This is for when a 
+product is retired, but you want to keep the bug reports around for posterity.)
+Feed this to MySQL:
+
+	alter table products add column disallownew tinyint not null;
+
+
 2/8/99 Added FreeBSD 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", "AIX", "BSDI", "HP-UX", "IRIX", "Linux", "FreeBSD", "OSF/1", "Solaris", "SunOS", "OS/2", "other") not null;
diff --git a/CVS/Entries b/CVS/Entries
index 23ffbdede..48efc1f1c 100644
--- a/CVS/Entries
+++ b/CVS/Entries
@@ -1,47 +1,56 @@
-/1x1.gif/1.1/Wed Feb 10 22:11:47 1999/-kb/
-/CGI.pl/1.6/Wed Feb 10 22:11:47 1999//
-/CHANGES/1.16/Wed Feb 10 22:11:47 1999//
-/README/1.12/Wed Feb 10 22:11:47 1999//
-/ant.jpg/1.2/Wed Feb 10 22:11:47 1999/-kb/
-/backdoor.cgi/1.6/Wed Feb 10 22:11:48 1999//
-/bug_form.pl/1.8/Wed Feb 10 22:11:48 1999//
-/bug_status.html/1.3/Wed Feb 10 22:11:48 1999//
-/buglist.cgi/1.23/Wed Feb 10 22:11:48 1999//
-/changepassword.cgi/1.6/Wed Feb 10 22:11:48 1999//
-/colchange.cgi/1.7/Wed Feb 10 22:11:48 1999//
-/collectstats.pl/1.3/Wed Feb 10 22:11:48 1999//
-/defparams.pl/1.5/Wed Feb 10 22:11:48 1999//
-/describecomponents.cgi/1.1/Wed Feb 10 22:11:48 1999//
-/doeditowners.cgi/1.3/Wed Feb 10 22:11:48 1999//
-/doeditparams.cgi/1.5/Wed Feb 10 22:11:48 1999//
-/editowners.cgi/1.3/Wed Feb 10 22:11:48 1999//
-/editparams.cgi/1.7/Wed Feb 10 22:11:48 1999//
-/enter_bug.cgi/1.14/Wed Feb 10 22:11:48 1999//
-/help.html/1.1/Wed Feb 10 22:11:48 1999//
-/helpemailquery.html/1.1/Wed Feb 10 22:11:48 1999//
-/how_to_mail.html/1.1/Wed Feb 10 22:11:49 1999//
-/index.html/1.3/Wed Feb 10 22:11:49 1999//
-/long_list.cgi/1.5/Wed Feb 10 22:11:49 1999//
-/makeactivitytable.sh/1.1/Wed Feb 10 22:11:49 1999//
-/makebugtable.sh/1.8/Wed Feb 10 22:11:49 1999//
-/makecctable.sh/1.2/Wed Feb 10 22:11:49 1999//
-/makecomponenttable.sh/1.38/Wed Feb 10 22:11:49 1999//
-/makelogincookiestable.sh/1.1/Wed Feb 10 22:11:49 1999//
-/makeproducttable.sh/1.8/Wed Feb 10 22:11:49 1999//
-/makeprofilestable.sh/1.2/Wed Feb 10 22:11:49 1999//
-/makeversiontable.sh/1.10/Wed Feb 10 22:11:49 1999//
-/new_comment.cgi/1.2/Wed Feb 10 22:11:49 1999//
-/newquip.html/1.2/Wed Feb 10 22:11:49 1999//
-/notargetmilestone.html/1.1/Wed Feb 10 22:11:49 1999//
-/post_bug.cgi/1.5/Wed Feb 10 22:11:49 1999//
-/process_bug.cgi/1.13/Wed Feb 10 22:11:49 1999//
-/processmail/1.11/Wed Feb 10 22:11:49 1999//
-/query.cgi/1.23/Wed Feb 10 22:11:50 1999//
-/relogin.cgi/1.5/Wed Feb 10 22:11:50 1999//
-/reports.cgi/1.10/Wed Feb 10 22:11:50 1999//
-/sanitycheck.cgi/1.4/Wed Feb 10 22:11:50 1999//
-/show_activity.cgi/1.2/Wed Feb 10 22:11:50 1999//
-/show_bug.cgi/1.5/Wed Feb 10 22:11:50 1999//
-/whineatnews.pl/1.1/Wed Feb 10 22:11:50 1999//
-/globals.pl/1.14/Wed Feb 10 22:11:51 1999//
+/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//
 D
diff --git a/CVS/Root b/CVS/Root
index 7c6d3086d..db0def341 100644
--- a/CVS/Root
+++ b/CVS/Root
@@ -1 +1 @@
-:pserver:terry%netscape.com@cvs.mozilla.org:/cvsroot
+:pserver:terry%mozilla.org@cvs.mozilla.org:/cvsroot
diff --git a/README b/README
index c792e802f..61364777c 100644
--- a/README
+++ b/README
@@ -5,139 +5,395 @@ This is Bugzilla.  See <http://www.mozilla.org/bugs/>.
 	DISCLAIMER
 	==========
 
-This is not very well packaged code.  It's not packaged at all.  Don't
+   This is not very well packaged code.  It's not packaged at all.  Don't
 come here expecting something you plop in a directory, twiddle a few
-things, and you're off and using it.  Work has to be done to get
-there.  We'd like to get there, but it wasn't clear when that would
-be, and so we decided to let people see it first.
-
-
+things, and you're off and using it.  Work has to be done to get there.  
+We'd like to get there, but it wasn't clear when that would be, and so we
+decided to let people see it first.
 
 	============
 	INSTALLATION
 	============
 
-(This section stolen heavily from the Bonsai INSTALL document.  It's
-also probably incomplete.  "We're accepting patches", especially to
-this document!)
+0. Introduction
+
+   Installation of bugzilla is pretty straight forward, especially if your
+machine already has MySQL and the MySQL-related perl packages installed.
+If those aren't installed yet, then that's the first order of business.  The
+other necessary ingredient is a web server set up to run cgi scripts.
+
+1. Installing the Prerequisites
+
+   The software packages necessary for the proper running of bugzilla are:
+
+	1. MySQL database server and the mysql client
+	2. Perl (5.004 or greater)
+	3. DBI Perl module 
+	4. Data::Dumper Perl module
+	5. MySQL related Perl module collection
+	6. TimeDate Perl module collection
+	7. GD perl module (1.18 or greater)
+	8. Chart::Base Perl module (0.99 or greater)
+	9. The web server of your choice
+
+   Bugzilla has quite a few prerequisites, but none of them are TCL.
+Previous versions required TCL, but it no longer needed (or used).
+
+1.1. Getting and setting up MySQL database
+
+   Visit MySQL homepage at http://www.mysql.org and grab the latest stable
+release of the server.  Both binaries and source are available and which you
+get shouldn't matter.  Be aware that many of the binary versions of MySQL store
+their data files in /var which on many installations (particularly common with
+linux installations) is part of a smaller root partition.  If you decide to
+build from sources you can easily set the dataDir as an option to configure.
+
+  If you've installed from source or non-package (RPM, deb, etc.) binaries
+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.
+
+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
+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
+sections you'll be installing quite a few perl modules; this can be quite
+ornery if your perl installation isn't up to snuff.
+
+1.3. DBI Perl module
+
+   The DBI module is a generic Perl module used by other database related
+Perl modules.  For our purposes it's required by the MySQL-related
+modules.  As long as your Perl installation was done correctly the DBI
+module should be a breeze.  It's a mixed Perl/C module, but Perl's
+MakeMaker system simplifies the C compilation greatly.
+
+  Like almost all Perl modules DBI can be found on the Comprehensive Perl
+Archive Network (CPAN) at http://www.cpan.org .  The CPAN servers have a
+real tendency to bog down, so please use mirrors.  The current location at
+the time of this writing (02/17/99) can be found in Appendix A.
+
+  Quality, general Perl module installation instructions can be found on
+the CPAN website, but basically you'll just need to:
+
+	1. Untar the module tarball -- it should create its own directory
+	2. Enter the following commands:
+		perl Makefile.PL
+		make
+		make test
+		make install
+
+   If everything went ok that should be all it takes.  For the vast
+majority of perl modules this is all that's required.
+
+1.4 Data::Dumper Perl module
+
+   The Data::Dumper module provides data structure persistence for Perl
+(similar to Java's serialization).  It comes with later sub-releases of
+Perl 5.004, but a re-installation just to be sure it's available won't
+hurt anything.
+
+   Data::Dumper is used by the MySQL related Perl modules.  It can be
+found on CPAN (link in Appendix A) and can be installed by following the
+same four step make sequence used for the DBI module.
+
+1.5. MySQL related Perl module collection
+
+   The Perl/MySQL interface requires a few mutually-dependent perl
+modules.  These modules are grouped together into the the
+Msql-Mysql-modules package.  This package can be found at CPAN (link in
+Appendix A).  After the archive file has been downloaded it should be
+untarred.
+
+   The MySQL modules are all build using one make file which is generated
+by running:
+
+	perl Makefile.PL
+
+   The MakeMaker process will ask you a few questions about the desired
+compilation target and your MySQL installation.  For many of the questions
+the provided default will be adequate.
+
+   When asked if your desired target is the MySQL or mSQL packages
+selected the MySQL related ones.  Later you will be asked if you wish to
+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.
+
+1.6. TimeDate Perl module collection
+
+   Many of the more common date/time/calendar related Perl modules have
+been grouped into a bundle similar to the MySQL modules bundle. This
+bundle is stored on the CPAN under the name TimeDate.  A (hopefully
+current) link can be found in Appendix A.  The component module we're most
+interested in is the Date::Format module, but installing all of them is
+probably a good idea anyway.  The standard Perl module installation
+instructions should work perfectly for this simple package.
+
+1.7. GD Perl module (1.18 or greater)
+
+   The GD library was written by Thomas Boutel a long while ago to
+programatically generate images in C.  Since then it's become almost a
+defacto standard for programatic image construction.  The Perl bindings to
+it found in the GD library are used on a million web pages to generate
+graphs on the fly.  That's what bugzilla will be using it for so you'd
+better install it if you want any of the graphing to work.
+  Actually bugzilla uses the Graph module which relies on GD itself, but
+isn't that always the way with OOP.  At any rate, you can find the GD
+library on CPAN (link in Appendix A) and it installs beautifully in the
+usual fashion.
+
+1.8. Chart::Base Perl module (0.99 or greater)
+
+   The Chart module provides bugzilla with on-the-fly charting abilities.
+It can be installed in the usual fashion after it has been fetched from
+CPAN where it is found as the Chart-x.x... tarball in a directory to be
+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.)
+
+   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
+means uncommenting the following line in the srm.conf file:
+
+	AddHandler cgi-script .cgi
+
+   With apache you'll also want to make sure that within the access.conf
+file the line:
+
+	Options ExecCGI
+
+is in the stanza that covers the directories you intend to put the
+bugzilla .html and .cgi files into.
+
+2. Installing the Bugzilla Files
+
+   You should untar the bugzilla files into a directory that you're
+willing to make writable by the default web server user (probably
+'nobody').  You may decide to put the files off of the main web space for
+your web server or perhaps off of /usr/local with a symbolic link in the
+web space that points to the bugzilla directory.  At any rate, just dump
+all the files in the same place (optionally omitting the CVS directory if
+it accidentally got tarred up with the rest of bugzilla) and make sure
+you can get at the files in that directory through your web server.
+
+   Once all the files are in a web accessible directory, make that
+directory writable by your webserver's user (which may require just
+making it world writable).  Inside this main bugzilla directory issue the
+following commands:
+
+	touch comments
+	touch nomail
+	touch mail
+	
+   Make sure the comments, nomail, and mail files are writable by the
+webserver too.
+
+   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,
+you'll have to hack all the .cgi files to change where they look for perl.
+
+3. Setting Up the MySQL database
+
+   After you've gotten all the software installed and working you're ready
+to start preparing the database for its life as a the back end to a high
+quality bug tracker.
 
-First, you need some other things:
+    First, you'll want to fix MySQL permissions.  Bugzilla always logs in as
+user "bugs", with no password.  That needs to work.  MySQL permissions are a
+deep, nasty complicated thing.  I've just turned them off.  If you want to do
+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):
 
-   1) MySQL database server.
-   2) Perl5.004 or greater, including MySQL support, and modules
-	    Date::Format and Chart::Base available from your nearest
-			CPAN server. See http://www.perl.com/CPAN.
-      ftp://ftp.cpan.org/pub/CPAN/authors/id/GBARR/TimeDate-1.08.tar.gz
-      ftp://ftp.cpan.org/pub/CPAN/authors/id/DBONNER/Chart-0.99.tar.gz
-      ftp://ftp.cpan.org/pub/CPAN/authors/id/LDS/GD-1.18.tar.gz
-   3) Some kind of HTTP server so you could use CGI scripts
+	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');
 
-Earlier versions of Bugzilla required TCL.  THIS IS NO LONGER TRUE.
-All dependencies on TCL have been removed.
+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.
 
-1.1 Getting and setting up MySQL database
+For much more information about MySQL permissions, see the MySQL documentation.
 
-   Visit MySQL homepage at http://www.tcx.se and grab the latest
-stable binary release of the server. Sure, you can get sources and
-compile them yourself, but binaries are the easiest and the fastest
-way to get it up and running. Follow instructions found in
-manual. There is a section about installing binary-only
-distributions. 
+   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
+available by running:
 
-   You should create database "bugs". It may be a good idea to make it
-writable by all users on your machine and change access level
-later. This would save you a lot of time trying to guess whether it's
-permissions or a mistake in the script that make things fail.
+	mysql --help
 
+from the command line.
 
-1.2 HTTP server
+   Once you've begun mysql you'll see a 'mysql>' prompt.  At the prompt you
+should enter:
 
-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. Make sure that you can access 'bugs' database with user
-id you're running the daemon with. 
+	create database bugs;
+	quit  
 
-globals.pl:     $::db = Mysql->Connect("localhost", "bugs", "nobody", "")
 
-In globals.pl, the database connect call uses a mysql account
-name "bugs" (third argument to Mysql->Connect) to access the 
-bugs database. You may have to hack the code to use "nobody" 
-or whatever your HTTP server is running as.
 
+   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.
 
-2. Tweaking the Tools
 
-  All scripts look in /usr/bonsaitools/bin for perl. Make 
-the appropriate links or modify the paths in each script.
+   When calling the eight scripts order doesn't matter, but this one is
+fine:
 
-   Make sure the directory containing the binaries is writable by the 
-web server.  Bugzilla keeps some temporary files here.
+	./makeactivitytable.sh
+	./makebugtable.sh
+	./makecctable.sh
+	./makecomponenttable.sh
+	./makelogincookiestable.sh
+	./makeproducttable.sh
+	./makeprofilestable.sh
+	./makeversiontable.sh
+	./makegroupstable.sh
 
-  Create an empty file in that directory named "comments"; make sure
-it is writable by the web server.  Also, create empty files named
-"nomail" and "mail".
+   After running those you've got a nearly empty copy of the mozilla bug
+tracking setup.
 
-3. Setting up database
+4. Tweaking the Bugzilla->MySQL Connection Data
 
-First, run mysql, and tell it "create database bugs;".
+   If you have played with MySQL permissions, rather than just opening it
+wide open as described above, then you may need to tweak the Bugzilla 
+code to connect appropriately.
 
-Now, you should be run all six scripts named make*.sh.  This creates the
-database tables and populates them a teeny bit.
+   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
+like:
 
+	$::db = Mysql->Connect("
 
-4. Setting the parameters
+   That line does the actual database connection.  The Connect method
+takes four parameters which are (with appropriate values):
 
-At this point, you ought to be able to go and browse some pages.  But you'd
-like to customize some things.
+	1. server's host: just use "localhost"
+	2. database name: "bugs" if you're following these directions
+	3. MySQL username: whatever you created for your webserver user
+		probably "nobody"
+	4. Password for the MySQL account in item 3.
 
-Create yourself an account.  (Try to enter a new bug, and it will
-prompt you for your login.  Give it your email address, and have it
-mail you your password.)  Go visit the query page; that ought to force
-the creation of the "data/params" file in your installation dir.  Edit the
-data/params file, and change the line that sets "$::param{'maintainer'}" to
-have your email address as the maintainer.  Go visit the query page
-again; there should now be a link at the bottom that invites you to
-edit the parameters.  (If you have cookies turned off, you'll have to
-go to editparams.cgi manually.)
+Just fill in those values and close up global.pl
 
-Tweak the parameters to taste.  Be careful.
+5. Setting up yourself as Maintainer
 
+    Start by creating your own bugzilla account.  To do so, just try to "add
+a bug" from the main bugzilla menu (now available from your system through your
+web browser!).  You'll be prompted for logon info, and you should enter your
+email address and then select 'mail me my password'.  When you get the password
+mail, log in with it.  Don't finish entering that new bug.
 
-5. Set up the whining cron job.
+    Now, bring up MySQL, and add yourself to every group.  This will
+effectively make you 'superuser'.  The SQL to type is:
 
-It's a good idea to set up a daily cronjob that does
+	update profiles set groupset=0x7fffffffffffffff where login_name = XXX;
 
-	cd <your-installation-dir> ; ./whineatnews.pl
+replacing XXX with your email address in quotes.
 
-This causes email that gets sent to anyone who has a NEW bug that
-hasn't been touched for several days.  For more info, see the
-whinedays and whinemail parameters.
+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.
 
+6. Setting Up the Whining Cron Job (Optional)
 
-6. Modifying your running system
+   By now you've got a fully functional bugzilla, but what good are bugs
+if they're not annoying?  To help make those bugs more annoying you can
+set up bugzilla's automatic whining system.  This can be done by adding
+the following command as a daily crontab entry (for help on that see that
+crontab man page):
 
-Bugzilla optimizes database lookups by storing all relatively static
-information in the versioncache file, located in the data/
-subdirectory under your installation directory (we said before it
-needs to be writable, right?!)
+	cd <your-bugzilla-directory> ; ./whineatnews.pl
 
-If you make a change to the structural data in your database (the
+7. Bug Graphs (Optional)
+
+   As long as you installed the GD and Graph::Base Perl modules you might
+as well turn on the nifty bugzilla bug reporting graphs.  Just add the
+command:
+	
+	cd <your-bugzilla-directory> ; ./collectstats.pl
+
+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.
+
+---------[ Appendices ]-----------------------
+
+Appendix A. Required Software Download Links
+
+   All of these sites are current as of February 17, 1999.  Hopefully
+they'll stay current for a while.
+
+MySQL: http://www.mysql.org
+
+Perl: http://www.perl.org
+
+CPAN: http://www.cpan.org
+
+DBI Perl module: ftp://ftp.cpan.org/pub/perl/CPAN/modules/by-module/DBI/
+
+Data::Dumper module:
+	ftp://ftp.cpan.org/pub/perl/CPAN/modules/by-module/Data/
+
+MySQL related Perl modules:
+	ftp://ftp.cpan.org/pub/perl/CPAN/modules/by-module/Mysql/
+
+TimeDate Perl module collection:
+	ftp://ftp.cpan.org/pub/perl/CPAN/modules/by-module/Date/
+
+
+GD Perl module: ftp://ftp.cpan.org/pub/perl/CPAN/modules/by-module/GD/
+
+Chart::Base module:
+	ftp://ftp.cpan.org/pub/perl/CPAN/modules/by-module/Chart/
+
+
+Appendix B. Modifying Your Running System
+
+   Bugzilla optimizes database lookups by storing all relatively static
+information in the versioncache file, located in the data/ subdirectory
+under your installation directory (we said before it needs to be writable,
+right?!)
+
+   If you make a change to the structural data in your database (the
 versions table for example), or to the "constants" encoded in
 defparams.pl, you will need to remove the cached content from the data
-directory (by doing a "rm data/versioncache"), or your changes won't
-show up!
+directory (by doing a "rm data/versioncache"), or your changes won't show
+up!
+
+   That file gets automatically regenerated whenever it's more than an
+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.
 
-That file gets automatically regenerated whenever it's more than an
-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
 
-7. Optional: Bug Graphs
+   This document was originally adapted from the Bonsai installation
+instructions by Terry Weissman <terry@mozilla.org>.
 
-Place collectstats.pl in your crontab once/day to take a snapshot
-of the number of open, assigned and reopened bugs for every 
-product. The tool will create a data/mining directory and append 
-the count to a file named for the product. After at least two points 
-of data are available, you can view a graph from the Bug Reports page. 
+   The February 25, 1999 re-write was done by Ry4an Brase
+<ry4an@ry4an.org>, with some edits by Terry Weissman.
diff --git a/addcomponent.cgi b/addcomponent.cgi
new file mode 100755
index 000000000..5c9ef7d45
--- /dev/null
+++ b/addcomponent.cgi
@@ -0,0 +1,99 @@
+#!/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): Sam Ziegler <sam@ziegler.org>
+# Terry Weissman <terry@mozilla.org>
+# Mark Hamby <mhamby@logicon.com>
+
+# Code derived from editcomponents.cgi, reports.cgi
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+# Shut up misguided -w warnings about "used only once":
+
+use vars @::legal_product;
+
+confirm_login();
+
+print "Content-type: text/html\n\n";
+
+if (!UserInGroup("editcomponents")) {
+    print "<H1>Sorry, you aren't a member of the 'editcomponents' group.</H1>\n";
+    print "And so, you aren't allowed to add a new component.\n";
+    exit;
+}
+
+
+PutHeader("Add Component");
+
+print "This page lets you add a component to bugzilla.\n";
+
+unlink "data/versioncache";
+GetVersionTable();
+
+my $prodcode = "P0";
+
+my $product_popup = make_options (\@::legal_product, $::legal_product[0]);
+
+print "
+      <form method=post action=doaddcomponent.cgi>
+
+      <TABLE>
+      <TR>
+              <th align=right>Component:</th>
+              <TD><input size=60 name=\"component\" value=\"\"></TD>
+      </TR>
+      <TR>
+              <TH  align=right>Program:</TH>
+              <TD><SELECT NAME=\"product\">
+                      $product_popup
+                      </SELECT></TD>
+      </TR>
+      <TR>
+              <TH  align=right>Description:</TH>
+              <TD><input size=60 name=\"description\" value=\"\"></TD>
+      </TR>
+      <TR>
+              <TH  align=right>Initial owner:</TH>
+              <TD><input size=60 name=\"initialowner\" value=\"\"></TD>
+      </TR>
+      ";
+
+if (Param('useqacontact')) {
+      print "
+              <TR>
+                      <TH  align=right>Initial QA contact:</TH>
+                      <TD><input size=60 name=\"initialqacontact\" value=\"\"></TD>
+              </TR>
+              ";
+}
+
+print "
+      </table>
+      <hr>
+      ";
+
+print "<input type=submit value=\"Submit changes\">\n";
+
+print "</form>\n";
+
+print "<p><a href=query.cgi>Skip all this, and go back to the query page</a>\n";
diff --git a/backdoor.cgi b/backdoor.cgi
index 93481f21a..0c4a013be 100755
--- a/backdoor.cgi
+++ b/backdoor.cgi
@@ -81,7 +81,7 @@ can teach me.";
 # Do remapping of things from BugSplat world to Bugzilla.
 
 if ($prod eq "Communicator") {
-    $prod = "Mozilla";
+    $prod = "Browser";
     $version = "other";
 }
 
@@ -133,7 +133,7 @@ if ($::FORM{'qa_contact'} ne "") {
 my @list = ('reporter', 'assigned_to', 'product', 'version', 'rep_platform',
             'op_sys', 'bug_status', 'bug_severity', 'priority', 'component',
             'short_desc', 'long_desc', 'creation_ts', 'delta_ts',
-            'bug_file_loc', 'qa_contact');
+            'bug_file_loc', 'qa_contact', 'groupset');
 
 my @vallist;
 foreach my $i (@list) {
diff --git a/bug_form.pl b/bug_form.pl
index d56d6b42a..1d7cae19d 100644
--- a/bug_form.pl
+++ b/bug_form.pl
@@ -21,6 +21,91 @@
 use diagnostics;
 use strict;
 
+
+my %knownattachments;
+
+# This routine quoteUrls contains inspirations from the HTML::FromText CPAN
+# module by Gareth Rees <garethr@cre.canon.co.uk>.  It has been heavily hacked,
+# all that is really recognizable from the original is bits of the regular
+# expressions.
+
+sub quoteUrls {
+    my $text = shift;		# Take a copy; don't modify in-place.
+    return $text unless $text;
+
+    my $base = Param('urlbase');
+
+    my $protocol = join '|',
+    qw(afs cid ftp gopher http https mid news nntp prospero telnet wais);
+
+    my %options = ( metachars => 1, @_ );
+
+    my $count = 0;
+
+    # Now, quote any "#" characters so they won't confuse stuff later
+    $text =~ s/#/%#/g;
+
+    # Next, find anything that looks like a URL or an email address and
+    # pull them out the the text, replacing them with a "##<digits>##
+    # marker, and writing them into an array.  All this confusion is
+    # necessary so that we don't match on something we've already replaced,
+    # which can happen if you do multiple s///g operations.
+
+    my @things;
+    while ($text =~ s%((mailto:)?([\w\.\-\+\=]+\@\w+(?:\.\w+)+)\b|
+                       (\b((?:$protocol):\S+[\w/])))%"##$count##"%exo) {
+        my $item = $&;
+
+        $item = value_quote($item);
+
+        if ($item !~ m/^$protocol:/o && $item !~ /^mailto:/) {
+            # We must have grabbed this one because it looks like an email
+            # address.
+            $item = qq{<A HREF="mailto:$item">$item</A>};
+        } else {
+            $item = qq{<A HREF="$item">$item</A>};
+        }
+
+        $things[$count++] = $item;
+    }
+    while ($text =~ s/\bbug(\s|%\#)*(\d+)/"##$count##"/ei) {
+        my $item = $&;
+        my $num = $2;
+        $item = value_quote($item); # Not really necessary, since we know
+                                # there's no special chars in it.
+        $item = qq{<A HREF="${base}show_bug.cgi?id=$num">$item</A>};
+        $things[$count++] = $item;
+    }
+    while ($text =~ s/\*\*\* This bug has been marked as a duplicate of (\d+) \*\*\*/"##$count##"/ei) {
+        my $item = $&;
+        my $num = $1;
+        $item =~ s@\d+@<A HREF="${base}show_bug.cgi?id=$num">$num</A>@;
+        $things[$count++] = $item;
+    }
+    while ($text =~ s/Created an attachment \(id=(\d+)\)/"##$count##"/e) {
+        my $item = $&;
+        my $num = $1;
+        if (exists $knownattachments{$num}) {
+            $item = qq{<A HREF="showattachment.cgi?attach_id=$num">$item</A>};
+        }
+        $things[$count++] = $item;
+    }
+
+    $text = value_quote($text);
+
+    # Stuff everything back from the array.
+    for (my $i=0 ; $i<$count ; $i++) {
+        $text =~ s/##$i##/$things[$i]/e;
+    }
+
+    # And undo the quoting of "#" characters.
+    $text =~ s/%#/#/g;
+
+    return $text;
+}
+
+quietly_check_login();
+
 my $query = "
 select
         bug_id,
@@ -40,9 +125,11 @@ select
 	target_milestone,
 	qa_contact,
 	status_whiteboard,
-        date_format(creation_ts,'Y-m-d')
+        date_format(creation_ts,'Y-m-d'),
+        groupset
 from bugs
-where bug_id = $::FORM{'id'}";
+where bug_id = $::FORM{'id'}
+and bugs.groupset & $::usergroupset = bugs.groupset";
 
 SendSQL($query);
 my %bug;
@@ -53,7 +140,8 @@ if (@row = FetchSQLData()) {
 		       "op_sys", "bug_status", "resolution", "priority",
 		       "bug_severity", "component", "assigned_to", "reporter",
 		       "bug_file_loc", "short_desc", "target_milestone",
-                       "qa_contact", "status_whiteboard", "creation_ts") {
+                       "qa_contact", "status_whiteboard", "creation_ts",
+                       "groupset") {
 	$bug{$field} = shift @row;
 	if (!defined $bug{$field}) {
 	    $bug{$field} = "";
@@ -110,7 +198,7 @@ print "
 <INPUT TYPE=HIDDEN NAME=\"id\" VALUE=$::FORM{'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>$bug{'bug_id'}</TD>
+    <TD ALIGN=RIGHT><B>Bug#:</B></TD><TD><A HREF=\"show_bug.cgi?id=$bug{'bug_id'}\">$bug{'bug_id'}</A></TD>
     <TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#rep_platform\">Platform:</A></B></TD>
     <TD><SELECT NAME=rep_platform>$platform_popup</SELECT></TD>
     <TD ALIGN=RIGHT><B>Version:</B></TD>
@@ -206,17 +294,48 @@ if (Param("usestatuswhiteboard")) {
   </TR>";
 }
 
+print "<tr><td align=right><B>Attachments:</b></td>\n";
+SendSQL("select attach_id, creation_ts, description from attachments where bug_id = $::FORM{'id'}");
+while (MoreSQLData()) {
+    my ($id, $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";
+    $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;
+}
+print "<td colspan=6><a href=createattachment.cgi?id=$::FORM{'id'}>Create a new attachment</a> (proposed patch, testcase, etc.)</td></tr>\n";
+
 
 print "
 </TABLE>
 <br>
 <B>Additional Comments:</B>
 <BR>
-<TEXTAREA WRAP=HARD NAME=comment ROWS=5 COLS=80></TEXTAREA><BR>
-<br>
+<TEXTAREA WRAP=HARD NAME=comment ROWS=5 COLS=80></TEXTAREA><BR>";
+
+
+if ($::usergroupset ne '0') {
+    SendSQL("select bit, description, (bit & $bug{'groupset'} != 0) from groups where bit & $::usergroupset != 0 and isbuggroup != 0 order by bit");
+    while (MoreSQLData()) {
+        my ($bit, $description, $ison) = (FetchSQLData());
+        my $check0 = !$ison ? " SELECTED" : "";
+        my $check1 = $ison ? " SELECTED" : "";
+        print "<select name=bit-$bit><option value=0$check0>\n";
+        print "People not in the \"$description\" group can see this bug\n";
+        print "<option value=1$check1>\n";
+        print "Only people in the \"$description\" group can see this bug\n";
+        print "</select><br>\n";
+    }
+}
+
+print "<br>
 <INPUT TYPE=radio NAME=knob VALUE=none CHECKED>
         Leave as <b>$bug{'bug_status'} $bug{'resolution'}</b><br>";
 
+
 # knum is which knob number we're generating, in javascript terms.
 
 my $knum = 1;
@@ -284,7 +403,9 @@ print "
 <td align=right>Opened:&nbsp;$bug{'creation_ts'}</td></tr></table>
 <HR>
 <PRE>
-" . html_quote($bug{'long_desc'}) . "
+";
+print quoteUrls($bug{'long_desc'}, email=>1, urls=>1);
+print "
 </PRE>
 <HR>\n";
 
diff --git a/bug_status.html b/bug_status.html
index e351b414a..a3706dffc 100755
--- a/bug_status.html
+++ b/bug_status.html
@@ -129,7 +129,8 @@ This field describes the impact of a bug.
 <a name="priority"><h2>Priority</h2></a>
 
 This field describes the importance and order in which a bug should be
-fixed.  The available priorities are:
+fixed.  This field is utilized by the programmers/engineers to
+prioritized their work to be done.  The available priorities are:
 
 <p>
 <p>
@@ -204,6 +205,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: Fri Jan 15 13:36:36 1999
+Last modified: Mon Mar  8 18:31:07 1999
 <!-- hhmts end -->
 </body> </html>
diff --git a/buglist.cgi b/buglist.cgi
index 0a1570273..a8c10d1c2 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -23,6 +23,7 @@ use diagnostics;
 use strict;
 
 require "CGI.pl";
+use Date::Parse;
 
 my $serverpush = 1;
 
@@ -42,7 +43,6 @@ if ($serverpush) {
 use vars @::legal_platform,
     @::versions,
     @::legal_product,
-    @::legal_component,
     %::MFORM,
     @::components,
     @::legal_severity,
@@ -178,12 +178,14 @@ my $dotweak = defined $::FORM{'tweak'};
 
 if ($dotweak) {
     confirm_login();
+} else {
+    quietly_check_login();
 }
 
 
 print "Content-type: text/html\n\n";
 
-my $query = "select bugs.bug_id";
+my $query = "select bugs.bug_id, bugs.groupset";
 
 
 foreach my $c (@collist) {
@@ -211,6 +213,7 @@ where  bugs.assigned_to = assign.userid
 and    bugs.reporter = report.userid
 and    bugs.product = projector.program
 and    bugs.version = projector.value
+and    bugs.groupset & $::usergroupset = bugs.groupset
 ";
 
 if ((defined $::FORM{'emailcc1'} && $::FORM{'emailcc1'}) ||
@@ -338,6 +341,57 @@ Click the <B>Back</B> button and try again.";
     }
 }
 
+my $ref = $::MFORM{'chfield'};
+
+
+sub SqlifyDate {
+    my ($str) = (@_);
+    if (!defined $str) {
+        $str = "";
+    }
+    my $date = str2time($str);
+    if (!defined $date) {
+        print "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;
+    }
+    return time2str("'%Y/%m/%d %H:%M:%S'", $date);
+}
+
+
+        
+
+
+if (defined $ref && 0 < @$ref) {
+    # Do surgery on the query to tell it to patch in the bugs_activity
+    # table.
+    $query =~ s/bugs,/bugs, bugs_activity,/;
+    
+    my @list;
+    foreach my $f (@$ref) {
+        push(@list, "\nbugs_activity.field = " . SqlQuote($f));
+    }
+    $query .= "and bugs_activity.bug_id = bugs.bug_id and (" .
+        join(' or ', @list) . ") ";
+    $query .= "and bugs_activity.when >= " .
+        SqlifyDate($::FORM{'chfieldfrom'}) . "\n";
+    my $to = $::FORM{'chfieldto'};
+    if (defined $to) {
+        $to = trim($to);
+        if ($to ne "" && $to !~ /^now$/i) {
+            $query .= "and bugs_activity.when <= " . SqlifyDate($to) . "\n";
+        }
+    }
+    my $value = $::FORM{'chfieldvalue'};
+    if (defined $value) {
+        $value = trim($value);
+        if ($value ne "") {
+            $query .= "and bugs_activity.newvalue = " .
+                SqlQuote($value) . "\n";
+        }
+    }
+}
+
 foreach my $f ("short_desc", "long_desc", "bug_file_loc",
                "status_whiteboard") {
     if (defined $::FORM{$f}) {
@@ -402,7 +456,7 @@ $fields =~ s/[&?]cmdtype=[^&]*//g;
 
 my $oldorder;
 
-if (defined $::FORM{'order'}) {
+if (defined $::FORM{'order'} && trim($::FORM{'order'}) ne "") {
     $oldorder = url_quote(", $::FORM{'order'}");
 } else {
     $oldorder = "";
@@ -440,9 +494,19 @@ my %seen;
 my @bugarray;
 my %prodhash;
 my %statushash;
+my $buggroupset = "";
 
 while (@row = FetchSQLData()) {
     my $bug_id = shift @row;
+    my $g = shift @row;         # Bug's group set.
+    if ($buggroupset eq "") {
+        $buggroupset = $g;
+    } elsif ($buggroupset ne $g) {
+        $buggroupset = "x";     # We only play games with tweaking the
+                                # buggroupset if all the bugs have exactly
+                                # the same group.  If they don't, we leave
+                                # it alone.
+    }
     if (!defined $seen{$bug_id}) {
         $seen{$bug_id} = 1;
         $count++;
@@ -564,7 +628,7 @@ document.write(\" <input type=button value=\\\"Uncheck All\\\" onclick=\\\"SetCh
     my @list = @prod_list;
     my @legal_versions;
     my @legal_component;
-    if ($#prod_list == 1) {
+    if (1 == @prod_list) {
         @legal_versions = @{$::versions{$prod_list[0]}};
         @legal_component = @{$::components{$prod_list[0]}};
     }
@@ -573,7 +637,7 @@ document.write(\" <input type=button value=\\\"Uncheck All\\\" onclick=\\\"SetCh
     my $platform_popup = make_options(\@::legal_platform, $::dontchange);
     my $priority_popup = make_options(\@::legal_priority, $::dontchange);
     my $sev_popup = make_options(\@::legal_severity, $::dontchange);
-    my $component_popup = make_options(\@::legal_component, $::dontchange);
+    my $component_popup = make_options(\@legal_component, $::dontchange);
     my $product_popup = make_options(\@::legal_product, $::dontchange);
 
 
@@ -628,6 +692,23 @@ document.write(\" <input type=button value=\\\"Uncheck All\\\" onclick=\\\"SetCh
 <BR>
 <TEXTAREA WRAP=HARD NAME=comment ROWS=5 COLS=80></TEXTAREA><BR>";
 
+if ($::usergroupset ne '0' && $buggroupset =~ /^\d*$/) {
+    SendSQL("select bit, description, (bit & $buggroupset != 0) from groups where bit & $::usergroupset != 0 and isbuggroup != 0 order by bit");
+    while (MoreSQLData()) {
+        my ($bit, $description, $ison) = (FetchSQLData());
+        my $check0 = !$ison ? " SELECTED" : "";
+        my $check1 = $ison ? " SELECTED" : "";
+        print "<select name=bit-$bit><option value=0$check0>\n";
+        print "People not in the \"$description\" group can see these bugs\n";
+        print "<option value=1$check1>\n";
+        print "Only people in the \"$description\" group can see these bugs\n";
+        print "</select><br>\n";
+    }
+}
+
+
+
+
     # knum is which knob number we're generating, in javascript terms.
 
     my $knum = 0;
@@ -707,7 +788,8 @@ if ($count > 0) {
 <INPUT TYPE=HIDDEN NAME=buglist VALUE=$buglist>
 <INPUT TYPE=SUBMIT VALUE=\"Long Format\">
 <A HREF=\"query.cgi\">Query Page</A>
-<A HREF=\"colchange.cgi?$::buffer\">Change columns</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>";
     if (!$dotweak && $count > 1) {
         print "<A HREF=\"buglist.cgi?$fields&tweak=1\">Make changes to several of these bugs at once.</A>\n";
diff --git a/collectstats.pl b/collectstats.pl
index 05e6204cb..e35c7aa23 100755
--- a/collectstats.pl
+++ b/collectstats.pl
@@ -31,55 +31,43 @@ require "globals.pl";
 ConnectToDatabase();
 GetVersionTable();
 
-foreach (@::legal_product)
-	{
-	my $dir = "data/mining";
+foreach (@::legal_product) {
+    my $dir = "data/mining";
 
-	&check_data_dir ($dir);
-	&collect_stats ($dir, $_);
-	}
+    &check_data_dir ($dir);
+    &collect_stats ($dir, $_);
+}
 
-sub check_data_dir
-	{
-	my $dir = shift;
+sub check_data_dir {
+    my $dir = shift;
 
-	if (! -d)
-		{
-		mkdir $dir, 0777;
-		chmod 0777, $dir;
-		}
-	}
+    if (! -d) {
+        mkdir $dir, 0777;
+        chmod 0777, $dir;
+    }
+}
 
-sub collect_stats
-	{
-	my $dir = shift;
-	my $product = shift;
-	my $when = localtime (time);
+sub collect_stats {
+    my $dir = shift;
+    my $product = shift;
+    my $when = localtime (time);
 
-	my $query = <<FIN;
-select count(bug_status) from bugs where 
-(bug_status='NEW' or  bug_status='ASSIGNED' or bug_status='REOPENED')
-and product='$product' group by bug_status
-FIN
-        $product =~ s/\//-/gs;
-	my $file = join '/', $dir, $product;
-	my $exists = -f $file;
-
-	if (open DATA, ">>$file")
-		{
-		SendSQL ($query);
-
-		my %count;
-		push my @row, &today;
-		
-		while (my @n = FetchSQLData())
-			{
-			push @row, @n;
-			}
-		
-		if (! $exists)
-			{
-			print DATA <<FIN;
+
+    $product =~ s/\//-/gs;
+    my $file = join '/', $dir, $product;
+    my $exists = -f $file;
+
+    if (open DATA, ">>$file") {
+        push my @row, &today;
+
+        foreach my $status ('NEW', 'ASSIGNED', 'REOPENED') {
+            SendSQL("select count(bug_status) from bugs where bug_status='$status' and product='$product'");
+            push @row, FetchOneColumn();
+        }
+
+        if (! $exists)
+        {
+            print DATA <<FIN;
 # Bugzilla daily bug stats
 #
 # do not edit me! this file is generated.
@@ -88,20 +76,17 @@ FIN
 # product: $product
 # created: $when
 FIN
-			}
-
-		print DATA (join '|', @row) . "\n";
-		close DATA;
-		}
-	else
-		{
-		print "$0: $file, $!";
-		}
 	}
 
-sub today
-  {
-  my ($dom, $mon, $year) = (localtime(time))[3, 4, 5];
-  return sprintf "%04d%02d%02d", 1900 + $year, ++$mon, $dom;
-  }
+        print DATA (join '|', @row) . "\n";
+        close DATA;
+    } else {
+        print "$0: $file, $!";
+    }
+}
+
+sub today {
+    my ($dom, $mon, $year) = (localtime(time))[3, 4, 5];
+    return sprintf "%04d%02d%02d", 1900 + $year, ++$mon, $dom;
+}
 
diff --git a/createaccount.cgi b/createaccount.cgi
new file mode 100755
index 000000000..c6bef90cf
--- /dev/null
+++ b/createaccount.cgi
@@ -0,0 +1,79 @@
+#!/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>
+#                 David Gardiner <david.gardiner@unisa.edu.au>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+# Shut up misguided -w warnings about "used only once":
+
+use vars %::FORM;
+
+ConnectToDatabase();
+
+# Clear out the login cookies.  Make people log in again if they create an
+# account; otherwise, they'll probably get confused.
+
+print "Set-Cookie: Bugzilla_login= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT
+Set-Cookie: Bugzilla_logincookie= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT
+Set-Cookie: Bugzilla_password= ; path=/; expires=Sun, 30-Jun-80 00:00:00 GMT
+Content-type: text/html
+
+";
+
+PutHeader("Create a new bugzilla account");
+
+my $login = $::FORM{'login'};
+if (defined $login) {
+    CheckEmailSyntax($login);
+    if (DBname_to_id($login) != 0) {
+	print "A bugzilla account for the name <tt>$login</tt> already\n";
+	print "exists.  If you have forgotten the password for it, then\n";
+	print "<a href=query.cgi?GoAheadAndLogIn>click here</a> and use\n";
+	print "the <b>E-mail me a password</b> button.\n";
+	exit;
+    }
+    DBNameToIdAndCheck($login, 1);
+    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";
+    print "here</a> and log in.  Or, you can just <a href=\"\">go back to\n";
+    print "the top</a>.";
+    exit;
+}
+
+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.
+
+<FORM method=get>
+<table>
+<tr>
+<td align=right><b>E-mail address:</b></td>
+<td><input size=35 name=login></td>
+</tr>
+</table>
+<input type=submit>
+};
+
diff --git a/createattachment.cgi b/createattachment.cgi
new file mode 100755
index 000000000..80dff3520
--- /dev/null
+++ b/createattachment.cgi
@@ -0,0 +1,112 @@
+#!/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>
+#                 David Gardiner <david.gardiner@unisa.edu.au>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+use vars %::COOKIE, %::FILENAME;
+
+sub Punt {
+    my ($str) = (@_);
+    print "$str<P>Please hit <b>Back</b> and try again.\n";
+    exit;
+}
+
+
+confirm_login();
+
+print "Content-type: text/html\n\n";
+
+my $id = $::FORM{'id'};
+
+PutHeader("Create an attachment", "Create attachment", "Bug $id");
+
+
+if (!defined($::FORM{'data'})) {
+    print qq{
+<form method=post ENCTYPE="multipart/form-data">
+<input type=hidden name=id value=$id>
+To attach a file to <a href="show_bug.cgi?id=$id">bug $id</a>, place it in a
+file on your local machine, and enter the path to that file here:<br>
+<input type=file name=data size=60>
+<P>
+Please also provide a one-line description of this attachment:<BR>
+<input name=description size=60>
+<BR>
+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="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>
+<input type=submit value="Submit">
+</form>
+<P>
+};
+} else {
+    if ($::FORM{'data'} eq "" || !defined $::FILENAME{'data'}) {
+        Punt("No file was provided, or it was empty.");
+    }
+    my $desc = trim($::FORM{'description'});
+    if ($desc eq "") {
+        Punt("You must provide a description of your attachment.");
+    }
+    my $ispatch = 0;
+    my $mimetype = $::FORM{'type'};
+    if (!defined $mimetype) {
+        Punt("You must select which kind of file you have.");
+    }
+    $mimetype = trim($mimetype);
+    if ($mimetype eq "patch") {
+        $mimetype = "text/plain";
+        $ispatch = 1;
+    }
+    if ($mimetype eq "other") {
+        $mimetype = $::FORM{'othertype'};
+    }
+    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," .
+            SqlQuote($::FILENAME{'data'}) . ", " . SqlQuote($desc) . ", " .
+            SqlQuote($mimetype) . ", $ispatch, " .
+            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 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 cfb41fbae..9bcc9a567 100644
--- a/defparams.pl
+++ b/defparams.pl
@@ -90,17 +90,10 @@ sub check_numeric {
 #	     the database tables.  The name of the parameter is of the form
 #	     "tablename.columnname".
 
-# This very first one is silly.  At some point, "superuserness" should be an
-# attribute of the person's profile entry, and not a single name like this.
-#
-# When first installing bugzilla, you need to either change this line to be
-# you, or (better) edit the initial "params" file and change the entry for
-# param(maintainer).
-
 DefParam("maintainer",
 	 "The email address of the person who maintains this installation of Bugzilla.",
 	 "t",
-         'terry@mozilla.org');
+         'THE MAINTAINER HAS NOT YET BEEN SET');
 
 DefParam("urlbase",
 	 "The URL that is the common initial leading part of all Bugzilla URLs.",
@@ -138,6 +131,10 @@ sub check_despotbaseurl {
 }
 
 
+DefParam("headerhtml",
+         "Additional HTML to add to the HEAD area of documents, eg. links to stylesheets.",
+         "l",
+         '');
 
 
 DefParam("bannerhtml",
@@ -243,6 +240,30 @@ DefParam("defaultquery",
 	 "t",
 	 "bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=Mozilla&order=%22Importance%22");
 
+
+DefParam("letsubmitterchoosepriority",
+         "If this is on, then people submitting bugs can choose an initial priority for that bug.  If off, then all bugs initially have the default priority selected above.",
+         "b",
+         1);
+
+
+sub check_priority {
+    my ($value) = (@_);
+    GetVersionTable();
+    if (lsearch(\@::legal_priority, $value) < 0) {
+        return "Must be a legal priority value: one of " .
+            join(", ", @::legal_priority);
+    }
+    return "";
+}
+
+DefParam("defaultpriority",
+         "This is the priority that newly entered bugs are set to.",
+         "t",
+         "P2",
+         \&check_priority);
+
+
 DefParam("usetargetmilestone",
 	 "Do you wish to use the Target Milestone field?",
 	 "b",
diff --git a/doaddcomponent.cgi b/doaddcomponent.cgi
new file mode 100755
index 000000000..c7856fff1
--- /dev/null
+++ b/doaddcomponent.cgi
@@ -0,0 +1,99 @@
+#!/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): Sam Ziegler <sam@ziegler.org>
+# Terry Weissman <terry@mozilla.org>
+# Mark Hamby <mhamby@logicon.com>
+
+# Code derived from doeditcomponents.cgi
+
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+confirm_login();
+
+print "Content-type: text/html\n\n";
+
+# foreach my $i (sort(keys %::FORM)) {
+#     print value_quote("$i $::FORM{$i}") . "<BR>\n";
+# }
+
+if (!UserInGroup("editcomponents")) {
+    print "<H1>Sorry, you aren't a member of the 'editcomponents' group.</H1>\n";
+    print "And so, you aren't allowed to add components.\n";
+    exit;
+}
+
+
+PutHeader("Adding new component");
+
+unlink "data/versioncache";
+GetVersionTable();
+
+my $component = trim($::FORM{"component"});
+my $product = trim($::FORM{"product"});
+my $description = trim($::FORM{"description"});
+my $initialowner = trim($::FORM{"initialowner"});
+my $initialqacontact = trim($::FORM{"initialqacontact"});
+
+if ($component eq "") {
+    print "You must enter a name for the new component.  Please press\n";
+    print "<b>Back</b> and try again.\n";
+    exit;
+}
+
+# Check to ensure the component doesn't exist already.
+SendSQL("SELECT value FROM components WHERE " .
+      "program = " . SqlQuote($product) . " and " .
+      "value = " . SqlQuote($component));
+my @row = FetchSQLData();
+if (@row) {
+        print "<H1>Component already exists</H1>";
+        print "The component '$component' already exists\n";
+        print "for product '$product'.<P>\n";
+        print "<p><a href=query.cgi>Go back to the query page</a>\n";
+        exit;
+}
+
+# Check that the email addresses are legitimate.
+foreach my $addr ($initialowner, $initialqacontact) {
+    if ($addr ne "") {
+        DBNameToIdAndCheck($addr);
+    }
+}
+
+# Add the new component.
+SendSQL("INSERT INTO components ( " .
+      "value, program, description, initialowner, initialqacontact" .
+      " ) VALUES ( " .
+      SqlQuote($component) . "," .
+      SqlQuote($product) . "," .
+      SqlQuote($description) . "," .
+      SqlQuote($initialowner) . "," .
+      SqlQuote($initialqacontact) . ")" );
+
+unlink "data/versioncache";
+
+print "OK, done.<p>\n";
+print "<a href=addcomponent.cgi>Edit another new component.</a><p>\n";
+print "<a href=editcomponents.cgi>Edit existing components.</a><p>\n";
+print "<a href=query.cgi>Go back to the query page.</a>\n";
diff --git a/doeditcomponents.cgi b/doeditcomponents.cgi
new file mode 100755
index 000000000..048ec1185
--- /dev/null
+++ b/doeditcomponents.cgi
@@ -0,0 +1,149 @@
+#!/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): Sam Ziegler <sam@ziegler.org>
+# Terry Weissman <terry@mozilla.org>
+
+# Code derived from doeditowners.cgi
+
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+
+# Shut up misguided -w warnings about "used only once":
+
+use vars @::legal_product;
+
+
+confirm_login();
+
+print "Content-type: text/html\n\n";
+
+# foreach my $i (sort(keys %::FORM)) {
+#     print value_quote("$i $::FORM{$i}") . "<BR>\n";
+# }
+
+if (!UserInGroup("editcomponents")) {
+    print "<H1>Sorry, you aren't a member of the 'editcomponents' group.</H1>\n";
+    print "And so, you aren't allowed to edit the owners.\n";
+    exit;
+}
+
+
+sub Check {
+    my ($code1, $code2) = (@_);
+    if ($code1 ne $code2) {
+        print "<H1>A race error has occurred.</H1>";
+        print "It appears that someone else has been changing the database\n";
+        print "while you've been editing it.  I'm afraid you will have to\n";
+        print "start all over.  Sorry! <P>\n";
+        print "<p><a href=query.cgi>Go back to the query page</a>\n";
+        exit;
+    }
+}
+
+
+my @cmds;
+
+sub DoOne {
+    my ($oldvalue, $field, $where, $checkemail) = (@_);
+    if (!defined $::FORM{$field}) {
+        print "ERROR -- $field not defined!";
+        exit;
+    }
+    if ($oldvalue ne $::FORM{$field}) {
+        my $name = $field;
+        $name =~ s/^.*-//;
+        my $table = "products";
+        if ($field =~ /^P\d+-C\d+-/) {
+            $table = "components";
+        }
+        push @cmds, "update $table set $name=" .
+            SqlQuote($::FORM{$field}) . "where $where";
+        print "Changed $name for $where <P>";
+        if ($checkemail) {
+            DBNameToIdAndCheck($::FORM{$field});
+        }
+    }
+}
+            
+
+
+PutHeader("Saving new component info");
+
+unlink "data/versioncache";
+GetVersionTable();
+
+my $prodcode = "P0";
+
+foreach my $product (@::legal_product) {
+    SendSQL("select description, milestoneurl, disallownew from products where product='$product'");
+    my @row = FetchSQLData();
+    if (!@row) {
+        next;
+    }
+    my ($description, $milestoneurl, $disallownew) = (@row);
+    $prodcode++;
+    Check($product, $::FORM{"prodcode-$prodcode"});
+
+    my $where = "product=" . SqlQuote($product);
+    DoOne($description, "$prodcode-description", $where);
+    if (Param('usetargetmilestone')) {
+        DoOne($milestoneurl, "$prodcode-milestoneurl", $where);
+    }
+    DoOne($disallownew, "$prodcode-disallownew", $where);
+
+    SendSQL("select value, initialowner, initialqacontact, description from components where program=" . SqlQuote($product) . " order by value");
+    my $c = 0;
+    while (my @row = FetchSQLData()) {
+        my ($component, $initialowner, $initialqacontact, $description) =
+            (@row);
+        $c++;
+        my $compcode = $prodcode . "-" . "C$c";
+
+	Check($component, $::FORM{"compcode-$compcode"});
+
+        my $where = "program=" . SqlQuote($product) . " and value=" .
+            SqlQuote($component);
+
+        DoOne($initialowner, "$compcode-initialowner", $where, 1);
+        if (Param('useqacontact')) {
+            DoOne($initialqacontact, "$compcode-initialqacontact", $where,
+                  1);
+        }
+        DoOne($description, "$compcode-description", $where);
+    }
+
+}
+
+print "Saving changes.<P>\n";
+
+foreach my $cmd (@cmds) {
+    print "$cmd <BR>";
+    SendSQL($cmd);
+}
+
+unlink "data/versioncache";
+
+print "OK, done.<p>\n";
+print "<a href=editcomponents.cgi>Edit the components some more.</a><p>\n";
+print "<a href=query.cgi>Go back to the query page.</a>\n";
diff --git a/doeditowners.cgi b/doeditowners.cgi
index b09d7298b..540b4bfb0 100755
--- a/doeditowners.cgi
+++ b/doeditowners.cgi
@@ -24,17 +24,13 @@ use strict;
 
 require "CGI.pl";
 
-# Shut up misguided -w warnings about "used only once":
-use vars %::COOKIE;
-
-
 confirm_login();
 
 print "Content-type: text/html\n\n";
 
-if (Param("maintainer") ne $::COOKIE{'Bugzilla_login'}) {
-    print "<H1>Sorry, you aren't the maintainer of this system.</H1>\n";
-    print "And so, you aren't allowed to edit the parameters of it.\n";
+if (!UserInGroup("editcomponents")) {
+    print "<H1>Sorry, you aren't a member of the 'editcomponents' group.</H1>\n";
+    print "And so, you aren't allowed to edit the owners.\n";
     exit;
 }
 
diff --git a/doeditparams.cgi b/doeditparams.cgi
index dd6214982..1df99f077 100755
--- a/doeditparams.cgi
+++ b/doeditparams.cgi
@@ -28,17 +28,16 @@ require "defparams.pl";
 # Shut up misguided -w warnings about "used only once":
 use vars %::param,
     %::param_default,
-    @::param_list,
-    %::COOKIE;
+    @::param_list;
 
 
 confirm_login();
 
 print "Content-type: text/html\n\n";
 
-if (Param("maintainer") ne $::COOKIE{'Bugzilla_login'}) {
-    print "<H1>Sorry, you aren't the maintainer of this system.</H1>\n";
-    print "And so, you aren't allowed to edit the parameters of it.\n";
+if (!UserInGroup("tweakparams")) {
+    print "<H1>Sorry, you aren't a member of the 'tweakparams' group.</H1>\n";
+    print "And so, you aren't allowed to edit the parameters.\n";
     exit;
 }
 
diff --git a/editcomponents.cgi b/editcomponents.cgi
new file mode 100755
index 000000000..1de259924
--- /dev/null
+++ b/editcomponents.cgi
@@ -0,0 +1,120 @@
+#!/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): Sam Ziegler <sam@ziegler.org>
+# Terry Weissman <terry@mozilla.org>
+
+# Code derived from editparams.cgi, editowners.cgi
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+# Shut up misguided -w warnings about "used only once":
+
+use vars @::legal_product;
+
+confirm_login();
+
+print "Content-type: text/html\n\n";
+
+if (!UserInGroup("editcomponents")) {
+    print "<H1>Sorry, you aren't a member of the 'editcomponents' group.</H1>\n";
+    print "And so, you aren't allowed to edit the owners.\n";
+    exit;
+}
+
+
+PutHeader("Edit Components");
+
+print "This lets you edit the program components of bugzilla.\n";
+print "<hr>";
+print "<a href=addcomponent.cgi>Add new component.</a><br>\n";
+print "<hr>";
+
+print "<form method=post action=doeditcomponents.cgi>\n";
+
+my $rowbreak = "<tr><td colspan=2><hr></td></tr>";
+
+unlink "data/versioncache";
+GetVersionTable();
+
+my $prodcode = "P0";
+
+foreach my $product (@::legal_product) {
+    SendSQL("select description, milestoneurl, disallownew from products where product='$product'");
+    my @row = FetchSQLData();
+    if (!@row) {
+        next;
+    }
+    my ($description, $milestoneurl, $disallownew) = (@row);
+    $prodcode++;
+    print "<input type=hidden name=prodcode-$prodcode value=\"" .
+        value_quote($product) . "\">\n";
+    print "<table><tr><th align=left valign=top>$product</th><td></td></tr>\n";
+    print "<tr><th align=right>Description:</th>\n";
+    print "<td><input size=80 name=$prodcode-description value=\"" .
+        value_quote($description) . "\"></td></tr>\n";
+    if (Param('usetargetmilestone')) {
+        print "<tr><th align=right>MilestoneURL:</th>\n";
+        print "<td><input size=80 name=$prodcode-milestoneurl value=\"" .
+            value_quote($milestoneurl) . "\"></td></tr>\n";
+    }
+    my $check0 = !$disallownew ? " SELECTED" : "";
+    my $check1 = $disallownew ? " SELECTED" : "";
+    print "<tr><td colspan=2><select name=$prodcode-disallownew>\n";
+    print "<option value=0$check0>Open; new bugs may be submitted against this project\n";
+    print "<option value=1$check1>Closed; no new bugs may be submitted against this project\n";
+    print "</select></td></tr>\n";
+
+    print "<tr><td colspan=2>Components:</td></tr></table>\n";
+    print "<table>\n";
+    
+    SendSQL("select value, initialowner, initialqacontact, description from components where program=" . SqlQuote($product) . " order by value");
+    my $c = 0;
+    while (my @row = FetchSQLData()) {
+        my ($component, $initialowner, $initialqacontact, $description) =
+            (@row);
+        $c++;
+        my $compcode = $prodcode . "-" . "C$c";
+        print "<input type=hidden name=compcode-$compcode value=\"" .
+            value_quote($component) . "\">\n";
+        print "<tr><th>$component</th><th align=right>Description:</th>\n";
+        print "<td><input size=80 name=$compcode-description value=\"" .
+        value_quote($description) . "\"></td></tr>\n";
+        print "<tr><td></td><th align=right>Initial owner:</th>\n";
+        print "<td><input size=60 name=$compcode-initialowner value=\"" .
+        value_quote($initialowner) . "\"></td></tr>\n";
+        if (Param('useqacontact')) {
+            print "<tr><td></td><th align=right>Initial QA contact:</th>\n";
+            print "<td><input size=60 name=$compcode-initialqacontact value=\"" .
+                value_quote($initialqacontact) . "\"></td></tr>\n";
+        }
+    }
+
+    print "</table><hr>\n";
+
+}
+
+print "<input type=submit value=\"Submit changes\">\n";
+
+print "</form>\n";
+
+print "<p><a href=query.cgi>Skip all this, and go back to the query page</a>\n";
diff --git a/editowners.cgi b/editowners.cgi
index 1bfb6ac13..1f4a7742a 100755
--- a/editowners.cgi
+++ b/editowners.cgi
@@ -26,16 +26,13 @@ use strict;
 
 require "CGI.pl";
 
-# Shut up misguided -w warnings about "used only once":
-use vars %::COOKIE;
-
 confirm_login();
 
 print "Content-type: text/html\n\n";
 
-if (Param("maintainer") ne $::COOKIE{Bugzilla_login}) {
-    print "<H1>Sorry, you aren't the maintainer of this system.</H1>\n";
-    print "And so, you aren't allowed to edit the parameters of it.\n";
+if (!UserInGroup("editcomponents")) {
+    print "<H1>Sorry, you aren't a member of the 'editcomponents' group.</H1>\n";
+    print "And so, you aren't allowed to edit the owners.\n";
     exit;
 }
 
diff --git a/editparams.cgi b/editparams.cgi
index 75c7500d7..0171860da 100755
--- a/editparams.cgi
+++ b/editparams.cgi
@@ -28,16 +28,15 @@ require "defparams.pl";
 
 # Shut up misguided -w warnings about "used only once":
 use vars @::param_desc,
-    @::param_list,
-    %::COOKIE;
+    @::param_list;
 
 confirm_login();
 
 print "Content-type: text/html\n\n";
 
-if (Param("maintainer") ne $::COOKIE{Bugzilla_login}) {
-    print "<H1>Sorry, you aren't the maintainer of this system.</H1>\n";
-    print "And so, you aren't allowed to edit the parameters of it.\n";
+if (!UserInGroup("tweakparams")) {
+    print "<H1>Sorry, you aren't a member of the 'tweakparams' group.</H1>\n";
+    print "And so, you aren't allowed to edit the parameters.\n";
     exit;
 }
 
diff --git a/enter_bug.cgi b/enter_bug.cgi
index 9b3651522..83792e851 100755
--- a/enter_bug.cgi
+++ b/enter_bug.cgi
@@ -43,6 +43,12 @@ if (!defined $::FORM{'product'}) {
         print "a bug.</H2>\n";
         print "<table>";
         foreach my $p (sort (@prodlist)) {
+            if (defined $::proddesc{$p} && $::proddesc{$p} eq '0') {
+                # Special hack.  If we stuffed a "0" into proddesc, that means
+                # that disallownew was set for this bug, and so we don't want
+                # 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";
             if (defined $::proddesc{$p}) {
                 print "<td valign=top>$::proddesc{$p}</td>\n";
@@ -157,8 +163,10 @@ my $assign_element = GeneratePersonInput('assigned_to', 1,
 my $cc_element = GeneratePeopleInput('cc', formvalue('cc'));
 
 
+my $priority = Param('defaultpriority');
+
 my $priority_popup = make_popup('priority', \@::legal_priority,
-                                formvalue('priority', 'P2'), 0);
+                                formvalue('priority', $priority), 0);
 my $sev_popup = make_popup('bug_severity', \@::legal_severity,
                            formvalue('bug_severity', 'normal'), 0);
 my $platform_popup = make_popup('rep_platform', \@::legal_platform,
@@ -174,7 +182,7 @@ print "
 <FORM NAME=enterForm 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=product VALUE=$product>
+<INPUT TYPE=HIDDEN NAME=product VALUE=\""  . value_quote($product) . "\">
   <TABLE CELLSPACING=2 CELLPADDING=0 BORDER=0>
   <TR>
     <td ALIGN=right valign=top><B>Product:</B></td>
@@ -197,9 +205,16 @@ print "
     <td rowspan=3></td>
     <td></td>
   </TR>
-  <TR>
+  <TR>";
+if (Param('letsubmitterchoosepriority')) {
+    print "
     <TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#priority\">Priority</A>:</B></TD>
-    <TD>$priority_popup</TD>
+    <TD>$priority_popup</TD>";
+} else {
+    print '<INPUT TYPE=HIDDEN NAME=priority VALUE="' .
+        value_quote($priority) . '">';
+}
+print "
     <TD ALIGN=RIGHT><B><A HREF=\"bug_status.html#severity\">Severity</A>:</B></TD>
     <TD>$sev_popup</TD>
     <td></td>
diff --git a/globals.pl b/globals.pl
index ac6b4b7cc..ddf2a1f5f 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.2';
+$::param{'version'} = '2.4';
 
 $::dontchange = "--do_not_change--";
 $::chooseone = "--Choose_one:--";
@@ -78,7 +78,13 @@ sub FetchOneColumn {
 
 sub AppendComment {
     my ($bugid,$who,$comment) = (@_);
-    $comment =~ s/\r\n/\n/;     # Get rid of windows-style line endings.
+    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;
     }
@@ -235,10 +241,16 @@ sub GenerateVersionTable {
     my $dotargetmilestone = Param("usetargetmilestone");
 
     my $mpart = $dotargetmilestone ? ", milestoneurl" : "";
-    SendSQL("select product, description$mpart from products");
+    SendSQL("select product, description, disallownew$mpart from products");
     while (@line = FetchSQLData()) {
-        my ($p, $d, $u) = (@line);
+        my ($p, $d, $dis, $u) = (@line);
         $::proddesc{$p} = $d;
+        if ($dis) {
+            # Special hack.  Stomp on the description and make it "0" if we're
+            # not supposed to allow new bugs against this product.  This is
+            # checked for in enter_bug.cgi.
+            $::proddesc{$p} = "0";
+        }
         if ($dotargetmilestone) {
             $::milestoneurl{$p} = $u;
         }
@@ -254,6 +266,7 @@ sub GenerateVersionTable {
             splice(@::log_columns, $w, 1);
         }
     }
+    @::log_columns = (sort(@::log_columns));
 
     @::legal_priority = SplitEnumType($cols->{"priority,type"});
     @::legal_severity = SplitEnumType($cols->{"bug_severity,type"});
@@ -350,7 +363,19 @@ sub InsertNewUser {
     for (my $i=0 ; $i<8 ; $i++) {
         $password .= substr("abcdefghijklmnopqrstuvwxyz", int(rand(26)), 1);
     }
-    SendSQL("insert into profiles (login_name, password, cryptpassword) values (@{[SqlQuote($username)]}, '$password', encrypt('$password'))");
+    SendSQL("select bit, userregexp from groups where userregexp != ''");
+    my $groupset = "0";
+    while (MoreSQLData()) {
+        my @row = FetchSQLData();
+        if ($username =~ m/$row[1]/) {
+            $groupset .= "+ $row[0]"; # Silly hack to let MySQL do the math,
+                                      # not Perl, since we're dealing with 64
+                                      # bit ints here, and I don't *think* Perl
+                                      # does that.
+        }
+    }
+            
+    SendSQL("insert into profiles (login_name, password, cryptpassword, groupset) values (@{[SqlQuote($username)]}, '$password', encrypt('$password'), $groupset)");
     return $password;
 }
 
@@ -478,6 +503,21 @@ sub SqlQuote {
 
 
 
+sub UserInGroup {
+    my ($groupname) = (@_);
+    if ($::usergroupset eq "0") {
+        return 0;
+    }
+    ConnectToDatabase();
+    SendSQL("select (bit & $::usergroupset) != 0 from groups where name = " . SqlQuote($groupname));
+    my $bit = FetchOneColumn();
+    if ($bit) {
+        return 1;
+    }
+    return 0;
+}
+
+
 sub Param {
     my ($value) = (@_);
     if (defined $::param{$value}) {
diff --git a/index.html b/index.html
index a8e36662f..7cef3c1e2 100644
--- a/index.html
+++ b/index.html
@@ -69,7 +69,8 @@ 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>
+<a href="reports.cgi">Bug reports</a><br>
+<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>
 
diff --git a/long_list.cgi b/long_list.cgi
index 18f3c0e27..723aa2cf3 100755
--- a/long_list.cgi
+++ b/long_list.cgi
@@ -31,6 +31,9 @@ use vars %::FORM;
 print "Content-type: text/html\n\n";
 print "<TITLE>Full Text Bug Listing</TITLE>\n";
 
+ConnectToDatabase();
+quietly_check_login();
+
 my $generic_query  = "
 select
   bugs.bug_id,
@@ -52,9 +55,7 @@ select
   bugs.status_whiteboard
 from bugs,profiles assign,profiles report
 where assign.userid = bugs.assigned_to and report.userid = bugs.reporter and
-";
-
-ConnectToDatabase();
+bugs.groupset & $::usergroupset = bugs.groupset and";
 
 foreach my $bug (split(/:/, $::FORM{'buglist'})) {
     SendSQL("$generic_query bugs.bug_id = $bug");
diff --git a/makeactivitytable.sh b/makeactivitytable.sh
index 047d81e7e..9af62ef61 100755
--- a/makeactivitytable.sh
+++ b/makeactivitytable.sh
@@ -24,7 +24,6 @@ use bugs;
 
 drop table bugs_activity
 OK_ALL_DONE
-
 mysql << OK_ALL_DONE
 use bugs;
 create table bugs_activity (
@@ -36,7 +35,8 @@ create table bugs_activity (
         newvalue tinytext,
 
         index (bug_id),
-        index (when)
+        index (when),
+	index (field)
 );
 
 
diff --git a/makeattachmenttable.sh b/makeattachmenttable.sh
new file mode 100755
index 000000000..b93862674
--- /dev/null
+++ b/makeattachmenttable.sh
@@ -0,0 +1,45 @@
+#!/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 attachments;
+OK_ALL_DONE
+
+mysql << OK_ALL_DONE
+use bugs;
+
+create table attachments (
+attach_id mediumint not null auto_increment primary key,
+bug_id mediumint not null,
+creation_ts timestamp,
+description mediumtext not null,
+mimetype mediumtext not null,
+ispatch tinyint,
+filename mediumtext not null,
+thedata longblob not null,
+
+index(bug_id),
+index(creation_ts)
+);
+
+OK_ALL_DONE
diff --git a/makebugtable.sh b/makebugtable.sh
index b6ca5473d..bad74010e 100755
--- a/makebugtable.sh
+++ b/makebugtable.sh
@@ -29,6 +29,7 @@ mysql << OK_ALL_DONE
 use bugs;
 create table bugs (
 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,
diff --git a/makecomponenttable.sh b/makecomponenttable.sh
index 5688e91c7..8fe417480 100755
--- a/makecomponenttable.sh
+++ b/makecomponenttable.sh
@@ -37,135 +37,8 @@ description mediumtext not null
 
 
 
-insert into components (value, program, initialowner, initialqacontact) values ("XPFC", "Calendar", "spider@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("config", "Calendar", "spider@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Core", "Calendar", "sman@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("NLS", "Calendar", "jusn@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("UI", "Calendar", "eyork@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Test", "Calendar", "sman@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Install", "Calendar", "sman@netscape.com", "");
 
-insert into components (value, program, initialowner, initialqacontact, description) values ("CCK-Wizard", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "The CCK wizard allows the user to gather data by walking through a series of screens to create a set of customizations and build a media image.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("CCK-Installation", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "The CCK installation includes verifying the CD-layout. Other areas include the installations screens, directory structure, branding and launching the CCK Wizard.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("CCK-Shell", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "CCK allows an ISP to customize the first screen seen by the user. The shell dialog(s) displays a background image as well as buttons and text explaining the installation options to the user.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("CCK-Whitebox", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "This is a series of whitebox tests to test the critical functions of the CCK Wizard.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Dialup-Install", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "This is the Installation of a custom build which includes the Account Setup module created by CCK");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Dialup-Account Setup", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "The Account Setup Wizard is used by the end users who want to create or modify an existing Internet account.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Dialup-Mup/Muc", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "This refers to multiple user configurations which allows association of Microsoft dialup networking connectoids to Netscape user profiles.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Dialup-Upgrade", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "This is the upgrade path from 4.5 to the latest release where the user preferences are preserved.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("AS-Whitebox", "CCK", "selmer@netscape.com", "bmartin@netscape.com", "This is a series of whitebox tests to test the critical functions of the Account Setup.");
-
-insert into components (value, program, initialowner, initialqacontact) values ("LDAP C SDK", "Directory", "chuckb@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("LDAP Java SDK", "Directory", "chuckb@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("PerLDAP", "Directory", "leif@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("LDAP Tools", "Directory", "chuckb@netscape.com", "");
-
-
-insert into components (value, program, initialowner, initialqacontact, description) values ("Networking", "MailNews", "mscott@netscape.com", "lchiang@netscape.com", "Integration with libnet, protocol support for POP3, IMAP4, SMTP, NNTP and LDAP");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Database", "MailNews", "davidmc@netscape.com", "lchiang@netscape.com", "Persistent storage of address books and mail/news summary files");
-insert into components (value, program, initialowner, initialqacontact, description) values ("MIME", "MailNews", "rhp@netscape.com", "lchiang@netscape.com", "Parsing the MIME structure");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Security", "MailNews", "jefft@netscape.com", "lchiang@netscape.com", "SSL, S/MIME for mail");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Composition", "MailNews", "ducarroz@netscape.com", "lchiang@netscape.com", "Front-end and back-end of message composition and sending");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Address Book", "MailNews", "putterman@netscape.com", "lchiang@netscape.com", "Names, email addresses, phone numbers, etc.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Front End", "MailNews", "phil@netscape.com", "lchiang@netscape.com", "Three pane view, sidebar contents, toolbars, dialogs, etc.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Back End", "MailNews", "phil@netscape.com", "lchiang@netscape.com", "RDF data sources and application logic for local mail, news, IMAP and LDAP");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Internationalization", "MailNews", "nhotta@netscape.com", "momoi@netscape.com", "Internationalization is the process of designing and developing a software product to function in multiple locales. This process involves identifying the locales that must be supported, designing features which support those locales, and writing code that functions equally well in any of the supported locales.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Localization", "MailNews", "rchen@netscape.com", "momoi@netscape.com", "Localization is the process of adapting software for a specific international market; this process includes translating the user interface, resizing dialog boxes, replacing icons and other culturally sensitive graphics (if necessary), customizing features (if necessary), and testing the localized product to ensure that the program still works.");
-
-
-
-insert into components (value, program, initialowner, initialqacontact) values ("Macintosh FE", "Mozilla", "sdagley@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Windows FE", "Mozilla", "blythe@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XFE", "Mozilla", "ramiro@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("StubFE", "Mozilla", "rickg@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Aurora/RDF FE", "Mozilla", "don@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Aurora/RDF BE", "Mozilla", "guha@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Berkeley DB", "Mozilla", "montulli@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Browser Hooks", "Mozilla", "ebina@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Build Config", "Mozilla", "briano@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Composer", "Mozilla", "akkana@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Compositor Library", "Mozilla", "vidur@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Dialup", "Mozilla", "selmer@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("FontLib", "Mozilla", "dp@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("HTML Dialogs", "Mozilla", "nisheeth@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("HTML to Text/PostScript Translation", "Mozilla", "brendan@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("ImageLib", "Mozilla", "pnunn@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("JPEG Image Handling", "Mozilla", "tgl@sss.pgh.pa.us", "");
-insert into components (value, program, initialowner, initialqacontact) values ("PNG Image Handling", "Mozilla", "png@wco.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Image Conversion Library", "Mozilla", "mjudge@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("I18N Library", "Mozilla", "bobj@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Java Stubs", "Mozilla", "warren@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("JavaScript", "Mozilla", "mccabe@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("JavaScript/Java Reflection", "Mozilla", "fur@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Layout", "Mozilla", "rickg@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("LibMocha", "Mozilla", "mlm@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("MIMELib", "Mozilla", "terry@mozilla.org", "");
-insert into components (value, program, initialowner, initialqacontact) values ("NetLib", "Mozilla", "gagan@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("NSPR", "Mozilla", "srinivas@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Password Cache", "Mozilla", "montulli@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("PICS", "Mozilla", "montulli@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Plugins", "Mozilla", "amusil@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Preferences", "Mozilla", "aoki@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Progress Window", "Mozilla", "atotic@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Registry", "Mozilla", "dveditz@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Scheduler", "Mozilla", "aoki@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Security Stubs", "Mozilla", "jsw@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("SmartUpdate", "Mozilla", "dveditz@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XML", "Mozilla", "guha@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XP-COM", "Mozilla", "scullin@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XP File Handling", "Mozilla", "atotic@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XP Miscellany", "Mozilla", "brendan@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XP Utilities", "Mozilla", "rickg@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Zlib", "Mozilla", "pnunn@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Internationalization", "Mozilla", "ftang@netscape.com", "teruko@netscape.com", "Internationalization is the process of designing and developing a software product to function in multiple locales. This process involves identifying the locales that must be supported, designing features which support those locales, and writing code that functions equally well in any of the supported locales.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Localization", "Mozilla", "rchen@netscape.com", "teruko@netscape.com", "Localization is the process of adapting software for a specific international market; this process includes translating the user interface, resizing dialog boxes, replacing icons and other culturally sensitive graphics (if necessary), customizing features (if necessary), and testing the localized product to ensure that the program still works.");
-
-
-
-insert into components (value, program, initialowner, initialqacontact) values ("Platform: Lesstif on Linux", "Mozilla", "ramiro@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Platform: OS/2", "Mozilla", "law@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Platform: MacOS/PPC", "Mozilla", "sdagley@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Platform: Rhapsody", "Mozilla", "mcafee@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Platform: MFC/Win32 on Windows", "Mozilla", "blythe@netscape.com", "");
-
-
-insert into components (value, program, initialowner, initialqacontact) values ("ActiveX Wrapper", "NGLayout", "locka@iol.ie", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Content Model", "NGLayout", "kipp@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Cookies", "NGLayout", "scullin@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("DOM", "NGLayout", "vidur@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("DOM", "NGLayout", "vidur@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Editor", "NGLayout", "kostello@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Embedding APIs", "NGLayout", "jevering@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Event Handling", "NGLayout", "joki@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Event Handling", "NGLayout", "joki@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Form Submission", "NGLayout", "pollmann@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("HTMLFrames", "NGLayout", "karnaze@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("HTMLTables", "NGLayout", "karnaze@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Layout", "NGLayout", "troy@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Networking Library", "NGLayout", "gagan@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Parser", "NGLayout", "rickg@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Plug-ins", "NGLayout", "amusil@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Compositor", "NGLayout", "michaelp@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Selection and Search", "NGLayout", "kostello@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Style System", "NGLayout", "peterl@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Threading", "NGLayout", "rpotts@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Viewer App", "NGLayout", "rickg@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("Widget Set", "NGLayout", "kmcclusk@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("XPCOM", "NGLayout", "scullin@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact) values ("xpidl", "NGLayout", "shaver@netscape.com", "");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Internationalization", "NGLayout", "ftang@netscape.com", "teruko@netscape.com", "Internationalization is the process of designing and developing a software product to function in multiple locales. This process involves identifying the locales that must be supported, designing features which support those locales, and writing code that functions equally well in any of the supported locales.");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Localization", "NGLayout", "rchen@netscape.com", "teruko@netscape.com", "Localization is the process of adapting software for a specific international market; this process includes translating the user interface, resizing dialog boxes, replacing icons and other culturally sensitive graphics (if necessary), customizing features (if necessary), and testing the localized product to ensure that the program still works.");
-
-
-insert into components (value, program, initialowner, initialqacontact, description) values ("Bonsai", "Webtools", "terry@mozilla.org", "", 'Web based <a href="http://www.mozilla.org/bonsai.html">Tree control system</a> for watching the up-to-the-minute goings-on in a CVS repository');
-insert into components (value, program, initialowner, initialqacontact, description) values ("Bugzilla", "Webtools", "terry@mozilla.org", "", "Use this component to report bugs in the bug system itself");
-insert into components (value, program, initialowner, initialqacontact, description) values ("Despot", "Webtools", "terry@mozilla.org", "", "mozilla.org's <a href=http://cvs-mirror.mozilla.org/webtools/despot.cgi>account management</a> system");
-insert into components (value, program, initialowner, initialqacontact, description) values ("LXR", "Webtools", "endico@mozilla.org", "", 'Source code <a href="http://cvs-mirror.mozilla.org/webtools/lxr/">cross reference</a> system');
-insert into components (value, program, initialowner, initialqacontact, description) values ("Mozbot", "Webtools", "terry@mozilla.org", "", 'IRC robot that watches tinderbox and other things');
-insert into components (value, program, initialowner, initialqacontact, description) values ("Tinderbox", "Webtools", "terry@mozilla.org", "", 'Tree-watching system to monitor <a href="http://www.mozilla.org/tinderbox.html">continuous builds</a> that we run on multiple platforms.');
-
-
-select * from components;
+insert into components (value, program, description) values ("TestComponent", "TestProduct", "This is a test component in the test product database.  This ought to be blown away and replaced with real stuff in a finished installation of bugzilla.");
 
 show columns from components;
 show index from components;
diff --git a/makegroupstable.sh b/makegroupstable.sh
new file mode 100755
index 000000000..561823878
--- /dev/null
+++ b/makegroupstable.sh
@@ -0,0 +1,69 @@
+#!/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 groups
+OK_ALL_DONE
+
+mysql << OK_ALL_DONE
+use bugs;
+create table groups (
+    # This must be a power of two.  Groups are identified by a bit; sets of
+    # groups are indicated by or-ing these values together.
+    bit bigint not null,
+    name varchar(255) not null,
+    description text not null,
+
+    # isbuggroup is nonzero if this is a group that controls access to a set
+    # of bugs.  In otherword, the groupset field in the bugs table should only
+    # have this group's bit set if isbuggroup is nonzero.
+    isbuggroup tinyint not null,
+
+    # User regexp is which email addresses are initially put into this group.
+    # This is only used when an email account is created; otherwise, profiles
+    # may be individually tweaked to add them in and out of groups.
+    userregexp tinytext not null,
+
+
+    unique(bit),
+    unique(name)
+
+);
+
+
+insert into groups (bit, name, description, userregexp) values (pow(2,0), "tweakparams", "Can tweak operating parameters", "");
+insert into groups (bit, name, description, userregexp) values (pow(2,1), "editgroupmembers", "Can put people in and out of groups that they are members of.", "");
+insert into groups (bit, name, description, userregexp) values (pow(2,2), "creategroups", "Can create and destroy groups.", "");
+insert into groups (bit, name, description, userregexp) values (pow(2,3), "editcomponents", "Can create, destroy, and edit components.", "");
+
+
+
+show columns from groups;
+show index from groups;
+
+
+# Check for bad bit values.
+
+select "*** Bad bit value", bit from groups where bit != pow(2, round(log(bit) / log(2)));
+
+OK_ALL_DONE
diff --git a/makeproducttable.sh b/makeproducttable.sh
index e9d8fc244..ce5dae79c 100755
--- a/makeproducttable.sh
+++ b/makeproducttable.sh
@@ -30,15 +30,13 @@ use bugs;
 create table products (
 product tinytext,
 description mediumtext,
-milestoneurl tinytext not null
+milestoneurl tinytext not null,
+disallownew tinyint not null
 );
 
 
-insert into products (product, description) values ("Calendar", 'For bugs about the <a href="http://www.mozilla.org/projects/calendar">Calendar</a> project');
-insert into products (product, description) values ("CCK", 'For bugs about the <a href="http://www.mozilla.org/projects/cck">Client Customization Kit<a> project');
-insert into products (product, description) values ("Directory", 'For bugs about the <a href="http://www.mozilla.org/directory">Directory (LDAP)</a> project');
-insert into products (product, description) values ("MailNews", 'For bugs about the <a href="http://www.mozilla.org/mailnews/index.html">Mozilla Mail/News</a> project');
-insert into products (product, description) values ("Mozilla", "For bugs about the Mozilla web browser");
-insert into products (product, description, milestoneurl) values ("NGLayout", 'For bugs about the <a href="http://www.mozilla.org/newlayout/">New Layout</a> project', 'http://www.mozilla.org/projects/seamonkey/milestones');
-insert into products (product, description) values ("Webtools", 'For bugs about the web-based tools that mozilla.org uses.  This include Bugzilla (problems you are having with this bug system itself), <a href="http://www.mozilla.org/bonsai.html">Bonsai</a>, and <a href="http://www.mozilla.org/tinderbox.html">Tinderbox</a>.');
 
+insert into products(product, description) values ("TestProduct", "This is a test product.  This ought to be blown away and replaced with real stuff in a finished installation of bugzilla.");
+
+
+OK_ALL_DONE
diff --git a/makeprofilestable.sh b/makeprofilestable.sh
index 76ce65c31..be4c03e4b 100755
--- a/makeprofilestable.sh
+++ b/makeprofilestable.sh
@@ -33,6 +33,7 @@ login_name varchar(255) not null,
 password varchar(16),
 cryptpassword varchar(64),
 realname varchar(255),
+groupset bigint not null,
 index(login_name)
 );
 
diff --git a/makeversiontable.sh b/makeversiontable.sh
index 4dd6c565f..f4d8f7584 100755
--- a/makeversiontable.sh
+++ b/makeversiontable.sh
@@ -33,20 +33,7 @@ program tinytext
 );
 
 
-
-insert into versions (value, program) values ("other", "Calendar");
-insert into versions (value, program) values ("other", "CCK");
-insert into versions (value, program) values ("other", "Directory");
-insert into versions (value, program) values ("other", "MailNews");
-insert into versions (value, program) values ("other", "Mozilla");
-insert into versions (value, program) values ("1998-03-31", "Mozilla");
-insert into versions (value, program) values ("1998-04-08", "Mozilla");
-insert into versions (value, program) values ("1998-04-29", "Mozilla");
-insert into versions (value, program) values ("1998-06-03", "Mozilla");
-insert into versions (value, program) values ("1998-07-28", "Mozilla");
-insert into versions (value, program) values ("1998-09-04", "Mozilla");
-insert into versions (value, program) values ("other", "NGLayout");
-insert into versions (value, program) values ("other", "Webtools");
+insert into versions (value, program) values ("other", "TestProduct");
 
 select * from versions;
 
diff --git a/post_bug.cgi b/post_bug.cgi
index 1cff52326..c0102fdbf 100755
--- a/post_bug.cgi
+++ b/post_bug.cgi
@@ -105,7 +105,12 @@ foreach my $field (@bug_fields) {
     $query .= SqlQuote($::FORM{$field}) . ",\n";
 }
 
-$query .= "now(), " . SqlQuote($::FORM{'comment'}) . " )\n";
+my $comment = $::FORM{'comment'};
+$comment =~ s/\r\n/\n/g;     # Get rid of windows-style line endings.
+$comment =~ s/\r/\n/g;       # Get rid of mac-style line endings.
+$comment = trim($comment);
+
+$query .= "now(), " . SqlQuote($comment) . " )\n";
 
 
 my %ccids;
diff --git a/process_bug.cgi b/process_bug.cgi
index f68b7e186..fc425a199 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -120,6 +120,22 @@ sub ChangeResolution {
 }
 
 
+my $foundbit = 0;
+foreach my $b (grep(/^bit-\d*$/, keys %::FORM)) {
+    if (!$foundbit) {
+        $foundbit = 1;
+        DoComma();
+        $::query .= "groupset = 0";
+    }
+    if ($::FORM{$b}) {
+        my $v = substr($b, 4);
+        $::query .= "+ $v";     # Carefully written so that the math is
+                                # done by MySQL, which can handle 64-bit math,
+                                # and not by Perl, which I *think* can not.
+    }
+}
+
+
 foreach my $field ("rep_platform", "priority", "bug_severity", "url",
                    "summary", "component", "bug_file_loc", "short_desc",
                    "product", "version", "component", "op_sys",
diff --git a/processmail b/processmail
index df1001fd9..3da6448e1 100755
--- a/processmail
+++ b/processmail
@@ -222,14 +222,15 @@ if (open(FID, "<data/nomail")) {
 
 my $regenerate = 0;
 
-if (($#ARGV >= 1) && ($ARGV[0] eq "regenerate")) {
+if ($ARGV[0] eq "regenerate") {
     $regenerate = 1;
-    $#ARGV = -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) {
@@ -252,7 +253,7 @@ foreach my $i (@ARGV) {
     print FID $text;
     close FID;
     if (Different($old, $new)) {
-        system("diff -c $old $new > $diffs");
+        system("diff -c -b $old $new > $diffs");
         my $tolist = fixaddresses([$::bug{'assigned_to'}, $::bug{'reporter'},
                                    $::bug{'qa_contact'}]);
         my $cclist = fixaddresses($::bug{'cclist'});
diff --git a/query.cgi b/query.cgi
index f782bd5c1..5ee513ec0 100755
--- a/query.cgi
+++ b/query.cgi
@@ -18,6 +18,7 @@
 # Netscape Communications Corporation. All Rights Reserved.
 # 
 # Contributor(s): Terry Weissman <terry@mozilla.org>
+#                 David Gardiner <david.gardiner@unisa.edu.au>
 
 use diagnostics;
 use strict;
@@ -30,13 +31,15 @@ use vars @::legal_resolution,
   @::legal_product,
   @::legal_bug_status,
   @::legal_priority,
-  @::legal_resolution,
   @::legal_opsys,
   @::legal_platform,
   @::legal_components,
   @::legal_versions,
   @::legal_severity,
   @::legal_target_milestone,
+  @::log_columns,
+  %::versions,
+  %::components,
   %::FORM;
 
 
@@ -59,7 +62,8 @@ my %type;
 
 foreach my $name ("bug_status", "resolution", "assigned_to", "rep_platform",
                   "priority", "bug_severity", "product", "reporter", "op_sys",
-                  "component", "version",
+                  "component", "version", "chfield", "chfieldfrom",
+                  "chfieldto", "chfieldvalue",
                   "email1", "emailtype1", "emailreporter1",
                   "emailassigned_to1", "emailcc1", "emailqa_contact1",
                   "email2", "emailtype2", "emailreporter2",
@@ -89,6 +93,11 @@ foreach my $item (split(/\&/, $::buffer)) {
 }
                   
 
+if ($default{'chfieldto'} eq "") {
+    $default{'chfieldto'} = "Now";
+}
+
+
 
 my $namelist = "";
 
@@ -181,6 +190,142 @@ my $emailinput1 = GenerateEmailInput(1);
 my $emailinput2 = GenerateEmailInput(2);
 
 
+# javascript
+    
+my $jscript = << 'ENDSCRIPT';
+<script language="Javascript1.2">
+<!--
+var cpts = new Array();
+var vers = new Array();
+// Apparently, IE4 chokes on the below, so do nothing if running that.
+var agt=navigator.userAgent.toLowerCase();
+if ((agt.indexOf("msie") == -1)) {
+ENDSCRIPT
+
+
+my $p;
+my $v;
+my $c;
+my $i = 0;
+my $j = 0;
+
+foreach $c (@::legal_components) {
+    $jscript .= "cpts['$c'] = [];\n";
+}
+
+foreach $v (@::legal_versions) {
+    $jscript .= "vers['$v'] = [];\n";
+}
+
+
+for $p (@::legal_product) {
+    if ($::components{$p}) {
+        foreach $c (@{$::components{$p}}) {
+            $jscript .= "cpts['$c'].push('$p');\n";
+        }
+    }
+
+    if ($::versions{$p}) {
+        foreach $v (@{$::versions{$p}}) {
+            $jscript .= "vers['$v'].push('$p');\n";
+        }
+    }
+}
+
+$i = 0;
+$jscript .= q{
+
+\} // end IE4 choke around
+// Only display versions/components valid for selected product(s)
+
+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;
+
+    var cnt = 0;
+    var i;
+    var j;
+    for (i=0 ; i<f.product.length ; i++) {
+        if (f.product[i].selected) {
+            cnt++;
+        }
+    }
+    var doall = (cnt == f.product.length || cnt == 0);
+
+    var csel = new Array();
+    for (i=0 ; i<f.component.length ; i++) {
+        if (f.component[i].selected) {
+            csel[f.component[i].value] = 1;
+        }
+    }
+
+    f.component.options.length = 0;
+
+    for (c in cpts) {
+        var doit = doall;
+        for (i=0 ; !doit && i<f.product.length ; i++) {
+            if (f.product[i].selected) {
+                var p = f.product[i].value;
+                for (j in cpts[c]) {
+                    var p2 = cpts[c][j];
+                    if (p2 == p) {
+                        doit = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (doit) {
+            var l = f.component.length;
+            f.component[l] = new Option(c, c);
+            if (csel[c] != undefined) {
+                f.component[l].selected = true;
+            }
+        }
+    }
+
+    var vsel = new Array();
+    for (i=0 ; i<f.version.length ; i++) {
+        if (f.version[i].selected) {
+            vsel[f.version[i].value] = 1;
+        }
+    }
+
+    f.version.options.length = 0;
+
+    for (v in vers) {
+        var doit = doall;
+        for (i=0 ; !doit && i<f.product.length ; i++) {
+            if (f.product[i].selected) {
+                var p = f.product[i].value;
+                for (j in vers[v]) {
+                    var p2 = vers[v][j];
+                    if (p2 == p) {
+                        doit = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (doit) {
+            var l = f.version.length;
+            f.version[l] = new Option(v, v);
+            if (vsel[v] != undefined) {
+                f.version[l].selected = true;
+            }
+        }
+    }
+
+
+
+
+}
+// -->
+</script>
+
+};
+
 
 
 # Muck the "legal product" list so that the default one is always first (and
@@ -198,6 +343,8 @@ PutHeader("Bugzilla Query Page", "Query Page");
 push @::legal_resolution, "---"; # Oy, what a hack.
 push @::legal_target_milestone, "---"; # Oy, what a hack.
 
+print $jscript;
+
 print "
 <FORM NAME=queryForm METHOD=GET ACTION=\"buglist.cgi\">
 
@@ -249,8 +396,26 @@ $emailinput2<p>
 
 
 
+Changed in the <NOBR>last <INPUT NAME=changedin SIZE=2 VALUE=\"$default{'changedin'}\"> days.</NOBR>
 
-<NOBR>Changed in the last <INPUT NAME=changedin SIZE=2> days.</NOBR>
+<table>
+<tr>
+<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'})]}
+</SELECT>
+</td><td rowspan=2>
+changed.
+</td><td>
+<nobr>dates <INPUT NAME=chfieldfrom SIZE=10 VALUE=\"$default{'chfieldfrom'}\"></nobr>
+<nobr>to <INPUT NAME=chfieldto SIZE=10 VALUE=\"$default{'chfieldto'}\"></nobr>
+</td>
+</tr>
+<tr>
+<td>changed to value <nobr><INPUT NAME=chfieldvalue SIZE=10> (optional)</nobr>
+</td>
+</table>
 
 
 <P>
@@ -271,7 +436,7 @@ print "
 <tr>
 
 <td align=left valign=top>
-<SELECT NAME=\"product\" MULTIPLE SIZE=5>
+<SELECT NAME=\"product\" MULTIPLE SIZE=5 onChange=\"selectProduct(this.form);\">
 @{[make_options(\@::legal_product, $default{'product'}, $type{'product'})]}
 </SELECT>
 </td>
@@ -377,17 +542,18 @@ print "
 ";
 
 
+quietly_check_login();
+
+if (UserInGroup("tweakparams")) {
+    print "<a href=editparams.cgi>Edit Bugzilla operating parameters</a><br>\n";
+}
+if (UserInGroup("editcomponents")) {
+    print "<a href=editcomponents.cgi>Edit Bugzilla components</a><br>\n";
+}
 if (defined $::COOKIE{"Bugzilla_login"}) {
-    if ($::COOKIE{"Bugzilla_login"} eq Param("maintainer")) {
-        print "<a href=editparams.cgi>Edit Bugzilla operating parameters</a><br>\n";
-        print "<a href=editowners.cgi>Edit Bugzilla component owners</a><br>\n";
-    }
     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=\"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/sanitycheck.cgi b/sanitycheck.cgi
index 814a1f031..969cfd3c4 100755
--- a/sanitycheck.cgi
+++ b/sanitycheck.cgi
@@ -50,12 +50,64 @@ PutHeader("Bugzilla Sanity Check");
 
 print "OK, now running sanity checks.<P>\n";
 
+my @row;
+my @checklist;
+
+Status("Checking groups");
+SendSQL("select bit from groups where bit != pow(2, round(log(bit) / log(2)))");
+while (my $bit = FetchOneColumn()) {
+    Alert("Illegal bit number found in group table: $bit");
+}
+    
+SendSQL("select sum(bit) from groups where isbuggroup != 0");
+my $buggroupset = FetchOneColumn();
+SendSQL("select bug_id, groupset from bugs where groupset & $buggroupset != groupset");
+while (@row = FetchSQLData()) {
+    Alert("Bad groupset $row[1] found in bug " . BugLink($row[0]));
+}
+
+
+
+
+Status("Checking version/products");
+
+SendSQL("select distinct product, version from bugs");
+while (@row = FetchSQLData()) {
+    my @copy = @row;
+    push(@checklist, \@copy);
+}
+
+foreach my $ref (@checklist) {
+    my ($product, $version) = (@$ref);
+    SendSQL("select count(*) from versions where program = '$product' and value = '$version'");
+    if (FetchOneColumn() != 1) {
+        Alert("Bug(s) found with invalid product/version: $product/$version");
+    }
+}
+
+
+Status("Checking components/products");
+
+@checklist = ();
+SendSQL("select distinct product, component from bugs");
+while (@row = FetchSQLData()) {
+    my @copy = @row;
+    push(@checklist, \@copy);
+}
+
+foreach my $ref (@checklist) {
+    my ($product, $component) = (@$ref);
+    SendSQL("select count(*) from components where program = '$product' and value = '$component'");
+    if (FetchOneColumn() != 1) {
+        Alert("Bug(s) found with invalid product/component: $product/$component");
+    }
+}
+
+
 Status("Checking profile ids...");
 
 SendSQL("select userid,login_name from profiles");
 
-my @row;
-
 my %profid;
 
 while (@row = FetchSQLData()) {
@@ -71,13 +123,13 @@ while (@row = FetchSQLData()) {
 undef $profid{0};
 
 
-Status("Checking reporter/assigned_to ids");
-SendSQL("select bug_id,reporter,assigned_to from bugs");
+Status("Checking reporter/assigned_to/qa_contact ids");
+SendSQL("select bug_id,reporter,assigned_to,qa_contact from bugs");
 
 my %bugid;
 
 while (@row = FetchSQLData()) {
-    my($id, $reporter, $assigned_to) = (@row);
+    my($id, $reporter, $assigned_to, $qa_contact) = (@row);
     $bugid{$id} = 1;
     if (!defined $profid{$reporter}) {
         Alert("Bad reporter $reporter in " . BugLink($id));
@@ -85,6 +137,9 @@ while (@row = FetchSQLData()) {
     if (!defined $profid{$assigned_to}) {
         Alert("Bad assigned_to $assigned_to in" . BugLink($id));
     }
+    if ($qa_contact != 0 && !defined $profid{$qa_contact}) {
+        Alert("Bad qa_contact $qa_contact in" . BugLink($id));
+    }
 }
 
 Status("Checking CC table");
diff --git a/showattachment.cgi b/showattachment.cgi
new file mode 100755
index 000000000..147ce8fef
--- /dev/null
+++ b/showattachment.cgi
@@ -0,0 +1,43 @@
+#!/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>
+#                 David Gardiner <david.gardiner@unisa.edu.au>
+
+use diagnostics;
+use strict;
+
+require "CGI.pl";
+
+ConnectToDatabase();
+
+my @row;
+if (defined $::FORM{'attach_id'}) {
+    SendSQL("select mimetype, filename, thedata from attachments where attach_id = $::FORM{'attach_id'}");
+    @row = FetchSQLData();
+}
+if (!@row) {
+    print "Content-type: text/html\n\n";
+    PutHeader("Bad ID");
+    print "Please hit back and try again.\n";
+    exit;
+}
+print qq{Content-type: $row[0]; name="$row[1]"; \n\n$row[2]};
+
+    
-- 
GitLab