blob: ee41efe3d5c2a15e58d97b3793f76bfeef0b3052 [file] [log] [blame]
# -*- 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.
#
# 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>
# Dawn Endico <endico@mozilla.org>
# Dan Mosedale <dmose@mozilla.org>
# Joe Robins <jmrobins@tgix.com>
# Jake <jake@bugzilla.org>
# J. Paul Reed <preed@sigkill.com>
# Bradley Baetz <bbaetz@student.usyd.edu.au>
# Christopher Aillon <christopher@aillon.com>
# Erik Stambaugh <erik@dasbistro.com>
package Bugzilla::Config;
use strict;
use base qw(Exporter);
# Under mod_perl, get this from a .htaccess config variable,
# and/or default from the current 'real' dir
# At some stage after this, it may be possible for these dir locations
# to go into localconfig. localconfig can't be specified in a config file,
# except possibly with mod_perl. If you move localconfig, you need to change
# the define here.
# $libpath is really only for mod_perl; its not yet possible to move the
# .pms elsewhere.
# $webdotdir must be in the webtree somewhere. Even if you use a local dot,
# we output images to there. Also, if $webdot dir is not relative to the
# bugzilla root directory, you'll need to change showdependencygraph.cgi to
# set image_url to the correct location.
# The script should really generate these graphs directly...
# Note that if $libpath is changed, some stuff will break, notably dependency
# graphs (since the path will be wrong in the HTML). This will be fixed at
# some point.
our $libpath = '.';
our $localconfig = "$libpath/localconfig";
our $datadir = "$libpath/data";
our $attachdir = "$datadir/attachments";
our $templatedir = "$libpath/template";
our $webdotdir = "$datadir/webdot";
# Module stuff
@Bugzilla::Config::EXPORT = qw(Param);
# Don't export localvars by default - people should have to explicitly
# ask for it, as a (probably futile) attempt to stop code using it
# when it shouldn't
# ChmodDataFile is here until that stuff all moves out of globals.pl
# into this file
@Bugzilla::Config::EXPORT_OK = qw(ChmodDataFile);
%Bugzilla::Config::EXPORT_TAGS =
(
admin => [qw(GetParamList UpdateParams SetParam WriteParams)],
db => [qw($db_driver $db_host $db_port $db_name $db_user $db_pass $db_sock)],
locations => [qw($libpath $localconfig $attachdir
$datadir $templatedir $webdotdir)],
);
Exporter::export_ok_tags('admin', 'db', 'locations');
# Bugzilla version
$Bugzilla::Config::VERSION = "2.20.1";
use Safe;
use vars qw(@param_list);
# Data::Dumper is required as needed, below. The problem is that then when
# the code locally sets $Data::Dumper::Foo, this triggers 'used only once'
# warnings.
# We can't predeclare another package's vars, though, so just use them
{
local $Data::Dumper::Sortkeys;
local $Data::Dumper::Terse;
local $Data::Dumper::Indent;
}
my %param;
# INITIALISATION CODE
# XXX - mod_perl - need to register Apache init handler for params
sub _load_datafiles {
# read in localconfig variables
do $localconfig;
if (-e "$datadir/params") {
# Handle reading old param files by munging the symbol table
# Don't have to do this if we use safe mode, since its evaled
# in a sandbox where $foo is in the same module as $::foo
#local *::param = \%param;
# Note that checksetup.pl sets file permissions on '$datadir/params'
# Using Safe mode is _not_ a guarantee of safety if someone does
# manage to write to the file. However, it won't hurt...
# See bug 165144 for not needing to eval this at all
my $s = new Safe;
$s->rdo("$datadir/params");
die "Error reading $datadir/params: $!" if $!;
die "Error evaluating $datadir/params: $@" if $@;
# Now read the param back out from the sandbox
%param = %{$s->varglob('param')};
}
}
# Load in the datafiles
_load_datafiles();
# Load in the param defintions
unless (my $ret = do 'defparams.pl') {
die "Couldn't parse defparams.pl: $@" if $@;
die "Couldn't do defparams.pl: $!" unless defined $ret;
die "Couldn't run defparams.pl" unless $ret;
}
# Stick the params into a hash
my %params;
foreach my $item (@param_list) {
$params{$item->{'name'}} = $item;
}
# END INIT CODE
# Subroutines go here
sub Param {
my ($param) = @_;
# By this stage, the param must be in the hash
die "Can't find param named $param" unless (exists $params{$param});
# When module startup code runs (which is does even via -c, when using
# |use|), we may try to grab params which don't exist yet. This affects
# tests, so have this as a fallback for the -c case
return $params{$param}->{default} if ($^C && not exists $param{$param});
# If we have a value for the param, return it
return $param{$param} if exists $param{$param};
# Else error out
die "No value for param $param (try running checksetup.pl again)";
}
sub GetParamList {
return @param_list;
}
sub SetParam {
my ($name, $value) = @_;
die "Unknown param $name" unless (exists $params{$name});
my $entry = $params{$name};
# sanity check the value
# XXX - This runs the checks. Which would be good, except that
# check_shadowdb creates the database as a sideeffect, and so the
# checker fails the second time arround...
if ($name ne 'shadowdb' && exists $entry->{'checker'}) {
my $err = $entry->{'checker'}->($value, $entry);
die "Param $name is not valid: $err" unless $err eq '';
}
$param{$name} = $value;
}
sub UpdateParams {
# --- PARAM CONVERSION CODE ---
# Note that this isn't particuarly 'clean' in terms of separating
# the backend code (ie this) from the actual params.
# We don't care about that, though
# Old bugzilla versions stored the version number in the params file
# We don't want it, so get rid of it
delete $param{'version'};
# Change from usebrowserinfo to defaultplatform/defaultopsys combo
if (exists $param{'usebrowserinfo'}) {
if (!$param{'usebrowserinfo'}) {
if (!exists $param{'defaultplatform'}) {
$param{'defaultplatform'} = 'Other';
}
if (!exists $param{'defaultopsys'}) {
$param{'defaultopsys'} = 'Other';
}
}
delete $param{'usebrowserinfo'};
}
# Change from a boolean for quips to multi-state
if (exists $param{'usequip'} && !exists $param{'enablequips'}) {
$param{'enablequips'} = $param{'usequip'} ? 'on' : 'off';
delete $param{'usequip'};
}
# Change from old product groups to controls for group_control_map
# 2002-10-14 bug 147275 bugreport@peshkin.net
if (exists $param{'usebuggroups'} && !exists $param{'makeproductgroups'}) {
$param{'makeproductgroups'} = $param{'usebuggroups'};
}
if (exists $param{'usebuggroupsentry'}
&& !exists $param{'useentrygroupdefault'}) {
$param{'useentrygroupdefault'} = $param{'usebuggroupsentry'};
}
# Modularise auth code
if (exists $param{'useLDAP'} && !exists $param{'loginmethod'}) {
$param{'loginmethod'} = $param{'useLDAP'} ? "LDAP" : "DB";
}
# set verify method to whatever loginmethod was
if (exists $param{'loginmethod'} && !exists $param{'user_verify_class'}) {
$param{'user_verify_class'} = $param{'loginmethod'};
delete $param{'loginmethod'};
}
# Remove quip-display control from parameters
# and give it to users via User Settings (Bug 41972)
if ( exists $param{'enablequips'}
&& !exists $param{'quip_list_entry_control'})
{
my $new_value;
($param{'enablequips'} eq 'on') && do {$new_value = 'open';};
($param{'enablequips'} eq 'approved') && do {$new_value = 'moderated';};
($param{'enablequips'} eq 'frozen') && do {$new_value = 'closed';};
($param{'enablequips'} eq 'off') && do {$new_value = 'closed';};
$param{'quip_list_entry_control'} = $new_value;
delete $param{'enablequips'};
}
# --- DEFAULTS FOR NEW PARAMS ---
foreach my $item (@param_list) {
my $name = $item->{'name'};
$param{$name} = $item->{'default'} unless exists $param{$name};
}
# --- REMOVE OLD PARAMS ---
my @oldparams = ();
# Remove any old params
foreach my $item (keys %param) {
if (!grep($_ eq $item, map ($_->{'name'}, @param_list))) {
require Data::Dumper;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;
push (@oldparams, [$item, Data::Dumper->Dump([$param{$item}])]);
delete $param{$item};
}
}
return @oldparams;
}
sub WriteParams {
require Data::Dumper;
# This only has an affect for Data::Dumper >= 2.12 (ie perl >= 5.8.0)
# Its just cosmetic, though, so that doesn't matter
local $Data::Dumper::Sortkeys = 1;
require File::Temp;
my ($fh, $tmpname) = File::Temp::tempfile('params.XXXXX',
DIR => $datadir );
print $fh (Data::Dumper->Dump([ \%param ], [ '*param' ]))
|| die "Can't write param file: $!";
close $fh;
rename $tmpname, "$datadir/params"
|| die "Can't rename $tmpname to $datadir/params: $!";
ChmodDataFile("$datadir/params", 0666);
}
# Some files in the data directory must be world readable if and only if
# we don't have a webserver group. Call this function to do this.
# This will become a private function once all the datafile handling stuff
# moves into this package
# This sub is not perldoc'd for that reason - noone should know about it
sub ChmodDataFile {
my ($file, $mask) = @_;
my $perm = 0770;
if ((stat($datadir))[2] & 0002) {
$perm = 0777;
}
$perm = $perm & $mask;
chmod $perm,$file;
}
sub check_multi {
my ($value, $param) = (@_);
if ($param->{'type'} eq "s") {
unless (scalar(grep {$_ eq $value} (@{$param->{'choices'}}))) {
return "Invalid choice '$value' for single-select list param '$param->{'name'}'";
}
return "";
}
elsif ($param->{'type'} eq "m") {
foreach my $chkParam (@$value) {
unless (scalar(grep {$_ eq $chkParam} (@{$param->{'choices'}}))) {
return "Invalid choice '$chkParam' for multi-select list param '$param->{'name'}'";
}
}
return "";
}
else {
return "Invalid param type '$param->{'type'}' for check_multi(); " .
"contact your Bugzilla administrator";
}
}
sub check_numeric {
my ($value) = (@_);
if ($value !~ /^[0-9]+$/) {
return "must be a numeric value";
}
return "";
}
sub check_regexp {
my ($value) = (@_);
eval { qr/$value/ };
return $@;
}
1;
__END__
=head1 NAME
Bugzilla::Config - Configuration parameters for Bugzilla
=head1 SYNOPSIS
# Getting parameters
use Bugzilla::Config;
my $fooSetting = Param('foo');
# Administration functions
use Bugzilla::Config qw(:admin);
my @valid_params = GetParamList();
my @removed_params = UpgradeParams();
SetParam($param, $value);
WriteParams();
# Localconfig variables may also be imported
use Bugzilla::Config qw(:db);
print "Connecting to $db_name as $db_user with $db_pass\n";
=head1 DESCRIPTION
This package contains ways to access Bugzilla configuration parameters.
=head1 FUNCTIONS
=head2 Parameters
Parameters can be set, retrieved, and updated.
=over 4
=item C<Param($name)>
Returns the Param with the specified name. Either a string, or, in the case
of multiple-choice parameters, an array reference.
=item C<GetParamList()>
Returns the list of known parameter types, from defparams.pl. Users should not
rely on this method; it is intended for editparams/doeditparams only
The format for the list is specified in defparams.pl
=item C<SetParam($name, $value)>
Sets the param named $name to $value. Values are checked using the checker
function for the given param if one exists.
=item C<UpdateParams()>
Updates the parameters, by transitioning old params to new formats, setting
defaults for new params, and removing obsolete ones.
Any removed params are returned in a list, with elements [$item, $oldvalue]
where $item is the entry in the param list.
=item C<WriteParams()>
Writes the parameters to disk.
=back
=head2 Parameter checking functions
All parameter checking functions are called with two parameters:
=over 4
=item *
The new value for the parameter
=item *
A reference to the entry in the param list for this parameter
=back
Functions should return error text, or the empty string if there was no error.
=over 4
=item C<check_multi>
Checks that a multi-valued parameter (ie type C<s> or type C<m>) satisfies
its contraints.
=item C<check_numeric>
Checks that the value is a valid number
=item C<check_regexp>
Checks that the value is a valid regexp
=back