| # -*- 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> |
| # Jacob Steenhagen <jake@bugzilla.org> |
| # J. Paul Reed <preed@sigkill.com> |
| # Bradley Baetz <bbaetz@student.usyd.edu.au> |
| # Joseph Heenan <joseph@heenan.me.uk> |
| # Erik Stambaugh <erik@dasbistro.com> |
| # Frédéric Buclin <LpSolit@gmail.com> |
| # Marc Schumann <wurblzap@gmail.com> |
| # |
| |
| package Bugzilla::Config::Common; |
| |
| use strict; |
| |
| use Socket; |
| use Time::Zone; |
| |
| use Bugzilla::Util; |
| use Bugzilla::Constants; |
| use Bugzilla::Field; |
| use Bugzilla::Group; |
| use Bugzilla::Status; |
| |
| use base qw(Exporter); |
| @Bugzilla::Config::Common::EXPORT = |
| qw(check_multi check_numeric check_regexp check_url check_group |
| check_sslbase check_priority check_severity check_platform |
| check_opsys check_shadowdb check_urlbase check_webdotbase |
| check_netmask check_user_verify_class check_image_converter |
| check_mail_delivery_method check_notification check_timezone check_utf8 |
| check_bug_status check_smtp_auth |
| check_maxattachmentsize |
| ); |
| |
| # Checking functions for the various values |
| |
| 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' || $param->{'type'} eq 'o') { |
| foreach my $chkParam (split(',', $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 $@; |
| } |
| |
| sub check_sslbase { |
| my $url = shift; |
| if ($url ne '') { |
| if ($url !~ m#^https://([^/]+).*/$#) { |
| return "must be a legal URL, that starts with https and ends with a slash."; |
| } |
| my $host = $1; |
| # Fall back to port 443 if for some reason getservbyname() fails. |
| my $port = getservbyname('https', 'tcp') || 443; |
| if ($host =~ /^(.+):(\d+)$/) { |
| $host = $1; |
| $port = $2; |
| } |
| local *SOCK; |
| my $proto = getprotobyname('tcp'); |
| socket(SOCK, PF_INET, SOCK_STREAM, $proto); |
| my $iaddr = inet_aton($host) || return "The host $host cannot be resolved"; |
| my $sin = sockaddr_in($port, $iaddr); |
| if (!connect(SOCK, $sin)) { |
| return "Failed to connect to $host:$port; unable to enable SSL"; |
| } |
| close(SOCK); |
| } |
| return ""; |
| } |
| |
| sub check_utf8 { |
| my $utf8 = shift; |
| # You cannot turn off the UTF-8 parameter if you've already converted |
| # your tables to utf-8. |
| my $dbh = Bugzilla->dbh; |
| if ($dbh->isa('Bugzilla::DB::Mysql') && $dbh->bz_db_is_utf8 && !$utf8) { |
| return "You cannot disable UTF-8 support, because your MySQL database" |
| . " is encoded in UTF-8"; |
| } |
| return ""; |
| } |
| |
| sub check_priority { |
| my ($value) = (@_); |
| my $legal_priorities = get_legal_field_values('priority'); |
| if (lsearch($legal_priorities, $value) < 0) { |
| return "Must be a legal priority value: one of " . |
| join(", ", @$legal_priorities); |
| } |
| return ""; |
| } |
| |
| sub check_severity { |
| my ($value) = (@_); |
| my $legal_severities = get_legal_field_values('bug_severity'); |
| if (lsearch($legal_severities, $value) < 0) { |
| return "Must be a legal severity value: one of " . |
| join(", ", @$legal_severities); |
| } |
| return ""; |
| } |
| |
| sub check_platform { |
| my ($value) = (@_); |
| my $legal_platforms = get_legal_field_values('rep_platform'); |
| if (lsearch(['', @$legal_platforms], $value) < 0) { |
| return "Must be empty or a legal platform value: one of " . |
| join(", ", @$legal_platforms); |
| } |
| return ""; |
| } |
| |
| sub check_opsys { |
| my ($value) = (@_); |
| my $legal_OS = get_legal_field_values('op_sys'); |
| if (lsearch(['', @$legal_OS], $value) < 0) { |
| return "Must be empty or a legal operating system value: one of " . |
| join(", ", @$legal_OS); |
| } |
| return ""; |
| } |
| |
| sub check_bug_status { |
| my $bug_status = shift; |
| my @closed_bug_statuses = map {$_->name} closed_bug_statuses(); |
| if (lsearch(\@closed_bug_statuses, $bug_status) < 0) { |
| return "Must be a valid closed status: one of " . join(', ', @closed_bug_statuses); |
| } |
| return ""; |
| } |
| |
| sub check_group { |
| my $group_name = shift; |
| return "" unless $group_name; |
| my $group = new Bugzilla::Group({'name' => $group_name}); |
| unless (defined $group) { |
| return "Must be an existing group name"; |
| } |
| return ""; |
| } |
| |
| sub check_shadowdb { |
| my ($value) = (@_); |
| $value = trim($value); |
| if ($value eq "") { |
| return ""; |
| } |
| |
| if (!Bugzilla->params->{'shadowdbhost'}) { |
| return "You need to specify a host when using a shadow database"; |
| } |
| |
| # Can't test existence of this because ConnectToDatabase uses the param, |
| # but we can't set this before testing.... |
| # This can really only be fixed after we can use the DBI more openly |
| return ""; |
| } |
| |
| sub check_urlbase { |
| my ($url) = (@_); |
| if ($url && $url !~ m:^http.*/$:) { |
| return "must be a legal URL, that starts with http and ends with a slash."; |
| } |
| return ""; |
| } |
| |
| sub check_url { |
| my ($url) = (@_); |
| return '' if $url eq ''; # Allow empty URLs |
| if ($url !~ m:/$:) { |
| return 'must be a legal URL, absolute or relative, ending with a slash.'; |
| } |
| return ''; |
| } |
| |
| sub check_webdotbase { |
| my ($value) = (@_); |
| $value = trim($value); |
| if ($value eq "") { |
| return ""; |
| } |
| if($value !~ /^https?:/) { |
| if(! -x $value) { |
| return "The file path \"$value\" is not a valid executable. Please specify the complete file path to 'dot' if you intend to generate graphs locally."; |
| } |
| # Check .htaccess allows access to generated images |
| my $webdotdir = bz_locations()->{'webdotdir'}; |
| if(-e "$webdotdir/.htaccess") { |
| open HTACCESS, "$webdotdir/.htaccess"; |
| if(! grep(/ \\\.png\$/,<HTACCESS>)) { |
| return "Dependency graph images are not accessible.\nAssuming that you have not modified the file, delete $webdotdir/.htaccess and re-run checksetup.pl to rectify.\n"; |
| } |
| close HTACCESS; |
| } |
| } |
| return ""; |
| } |
| |
| sub check_netmask { |
| my ($mask) = @_; |
| my $res = check_numeric($mask); |
| return $res if $res; |
| if ($mask < 0 || $mask > 32) { |
| return "an IPv4 netmask must be between 0 and 32 bits"; |
| } |
| # Note that if we changed the netmask from anything apart from 32, then |
| # existing logincookies which aren't for a single IP won't work |
| # any more. We can't know which ones they are, though, so they'll just |
| # take space until they're periodically cleared, later. |
| |
| return ""; |
| } |
| |
| sub check_user_verify_class { |
| # doeditparams traverses the list of params, and for each one it checks, |
| # then updates. This means that if one param checker wants to look at |
| # other params, it must be below that other one. So you can't have two |
| # params mutually dependent on each other. |
| # This means that if someone clears the LDAP config params after setting |
| # the login method as LDAP, we won't notice, but all logins will fail. |
| # So don't do that. |
| |
| my ($list, $entry) = @_; |
| $list || return 'You need to specify at least one authentication mechanism'; |
| for my $class (split /,\s*/, $list) { |
| my $res = check_multi($class, $entry); |
| return $res if $res; |
| if ($class eq 'DB') { |
| # No params |
| } |
| elsif ($class eq 'RADIUS') { |
| eval "require Authen::Radius"; |
| return "Error requiring Authen::Radius: '$@'" if $@; |
| return "RADIUS servername (RADIUS_server) is missing" unless Bugzilla->params->{"RADIUS_server"}; |
| return "RADIUS_secret is empty" unless Bugzilla->params->{"RADIUS_secret"}; |
| } |
| elsif ($class eq 'LDAP') { |
| eval "require Net::LDAP"; |
| return "Error requiring Net::LDAP: '$@'" if $@; |
| return "LDAP servername (LDAPserver) is missing" unless Bugzilla->params->{"LDAPserver"}; |
| return "LDAPBaseDN is empty" unless Bugzilla->params->{"LDAPBaseDN"}; |
| } |
| else { |
| return "Unknown user_verify_class '$class' in check_user_verify_class"; |
| } |
| } |
| return ""; |
| } |
| |
| sub check_image_converter { |
| my ($value, $hash) = @_; |
| if ($value == 1){ |
| eval "require Image::Magick"; |
| return "Error requiring Image::Magick: '$@'" if $@; |
| } |
| return ""; |
| } |
| |
| sub check_mail_delivery_method { |
| my $check = check_multi(@_); |
| return $check if $check; |
| my $mailer = shift; |
| if ($mailer eq 'sendmail' && $^O =~ /MSWin32/i) { |
| # look for sendmail.exe |
| return "Failed to locate " . SENDMAIL_EXE |
| unless -e SENDMAIL_EXE; |
| } |
| return ""; |
| } |
| |
| sub check_maxattachmentsize { |
| my $check = check_numeric(@_); |
| return $check if $check; |
| my $size = shift; |
| my $dbh = Bugzilla->dbh; |
| if ($dbh->isa('Bugzilla::DB::Mysql')) { |
| my (undef, $max_packet) = $dbh->selectrow_array( |
| q{SHOW VARIABLES LIKE 'max\_allowed\_packet'}); |
| my $byte_size = $size * 1024; |
| if ($max_packet < $byte_size) { |
| return "You asked for a maxattachmentsize of $byte_size bytes," |
| . " but the max_allowed_packet setting in MySQL currently" |
| . " only allows packets up to $max_packet bytes"; |
| } |
| } |
| return ""; |
| } |
| |
| sub check_notification { |
| my $option = shift; |
| my @current_version = |
| (BUGZILLA_VERSION =~ m/^(\d+)\.(\d+)(?:(rc|\.)(\d+))?\+?$/); |
| if ($current_version[1] % 2 && $option eq 'stable_branch_release') { |
| return "You are currently running a development snapshot, and so your " . |
| "installation is not based on a branch. If you want to be notified " . |
| "about the next stable release, you should select " . |
| "'latest_stable_release' instead"; |
| } |
| return ""; |
| } |
| |
| sub check_timezone { |
| my $tz = shift; |
| unless (defined(tz_offset($tz))) { |
| return "must be empty or a legal timezone name, such as PDT or JST"; |
| } |
| return ""; |
| } |
| |
| sub check_smtp_auth { |
| my $username = shift; |
| if ($username) { |
| eval "require Authen::SASL"; |
| return "Error requiring Authen::SASL: '$@'" if $@; |
| } |
| return ""; |
| } |
| |
| # OK, here are the parameter definitions themselves. |
| # |
| # Each definition is a hash with keys: |
| # |
| # name - name of the param |
| # desc - description of the param (for editparams.cgi) |
| # type - see below |
| # choices - (optional) see below |
| # default - default value for the param |
| # checker - (optional) checking function for validating parameter entry |
| # It is called with the value of the param as the first arg and a |
| # reference to the param's hash as the second argument |
| # |
| # The type value can be one of the following: |
| # |
| # t -- A short text entry field (suitable for a single line) |
| # p -- A short text entry field (as with type = 't'), but the string is |
| # replaced by asterisks (appropriate for passwords) |
| # l -- A long text field (suitable for many lines) |
| # b -- A boolean value (either 1 or 0) |
| # m -- A list of values, with many selectable (shows up as a select box) |
| # To specify the list of values, make the 'choices' key be an array |
| # reference of the valid choices. The 'default' key should be a string |
| # with a list of selected values (as a comma-separated list), i.e.: |
| # { |
| # name => 'multiselect', |
| # desc => 'A list of options, choose many', |
| # type => 'm', |
| # choices => [ 'a', 'b', 'c', 'd' ], |
| # default => [ 'a', 'd' ], |
| # checker => \&check_multi |
| # } |
| # |
| # Here, 'a' and 'd' are the default options, and the user may pick any |
| # combination of a, b, c, and d as valid options. |
| # |
| # &check_multi should always be used as the param verification function |
| # for list (single and multiple) parameter types. |
| # |
| # o -- A list of values, orderable, and with many selectable (shows up as a |
| # JavaScript-enhanced select box if JavaScript is enabled, and a text |
| # entry field if not) |
| # Set up in the same way as type m. |
| # |
| # s -- A list of values, with one selectable (shows up as a select box) |
| # To specify the list of values, make the 'choices' key be an array |
| # reference of the valid choices. The 'default' key should be one of |
| # those values, i.e.: |
| # { |
| # name => 'singleselect', |
| # desc => 'A list of options, choose one', |
| # type => 's', |
| # choices => [ 'a', 'b', 'c' ], |
| # default => 'b', |
| # checker => \&check_multi |
| # } |
| # |
| # Here, 'b' is the default option, and 'a' and 'c' are other possible |
| # options, but only one at a time! |
| # |
| # &check_multi should always be used as the param verification function |
| # for list (single and multiple) parameter types. |
| |
| sub get_param_list { |
| return; |
| } |
| |
| 1; |
| |
| __END__ |
| |
| =head1 NAME |
| |
| Bugzilla::Config::Common - Parameter checking functions |
| |
| =head1 DESCRIPTION |
| |
| All parameter checking functions are called with two parameters: |
| |
| =head2 Functions |
| |
| =over |
| |
| =item C<check_multi> |
| |
| Checks that a multi-valued parameter (ie types C<s>, C<o> or 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 |