Config.pm 12.2 KB
Newer Older
1 2 3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
#
5 6
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
7 8 9

package Bugzilla::Config;

10
use 5.10.1;
11 12
use strict;

13
use parent qw(Exporter);
14
use Bugzilla::Constants;
15
use Bugzilla::Hook;
16 17
use Data::Dumper;
use File::Temp;
18

19 20 21 22 23
# 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
%Bugzilla::Config::EXPORT_TAGS =
  (
24
   admin => [qw(update_params SetParam write_params)],
25
  );
26
Exporter::export_ok_tags('admin');
27 28

# INITIALISATION CODE
29
# Perl throws a warning if we use bz_locations() directly after do.
30
our %params;
31
# Load in the param definitions
32
sub _load_params {
33
    my $panels = param_panels();
34
    my %hook_panels;
35 36 37
    foreach my $panel (keys %$panels) {
        my $module = $panels->{$panel};
        eval("require $module") || die $@;
38 39
        my @new_param_list = $module->get_param_list();
        $hook_panels{lc($panel)} = { params => \@new_param_list };
40
    }
41 42
    # This hook is also called in editparams.cgi. This call here is required
    # to make SetParam work.
43
    Bugzilla::Hook::process('config_modify_panels', 
44
                            { panels => \%hook_panels });
45 46 47 48 49 50

    foreach my $panel (keys %hook_panels) {
        foreach my $item (@{$hook_panels{$panel}->{params}}) {
            $params{$item->{'name'}} = $item;
        }
    }
51 52 53 54 55
}
# END INIT CODE

# Subroutines go here

56
sub param_panels {
57
    my $param_panels = {};
58 59 60 61
    my $libpath = bz_locations()->{'libpath'};
    foreach my $item ((glob "$libpath/Bugzilla/Config/*.pm")) {
        $item =~ m#/([^/]+)\.pm$#;
        my $module = $1;
62
        $param_panels->{$module} = "Bugzilla::Config::$module" unless $module eq 'Common';
63
    }
64
    # Now check for any hooked params
65
    Bugzilla::Hook::process('config_add_panels', 
66
                            { panel_modules => $param_panels });
67
    return $param_panels;
68 69
}

70 71 72
sub SetParam {
    my ($name, $value) = @_;

73
    _load_params unless %params;
74 75 76 77 78
    die "Unknown param $name" unless (exists $params{$name});

    my $entry = $params{$name};

    # sanity check the value
79 80

    # XXX - This runs the checks. Which would be good, except that
81 82
    # check_shadowdb creates the database as a side effect, and so the
    # checker fails the second time around...
83
    if ($name ne 'shadowdb' && exists $entry->{'checker'}) {
84 85 86 87
        my $err = $entry->{'checker'}->($value, $entry);
        die "Param $name is not valid: $err" unless $err eq '';
    }

88
    Bugzilla->params->{$name} = $value;
89 90
}

91 92
sub update_params {
    my ($params) = @_;
93
    my $answer = Bugzilla->installation_answers;
94 95

    my $param = read_param_file();
96
    my %new_params;
97 98 99

    # If we didn't return any param values, then this is a new installation.
    my $new_install = !(keys %$param);
100

101
    # --- UPDATE OLD PARAMS ---
102

103
    # Change from usebrowserinfo to defaultplatform/defaultopsys combo
104 105 106
    if (exists $param->{'usebrowserinfo'}) {
        if (!$param->{'usebrowserinfo'}) {
            if (!exists $param->{'defaultplatform'}) {
107
                $new_params{'defaultplatform'} = 'Other';
108
            }
109
            if (!exists $param->{'defaultopsys'}) {
110
                $new_params{'defaultopsys'} = 'Other';
111 112 113 114
            }
        }
    }

115
    # Change from a boolean for quips to multi-state
116
    if (exists $param->{'usequip'} && !exists $param->{'enablequips'}) {
117
        $new_params{'enablequips'} = $param->{'usequip'} ? 'on' : 'off';
118 119
    }

120 121
    # Change from old product groups to controls for group_control_map
    # 2002-10-14 bug 147275 bugreport@peshkin.net
122 123 124
    if (exists $param->{'usebuggroups'} && 
        !exists $param->{'makeproductgroups'}) 
    {
125
        $new_params{'makeproductgroups'} = $param->{'usebuggroups'};
126 127
    }

128
    # Modularise auth code
129
    if (exists $param->{'useLDAP'} && !exists $param->{'loginmethod'}) {
130
        $new_params{'loginmethod'} = $param->{'useLDAP'} ? "LDAP" : "DB";
131 132
    }

133
    # set verify method to whatever loginmethod was
134 135 136
    if (exists $param->{'loginmethod'} 
        && !exists $param->{'user_verify_class'}) 
    {
137
        $new_params{'user_verify_class'} = $param->{'loginmethod'};
138 139
    }

140 141
    # Remove quip-display control from parameters
    # and give it to users via User Settings (Bug 41972)
142 143
    if ( exists $param->{'enablequips'} 
         && !exists $param->{'quip_list_entry_control'}) 
144 145
    {
        my $new_value;
146 147 148 149
        ($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';};
150
        $new_params{'quip_list_entry_control'} = $new_value;
151 152
    }

153 154 155 156 157 158 159 160 161 162 163 164 165
    # Old mail_delivery_method choices contained no uppercase characters
    if (exists $param->{'mail_delivery_method'}
        && $param->{'mail_delivery_method'} !~ /[A-Z]/) {
        my $method = $param->{'mail_delivery_method'};
        my %translation = (
            'sendmail' => 'Sendmail',
            'smtp'     => 'SMTP',
            'qmail'    => 'Qmail',
            'testfile' => 'Test',
            'none'     => 'None');
        $param->{'mail_delivery_method'} = $translation{$method};
    }

166 167 168
    # Convert the old "ssl" parameter to the new "ssl_redirect" parameter.
    # Both "authenticated sessions" and "always" turn on "ssl_redirect"
    # when upgrading.
169
    if (exists $param->{'ssl'} and $param->{'ssl'} ne 'never') {
170
        $new_params{'ssl_redirect'} = 1;
171
    }
172

173 174 175 176 177
    # "specific_search_allow_empty_words" has been renamed to "search_allow_no_criteria".
    if (exists $param->{'specific_search_allow_empty_words'}) {
        $new_params{'search_allow_no_criteria'} = $param->{'specific_search_allow_empty_words'};
    }

178 179
    # --- DEFAULTS FOR NEW PARAMS ---

180
    _load_params unless %params;
181 182
    foreach my $name (keys %params) {
        my $item = $params{$name};
183 184
        unless (exists $param->{$name}) {
            print "New parameter: $name\n" unless $new_install;
185 186 187 188
            if (exists $new_params{$name}) {
                $param->{$name} = $new_params{$name};
            }
            elsif (exists $answer->{$name}) {
189 190 191 192 193
                $param->{$name} = $answer->{$name};
            }
            else {
                $param->{$name} = $item->{'default'};
            }
194
        }
195 196
    }

197
    $param->{'utf8'} = 1 if $new_install;
198

199 200 201
    # Bug 452525: OR based groups are on by default for new installations
    $param->{'or_groups'} = 1 if $new_install;

202
    # --- REMOVE OLD PARAMS ---
203

204
    my %oldparams;
205
    # Remove any old params
206
    foreach my $item (keys %$param) {
207 208
        if (!exists $params{$item}) {
            $oldparams{$item} = delete $param->{$item};
209 210 211
        }
    }

212 213 214
    # Write any old parameters to old-params.txt
    my $datadir = bz_locations()->{'datadir'};
    my $old_param_file = "$datadir/old-params.txt";
215
    if (scalar(keys %oldparams)) {
216 217
        my $op_file = new IO::File($old_param_file, '>>', 0600)
          || die "Couldn't create $old_param_file: $!";
218 219 220

        print "The following parameters are no longer used in Bugzilla,",
              " and so have been\nmoved from your parameters file into",
221
              " $old_param_file:\n";
222

223 224 225 226 227 228 229 230
        local $Data::Dumper::Terse  = 1;
        local $Data::Dumper::Indent = 0;

        my $comma = "";
        foreach my $item (keys %oldparams) {
            print $op_file "\n\n$item:\n" . Data::Dumper->Dump([$oldparams{$item}]) . "\n";
            print "${comma}$item";
            $comma = ", ";
231 232 233 234 235 236
        }
        print "\n";
        $op_file->close;
    }

    if (ON_WINDOWS && !-e SENDMAIL_EXE
237
        && $param->{'mail_delivery_method'} eq 'Sendmail')
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    {
        my $smtp = $answer->{'SMTP_SERVER'};
        if (!$smtp) {
            print "\nBugzilla requires an SMTP server to function on",
                  " Windows.\nPlease enter your SMTP server's hostname: ";
            $smtp = <STDIN>;
            chomp $smtp;
            if ($smtp) {
                $param->{'smtpserver'} = $smtp;
             }
             else {
                print "\nWarning: No SMTP Server provided, defaulting to",
                      " localhost\n";
            }
        }

254
        $param->{'mail_delivery_method'} = 'SMTP';
255 256 257
    }

    write_params($param);
258 259 260 261

    # Return deleted params and values so that checksetup.pl has a chance
    # to convert old params to new data.
    return %oldparams;
262 263
}

264 265 266 267 268 269
sub write_params {
    my ($param_data) = @_;
    $param_data ||= Bugzilla->params;

    my $datadir    = bz_locations()->{'datadir'};
    my $param_file = "$datadir/params";
270

271
    local $Data::Dumper::Sortkeys = 1;
272 273

    my ($fh, $tmpname) = File::Temp::tempfile('params.XXXXX',
274
                                              DIR => $datadir );
275

276
    print $fh (Data::Dumper->Dump([$param_data], ['*param']))
277 278 279 280
      || die "Can't write param file: $!";

    close $fh;

281
    rename $tmpname, $param_file
282
      or die "Can't rename $tmpname to $param_file: $!";
283

284 285 286 287
    # It's not common to edit parameters and loading
    # Bugzilla::Install::Filesystem is slow.
    require Bugzilla::Install::Filesystem;
    Bugzilla::Install::Filesystem::fix_file_permissions($param_file);
288

289 290 291
    # And now we have to reset the params cache so that Bugzilla will re-read
    # them.
    delete Bugzilla->request_cache->{params};
292 293
}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
sub read_param_file {
    my %params;
    my $datadir = bz_locations()->{'datadir'};
    if (-e "$datadir/params") {
        # 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
        %params = %{$s->varglob('param')};
    }
312 313 314 315 316 317 318 319 320 321 322
    elsif ($ENV{'SERVER_SOFTWARE'}) {
       # We're in a CGI, but the params file doesn't exist. We can't
       # Template Toolkit, or even install_string, since checksetup
       # might not have thrown an error. Bugzilla::CGI->new
       # hasn't even been called yet, so we manually use CGI::Carp here
       # so that the user sees the error.
       require CGI::Carp;
       CGI::Carp->import('fatalsToBrowser');
       die "The $datadir/params file does not exist."
           . ' You probably need to run checksetup.pl.',
    }
323 324 325
    return \%params;
}

326 327 328 329 330 331 332 333 334 335 336 337 338
1;

__END__

=head1 NAME

Bugzilla::Config - Configuration parameters for Bugzilla

=head1 SYNOPSIS

  # Administration functions
  use Bugzilla::Config qw(:admin);

339
  update_params();
340
  SetParam($param, $value);
341
  write_params();
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

=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<SetParam($name, $value)>

Sets the param named $name to $value. Values are checked using the checker
function for the given param if one exists.

360
=item C<update_params()>
361 362

Updates the parameters, by transitioning old params to new formats, setting
363 364
defaults for new params, and removing obsolete ones. Used by F<checksetup.pl>
in the process of an installation or upgrade.
365

366
Prints out information about what it's doing, if it makes any changes.
367

368 369
May prompt the user for input, if certain required parameters are not
specified.
370

371
=item C<write_params($params)>
372

373
Description: Writes the parameters to disk.
374

375 376 377
Params:      C<$params> (optional) - A hashref to write to the disk
               instead of C<Bugzilla->params>. Used only by
               C<update_params>.
378

379
Returns:     nothing
380

381
=item C<read_param_file()>
382

383 384 385
Description: Most callers should never need this. This is used
             by C<Bugzilla->params> to directly read C<$datadir/params>
             and load it into memory. Use C<Bugzilla->params> instead.
386

387
Params:      none
388

389
Returns:     A hashref containing the current params in C<$datadir/params>.
390

391 392
=item C<param_panels()>

393
=back