diff options
author | mkanat%bugzilla.org <> | 2006-07-30 11:46:43 +0200 |
---|---|---|
committer | mkanat%bugzilla.org <> | 2006-07-30 11:46:43 +0200 |
commit | cd9cf6add14c1d76c571eeb99331631d74d209d9 (patch) | |
tree | 02cb51c53a3c6a0a74eec63b4f487fce3db31d8f | |
parent | 856d2d2fcf391a48bc50054e383ba8de617b4c2f (diff) | |
download | bugzilla-cd9cf6add14c1d76c571eeb99331631d74d209d9.tar.gz bugzilla-cd9cf6add14c1d76c571eeb99331631d74d209d9.tar.xz |
Bug 346414: Move the creation/maintenance of the data/ directory out of checksetup and into a module
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=justdave
-rw-r--r-- | Bugzilla/DB.pm | 9 | ||||
-rw-r--r-- | Bugzilla/Install/Filesystem.pm | 388 | ||||
-rwxr-xr-x | checksetup.pl | 319 |
3 files changed, 404 insertions, 312 deletions
diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index b268a12f3..2706f1b40 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -149,7 +149,10 @@ EOT # We don't try to connect to the actual database if $db_check is # disabled. - return unless $db_check; + unless ($db_check) { + print "\n" if $output; + return; + } # And now check the version of the database server itself. my $dbh = _get_no_db_connection(); @@ -173,6 +176,8 @@ newer version. EOT exit; } + + print "\n" if $output; } # Note that this function requires that localconfig exist and @@ -184,7 +189,7 @@ sub bz_create_database { if (!$conn_success) { $dbh = _get_no_db_connection(); - print "\nCreating database $db_name...\n"; + print "Creating database $db_name...\n"; # Try to create the DB, and if we fail print a friendly error. if (!eval { $dbh->do("CREATE DATABASE $db_name") }) { diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm new file mode 100644 index 000000000..111e68c9d --- /dev/null +++ b/Bugzilla/Install/Filesystem.pm @@ -0,0 +1,388 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (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. +# +# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org> + +package Bugzilla::Install::Filesystem; + +# NOTE: This package may "use" any modules that it likes, +# and localconfig is available. However, all functions in this +# package should assume that: +# +# * Templates are not available. +# * Files do not have the correct permissions. +# * The database does not exist. + +use strict; + +use Bugzilla::Constants; +use Bugzilla::Install::Localconfig; + +use Fcntl; +use IO::File; + +use base qw(Exporter); +our @EXPORT = qw( + update_filesystem + create_htaccess +); + +# This looks like a constant because it effectively is, but +# it has to call other subroutines and read the current filesystem, +# so it's defined as a sub. This is not exported, so it doesn't have +# a perldoc. However, look at the various hashes defined inside this +# function to understand what it returns. (There are comments throughout.) +sub FILESYSTEM { + my $datadir = bz_locations()->{'datadir'}; + my $attachdir = bz_locations()->{'attachdir'}; + my $extensionsdir = bz_locations()->{'extensionsdir'}; + my $webdotdir = bz_locations()->{'webdotdir'}; + my $templatedir = bz_locations()->{'templatedir'}; + my $libdir = bz_locations()->{'libpath'}; + + my $ws_group = read_localconfig()->{'webservergroup'}; + + # The name of each directory, pointing at its default permissions. + my %dirs = ( + $datadir => 0770, + "$datadir/mimedump-tmp" => 01777, + "$datadir/mining" => 0700, + "$datadir/duplicates" => $ws_group ? 0770 : 01777, + $attachdir => 0770, + $extensionsdir => 0770, + graphs => 0770, + $webdotdir => 0700, + 'skins/custom' => 0700, + ); + + # The name of each file, pointing at its default permissions and + # default contents. + my %files = ( + "$datadir/mail" => {}, + 'skins/.cvsignore' => { contents => ".cvsignore\ncustom\n" }, + ); + + # Each standard stylesheet has an associated custom stylesheet that + # we create. + foreach my $standard (<skins/standard/*.css>) { + my $custom = $standard; + $custom =~ s|^skins/standard|skins/custom|; + $files{$custom} = { contents => <<EOT +/* + * Custom rules for $standard. + * The rules you put here override rules in that stylesheet. + */ +EOT + }; + } + + # Because checksetup controls the creation of index.html separately + # from all other files, it gets its very own hash. + my %index_html = ( + 'index.html' => { contents => <<EOT +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta http-equiv="Refresh" content="0; URL=index.cgi"> +</head> +<body> + <h1>I think you are looking for <a href="index.cgi">index.cgi</a></h1> +</body> +</html> +EOT + } + ); + + # Because checksetup controls the .htaccess creation separately + # by a localconfig variable, these go in a separate variable from + # %files. + my $ht_perms = $ws_group ? 0640 : 0644; + my $ht_default_deny = <<EOT; +# nothing in this directory is retrievable unless overridden by an .htaccess +# in a subdirectory +deny from all +EOT + + my %htaccess = ( + "$attachdir/.htaccess" => { perms => $ht_perms, + contents => $ht_default_deny }, + "$libdir/Bugzilla/.htaccess" => { perms => $ht_perms, + contents => $ht_default_deny }, + "$templatedir/.htaccess" => { perms => $ht_perms, + contents => $ht_default_deny }, + + '.htaccess' => { perms => $ht_perms, contents => <<EOT +# Don't allow people to retrieve non-cgi executable files or our private data +<FilesMatch ^(.*\\.pm|.*\\.pl|.*localconfig.*)\$> + deny from all +</FilesMatch> +EOT + }, + + "$webdotdir/.htaccess" => { perms => $ht_perms, contents => <<EOT +# Restrict access to .dot files to the public webdot server at research.att.com +# if research.att.com ever changes their IP, or if you use a different +# webdot server, you'll need to edit this +<FilesMatch \\.dot\$> + Allow from 192.20.225.10 + Deny from all +</FilesMatch> + +# Allow access to .png files created by a local copy of 'dot' +<FilesMatch \\.png\$> + Allow from all +</FilesMatch> + +# And no directory listings, either. +Deny from all +EOT + }, + + # Even though $datadir may not (and should not) be in the webtree, + # we can't know for sure, so create the .htaccess anyway. It's harmless + # if it's not accessible... + "$datadir/.htaccess" => { perms => $ht_perms, contents => <<EOT +# Nothing in this directory is retrievable unless overridden by an .htaccess +# in a subdirectory; the only exception is duplicates.rdf, which is used by +# duplicates.xul and must be loadable over the web +deny from all +<Files duplicates.rdf> + allow from all +</Files> +EOT + + + }, + ); + + return { + dirs => \%dirs, + files => \%files, + htaccess => \%htaccess, + index_html => \%index_html, + }; +} + +sub update_filesystem { + my ($params) = @_; + my $fs = FILESYSTEM(); + my %dirs = %{$fs->{dirs}}; + my %files = %{$fs->{files}}; + + my $datadir = bz_locations->{'datadir'}; + # If the graphs/ directory doesn't exist, we're upgrading from + # a version old enough that we need to update the $datadir/mining + # format. + if (-d "$datadir/mining" && !-d 'graphs') { + _update_old_charts($datadir); + } + + # By sorting the dirs, we assure that shorter-named directories + # (meaning parent directories) are always created before their + # child directories. + foreach my $dir (sort keys %dirs) { + unless (-d $dir) { + print "Creating $dir directory...\n"; + mkdir $dir || die $!; + # For some reason, passing in the permissions to "mkdir" + # doesn't work right, but doing a "chmod" does. + chmod $dirs{$dir}, $dir || die $!; + } + } + + _create_files(%files); + if ($params->{index_html}) { + _create_files(%{$fs->{index_html}}); + } + elsif (-e 'index.html') { + my $templatedir = bz_locations()->{'templatedir'}; + print <<EOT; + +*** It appears that you still have an old index.html hanging around. + Either the contents of this file should be moved into a template and + placed in the '$templatedir/en/custom' directory, or you should delete + the file. + +EOT + } +} + +sub create_htaccess { + _create_files(%{FILESYSTEM()->{htaccess}}); + + # Repair old .htaccess files + my $htaccess = new IO::File('.htaccess', 'r') || die ".htaccess: $!"; + my $old_data; + { local $/; $old_data = <$htaccess>; } + $htaccess->close; + + my $repaired = 0; + if ($old_data =~ s/\|localconfig\|/\|.*localconfig.*\|/) { + $repaired = 1; + } + if ($old_data !~ /\(\.\*\\\.pm\|/) { + $old_data =~ s/\(/(.*\\.pm\|/; + $repaired = 1; + } + if ($repaired) { + print "Repairing .htaccess...\n"; + $htaccess = new IO::File('.htaccess', 'w') || die $!; + print $htaccess $old_data; + $htaccess->close; + } +} + +# A helper for the above functions. +sub _create_files { + my (%files) = @_; + + # It's not necessary to sort these, but it does make the + # output of checksetup.pl look a bit nicer. + foreach my $file (sort keys %files) { + unless (-e $file) { + print "Creating $file...\n"; + my $info = $files{$file}; + my $fh = new IO::File($file, O_WRONLY | O_CREAT, $info->{perms}) + || die $!; + print $fh $info->{contents} if $info->{contents}; + $fh->close; + } + } +} + +# If you ran a REALLY old version of Bugzilla, your chart files are in the +# wrong format. This code is a little messy, because it's very old, and +# when moving it into this module, I couldn't test it so I left it almost +# completely alone. +sub _update_old_charts { + my ($datadir) = @_; + print "Updating old chart storage format...\n"; + foreach my $in_file (glob("$datadir/mining/*")) { + # Don't try and upgrade image or db files! + next if (($in_file =~ /\.gif$/i) || + ($in_file =~ /\.png$/i) || + ($in_file =~ /\.db$/i) || + ($in_file =~ /\.orig$/i)); + + rename("$in_file", "$in_file.orig") or next; + open(IN, "$in_file.orig") or next; + open(OUT, '>', $in_file) or next; + + # Fields in the header + my @declared_fields; + + # Fields we changed to half way through by mistake + # This list comes from an old version of collectstats.pl + # This part is only for people who ran later versions of 2.11 (devel) + my @intermediate_fields = qw(DATE UNCONFIRMED NEW ASSIGNED REOPENED + RESOLVED VERIFIED CLOSED); + + # Fields we actually want (matches the current collectstats.pl) + my @out_fields = qw(DATE NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED + VERIFIED CLOSED FIXED INVALID WONTFIX LATER REMIND + DUPLICATE WORKSFORME MOVED); + + while (<IN>) { + if (/^# fields?: (.*)\s$/) { + @declared_fields = map uc, (split /\||\r/, $1); + print OUT "# fields: ", join('|', @out_fields), "\n"; + } + elsif (/^(\d+\|.*)/) { + my @data = split(/\||\r/, $1); + my %data; + if (@data == @declared_fields) { + # old format + for my $i (0 .. $#declared_fields) { + $data{$declared_fields[$i]} = $data[$i]; + } + } + elsif (@data == @intermediate_fields) { + # Must have changed over at this point + for my $i (0 .. $#intermediate_fields) { + $data{$intermediate_fields[$i]} = $data[$i]; + } + } + elsif (@data == @out_fields) { + # This line's fine - it has the right number of entries + for my $i (0 .. $#out_fields) { + $data{$out_fields[$i]} = $data[$i]; + } + } + else { + print "Oh dear, input line $. of $in_file had " . + scalar(@data) . " fields\nThis was unexpected.", + " You may want to check your data files.\n"; + } + + print OUT join('|', + map { defined ($data{$_}) ? ($data{$_}) : "" } @out_fields), + "\n"; + } + else { + print OUT; + } + } + + close(IN); + close(OUT); + } +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Install::Filesystem - Fix up the filesystem during + installation. + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +This module is used primarily by L<checksetup.pl> to modify the +filesystem during installation, including creating the data/ directory. + +=over + +=back + +=head1 SUBROUTINES + +=over + +=item C<update_filesystem({ index_html => 0 })> + +Description: Creates all the directories and files that Bugzilla + needs to function but doesn't ship with. Also does + any updates to these files as necessary during an + upgrade. + +Params: C<index_html> - Whether or not we should create + the F<index.html> file. + +Returns: nothing + +=item C<create_htaccess()> + +Description: Creates all of the .htaccess files for Apache, + in the various Bugzilla directories. Also updates + the .htaccess files if they need updating. + +Params: none + +Returns: nothing + +=back diff --git a/checksetup.pl b/checksetup.pl index a08ae08f9..8f1fdd167 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -304,6 +304,9 @@ import Bugzilla::Bug qw(is_open_state); require Bugzilla::Install::Localconfig; import Bugzilla::Install::Localconfig qw(read_localconfig update_localconfig); +require Bugzilla::Install::Filesystem; +import Bugzilla::Install::Filesystem qw(update_filesystem create_htaccess); + require Bugzilla::DB; ########################################################################### @@ -336,317 +339,22 @@ my $dbh = Bugzilla->dbh; # Create tables ########################################################################### -# Note: table definitions are now in Bugzilla::DB::Schema. +# Note: --TABLE-- definitions are now in Bugzilla::DB::Schema. $dbh->bz_setup_database(); # Populate the tables that hold the values for the <select> fields. $dbh->bz_populate_enum_tables(); ########################################################################### -# Check data directory +# Check --DATA-- directory ########################################################################### -# -# Create initial --DATA-- directory and make the initial empty files there: -# -my $datadir = bz_locations()->{'datadir'}; -unless (-d $datadir && -e "$datadir/nomail") { - print "Creating data directory ($datadir) ...\n"; - # permissions for non-webservergroup are fixed later on - mkdir $datadir, 0770; - mkdir "$datadir/mimedump-tmp", 01777; - open FILE, '>>', "$datadir/nomail"; close FILE; - open FILE, '>>', "$datadir/mail"; close FILE; -} - -my $attachdir = bz_locations->{'attachdir'}; -unless (-d $attachdir) { - print "Creating local attachments directory ...\n"; - # permissions for non-webservergroup are fixed later on - mkdir $attachdir, 0770; -} - -my $extensionsdir = bz_locations->{'extensionsdir'}; -# ZLL: 2005-08-20 Create extensions/ if it does not already exist: -unless (-d $extensionsdir) { - print "Creating extensions directory ($extensionsdir) ...\n"; - mkdir $extensionsdir, 0770; -} - - -# 2000-12-14 New graphing system requires a directory to put the graphs in -# This code copied from what happens for the data dir above. -# If the graphs dir is not present, we assume that they have been using -# a Bugzilla with the old data format, and upgrade their data files. - -# NB - the graphs dir isn't movable yet, unlike the datadir -unless (-d 'graphs') { - print "Creating graphs directory...\n"; - # permissions for non-webservergroup are fixed later on - mkdir 'graphs', 0770; - # Upgrade data format - foreach my $in_file (glob("$datadir/mining/*")) - { - # Don't try and upgrade image or db files! - if (($in_file =~ /\.gif$/i) || - ($in_file =~ /\.png$/i) || - ($in_file =~ /\.db$/i) || - ($in_file =~ /\.orig$/i)) { - next; - } - - rename("$in_file", "$in_file.orig") or next; - open(IN, "$in_file.orig") or next; - open(OUT, '>', $in_file) or next; - - # Fields in the header - my @declared_fields = (); - - # Fields we changed to half way through by mistake - # This list comes from an old version of collectstats.pl - # This part is only for people who ran later versions of 2.11 (devel) - my @intermediate_fields = qw(DATE UNCONFIRMED NEW ASSIGNED REOPENED - RESOLVED VERIFIED CLOSED); - - # Fields we actually want (matches the current collectstats.pl) - my @out_fields = qw(DATE NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED - VERIFIED CLOSED FIXED INVALID WONTFIX LATER REMIND - DUPLICATE WORKSFORME MOVED); - - while (<IN>) { - if (/^# fields?: (.*)\s$/) { - @declared_fields = map uc, (split /\||\r/, $1); - print OUT "# fields: ", join('|', @out_fields), "\n"; - } - elsif (/^(\d+\|.*)/) { - my @data = split /\||\r/, $1; - my %data = (); - if (@data == @declared_fields) { - # old format - for my $i (0 .. $#declared_fields) { - $data{$declared_fields[$i]} = $data[$i]; - } - } - elsif (@data == @intermediate_fields) { - # Must have changed over at this point - for my $i (0 .. $#intermediate_fields) { - $data{$intermediate_fields[$i]} = $data[$i]; - } - } - elsif (@data == @out_fields) { - # This line's fine - it has the right number of entries - for my $i (0 .. $#out_fields) { - $data{$out_fields[$i]} = $data[$i]; - } - } - else { - print "Oh dear, input line $. of $in_file had " . - scalar(@data) . " fields\n"; - print "This was unexpected. You may want to check your data files.\n"; - } - - print OUT join('|', map { - defined ($data{$_}) ? ($data{$_}) : "" - } @out_fields), "\n"; - } - else { - print OUT; - } - } - - close(IN); - close(OUT); - } -} - -unless (-d "$datadir/mining") { - mkdir "$datadir/mining", 0700; -} +update_filesystem({ index_html => $lc_hash->{'index_html'} }); +create_htaccess() if $lc_hash->{'create_htaccess'}; +# XXX Some parts of checksetup still need these, right now. +my $datadir = bz_locations()->{'datadir'}; my $webdotdir = bz_locations()->{'webdotdir'}; -unless (-d "$webdotdir") { - # perms/ownership are fixed up later - mkdir "$webdotdir", 0700; -} - -if (!-d "skins/custom") { - # perms/ownership are fixed up later - mkdir "skins/custom", 0700; -} - -if (!-e "skins/.cvsignore") { - open CVSIGNORE, '>>', "skins/.cvsignore"; - print CVSIGNORE ".cvsignore\n"; - print CVSIGNORE "custom\n"; - close CVSIGNORE; -} - -# Create custom stylesheets for each standard stylesheet. -foreach my $standard (<skins/standard/*.css>) { - my $custom = $standard; - $custom =~ s|^skins/standard|skins/custom|; - if (!-e $custom) { - open STYLESHEET, '>', $custom; - print STYLESHEET <<"END"; -/* - * Custom rules for $standard. - * The rules you put here override rules in that stylesheet. - */ -END - close STYLESHEET; - } -} - -if ($lc_hash->{'create_htaccess'}) { - my $fileperm = 0644; - my $dirperm = 01777; - if ($my_webservergroup) { - $fileperm = 0640; - $dirperm = 0770; - } - if (!-e ".htaccess") { - print "Creating .htaccess...\n"; - open HTACCESS, '>', '.htaccess'; - print HTACCESS <<'END'; -# don't allow people to retrieve non-cgi executable files or our private data -<FilesMatch ^(.*\.pm|.*\.pl|.*localconfig.*)$> - deny from all -</FilesMatch> -END - close HTACCESS; - chmod $fileperm, ".htaccess"; - } else { - # 2002-12-21 Bug 186383 - open HTACCESS, ".htaccess"; - my $oldaccess = ""; - while (<HTACCESS>) { - $oldaccess .= $_; - } - close HTACCESS; - my $repaired = 0; - if ($oldaccess =~ s/\|localconfig\|/\|.*localconfig.*\|/) { - $repaired = 1; - } - if ($oldaccess !~ /\(\.\*\\\.pm\|/) { - $oldaccess =~ s/\(/(.*\\.pm\|/; - $repaired = 1; - } - if ($repaired) { - print "Repairing .htaccess...\n"; - open HTACCESS, '>', '.htaccess'; - print HTACCESS $oldaccess; - close HTACCESS; - } - - } - if (!-e "$attachdir/.htaccess") { - print "Creating $attachdir/.htaccess...\n"; - open HTACCESS, ">$attachdir/.htaccess"; - print HTACCESS <<'END'; -# nothing in this directory is retrievable unless overridden by an .htaccess -# in a subdirectory; -deny from all -END - close HTACCESS; - chmod $fileperm, "$attachdir/.htaccess"; - } - if (!-e "Bugzilla/.htaccess") { - print "Creating Bugzilla/.htaccess...\n"; - open HTACCESS, '>', 'Bugzilla/.htaccess'; - print HTACCESS <<'END'; -# nothing in this directory is retrievable unless overridden by an .htaccess -# in a subdirectory -deny from all -END - close HTACCESS; - chmod $fileperm, "Bugzilla/.htaccess"; - } - # Even though $datadir may not (and should not) be in the webtree, - # we can't know for sure, so create the .htaccess anyway. It's harmless - # if it's not accessible... - if (!-e "$datadir/.htaccess") { - print "Creating $datadir/.htaccess...\n"; - open HTACCESS, '>', "$datadir/.htaccess"; - print HTACCESS <<'END'; -# nothing in this directory is retrievable unless overridden by an .htaccess -# in a subdirectory; the only exception is duplicates.rdf, which is used by -# duplicates.xul and must be loadable over the web -deny from all -<Files duplicates.rdf> - allow from all -</Files> -END - close HTACCESS; - chmod $fileperm, "$datadir/.htaccess"; - } - # Ditto for the template dir - my $templatedir = bz_locations()->{'templatedir'}; - if (!-e "$templatedir/.htaccess") { - print "Creating $templatedir/.htaccess...\n"; - open HTACCESS, '>', "$templatedir/.htaccess"; - print HTACCESS <<'END'; -# nothing in this directory is retrievable unless overridden by an .htaccess -# in a subdirectory -deny from all -END - close HTACCESS; - chmod $fileperm, "$templatedir/.htaccess"; - } - if (!-e "$webdotdir/.htaccess") { - print "Creating $webdotdir/.htaccess...\n"; - open HTACCESS, '>', "$webdotdir/.htaccess"; - print HTACCESS <<'END'; -# Restrict access to .dot files to the public webdot server at research.att.com -# if research.att.com ever changes their IP, or if you use a different -# webdot server, you'll need to edit this -<FilesMatch \.dot$> - Allow from 192.20.225.10 - Deny from all -</FilesMatch> - -# Allow access to .png files created by a local copy of 'dot' -<FilesMatch \.png$> - Allow from all -</FilesMatch> - -# And no directory listings, either. -Deny from all -END - close HTACCESS; - chmod $fileperm, "$webdotdir/.htaccess"; - } - -} - -if ($lc_hash->{'index_html'}) { - if (!-e "index.html") { - print "Creating index.html...\n"; - open HTML, '>', 'index.html'; - print HTML <<'END'; -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> -<meta http-equiv="Refresh" content="0; URL=index.cgi"> -</head> -<body> -<h1>I think you are looking for <a href="index.cgi">index.cgi</a></h1> -</body> -</html> -END - close HTML; - } - else { - open HTML, "index.html"; - if (! grep /index\.cgi/, <HTML>) { - print "\n\n"; - print "*** It appears that you still have an old index.html hanging\n"; - print " around. The contents of this file should be moved into a\n"; - print " template and placed in the 'en/custom' directory within"; - print " your template directory.\n\n"; - } - close HTML; - } -} # Check for a new install my $newinstall = !-e "$datadir/params"; @@ -1833,15 +1541,6 @@ if ($dbh->bz_table_info('comments')) { $dbh->bz_drop_table("comments"); } -# 2001-04-08 Added a special directory for the duplicates stats. -unless (-d "$datadir/duplicates") { - print "Creating duplicates directory...\n"; - mkdir "$datadir/duplicates", 0770; - if ($my_webservergroup eq "") { - chmod 01777, "$datadir/duplicates"; - } -} - # # 2001-04-10 myk@mozilla.org: # isactive determines whether or not a group is active. An inactive group |