Commit 7f3a749d authored by Perl Tidy's avatar Perl Tidy Committed by Dylan William Hardison

no bug - reformat all the code using the new perltidy rules

parent 3395d78c
-pbp # Start with Perl Best Practices
-w # Show all warnings
-iob # Ignore old breakpoints
-l=80 # 80 characters per line
-vmll
-ibc
-iscl
-hsc
-mbl=2 # No more than 2 blank lines
-i=2 # Indentation is 2 columns
-ci=2 # Continuation indentation is 2 columns
-vt=0 # Less vertical tightness
-pt=2 # High parenthesis tightness
-bt=2 # High brace tightness
-sbt=2 # High square bracket tightness
-wn # Weld nested containers
-isbc # Don't indent comments without leading space
...@@ -35,6 +35,7 @@ sub process_diff { ...@@ -35,6 +35,7 @@ sub process_diff {
if ($format eq 'raw') { if ($format eq 'raw') {
require PatchReader::DiffPrinter::raw; require PatchReader::DiffPrinter::raw;
$reader->sends_data_to(new PatchReader::DiffPrinter::raw()); $reader->sends_data_to(new PatchReader::DiffPrinter::raw());
# Actually print out the patch. # Actually print out the patch.
print $cgi->header(-type => 'text/plain'); print $cgi->header(-type => 'text/plain');
disable_utf8(); disable_utf8();
...@@ -43,11 +44,14 @@ sub process_diff { ...@@ -43,11 +44,14 @@ sub process_diff {
else { else {
my @other_patches = (); my @other_patches = ();
if ($lc->{interdiffbin} && $lc->{diffpath}) { if ($lc->{interdiffbin} && $lc->{diffpath}) {
# Get the list of attachments that the user can view in this bug. # Get the list of attachments that the user can view in this bug.
my @attachments = my @attachments
@{Bugzilla::Attachment->get_attachments_by_bug($attachment->bug)}; = @{Bugzilla::Attachment->get_attachments_by_bug($attachment->bug)};
# Extract patches only. # Extract patches only.
@attachments = grep {$_->ispatch == 1} @attachments; @attachments = grep { $_->ispatch == 1 } @attachments;
# We want them sorted from newer to older. # We want them sorted from newer to older.
@attachments = sort { $b->id <=> $a->id } @attachments; @attachments = sort { $b->id <=> $a->id } @attachments;
...@@ -59,9 +63,14 @@ sub process_diff { ...@@ -59,9 +63,14 @@ sub process_diff {
$select_next_patch = 1; $select_next_patch = 1;
} }
else { else {
push(@other_patches, { 'id' => $attach->id, push(
@other_patches,
{
'id' => $attach->id,
'desc' => $attach->description, 'desc' => $attach->description,
'selected' => $select_next_patch }); 'selected' => $select_next_patch
}
);
$select_next_patch = 0; $select_next_patch = 0;
} }
} }
...@@ -73,6 +82,7 @@ sub process_diff { ...@@ -73,6 +82,7 @@ sub process_diff {
$vars->{'other_patches'} = \@other_patches; $vars->{'other_patches'} = \@other_patches;
setup_template_patch_reader($reader, $vars); setup_template_patch_reader($reader, $vars);
# The patch is going to be displayed in a HTML page and if the utf8 # The patch is going to be displayed in a HTML page and if the utf8
# param is enabled, we have to encode attachment data as utf8. # param is enabled, we have to encode attachment data as utf8.
if (Bugzilla->params->{'utf8'}) { if (Bugzilla->params->{'utf8'}) {
...@@ -102,6 +112,7 @@ sub process_interdiff { ...@@ -102,6 +112,7 @@ sub process_interdiff {
# Get old patch data. # Get old patch data.
my ($old_filename, $old_file_list) = get_unified_diff($old_attachment, $format); my ($old_filename, $old_file_list) = get_unified_diff($old_attachment, $format);
# Get new patch data. # Get new patch data.
my ($new_filename, $new_file_list) = get_unified_diff($new_attachment, $format); my ($new_filename, $new_file_list) = get_unified_diff($new_attachment, $format);
...@@ -119,21 +130,23 @@ sub process_interdiff { ...@@ -119,21 +130,23 @@ sub process_interdiff {
require Apache2::RequestUtil; require Apache2::RequestUtil;
require Apache2::SubProcess; require Apache2::SubProcess;
my $request = Apache2::RequestUtil->request; my $request = Apache2::RequestUtil->request;
(undef, $interdiff_stdout, $interdiff_stderr) = $request->spawn_proc_prog( (undef, $interdiff_stdout, $interdiff_stderr)
$lc->{interdiffbin}, [$old_filename, $new_filename] = $request->spawn_proc_prog($lc->{interdiffbin},
); [$old_filename, $new_filename]);
$use_select = !PERLIO_IS_ENABLED; $use_select = !PERLIO_IS_ENABLED;
} else { }
else {
$interdiff_stderr = gensym; $interdiff_stderr = gensym;
$pid = open3(gensym, $interdiff_stdout, $interdiff_stderr, $pid = open3(gensym, $interdiff_stdout, $interdiff_stderr, $lc->{interdiffbin},
$lc->{interdiffbin}, $old_filename, $new_filename); $old_filename, $new_filename);
$use_select = 1; $use_select = 1;
} }
if ($format ne 'raw' && Bugzilla->params->{'utf8'}) { if ($format ne 'raw' && Bugzilla->params->{'utf8'}) {
binmode $interdiff_stdout, ':utf8'; binmode $interdiff_stdout, ':utf8';
binmode $interdiff_stderr, ':utf8'; binmode $interdiff_stderr, ':utf8';
} else { }
else {
binmode $interdiff_stdout; binmode $interdiff_stdout;
binmode $interdiff_stderr; binmode $interdiff_stderr;
} }
...@@ -150,14 +163,16 @@ sub process_interdiff { ...@@ -150,14 +163,16 @@ sub process_interdiff {
} }
if ($handle == $interdiff_stdout) { if ($handle == $interdiff_stdout) {
$stdout .= $line; $stdout .= $line;
} else { }
else {
$stderr .= $line; $stderr .= $line;
} }
} }
} }
waitpid($pid, 0) if $pid; waitpid($pid, 0) if $pid;
} else { }
else {
local $/ = undef; local $/ = undef;
$stdout = <$interdiff_stdout>; $stdout = <$interdiff_stdout>;
$stdout //= ''; $stdout //= '';
...@@ -165,8 +180,7 @@ sub process_interdiff { ...@@ -165,8 +180,7 @@ sub process_interdiff {
$stderr //= ''; $stderr //= '';
} }
close($interdiff_stdout), close($interdiff_stdout), close($interdiff_stderr);
close($interdiff_stderr);
# Tidy up # Tidy up
unlink($old_filename) or warn "Could not unlink $old_filename: $!"; unlink($old_filename) or warn "Could not unlink $old_filename: $!";
...@@ -185,6 +199,7 @@ sub process_interdiff { ...@@ -185,6 +199,7 @@ sub process_interdiff {
if ($format eq 'raw') { if ($format eq 'raw') {
require PatchReader::DiffPrinter::raw; require PatchReader::DiffPrinter::raw;
$reader->sends_data_to(new PatchReader::DiffPrinter::raw()); $reader->sends_data_to(new PatchReader::DiffPrinter::raw());
# Actually print out the patch. # Actually print out the patch.
print $cgi->header(-type => 'text/plain'); print $cgi->header(-type => 'text/plain');
disable_utf8(); disable_utf8();
...@@ -199,8 +214,8 @@ sub process_interdiff { ...@@ -199,8 +214,8 @@ sub process_interdiff {
setup_template_patch_reader($reader, $vars); setup_template_patch_reader($reader, $vars);
} }
$reader->iterate_string('interdiff #' . $old_attachment->id . $reader->iterate_string(
' #' . $new_attachment->id, $stdout); 'interdiff #' . $old_attachment->id . ' #' . $new_attachment->id, $stdout);
} }
###################### ######################
...@@ -217,7 +232,7 @@ sub get_unified_diff { ...@@ -217,7 +232,7 @@ sub get_unified_diff {
require File::Temp; require File::Temp;
$attachment->ispatch $attachment->ispatch
|| ThrowCodeError('must_be_patch', { 'attach_id' => $attachment->id }); || ThrowCodeError('must_be_patch', {'attach_id' => $attachment->id});
# Reads in the patch, converting to unified diff in a temp file. # Reads in the patch, converting to unified diff in a temp file.
my $reader = new PatchReader::Raw; my $reader = new PatchReader::Raw;
...@@ -231,6 +246,7 @@ sub get_unified_diff { ...@@ -231,6 +246,7 @@ sub get_unified_diff {
# Prints out to temporary file. # Prints out to temporary file.
my ($fh, $filename) = File::Temp::tempfile(); my ($fh, $filename) = File::Temp::tempfile();
if ($format ne 'raw' && Bugzilla->params->{'utf8'}) { if ($format ne 'raw' && Bugzilla->params->{'utf8'}) {
# The HTML page will be displayed with the UTF-8 encoding. # The HTML page will be displayed with the UTF-8 encoding.
binmode $fh, ':utf8'; binmode $fh, ':utf8';
} }
...@@ -250,15 +266,14 @@ sub warn_if_interdiff_might_fail { ...@@ -250,15 +266,14 @@ sub warn_if_interdiff_might_fail {
# Verify that the list of files diffed is the same. # Verify that the list of files diffed is the same.
my @old_files = sort keys %{$old_file_list}; my @old_files = sort keys %{$old_file_list};
my @new_files = sort keys %{$new_file_list}; my @new_files = sort keys %{$new_file_list};
if (@old_files != @new_files if (@old_files != @new_files || join(' ', @old_files) ne join(' ', @new_files))
|| join(' ', @old_files) ne join(' ', @new_files))
{ {
return 'interdiff1'; return 'interdiff1';
} }
# Verify that the revisions in the files are the same. # Verify that the revisions in the files are the same.
foreach my $file (keys %{$old_file_list}) { foreach my $file (keys %{$old_file_list}) {
if (exists $old_file_list->{$file}{old_revision} if ( exists $old_file_list->{$file}{old_revision}
&& exists $new_file_list->{$file}{old_revision} && exists $new_file_list->{$file}{old_revision}
&& $old_file_list->{$file}{old_revision} ne && $old_file_list->{$file}{old_revision} ne
$new_file_list->{$file}{old_revision}) $new_file_list->{$file}{old_revision})
...@@ -289,11 +304,11 @@ sub setup_template_patch_reader { ...@@ -289,11 +304,11 @@ sub setup_template_patch_reader {
# Print everything out. # Print everything out.
print $cgi->header(-type => 'text/html'); print $cgi->header(-type => 'text/html');
$last_reader->sends_data_to(new PatchReader::DiffPrinter::template($template, $last_reader->sends_data_to(new PatchReader::DiffPrinter::template(
'attachment/diff-header.html.tmpl', $template, 'attachment/diff-header.html.tmpl',
'attachment/diff-file.html.tmpl', 'attachment/diff-file.html.tmpl', 'attachment/diff-footer.html.tmpl',
'attachment/diff-footer.html.tmpl', $vars
$vars)); ));
} }
1; 1;
......
...@@ -37,6 +37,7 @@ sub new { ...@@ -37,6 +37,7 @@ sub new {
$self->{_info_getter} = new Bugzilla::Auth::Login::Stack($params->{Login}); $self->{_info_getter} = new Bugzilla::Auth::Login::Stack($params->{Login});
$self->{_verifier} = new Bugzilla::Auth::Verify::Stack($params->{Verify}); $self->{_verifier} = new Bugzilla::Auth::Verify::Stack($params->{Verify});
# If we ever have any other login persistence methods besides cookies, # If we ever have any other login persistence methods besides cookies,
# this could become more configurable. # this could become more configurable.
$self->{_persister} = new Bugzilla::Auth::Persist::Cookie(); $self->{_persister} = new Bugzilla::Auth::Persist::Cookie();
...@@ -60,8 +61,8 @@ sub login { ...@@ -60,8 +61,8 @@ sub login {
if ($login_info->{failure}) { if ($login_info->{failure}) {
return $self->_handle_login_result($login_info, $type); return $self->_handle_login_result($login_info, $type);
} }
$login_info = $login_info
$self->{_verifier}->{successful}->create_or_update_user($login_info); = $self->{_verifier}->{successful}->create_or_update_user($login_info);
} }
else { else {
$login_info = $self->{_verifier}->create_or_update_user($login_info); $login_info = $self->{_verifier}->create_or_update_user($login_info);
...@@ -74,8 +75,8 @@ sub login { ...@@ -74,8 +75,8 @@ sub login {
# Make sure the user isn't disabled. # Make sure the user isn't disabled.
my $user = $login_info->{user}; my $user = $login_info->{user};
if (!$user->is_enabled) { if (!$user->is_enabled) {
return $self->_handle_login_result({ failure => AUTH_DISABLED, return $self->_handle_login_result({failure => AUTH_DISABLED, user => $user},
user => $user }, $type); $type);
} }
$user->set_authorizer($self); $user->set_authorizer($self);
...@@ -89,8 +90,7 @@ sub can_change_password { ...@@ -89,8 +90,7 @@ sub can_change_password {
my $getter = $self->{_info_getter}->{successful}; my $getter = $self->{_info_getter}->{successful};
$getter = $self->{_info_getter} $getter = $self->{_info_getter}
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie')); if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
return $verifier->can_change_password && return $verifier->can_change_password && $getter->user_can_create_account;
$getter->user_can_create_account;
} }
sub can_login { sub can_login {
...@@ -104,6 +104,7 @@ sub can_login { ...@@ -104,6 +104,7 @@ sub can_login {
sub can_logout { sub can_logout {
my ($self) = @_; my ($self) = @_;
my $getter = $self->{_info_getter}->{successful}; my $getter = $self->{_info_getter}->{successful};
# If there's no successful getter, we're not logged in, so of # If there's no successful getter, we're not logged in, so of
# course we can't log out! # course we can't log out!
return 0 unless $getter; return 0 unless $getter;
...@@ -126,8 +127,7 @@ sub user_can_create_account { ...@@ -126,8 +127,7 @@ sub user_can_create_account {
my $getter = $self->{_info_getter}->{successful}; my $getter = $self->{_info_getter}->{successful};
$getter = $self->{_info_getter} $getter = $self->{_info_getter}
if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie')); if (!$getter || $getter->isa('Bugzilla::Auth::Login::Cookie'));
return $verifier->user_can_create_account return $verifier->user_can_create_account && $getter->user_can_create_account;
&& $getter->user_can_create_account;
} }
sub extern_id_used { sub extern_id_used {
...@@ -148,6 +148,7 @@ sub _handle_login_result { ...@@ -148,6 +148,7 @@ sub _handle_login_result {
my $fail_code = $result->{failure}; my $fail_code = $result->{failure};
if (!$fail_code) { if (!$fail_code) {
# We don't persist logins over GET requests in the WebService, # We don't persist logins over GET requests in the WebService,
# because the persistance information can't be re-used again. # because the persistance information can't be re-used again.
# (See Bugzilla::WebService::Server::JSONRPC for more info.) # (See Bugzilla::WebService::Server::JSONRPC for more info.)
...@@ -166,27 +167,28 @@ sub _handle_login_result { ...@@ -166,27 +167,28 @@ sub _handle_login_result {
} }
} }
elsif ($fail_code == AUTH_NODATA) { elsif ($fail_code == AUTH_NODATA) {
$self->{_info_getter}->fail_nodata($self) $self->{_info_getter}->fail_nodata($self) if $login_type == LOGIN_REQUIRED;
if $login_type == LOGIN_REQUIRED;
# If we're not LOGIN_REQUIRED, we just return the default user. # If we're not LOGIN_REQUIRED, we just return the default user.
$user = Bugzilla->user; $user = Bugzilla->user;
} }
# The username/password may be wrong # The username/password may be wrong
# Don't let the user know whether the username exists or whether # Don't let the user know whether the username exists or whether
# the password was just wrong. (This makes it harder for a cracker # the password was just wrong. (This makes it harder for a cracker
# to find account names by brute force) # to find account names by brute force)
elsif ($fail_code == AUTH_LOGINFAILED or $fail_code == AUTH_NO_SUCH_USER) { elsif ($fail_code == AUTH_LOGINFAILED or $fail_code == AUTH_NO_SUCH_USER) {
my $remaining_attempts = MAX_LOGIN_ATTEMPTS my $remaining_attempts = MAX_LOGIN_ATTEMPTS - ($result->{failure_count} || 0);
- ($result->{failure_count} || 0); ThrowUserError("invalid_login_or_password", {remaining => $remaining_attempts});
ThrowUserError("invalid_login_or_password",
{ remaining => $remaining_attempts });
} }
# The account may be disabled # The account may be disabled
elsif ($fail_code == AUTH_DISABLED) { elsif ($fail_code == AUTH_DISABLED) {
$self->{_persister}->logout(); $self->{_persister}->logout();
# XXX This is NOT a good way to do this, architecturally. # XXX This is NOT a good way to do this, architecturally.
$self->{_persister}->clear_browser_cookies(); $self->{_persister}->clear_browser_cookies();
# and throw a user error # and throw a user error
ThrowUserError("account_disabled", ThrowUserError("account_disabled",
{'disabled_reason' => $result->{user}->disabledtext}); {'disabled_reason' => $result->{user}->disabledtext});
...@@ -198,25 +200,27 @@ sub _handle_login_result { ...@@ -198,25 +200,27 @@ sub _handle_login_result {
# determined by the 5th-from-last login failure (or more/less than # determined by the 5th-from-last login failure (or more/less than
# 5th, if MAX_LOGIN_ATTEMPTS is not 5). # 5th, if MAX_LOGIN_ATTEMPTS is not 5).
my $determiner = $attempts->[scalar(@$attempts) - MAX_LOGIN_ATTEMPTS]; my $determiner = $attempts->[scalar(@$attempts) - MAX_LOGIN_ATTEMPTS];
my $unlock_at = datetime_from($determiner->{login_time}, my $unlock_at
Bugzilla->local_timezone); = datetime_from($determiner->{login_time}, Bugzilla->local_timezone);
$unlock_at->add(minutes => LOGIN_LOCKOUT_INTERVAL); $unlock_at->add(minutes => LOGIN_LOCKOUT_INTERVAL);
# If we were *just* locked out, notify the maintainer about the # If we were *just* locked out, notify the maintainer about the
# lockout. # lockout.
if ($result->{just_locked_out}) { if ($result->{just_locked_out}) {
# We're sending to the maintainer, who may be not a Bugzilla # We're sending to the maintainer, who may be not a Bugzilla
# account, but just an email address. So we use the # account, but just an email address. So we use the
# installation's default language for sending the email. # installation's default language for sending the email.
my $default_settings = Bugzilla::User::Setting::get_defaults(); my $default_settings = Bugzilla::User::Setting::get_defaults();
my $template = Bugzilla->template_inner( my $template
$default_settings->{lang}->{default_value}); = Bugzilla->template_inner($default_settings->{lang}->{default_value});
my $address = $attempts->[0]->{ip_addr}; my $address = $attempts->[0]->{ip_addr};
# Note: inet_aton will only resolve IPv4 addresses. # Note: inet_aton will only resolve IPv4 addresses.
# For IPv6 we'll need to use inet_pton which requires Perl 5.12. # For IPv6 we'll need to use inet_pton which requires Perl 5.12.
my $n = inet_aton($address); my $n = inet_aton($address);
if ($n) { if ($n) {
$address = gethostbyaddr($n, AF_INET) . " ($address)" $address = gethostbyaddr($n, AF_INET) . " ($address)";
} }
my $vars = { my $vars = {
locked_user => $user, locked_user => $user,
...@@ -232,11 +236,12 @@ sub _handle_login_result { ...@@ -232,11 +236,12 @@ sub _handle_login_result {
$unlock_at->set_time_zone($user->timezone); $unlock_at->set_time_zone($user->timezone);
ThrowUserError('account_locked', ThrowUserError('account_locked',
{ ip_addr => $determiner->{ip_addr}, unlock_at => $unlock_at }); {ip_addr => $determiner->{ip_addr}, unlock_at => $unlock_at});
} }
# If we get here, then we've run out of options, which shouldn't happen. # If we get here, then we've run out of options, which shouldn't happen.
else { else {
ThrowCodeError("authres_unhandled", { value => $fail_code }); ThrowCodeError("authres_unhandled", {value => $fail_code});
} }
return $user; return $user;
......
...@@ -32,12 +32,13 @@ sub get_login_info { ...@@ -32,12 +32,13 @@ sub get_login_info {
my $api_key_text = trim(delete $params->{'Bugzilla_api_key'}); my $api_key_text = trim(delete $params->{'Bugzilla_api_key'});
if (!i_am_webservice() || !$api_key_text) { if (!i_am_webservice() || !$api_key_text) {
return { failure => AUTH_NODATA }; return {failure => AUTH_NODATA};
} }
my $api_key = Bugzilla::User::APIKey->new({ name => $api_key_text }); my $api_key = Bugzilla::User::APIKey->new({name => $api_key_text});
if (!$api_key or $api_key->api_key ne $api_key_text) { if (!$api_key or $api_key->api_key ne $api_key_text) {
# The second part checks the correct capitalisation. Silly MySQL # The second part checks the correct capitalisation. Silly MySQL
ThrowUserError("api_key_not_valid"); ThrowUserError("api_key_not_valid");
} }
...@@ -47,7 +48,7 @@ sub get_login_info { ...@@ -47,7 +48,7 @@ sub get_login_info {
$api_key->update_last_used(); $api_key->update_last_used();
return { user_id => $api_key->user_id }; return {user_id => $api_key->user_id};
} }
1; 1;
...@@ -27,24 +27,29 @@ sub get_login_info { ...@@ -27,24 +27,29 @@ sub get_login_info {
my $login = trim(delete $params->{'Bugzilla_login'}); my $login = trim(delete $params->{'Bugzilla_login'});
my $password = delete $params->{'Bugzilla_password'}; my $password = delete $params->{'Bugzilla_password'};
# The token must match the cookie to authenticate the request. # The token must match the cookie to authenticate the request.
my $login_token = delete $params->{'Bugzilla_login_token'}; my $login_token = delete $params->{'Bugzilla_login_token'};
my $login_cookie = $cgi->cookie('Bugzilla_login_request_cookie'); my $login_cookie = $cgi->cookie('Bugzilla_login_request_cookie');
my $valid = 0; my $valid = 0;
# If the web browser accepts cookies, use them. # If the web browser accepts cookies, use them.
if ($login_token && $login_cookie) { if ($login_token && $login_cookie) {
my ($time, undef) = split(/-/, $login_token); my ($time, undef) = split(/-/, $login_token);
# Regenerate the token based on the information we have. # Regenerate the token based on the information we have.
my $expected_token = issue_hash_token(['login_request', $login_cookie], $time); my $expected_token = issue_hash_token(['login_request', $login_cookie], $time);
$valid = 1 if $expected_token eq $login_token; $valid = 1 if $expected_token eq $login_token;
$cgi->remove_cookie('Bugzilla_login_request_cookie'); $cgi->remove_cookie('Bugzilla_login_request_cookie');
} }
# WebServices and other local scripts can bypass this check. # WebServices and other local scripts can bypass this check.
# This is safe because we won't store a login cookie in this case. # This is safe because we won't store a login cookie in this case.
elsif (Bugzilla->usage_mode != USAGE_MODE_BROWSER) { elsif (Bugzilla->usage_mode != USAGE_MODE_BROWSER) {
$valid = 1; $valid = 1;
} }
# Else falls back to the Referer header and accept local URLs. # Else falls back to the Referer header and accept local URLs.
# Attachments are served from a separate host (ideally), and so # Attachments are served from a separate host (ideally), and so
# an evil attachment cannot abuse this check with a redirect. # an evil attachment cannot abuse this check with a redirect.
...@@ -52,18 +57,19 @@ sub get_login_info { ...@@ -52,18 +57,19 @@ sub get_login_info {
my $urlbase = correct_urlbase(); my $urlbase = correct_urlbase();
$valid = 1 if $referer =~ /^\Q$urlbase\E/; $valid = 1 if $referer =~ /^\Q$urlbase\E/;
} }
# If the web browser doesn't accept cookies and the Referer header # If the web browser doesn't accept cookies and the Referer header
# is missing, we have no way to make sure that the authentication # is missing, we have no way to make sure that the authentication
# request comes from the user. # request comes from the user.
elsif ($login && $password) { elsif ($login && $password) {
ThrowUserError('auth_untrusted_request', { login => $login }); ThrowUserError('auth_untrusted_request', {login => $login});
} }
if (!defined($login) || !defined($password) || !$valid) { if (!defined($login) || !defined($password) || !$valid) {
return { failure => AUTH_NODATA }; return {failure => AUTH_NODATA};
} }
return { username => $login, password => $password }; return {username => $login, password => $password};
} }
sub fail_nodata { sub fail_nodata {
...@@ -77,7 +83,7 @@ sub fail_nodata { ...@@ -77,7 +83,7 @@ sub fail_nodata {
print $cgi->header(); print $cgi->header();
$template->process("account/auth/login.html.tmpl", $template->process("account/auth/login.html.tmpl",
{ 'target' => $cgi->url(-relative=>1) }) {'target' => $cgi->url(-relative => 1)})
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
exit; exit;
} }
......
...@@ -42,12 +42,12 @@ sub get_login_info { ...@@ -42,12 +42,12 @@ sub get_login_info {
# If cookies cannot be found, this could mean that they haven't # If cookies cannot be found, this could mean that they haven't
# been made available yet. In this case, look at Bugzilla_cookie_list. # been made available yet. In this case, look at Bugzilla_cookie_list.
unless ($login_cookie) { unless ($login_cookie) {
my $cookie = first {$_->name eq 'Bugzilla_logincookie'} my $cookie = first { $_->name eq 'Bugzilla_logincookie' }
@{$cgi->{'Bugzilla_cookie_list'}}; @{$cgi->{'Bugzilla_cookie_list'}};
$login_cookie = $cookie->value if $cookie; $login_cookie = $cookie->value if $cookie;
} }
unless ($user_id) { unless ($user_id) {
my $cookie = first {$_->name eq 'Bugzilla_login'} my $cookie = first { $_->name eq 'Bugzilla_login' }
@{$cgi->{'Bugzilla_cookie_list'}}; @{$cgi->{'Bugzilla_cookie_list'}};
$user_id = $cookie->value if $cookie; $user_id = $cookie->value if $cookie;
} }
...@@ -58,11 +58,11 @@ sub get_login_info { ...@@ -58,11 +58,11 @@ sub get_login_info {
my $api_token = Bugzilla->input_params->{Bugzilla_api_token}; my $api_token = Bugzilla->input_params->{Bugzilla_api_token};
my ($token_user_id, undef, undef, $token_type) my ($token_user_id, undef, undef, $token_type)
= Bugzilla::Token::GetTokenData($api_token); = Bugzilla::Token::GetTokenData($api_token);
if (!defined $token_type if ( !defined $token_type
|| $token_type ne 'api_token' || $token_type ne 'api_token'
|| $user_id != $token_user_id) || $user_id != $token_user_id)
{ {
ThrowUserError('auth_invalid_token', { token => $api_token }); ThrowUserError('auth_invalid_token', {token => $api_token});
} }
} }
} }
...@@ -77,29 +77,34 @@ sub get_login_info { ...@@ -77,29 +77,34 @@ sub get_login_info {
my $ip_addr = remote_ip(); my $ip_addr = remote_ip();
if ($login_cookie && $user_id) { if ($login_cookie && $user_id) {
# Anything goes for these params - they're just strings which # Anything goes for these params - they're just strings which
# we're going to verify against the db # we're going to verify against the db
trick_taint($ip_addr); trick_taint($ip_addr);
trick_taint($login_cookie); trick_taint($login_cookie);
detaint_natural($user_id); detaint_natural($user_id);
my $db_cookie = my $db_cookie = $dbh->selectrow_array(
$dbh->selectrow_array('SELECT cookie 'SELECT cookie
FROM logincookies FROM logincookies
WHERE cookie = ? WHERE cookie = ?
AND userid = ? AND userid = ?
AND (ipaddr = ? OR ipaddr IS NULL)', AND (ipaddr = ? OR ipaddr IS NULL)', undef,
undef, ($login_cookie, $user_id, $ip_addr)); ($login_cookie, $user_id, $ip_addr)
);
# If the cookie or token is valid, return a valid username. # If the cookie or token is valid, return a valid username.
# If they were not valid and we are using a webservice, then # If they were not valid and we are using a webservice, then
# throw an error notifying the client. # throw an error notifying the client.
if (defined $db_cookie && $login_cookie eq $db_cookie) { if (defined $db_cookie && $login_cookie eq $db_cookie) {
# If we logged in successfully, then update the lastused # If we logged in successfully, then update the lastused
# time on the login cookie # time on the login cookie
$dbh->do("UPDATE logincookies SET lastused = NOW() $dbh->do(
WHERE cookie = ?", undef, $login_cookie); "UPDATE logincookies SET lastused = NOW()
return { user_id => $user_id }; WHERE cookie = ?", undef, $login_cookie
);
return {user_id => $user_id};
} }
elsif (i_am_webservice()) { elsif (i_am_webservice()) {
ThrowUserError('invalid_cookies_or_token'); ThrowUserError('invalid_cookies_or_token');
...@@ -111,7 +116,7 @@ sub get_login_info { ...@@ -111,7 +116,7 @@ sub get_login_info {
# want to ever return AUTH_LOGINFAILED, because we don't want Bugzilla to # want to ever return AUTH_LOGINFAILED, because we don't want Bugzilla to
# actually throw an error when it gets a bad cookie or token. It should just # actually throw an error when it gets a bad cookie or token. It should just
# look like there was no cookie or token to begin with. # look like there was no cookie or token to begin with.
return { failure => AUTH_NODATA }; return {failure => AUTH_NODATA};
} }
sub login_token { sub login_token {
...@@ -134,10 +139,8 @@ sub login_token { ...@@ -134,10 +139,8 @@ sub login_token {
return $self->{'_login_token'} = undef; return $self->{'_login_token'} = undef;
} }
return $self->{'_login_token'} = { return $self->{'_login_token'}
user_id => $user_id, = {user_id => $user_id, login_token => $login_token};
login_token => $login_token
};
} }
1; 1;
...@@ -30,10 +30,13 @@ sub get_login_info { ...@@ -30,10 +30,13 @@ sub get_login_info {
my $env_email = $ENV{Bugzilla->params->{"auth_env_email"}} || ''; my $env_email = $ENV{Bugzilla->params->{"auth_env_email"}} || '';
my $env_realname = $ENV{Bugzilla->params->{"auth_env_realname"}} || ''; my $env_realname = $ENV{Bugzilla->params->{"auth_env_realname"}} || '';
return { failure => AUTH_NODATA } if !$env_email; return {failure => AUTH_NODATA} if !$env_email;
return { username => $env_email, extern_id => $env_id, return {
realname => $env_realname }; username => $env_email,
extern_id => $env_id,
realname => $env_realname
};
} }
sub fail_nodata { sub fail_nodata {
......
...@@ -27,7 +27,7 @@ sub new { ...@@ -27,7 +27,7 @@ sub new {
my $list = shift; my $list = shift;
my %methods = map { $_ => "Bugzilla/Auth/Login/$_.pm" } split(',', $list); my %methods = map { $_ => "Bugzilla/Auth/Login/$_.pm" } split(',', $list);
lock_keys(%methods); lock_keys(%methods);
Bugzilla::Hook::process('auth_login_methods', { modules => \%methods }); Bugzilla::Hook::process('auth_login_methods', {modules => \%methods});
$self->{_stack} = []; $self->{_stack} = [];
foreach my $login_method (split(',', $list)) { foreach my $login_method (split(',', $list)) {
...@@ -44,6 +44,7 @@ sub get_login_info { ...@@ -44,6 +44,7 @@ sub get_login_info {
my $self = shift; my $self = shift;
my $result; my $result;
foreach my $object (@{$self->{_stack}}) { foreach my $object (@{$self->{_stack}}) {
# See Bugzilla::WebService::Server::JSONRPC for where and why # See Bugzilla::WebService::Server::JSONRPC for where and why
# auth_no_automatic_login is used. # auth_no_automatic_login is used.
if (Bugzilla->request_cache->{auth_no_automatic_login}) { if (Bugzilla->request_cache->{auth_no_automatic_login}) {
...@@ -53,8 +54,9 @@ sub get_login_info { ...@@ -53,8 +54,9 @@ sub get_login_info {
$self->{successful} = $object; $self->{successful} = $object;
# We only carry on down the stack if this method denied all knowledge. # We only carry on down the stack if this method denied all knowledge.
last unless ($result->{failure} last
&& ($result->{failure} eq AUTH_NODATA unless ($result->{failure}
&& ( $result->{failure} eq AUTH_NODATA
|| $result->{failure} eq AUTH_NO_SUCH_USER)); || $result->{failure} eq AUTH_NO_SUCH_USER));
# If none of the methods succeed, it's undef. # If none of the methods succeed, it's undef.
...@@ -65,9 +67,11 @@ sub get_login_info { ...@@ -65,9 +67,11 @@ sub get_login_info {
sub fail_nodata { sub fail_nodata {
my $self = shift; my $self = shift;
# We fail from the bottom of the stack. # We fail from the bottom of the stack.
my @reverse_stack = reverse @{$self->{_stack}}; my @reverse_stack = reverse @{$self->{_stack}};
foreach my $object (@reverse_stack) { foreach my $object (@reverse_stack) {
# We pick the first object that actually has the method # We pick the first object that actually has the method
# implemented. # implemented.
if ($object->can('fail_nodata')) { if ($object->can('fail_nodata')) {
...@@ -78,6 +82,7 @@ sub fail_nodata { ...@@ -78,6 +82,7 @@ sub fail_nodata {
sub can_login { sub can_login {
my ($self) = @_; my ($self) = @_;
# We return true if any method can log in. # We return true if any method can log in.
foreach my $object (@{$self->{_stack}}) { foreach my $object (@{$self->{_stack}}) {
return 1 if $object->can_login; return 1 if $object->can_login;
...@@ -87,6 +92,7 @@ sub can_login { ...@@ -87,6 +92,7 @@ sub can_login {
sub user_can_create_account { sub user_can_create_account {
my ($self) = @_; my ($self) = @_;
# We return true if any method allows users to create accounts. # We return true if any method allows users to create accounts.
foreach my $object (@{$self->{_stack}}) { foreach my $object (@{$self->{_stack}}) {
return 1 if $object->user_can_create_account; return 1 if $object->user_can_create_account;
...@@ -96,7 +102,7 @@ sub user_can_create_account { ...@@ -96,7 +102,7 @@ sub user_can_create_account {
sub extern_id_used { sub extern_id_used {
my ($self) = @_; my ($self) = @_;
return any { $_->extern_id_used } @{ $self->{_stack} }; return any { $_->extern_id_used } @{$self->{_stack}};
} }
1; 1;
...@@ -34,6 +34,7 @@ sub persist_login { ...@@ -34,6 +34,7 @@ sub persist_login {
my $ip_addr; my $ip_addr;
if ($input_params->{'Bugzilla_restrictlogin'}) { if ($input_params->{'Bugzilla_restrictlogin'}) {
$ip_addr = remote_ip(); $ip_addr = remote_ip();
# The IP address is valid, at least for comparing with itself in a # The IP address is valid, at least for comparing with itself in a
# subsequent login # subsequent login
trick_taint($ip_addr); trick_taint($ip_addr);
...@@ -41,18 +42,18 @@ sub persist_login { ...@@ -41,18 +42,18 @@ sub persist_login {
$dbh->bz_start_transaction(); $dbh->bz_start_transaction();
my $login_cookie = my $login_cookie
Bugzilla::Token::GenerateUniqueToken('logincookies', 'cookie'); = Bugzilla::Token::GenerateUniqueToken('logincookies', 'cookie');
$dbh->do("INSERT INTO logincookies (cookie, userid, ipaddr, lastused) $dbh->do(
VALUES (?, ?, ?, NOW())", "INSERT INTO logincookies (cookie, userid, ipaddr, lastused)
undef, $login_cookie, $user->id, $ip_addr); VALUES (?, ?, ?, NOW())", undef, $login_cookie, $user->id, $ip_addr
);
# Issuing a new cookie is a good time to clean up the old # Issuing a new cookie is a good time to clean up the old
# cookies. # cookies.
$dbh->do("DELETE FROM logincookies WHERE lastused < " $dbh->do("DELETE FROM logincookies WHERE lastused < "
. $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', . $dbh->sql_date_math('LOCALTIMESTAMP(0)', '-', MAX_LOGINCOOKIE_AGE, 'DAY'));
MAX_LOGINCOOKIE_AGE, 'DAY'));
$dbh->bz_commit_transaction(); $dbh->bz_commit_transaction();
...@@ -65,26 +66,29 @@ sub persist_login { ...@@ -65,26 +66,29 @@ sub persist_login {
# Remember cookie only if admin has told so # Remember cookie only if admin has told so
# or admin didn't forbid it and user told to remember. # or admin didn't forbid it and user told to remember.
if ( Bugzilla->params->{'rememberlogin'} eq 'on' || if (
(Bugzilla->params->{'rememberlogin'} ne 'off' && Bugzilla->params->{'rememberlogin'} eq 'on'
$input_params->{'Bugzilla_remember'} && || ( Bugzilla->params->{'rememberlogin'} ne 'off'
$input_params->{'Bugzilla_remember'} eq 'on') ) && $input_params->{'Bugzilla_remember'}
&& $input_params->{'Bugzilla_remember'} eq 'on')
)
{ {
# Not a session cookie, so set an infinite expiry # Not a session cookie, so set an infinite expiry
$cookieargs{'-expires'} = 'Fri, 01-Jan-2038 00:00:00 GMT'; $cookieargs{'-expires'} = 'Fri, 01-Jan-2038 00:00:00 GMT';
} }
if (Bugzilla->params->{'ssl_redirect'}) { if (Bugzilla->params->{'ssl_redirect'}) {
# Make these cookies only be sent to us by the browser during # Make these cookies only be sent to us by the browser during
# HTTPS sessions, if we're using SSL. # HTTPS sessions, if we're using SSL.
$cookieargs{'-secure'} = 1; $cookieargs{'-secure'} = 1;
} }
$cgi->send_cookie(-name => 'Bugzilla_login', $cgi->send_cookie(-name => 'Bugzilla_login', -value => $user->id, %cookieargs);
-value => $user->id, $cgi->send_cookie(
%cookieargs); -name => 'Bugzilla_logincookie',
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
-value => $login_cookie, -value => $login_cookie,
%cookieargs); %cookieargs
);
} }
sub logout { sub logout {
...@@ -98,8 +102,7 @@ sub logout { ...@@ -98,8 +102,7 @@ sub logout {
my $type = $param->{type} || LOGOUT_ALL; my $type = $param->{type} || LOGOUT_ALL;
if ($type == LOGOUT_ALL) { if ($type == LOGOUT_ALL) {
$dbh->do("DELETE FROM logincookies WHERE userid = ?", $dbh->do("DELETE FROM logincookies WHERE userid = ?", undef, $user->id);
undef, $user->id);
return; return;
} }
...@@ -107,7 +110,7 @@ sub logout { ...@@ -107,7 +110,7 @@ sub logout {
# If a new cookie has been issued during this run, that's the current one. # If a new cookie has been issued during this run, that's the current one.
# If not, it's the one we've received. # If not, it's the one we've received.
my @login_cookies; my @login_cookies;
my $cookie = first {$_->name eq 'Bugzilla_logincookie'} my $cookie = first { $_->name eq 'Bugzilla_logincookie' }
@{$cgi->{'Bugzilla_cookie_list'}}; @{$cgi->{'Bugzilla_cookie_list'}};
if ($cookie) { if ($cookie) {
push(@login_cookies, $cookie->value); push(@login_cookies, $cookie->value);
...@@ -135,16 +138,22 @@ sub logout { ...@@ -135,16 +138,22 @@ sub logout {
map { trick_taint($_) } @login_cookies; map { trick_taint($_) } @login_cookies;
@login_cookies = map { $dbh->quote($_) } @login_cookies; @login_cookies = map { $dbh->quote($_) } @login_cookies;
if ($type == LOGOUT_KEEP_CURRENT) { if ($type == LOGOUT_KEEP_CURRENT) {
$dbh->do("DELETE FROM logincookies WHERE " . $dbh->do(
$dbh->sql_in('cookie', \@login_cookies, 1) . "DELETE FROM logincookies WHERE "
" AND userid = ?", . $dbh->sql_in('cookie', \@login_cookies, 1)
undef, $user->id); . " AND userid = ?",
} elsif ($type == LOGOUT_CURRENT) { undef, $user->id
$dbh->do("DELETE FROM logincookies WHERE " . );
$dbh->sql_in('cookie', \@login_cookies) . }
" AND userid = ?", elsif ($type == LOGOUT_CURRENT) {
undef, $user->id); $dbh->do(
} else { "DELETE FROM logincookies WHERE "
. $dbh->sql_in('cookie', \@login_cookies)
. " AND userid = ?",
undef, $user->id
);
}
else {
die("Invalid type $type supplied to logout()"); die("Invalid type $type supplied to logout()");
} }
......
...@@ -48,30 +48,37 @@ sub create_or_update_user { ...@@ -48,30 +48,37 @@ sub create_or_update_user {
my $extern_user_id; my $extern_user_id;
if ($extern_id) { if ($extern_id) {
trick_taint($extern_id); trick_taint($extern_id);
$extern_user_id = $dbh->selectrow_array('SELECT userid $extern_user_id = $dbh->selectrow_array(
FROM profiles WHERE extern_id = ?', undef, $extern_id); 'SELECT userid
FROM profiles WHERE extern_id = ?', undef, $extern_id
);
} }
# If we have both a valid extern_id and a valid username, and they are # If we have both a valid extern_id and a valid username, and they are
# not the same id, then we have a conflict. # not the same id, then we have a conflict.
if ($username_user_id && $extern_user_id if ( $username_user_id
&& $extern_user_id
&& $username_user_id ne $extern_user_id) && $username_user_id ne $extern_user_id)
{ {
my $extern_name = Bugzilla::User->new($extern_user_id)->login; my $extern_name = Bugzilla::User->new($extern_user_id)->login;
return { failure => AUTH_ERROR, error => "extern_id_conflict", return {
details => {extern_id => $extern_id, failure => AUTH_ERROR,
extern_user => $extern_name, error => "extern_id_conflict",
username => $username} }; details =>
{extern_id => $extern_id, extern_user => $extern_name, username => $username}
};
} }
# If we have a valid username, but no valid id, # If we have a valid username, but no valid id,
# then we have to create the user. This happens when we're # then we have to create the user. This happens when we're
# passed only a username, and that username doesn't exist already. # passed only a username, and that username doesn't exist already.
if ($username && !$username_user_id && !$extern_user_id) { if ($username && !$username_user_id && !$extern_user_id) {
validate_email_syntax($username) validate_email_syntax($username) || return {
|| return { failure => AUTH_ERROR, failure => AUTH_ERROR,
error => 'auth_invalid_email', error => 'auth_invalid_email',
details => {addr => $username} }; details => {addr => $username}
};
# Usually we'd call validate_password, but external authentication # Usually we'd call validate_password, but external authentication
# systems might follow different standards than ours. So in this # systems might follow different standards than ours. So in this
# place here, we call trick_taint without checks. # place here, we call trick_taint without checks.
...@@ -79,10 +86,10 @@ sub create_or_update_user { ...@@ -79,10 +86,10 @@ sub create_or_update_user {
# XXX Theoretically this could fail with an error, but the fix for # XXX Theoretically this could fail with an error, but the fix for
# that is too involved to be done right now. # that is too involved to be done right now.
my $user = Bugzilla::User->create({ my $user
login_name => $username, = Bugzilla::User->create({
cryptpassword => $password, login_name => $username, cryptpassword => $password, realname => $real_name
realname => $real_name}); });
$username_user_id = $user->id; $username_user_id = $user->id;
} }
...@@ -91,7 +98,7 @@ sub create_or_update_user { ...@@ -91,7 +98,7 @@ sub create_or_update_user {
if ($extern_id && $username_user_id && !$extern_user_id) { if ($extern_id && $username_user_id && !$extern_user_id) {
$dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?', $dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
undef, $extern_id, $username_user_id); undef, $extern_id, $username_user_id);
Bugzilla->memcached->clear({ table => 'profiles', id => $username_user_id }); Bugzilla->memcached->clear({table => 'profiles', id => $username_user_id});
} }
# Finally, at this point, one of these will give us a valid user id. # Finally, at this point, one of these will give us a valid user id.
...@@ -100,9 +107,13 @@ sub create_or_update_user { ...@@ -100,9 +107,13 @@ sub create_or_update_user {
# If we still don't have a valid user_id, then we weren't passed # If we still don't have a valid user_id, then we weren't passed
# enough information in $params, and we should die right here. # enough information in $params, and we should die right here.
ThrowCodeError('bad_arg', {argument => 'params', function => ThrowCodeError(
'Bugzilla::Auth::Verify::create_or_update_user'}) 'bad_arg',
unless $user_id; {
argument => 'params',
function => 'Bugzilla::Auth::Verify::create_or_update_user'
}
) unless $user_id;
my $user = new Bugzilla::User($user_id); my $user = new Bugzilla::User($user_id);
...@@ -110,13 +121,16 @@ sub create_or_update_user { ...@@ -110,13 +121,16 @@ sub create_or_update_user {
my $changed = 0; my $changed = 0;
if ($username && lc($user->login) ne lc($username)) { if ($username && lc($user->login) ne lc($username)) {
validate_email_syntax($username) validate_email_syntax($username) || return {
|| return { failure => AUTH_ERROR, error => 'auth_invalid_email', failure => AUTH_ERROR,
details => {addr => $username} }; error => 'auth_invalid_email',
details => {addr => $username}
};
$user->set_login($username); $user->set_login($username);
$changed = 1; $changed = 1;
} }
if ($real_name && $user->name ne $real_name) { if ($real_name && $user->name ne $real_name) {
# $real_name is more than likely tainted, but we only use it # $real_name is more than likely tainted, but we only use it
# in a placeholder and we never use it after this. # in a placeholder and we never use it after this.
trick_taint($real_name); trick_taint($real_name);
...@@ -125,7 +139,7 @@ sub create_or_update_user { ...@@ -125,7 +139,7 @@ sub create_or_update_user {
} }
$user->update() if $changed; $user->update() if $changed;
return { user => $user }; return {user => $user};
} }
1; 1;
......
...@@ -23,15 +23,15 @@ sub check_credentials { ...@@ -23,15 +23,15 @@ sub check_credentials {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $username = $login_data->{username}; my $username = $login_data->{username};
my $user = new Bugzilla::User({ name => $username }); my $user = new Bugzilla::User({name => $username});
return { failure => AUTH_NO_SUCH_USER } unless $user; return {failure => AUTH_NO_SUCH_USER} unless $user;
$login_data->{user} = $user; $login_data->{user} = $user;
$login_data->{bz_username} = $user->login; $login_data->{bz_username} = $user->login;
if ($user->account_is_locked_out) { if ($user->account_is_locked_out) {
return { failure => AUTH_LOCKOUT, user => $user }; return {failure => AUTH_LOCKOUT, user => $user};
} }
my $password = $login_data->{password}; my $password = $login_data->{password};
...@@ -42,32 +42,33 @@ sub check_credentials { ...@@ -42,32 +42,33 @@ sub check_credentials {
my $entered_password_crypted = bz_crypt($password, $real_password_crypted); my $entered_password_crypted = bz_crypt($password, $real_password_crypted);
if ($entered_password_crypted ne $real_password_crypted) { if ($entered_password_crypted ne $real_password_crypted) {
# Record the login failure # Record the login failure
$user->note_login_failure(); $user->note_login_failure();
# Immediately check if we are locked out # Immediately check if we are locked out
if ($user->account_is_locked_out) { if ($user->account_is_locked_out) {
return { failure => AUTH_LOCKOUT, user => $user, return {failure => AUTH_LOCKOUT, user => $user, just_locked_out => 1};
just_locked_out => 1 };
} }
return { failure => AUTH_LOGINFAILED, return {
failure_count => scalar(@{ $user->account_ip_login_failures }), failure => AUTH_LOGINFAILED,
failure_count => scalar(@{$user->account_ip_login_failures}),
}; };
} }
# Force the user to change their password if it does not meet the current # Force the user to change their password if it does not meet the current
# criteria. This should usually only happen if the criteria has changed. # criteria. This should usually only happen if the criteria has changed.
if (Bugzilla->usage_mode == USAGE_MODE_BROWSER && if ( Bugzilla->usage_mode == USAGE_MODE_BROWSER
Bugzilla->params->{password_check_on_login}) && Bugzilla->params->{password_check_on_login})
{ {
my $check = validate_password_check($password); my $check = validate_password_check($password);
if ($check) { if ($check) {
return { return {
failure => AUTH_ERROR, failure => AUTH_ERROR,
user_error => $check, user_error => $check,
details => { locked_user => $user } details => {locked_user => $user}
} };
} }
} }
...@@ -92,6 +93,7 @@ sub check_credentials { ...@@ -92,6 +93,7 @@ sub check_credentials {
# If needed, update the user's password. # If needed, update the user's password.
if ($update_password) { if ($update_password) {
# We can't call $user->set_password because we don't want the password # We can't call $user->set_password because we don't want the password
# complexity rules to apply here. # complexity rules to apply here.
$user->{cryptpassword} = bz_crypt($password); $user->{cryptpassword} = bz_crypt($password);
...@@ -107,7 +109,7 @@ sub change_password { ...@@ -107,7 +109,7 @@ sub change_password {
my $cryptpassword = bz_crypt($password); my $cryptpassword = bz_crypt($password);
$dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?", $dbh->do("UPDATE profiles SET cryptpassword = ? WHERE userid = ?",
undef, $cryptpassword, $user->id); undef, $cryptpassword, $user->id);
Bugzilla->memcached->clear({ table => 'profiles', id => $user->id }); Bugzilla->memcached->clear({table => 'profiles', id => $user->id});
} }
1; 1;
...@@ -43,19 +43,22 @@ sub check_credentials { ...@@ -43,19 +43,22 @@ sub check_credentials {
# Now, we verify that the user exists, and get a LDAP Distinguished # Now, we verify that the user exists, and get a LDAP Distinguished
# Name for the user. # Name for the user.
my $username = $params->{username}; my $username = $params->{username};
my $dn_result = $self->ldap->search(_bz_search_params($username), my $dn_result
attrs => ['dn']); = $self->ldap->search(_bz_search_params($username), attrs => ['dn']);
return { failure => AUTH_ERROR, error => "ldap_search_error", return {
failure => AUTH_ERROR,
error => "ldap_search_error",
details => {errstr => $dn_result->error, username => $username} details => {errstr => $dn_result->error, username => $username}
} if $dn_result->code; }
if $dn_result->code;
return { failure => AUTH_NO_SUCH_USER } if !$dn_result->count; return {failure => AUTH_NO_SUCH_USER} if !$dn_result->count;
my $dn = $dn_result->shift_entry->dn; my $dn = $dn_result->shift_entry->dn;
# Check the password. # Check the password.
my $pw_result = $self->ldap->bind($dn, password => $params->{password}); my $pw_result = $self->ldap->bind($dn, password => $params->{password});
return { failure => AUTH_LOGINFAILED } if $pw_result->code; return {failure => AUTH_LOGINFAILED} if $pw_result->code;
# And now we fill in the user's details. # And now we fill in the user's details.
...@@ -64,9 +67,11 @@ sub check_credentials { ...@@ -64,9 +67,11 @@ sub check_credentials {
my $error_string; my $error_string;
my $detail_result = $self->ldap->search(_bz_search_params($username)); my $detail_result = $self->ldap->search(_bz_search_params($username));
if ($detail_result->code) { if ($detail_result->code) {
# Stash away the original error, just in case # Stash away the original error, just in case
$error_string = $detail_result->error; $error_string = $detail_result->error;
} else { }
else {
$user_entry = $detail_result->shift_entry; $user_entry = $detail_result->shift_entry;
} }
...@@ -83,17 +88,22 @@ sub check_credentials { ...@@ -83,17 +88,22 @@ sub check_credentials {
} }
# If we *still* don't have anything in $user_entry then give up. # If we *still* don't have anything in $user_entry then give up.
return { failure => AUTH_ERROR, error => "ldap_search_error", return {
failure => AUTH_ERROR,
error => "ldap_search_error",
details => {errstr => $error_string, username => $username} details => {errstr => $error_string, username => $username}
} if !$user_entry; }
if !$user_entry;
my $mail_attr = Bugzilla->params->{"LDAPmailattribute"}; my $mail_attr = Bugzilla->params->{"LDAPmailattribute"};
if ($mail_attr) { if ($mail_attr) {
if (!$user_entry->exists($mail_attr)) { if (!$user_entry->exists($mail_attr)) {
return { failure => AUTH_ERROR, return {
failure => AUTH_ERROR,
error => "ldap_cannot_retreive_attr", error => "ldap_cannot_retreive_attr",
details => {attr => $mail_attr} }; details => {attr => $mail_attr}
};
} }
my @emails = $user_entry->get_value($mail_attr); my @emails = $user_entry->get_value($mail_attr);
...@@ -102,17 +112,19 @@ sub check_credentials { ...@@ -102,17 +112,19 @@ sub check_credentials {
$params->{bz_username} = $emails[0]; $params->{bz_username} = $emails[0];
if (@emails > 1) { if (@emails > 1) {
# Cycle through the adresses and check if they're Bugzilla logins. # Cycle through the adresses and check if they're Bugzilla logins.
# Use the first one that returns a valid id. # Use the first one that returns a valid id.
foreach my $email (@emails) { foreach my $email (@emails) {
if ( login_to_id($email) ) { if (login_to_id($email)) {
$params->{bz_username} = $email; $params->{bz_username} = $email;
last; last;
} }
} }
} }
} else { }
else {
$params->{bz_username} = $username; $params->{bz_username} = $username;
} }
...@@ -127,21 +139,22 @@ sub check_credentials { ...@@ -127,21 +139,22 @@ sub check_credentials {
sub _bz_search_params { sub _bz_search_params {
my ($username) = @_; my ($username) = @_;
$username = escape_filter_value($username); $username = escape_filter_value($username);
return (base => Bugzilla->params->{"LDAPBaseDN"}, return (
base => Bugzilla->params->{"LDAPBaseDN"},
scope => "sub", scope => "sub",
filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"} filter => '(&('
. Bugzilla->params->{"LDAPuidattribute"}
. "=$username)" . "=$username)"
. Bugzilla->params->{"LDAPfilter"} . ')'); . Bugzilla->params->{"LDAPfilter"} . ')'
);
} }
sub _bind_ldap_for_search { sub _bind_ldap_for_search {
my ($self) = @_; my ($self) = @_;
my $bind_result; my $bind_result;
if (Bugzilla->params->{"LDAPbinddn"}) { if (Bugzilla->params->{"LDAPbinddn"}) {
my ($LDAPbinddn,$LDAPbindpass) = my ($LDAPbinddn, $LDAPbindpass) = split(":", Bugzilla->params->{"LDAPbinddn"});
split(":",Bugzilla->params->{"LDAPbinddn"}); $bind_result = $self->ldap->bind($LDAPbinddn, password => $LDAPbindpass);
$bind_result =
$self->ldap->bind($LDAPbinddn, password => $LDAPbindpass);
} }
else { else {
$bind_result = $self->ldap->bind(); $bind_result = $self->ldap->bind();
...@@ -166,13 +179,13 @@ sub ldap { ...@@ -166,13 +179,13 @@ sub ldap {
$self->{ldap} = new Net::LDAP(trim($_)); $self->{ldap} = new Net::LDAP(trim($_));
last if $self->{ldap}; last if $self->{ldap};
} }
ThrowCodeError("ldap_connect_failed", { server => join(", ", @servers) }) ThrowCodeError("ldap_connect_failed", {server => join(", ", @servers)})
unless $self->{ldap}; unless $self->{ldap};
# try to start TLS if needed # try to start TLS if needed
if (Bugzilla->params->{"LDAPstarttls"}) { if (Bugzilla->params->{"LDAPstarttls"}) {
my $mesg = $self->{ldap}->start_tls(); my $mesg = $self->{ldap}->start_tls();
ThrowCodeError("ldap_start_tls_failed", { error => $mesg->error() }) ThrowCodeError("ldap_start_tls_failed", {error => $mesg->error()})
if $mesg->code(); if $mesg->code();
} }
......
...@@ -35,16 +35,20 @@ sub check_credentials { ...@@ -35,16 +35,20 @@ sub check_credentials {
} }
# Create RADIUS object. # Create RADIUS object.
my $radius = my $radius = new Authen::Radius(
new Authen::Radius(Host => Bugzilla->params->{'RADIUS_server'}, Host => Bugzilla->params->{'RADIUS_server'},
Secret => Bugzilla->params->{'RADIUS_secret'}) Secret => Bugzilla->params->{'RADIUS_secret'}
|| return { failure => AUTH_ERROR, error => 'radius_preparation_error', )
details => {errstr => Authen::Radius::strerror() } }; || return {
failure => AUTH_ERROR,
error => 'radius_preparation_error',
details => {errstr => Authen::Radius::strerror()}
};
# Check the password. # Check the password.
$radius->check_pwd($username, $params->{password}, $radius->check_pwd($username, $params->{password},
Bugzilla->params->{'RADIUS_NAS_IP'} || undef) Bugzilla->params->{'RADIUS_NAS_IP'} || undef)
|| return { failure => AUTH_LOGINFAILED }; || return {failure => AUTH_LOGINFAILED};
# Build the user account's e-mail address. # Build the user account's e-mail address.
$params->{bz_username} = $username . $address_suffix; $params->{bz_username} = $username . $address_suffix;
......
...@@ -28,7 +28,7 @@ sub new { ...@@ -28,7 +28,7 @@ sub new {
my $self = $class->SUPER::new(@_); my $self = $class->SUPER::new(@_);
my %methods = map { $_ => "Bugzilla/Auth/Verify/$_.pm" } split(',', $list); my %methods = map { $_ => "Bugzilla/Auth/Verify/$_.pm" } split(',', $list);
lock_keys(%methods); lock_keys(%methods);
Bugzilla::Hook::process('auth_verify_methods', { modules => \%methods }); Bugzilla::Hook::process('auth_verify_methods', {modules => \%methods});
$self->{_stack} = []; $self->{_stack} = [];
foreach my $verify_method (split(',', $list)) { foreach my $verify_method (split(',', $list)) {
...@@ -43,6 +43,7 @@ sub new { ...@@ -43,6 +43,7 @@ sub new {
sub can_change_password { sub can_change_password {
my ($self) = @_; my ($self) = @_;
# We return true if any method can change passwords. # We return true if any method can change passwords.
foreach my $object (@{$self->{_stack}}) { foreach my $object (@{$self->{_stack}}) {
return 1 if $object->can_change_password; return 1 if $object->can_change_password;
...@@ -57,9 +58,11 @@ sub check_credentials { ...@@ -57,9 +58,11 @@ sub check_credentials {
$result = $object->check_credentials(@_); $result = $object->check_credentials(@_);
$self->{successful} = $object; $self->{successful} = $object;
last if !$result->{failure}; last if !$result->{failure};
# So that if none of them succeed, it's undef. # So that if none of them succeed, it's undef.
$self->{successful} = undef; $self->{successful} = undef;
} }
# Returns the result at the bottom of the stack if they all fail. # Returns the result at the bottom of the stack if they all fail.
return $result; return $result;
} }
...@@ -71,12 +74,14 @@ sub create_or_update_user { ...@@ -71,12 +74,14 @@ sub create_or_update_user {
$result = $object->create_or_update_user(@_); $result = $object->create_or_update_user(@_);
last if !$result->{failure}; last if !$result->{failure};
} }
# Returns the result at the bottom of the stack if they all fail. # Returns the result at the bottom of the stack if they all fail.
return $result; return $result;
} }
sub user_can_create_account { sub user_can_create_account {
my ($self) = @_; my ($self) = @_;
# We return true if any method allows the user to create an account. # We return true if any method allows the user to create an account.
foreach my $object (@{$self->{_stack}}) { foreach my $object (@{$self->{_stack}}) {
return 1 if $object->user_can_create_account; return 1 if $object->user_can_create_account;
...@@ -86,7 +91,7 @@ sub user_can_create_account { ...@@ -86,7 +91,7 @@ sub user_can_create_account {
sub extern_id_used { sub extern_id_used {
my ($self) = @_; my ($self) = @_;
return any { $_->extern_id_used } @{ $self->{_stack} }; return any { $_->extern_id_used } @{$self->{_stack}};
} }
1; 1;
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -28,6 +28,7 @@ use URI::QueryParam; ...@@ -28,6 +28,7 @@ use URI::QueryParam;
use constant DB_TABLE => 'bug_see_also'; use constant DB_TABLE => 'bug_see_also';
use constant NAME_FIELD => 'value'; use constant NAME_FIELD => 'value';
use constant LIST_ORDER => 'id'; use constant LIST_ORDER => 'id';
# See Also is tracked in bugs_activity. # See Also is tracked in bugs_activity.
use constant AUDIT_CREATES => 0; use constant AUDIT_CREATES => 0;
use constant AUDIT_UPDATES => 0; use constant AUDIT_UPDATES => 0;
...@@ -84,19 +85,15 @@ sub new { ...@@ -84,19 +85,15 @@ sub new {
my $bug_id = $param->{bug_id}; my $bug_id = $param->{bug_id};
my $name = $param->{name} || $param->{value}; my $name = $param->{name} || $param->{value};
if (!defined $bug_id) { if (!defined $bug_id) {
ThrowCodeError('bad_arg', ThrowCodeError('bad_arg', {argument => 'bug_id', function => "${class}::new"});
{ argument => 'bug_id',
function => "${class}::new" });
} }
if (!defined $name) { if (!defined $name) {
ThrowCodeError('bad_arg', ThrowCodeError('bad_arg', {argument => 'name', function => "${class}::new"});
{ argument => 'name',
function => "${class}::new" });
} }
my $condition = 'bug_id = ? AND value = ?'; my $condition = 'bug_id = ? AND value = ?';
my @values = ($bug_id, $name); my @values = ($bug_id, $name);
$param = { condition => $condition, values => \@values }; $param = {condition => $condition, values => \@values};
} }
unshift @_, $param; unshift @_, $param;
...@@ -109,27 +106,26 @@ sub _do_list_select { ...@@ -109,27 +106,26 @@ sub _do_list_select {
foreach my $object (@$objects) { foreach my $object (@$objects) {
eval "use " . $object->class; eval "use " . $object->class;
# If the class cannot be loaded, then we build a generic object. # If the class cannot be loaded, then we build a generic object.
bless $object, ($@ ? 'Bugzilla::BugUrl' : $object->class); bless $object, ($@ ? 'Bugzilla::BugUrl' : $object->class);
} }
return $objects return $objects;
} }
# This is an abstract method. It must be overridden # This is an abstract method. It must be overridden
# in every subclass. # in every subclass.
sub should_handle { sub should_handle {
my ($class, $input) = @_; my ($class, $input) = @_;
ThrowCodeError('unknown_method', ThrowCodeError('unknown_method', {method => "${class}::should_handle"});
{ method => "${class}::should_handle" });
} }
sub class_for { sub class_for {
my ($class, $value) = @_; my ($class, $value) = @_;
my @sub_classes = $class->SUB_CLASSES; my @sub_classes = $class->SUB_CLASSES;
Bugzilla::Hook::process("bug_url_sub_classes", Bugzilla::Hook::process("bug_url_sub_classes", {sub_classes => \@sub_classes});
{ sub_classes => \@sub_classes });
my $uri = URI->new($value); my $uri = URI->new($value);
foreach my $subclass (@sub_classes) { foreach my $subclass (@sub_classes) {
...@@ -139,12 +135,13 @@ sub class_for { ...@@ -139,12 +135,13 @@ sub class_for {
if $subclass->should_handle($uri); if $subclass->should_handle($uri);
} }
ThrowUserError('bug_url_invalid', { url => $value }); ThrowUserError('bug_url_invalid', {url => $value});
} }
sub _check_class { sub _check_class {
my ($class, $subclass) = @_; my ($class, $subclass) = @_;
eval "use $subclass"; die $@ if $@; eval "use $subclass";
die $@ if $@;
return $subclass; return $subclass;
} }
...@@ -153,13 +150,14 @@ sub _check_bug_id { ...@@ -153,13 +150,14 @@ sub _check_bug_id {
my $bug; my $bug;
if (blessed $bug_id) { if (blessed $bug_id) {
# We got a bug object passed in, use it # We got a bug object passed in, use it
$bug = $bug_id; $bug = $bug_id;
$bug->check_is_visible; $bug->check_is_visible;
} }
else { else {
# We got a bug id passed in, check it and get the bug object # We got a bug id passed in, check it and get the bug object
$bug = Bugzilla::Bug->check({ id => $bug_id }); $bug = Bugzilla::Bug->check({id => $bug_id});
} }
return $bug->id; return $bug->id;
...@@ -172,19 +170,20 @@ sub _check_value { ...@@ -172,19 +170,20 @@ sub _check_value {
if (!$value) { if (!$value) {
ThrowCodeError('param_required', ThrowCodeError('param_required',
{ function => 'add_see_also', param => '$value' }); {function => 'add_see_also', param => '$value'});
} }
# We assume that the URL is an HTTP URL if there is no (something):// # We assume that the URL is an HTTP URL if there is no (something)://
# in front. # in front.
if (!$uri->scheme) { if (!$uri->scheme) {
# This works better than setting $uri->scheme('http'), because # This works better than setting $uri->scheme('http'), because
# that creates URLs like "http:domain.com" and doesn't properly # that creates URLs like "http:domain.com" and doesn't properly
# differentiate the path from the domain. # differentiate the path from the domain.
$uri = new URI("http://$value"); $uri = new URI("http://$value");
} }
elsif ($uri->scheme ne 'http' && $uri->scheme ne 'https') { elsif ($uri->scheme ne 'http' && $uri->scheme ne 'https') {
ThrowUserError('bug_url_invalid', { url => $value, reason => 'http' }); ThrowUserError('bug_url_invalid', {url => $value, reason => 'http'});
} }
# This stops the following edge cases from being accepted: # This stops the following edge cases from being accepted:
...@@ -192,12 +191,11 @@ sub _check_value { ...@@ -192,12 +191,11 @@ sub _check_value {
# * /show_bug.cgi?id=1 # * /show_bug.cgi?id=1
# * http:///show_bug.cgi?id=1 # * http:///show_bug.cgi?id=1
if (!$uri->authority or $uri->path !~ m{/}) { if (!$uri->authority or $uri->path !~ m{/}) {
ThrowUserError('bug_url_invalid', ThrowUserError('bug_url_invalid', {url => $value, reason => 'path_only'});
{ url => $value, reason => 'path_only' });
} }
if (length($uri->path) > MAX_BUG_URL_LENGTH) { if (length($uri->path) > MAX_BUG_URL_LENGTH) {
ThrowUserError('bug_url_too_long', { url => $uri->path }); ThrowUserError('bug_url_too_long', {url => $uri->path});
} }
return $uri; return $uri;
......
...@@ -31,6 +31,7 @@ sub _check_value { ...@@ -31,6 +31,7 @@ sub _check_value {
$uri = $class->SUPER::_check_value($uri); $uri = $class->SUPER::_check_value($uri);
my $bug_id = $uri->query_param('id'); my $bug_id = $uri->query_param('id');
# We don't currently allow aliases, because we can't check to see # We don't currently allow aliases, because we can't check to see
# if somebody's putting both an alias link and a numeric ID link. # if somebody's putting both an alias link and a numeric ID link.
# When we start validating the URL by accessing the other Bugzilla, # When we start validating the URL by accessing the other Bugzilla,
...@@ -38,11 +39,12 @@ sub _check_value { ...@@ -38,11 +39,12 @@ sub _check_value {
detaint_natural($bug_id); detaint_natural($bug_id);
if (!$bug_id) { if (!$bug_id) {
my $value = $uri->as_string; my $value = $uri->as_string;
ThrowUserError('bug_url_invalid', { url => $value, reason => 'id' }); ThrowUserError('bug_url_invalid', {url => $value, reason => 'id'});
} }
# Make sure that "id" is the only query parameter. # Make sure that "id" is the only query parameter.
$uri->query("id=$bug_id"); $uri->query("id=$bug_id");
# And remove any # part if there is one. # And remove any # part if there is one.
$uri->fragment(undef); $uri->fragment(undef);
......
...@@ -20,9 +20,7 @@ use Bugzilla::Util; ...@@ -20,9 +20,7 @@ use Bugzilla::Util;
#### Initialization #### #### Initialization ####
############################### ###############################
use constant VALIDATOR_DEPENDENCIES => { use constant VALIDATOR_DEPENDENCIES => {value => ['bug_id'],};
value => ['bug_id'],
};
############################### ###############################
#### Methods #### #### Methods ####
...@@ -35,9 +33,8 @@ sub ref_bug_url { ...@@ -35,9 +33,8 @@ sub ref_bug_url {
my $ref_bug_id = new URI($self->name)->query_param('id'); my $ref_bug_id = new URI($self->name)->query_param('id');
my $ref_bug = Bugzilla::Bug->check($ref_bug_id); my $ref_bug = Bugzilla::Bug->check($ref_bug_id);
my $ref_value = $self->local_uri($self->bug_id); my $ref_value = $self->local_uri($self->bug_id);
$self->{ref_bug_url} = $self->{ref_bug_url} = new Bugzilla::BugUrl::Bugzilla::Local(
new Bugzilla::BugUrl::Bugzilla::Local({ bug_id => $ref_bug->id, {bug_id => $ref_bug->id, value => $ref_value});
value => $ref_value });
} }
return $self->{ref_bug_url}; return $self->{ref_bug_url};
} }
...@@ -52,7 +49,7 @@ sub should_handle { ...@@ -52,7 +49,7 @@ sub should_handle {
# Bugzilla::BugUrl::Bugzilla to check if it's a valid Bugzilla # Bugzilla::BugUrl::Bugzilla to check if it's a valid Bugzilla
# see also url. # see also url.
my $canonical_local = URI->new($class->local_uri)->canonical; my $canonical_local = URI->new($class->local_uri)->canonical;
if ($canonical_local->authority eq $uri->canonical->authority if ( $canonical_local->authority eq $uri->canonical->authority
and $canonical_local->path eq $uri->canonical->path) and $canonical_local->path eq $uri->canonical->path)
{ {
return $class->SUPER::should_handle($uri); return $class->SUPER::should_handle($uri);
...@@ -69,7 +66,8 @@ sub _check_value { ...@@ -69,7 +66,8 @@ sub _check_value {
my $value = $uri->as_string; my $value = $uri->as_string;
if ($value =~ m/^\w+$/) { if ($value =~ m/^\w+$/) {
$uri = new URI($class->local_uri($value)); $uri = new URI($class->local_uri($value));
} else { }
else {
# It's not a word, then we have to check # It's not a word, then we have to check
# if it's a valid Bugzilla url. # if it's a valid Bugzilla url.
$uri = $class->SUPER::_check_value($uri); $uri = $class->SUPER::_check_value($uri);
......
...@@ -23,10 +23,12 @@ sub should_handle { ...@@ -23,10 +23,12 @@ sub should_handle {
# Debian BTS URLs can look like various things: # Debian BTS URLs can look like various things:
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1234 # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1234
# http://bugs.debian.org/1234 # http://bugs.debian.org/1234
return (lc($uri->authority) eq 'bugs.debian.org' return (
and (($uri->path =~ /bugreport\.cgi$/ lc($uri->authority) eq 'bugs.debian.org'
and $uri->query_param('bug') =~ m|^\d+$|) and
or $uri->path =~ m|^/\d+$|)) ? 1 : 0; (($uri->path =~ /bugreport\.cgi$/ and $uri->query_param('bug') =~ m|^\d+$|)
or $uri->path =~ m|^/\d+$|)
) ? 1 : 0;
} }
sub _check_value { sub _check_value {
......
...@@ -20,10 +20,10 @@ use parent qw(Bugzilla::BugUrl); ...@@ -20,10 +20,10 @@ use parent qw(Bugzilla::BugUrl);
sub should_handle { sub should_handle {
my ($class, $uri) = @_; my ($class, $uri) = @_;
# GitHub issue URLs have only one form: # GitHub issue URLs have only one form:
# https://github.com/USER_OR_TEAM_OR_ORGANIZATION_NAME/REPOSITORY_NAME/issues/111 # https://github.com/USER_OR_TEAM_OR_ORGANIZATION_NAME/REPOSITORY_NAME/issues/111
# GitHub pull request URLs have only one form: # GitHub pull request URLs have only one form:
# https://github.com/USER_OR_TEAM_OR_ORGANIZATION_NAME/REPOSITORY_NAME/pull/111 # https://github.com/USER_OR_TEAM_OR_ORGANIZATION_NAME/REPOSITORY_NAME/pull/111
return (lc($uri->authority) eq 'github.com' return (lc($uri->authority) eq 'github.com'
and $uri->path =~ m!^/[^/]+/[^/]+/(?:issues|pull)/\d+$!) ? 1 : 0; and $uri->path =~ m!^/[^/]+/[^/]+/(?:issues|pull)/\d+$!) ? 1 : 0;
} }
......
...@@ -33,6 +33,7 @@ sub _check_value { ...@@ -33,6 +33,7 @@ sub _check_value {
# Make sure there are no query parameters. # Make sure there are no query parameters.
$uri->query(undef); $uri->query(undef);
# And remove any # part if there is one. # And remove any # part if there is one.
$uri->fragment(undef); $uri->fragment(undef);
......
...@@ -24,8 +24,9 @@ sub should_handle { ...@@ -24,8 +24,9 @@ sub should_handle {
# https://bugs.launchpad.net/ubuntu/+bug/1234 # https://bugs.launchpad.net/ubuntu/+bug/1234
# https://launchpad.net/bugs/1234 # https://launchpad.net/bugs/1234
# All variations end with either "/bugs/1234" or "/+bug/1234" # All variations end with either "/bugs/1234" or "/+bug/1234"
return ($uri->authority =~ /launchpad\.net$/ return ($uri->authority =~ /launchpad\.net$/ and $uri->path =~ m|bugs?/\d+$|)
and $uri->path =~ m|bugs?/\d+$|) ? 1 : 0; ? 1
: 0;
} }
sub _check_value { sub _check_value {
......
...@@ -33,6 +33,7 @@ sub _check_value { ...@@ -33,6 +33,7 @@ sub _check_value {
# Make sure there are no query parameters. # Make sure there are no query parameters.
$uri->query(undef); $uri->query(undef);
# And remove any # part if there is one. # And remove any # part if there is one.
$uri->fragment(undef); $uri->fragment(undef);
......
...@@ -25,10 +25,12 @@ use constant LIST_ORDER => 'id'; ...@@ -25,10 +25,12 @@ use constant LIST_ORDER => 'id';
use constant NAME_FIELD => 'id'; use constant NAME_FIELD => 'id';
# turn off auditing and exclude these objects from memcached # turn off auditing and exclude these objects from memcached
use constant { AUDIT_CREATES => 0, use constant {
AUDIT_CREATES => 0,
AUDIT_UPDATES => 0, AUDIT_UPDATES => 0,
AUDIT_REMOVES => 0, AUDIT_REMOVES => 0,
USE_MEMCACHED => 0 }; USE_MEMCACHED => 0
};
##################################################################### #####################################################################
# Provide accessors for our columns # Provide accessors for our columns
...@@ -42,7 +44,7 @@ sub last_visit_ts { return $_[0]->{last_visit_ts} } ...@@ -42,7 +44,7 @@ sub last_visit_ts { return $_[0]->{last_visit_ts} }
sub user { sub user {
my $self = shift; my $self = shift;
$self->{user} //= Bugzilla::User->new({ id => $self->user_id, cache => 1 }); $self->{user} //= Bugzilla::User->new({id => $self->user_id, cache => 1});
return $self->{user}; return $self->{user};
} }
......
...@@ -34,6 +34,7 @@ sub new { ...@@ -34,6 +34,7 @@ sub new {
bless($self, $class); bless($self, $class);
if ($#_ == 0) { if ($#_ == 0) {
# Construct from a CGI object. # Construct from a CGI object.
$self->init($_[0]); $self->init($_[0]);
} }
...@@ -58,11 +59,11 @@ sub init { ...@@ -58,11 +59,11 @@ sub init {
# &cumulate=1&datefrom=2002-02-03&dateto=2002-04-04&ctype=html... # &cumulate=1&datefrom=2002-02-03&dateto=2002-04-04&ctype=html...
# &gt=1&labelgt=Grand+Total # &gt=1&labelgt=Grand+Total
foreach my $param ($cgi->param()) { foreach my $param ($cgi->param()) {
# Store all the lines # Store all the lines
if ($param =~ /^line(\d+)$/) { if ($param =~ /^line(\d+)$/) {
foreach my $series_id ($cgi->param($param)) { foreach my $series_id ($cgi->param($param)) {
detaint_natural($series_id) detaint_natural($series_id) || ThrowCodeError("invalid_series_id");
|| ThrowCodeError("invalid_series_id");
my $series = new Bugzilla::Series($series_id); my $series = new Bugzilla::Series($series_id);
push(@{$self->{'lines'}[$1]}, $series) if $series; push(@{$self->{'lines'}[$1]}, $series) if $series;
} }
...@@ -88,16 +89,22 @@ sub init { ...@@ -88,16 +89,22 @@ sub init {
foreach my $date ('datefrom', 'dateto') { foreach my $date ('datefrom', 'dateto') {
if ($self->{$date}) { if ($self->{$date}) {
$self->{$date} = str2time($self->{$date}) $self->{$date} = str2time($self->{$date})
|| ThrowUserError("illegal_date", { date => $self->{$date}}); || ThrowUserError("illegal_date", {date => $self->{$date}});
} }
} }
# datefrom can't be after dateto # datefrom can't be after dateto
if ($self->{'datefrom'} && $self->{'dateto'} && if ( $self->{'datefrom'}
$self->{'datefrom'} > $self->{'dateto'}) && $self->{'dateto'}
&& $self->{'datefrom'} > $self->{'dateto'})
{
ThrowUserError(
'misarranged_dates',
{ {
ThrowUserError('misarranged_dates', { 'datefrom' => scalar $cgi->param('datefrom'), 'datefrom' => scalar $cgi->param('datefrom'),
'dateto' => scalar $cgi->param('dateto') }); 'dateto' => scalar $cgi->param('dateto')
}
);
} }
} }
...@@ -111,6 +118,7 @@ sub add { ...@@ -111,6 +118,7 @@ sub add {
# Count the number of added series # Count the number of added series
my $added = 0; my $added = 0;
# Create new Series and push them on to the list of lines. # Create new Series and push them on to the list of lines.
# Note that new lines have no label; the display template is responsible # Note that new lines have no label; the display template is responsible
# for inventing something sensible. # for inventing something sensible.
...@@ -125,9 +133,7 @@ sub add { ...@@ -125,9 +133,7 @@ sub add {
# If we are going from < 2 to >= 2 series, add the Grand Total line. # If we are going from < 2 to >= 2 series, add the Grand Total line.
if (!$self->{'gt'}) { if (!$self->{'gt'}) {
if ($current_size < 2 && if ($current_size < 2 && $current_size + $added >= 2) {
$current_size + $added >= 2)
{
$self->{'gt'} = 1; $self->{'gt'} = 1;
} }
} }
...@@ -140,6 +146,7 @@ sub remove { ...@@ -140,6 +146,7 @@ sub remove {
foreach my $line_id (@line_ids) { foreach my $line_id (@line_ids) {
if ($line_id == 65536) { if ($line_id == 65536) {
# Magic value - delete Grand Total. # Magic value - delete Grand Total.
$self->{'gt'} = 0; $self->{'gt'} = 0;
} }
...@@ -209,18 +216,20 @@ sub readData { ...@@ -209,18 +216,20 @@ sub readData {
# The date used is the one given if it's in a sensible range; otherwise, # The date used is the one given if it's in a sensible range; otherwise,
# it's the earliest or latest date in the database as appropriate. # it's the earliest or latest date in the database as appropriate.
my $datefrom = $dbh->selectrow_array("SELECT MIN(series_date) " . my $datefrom
"FROM series_data " . = $dbh->selectrow_array("SELECT MIN(series_date) "
"WHERE series_id IN ($series_ids)"); . "FROM series_data "
. "WHERE series_id IN ($series_ids)");
$datefrom = str2time($datefrom); $datefrom = str2time($datefrom);
if ($self->{'datefrom'} && $self->{'datefrom'} > $datefrom) { if ($self->{'datefrom'} && $self->{'datefrom'} > $datefrom) {
$datefrom = $self->{'datefrom'}; $datefrom = $self->{'datefrom'};
} }
my $dateto = $dbh->selectrow_array("SELECT MAX(series_date) " . my $dateto
"FROM series_data " . = $dbh->selectrow_array("SELECT MAX(series_date) "
"WHERE series_id IN ($series_ids)"); . "FROM series_data "
. "WHERE series_id IN ($series_ids)");
$dateto = str2time($dateto); $dateto = str2time($dateto);
if ($self->{'dateto'} && $self->{'dateto'} < $dateto) { if ($self->{'dateto'} && $self->{'dateto'} < $dateto) {
...@@ -232,11 +241,14 @@ sub readData { ...@@ -232,11 +241,14 @@ sub readData {
my $sql_to = time2str('%Y-%m-%d', $dateto); my $sql_to = time2str('%Y-%m-%d', $dateto);
# Prepare the query which retrieves the data for each series # Prepare the query which retrieves the data for each series
my $query = "SELECT " . $dbh->sql_to_days('series_date') . " - " . my $query
$dbh->sql_to_days('?') . ", series_value " . = "SELECT "
"FROM series_data " . . $dbh->sql_to_days('series_date') . " - "
"WHERE series_id = ? " . . $dbh->sql_to_days('?')
"AND series_date >= ?"; . ", series_value "
. "FROM series_data "
. "WHERE series_id = ? "
. "AND series_date >= ?";
if ($dateto) { if ($dateto) {
$query .= " AND series_date <= ?"; $query .= " AND series_date <= ?";
} }
...@@ -251,6 +263,7 @@ sub readData { ...@@ -251,6 +263,7 @@ sub readData {
my @datediff_total; my @datediff_total;
foreach my $line (@{$self->{'lines'}}) { foreach my $line (@{$self->{'lines'}}) {
# Even if we end up with no data, we need an empty arrayref to prevent # Even if we end up with no data, we need an empty arrayref to prevent
# errors in the PNG-generating code # errors in the PNG-generating code
$data[$line_index] = []; $data[$line_index] = [];
...@@ -294,6 +307,7 @@ sub readData { ...@@ -294,6 +307,7 @@ sub readData {
# calculate maximum y value # calculate maximum y value
if ($self->{'cumulate'}) { if ($self->{'cumulate'}) {
# Make sure we do not try to take the max of an array with undef values # Make sure we do not try to take the max of an array with undef values
my @processed_datediff; my @processed_datediff;
while (@datediff_total) { while (@datediff_total) {
...@@ -317,7 +331,7 @@ sub readData { ...@@ -317,7 +331,7 @@ sub readData {
} }
else { else {
# First, get the # of digits in the y_max_value # First, get the # of digits in the y_max_value
my $num_digits = 1+int(log($self->{'y_max_value'})/log(10)); my $num_digits = 1 + int(log($self->{'y_max_value'}) / log(10));
# We want to zero out all but the top 2 digits # We want to zero out all but the top 2 digits
my $mask_length = $num_digits - 2; my $mask_length = $num_digits - 2;
...@@ -330,7 +344,7 @@ sub readData { ...@@ -330,7 +344,7 @@ sub readData {
# (Throwing in the -1 keeps at least the smallest digit at zero) # (Throwing in the -1 keeps at least the smallest digit at zero)
do { do {
$self->{'y_max_value'} += 10**$mask_length; $self->{'y_max_value'} += 10**$mask_length;
} while ($self->{'y_max_value'} % (8*(10**($mask_length-1))) != 0); } while ($self->{'y_max_value'} % (8 * (10**($mask_length - 1))) != 0);
} }
...@@ -339,6 +353,7 @@ sub readData { ...@@ -339,6 +353,7 @@ sub readData {
unshift(@data, $date_progression); unshift(@data, $date_progression);
if ($self->{'gt'}) { if ($self->{'gt'}) {
# Add Grand Total to label list # Add Grand Total to label list
push(@{$self->{'labels'}}, $self->{'labelgt'}); push(@{$self->{'labels'}}, $self->{'labelgt'});
...@@ -371,20 +386,24 @@ sub getVisibleSeries { ...@@ -371,20 +386,24 @@ sub getVisibleSeries {
# Get all visible series # Get all visible series
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $serieses = $dbh->selectall_arrayref("SELECT cc1.name, cc2.name, " . my $serieses = $dbh->selectall_arrayref(
"series.name, series.series_id " . "SELECT cc1.name, cc2.name, "
"FROM series " . . "series.name, series.series_id "
"INNER JOIN series_categories AS cc1 " . . "FROM series "
" ON series.category = cc1.id " . . "INNER JOIN series_categories AS cc1 "
"INNER JOIN series_categories AS cc2 " . . " ON series.category = cc1.id "
" ON series.subcategory = cc2.id " . . "INNER JOIN series_categories AS cc2 "
"LEFT JOIN category_group_map AS cgm " . . " ON series.subcategory = cc2.id "
" ON series.category = cgm.category_id " . . "LEFT JOIN category_group_map AS cgm "
" AND cgm.group_id NOT IN($grouplist) " . . " ON series.category = cgm.category_id "
"WHERE creator = ? OR (is_public = 1 AND cgm.category_id IS NULL) " . . " AND cgm.group_id NOT IN($grouplist) "
$dbh->sql_group_by('series.series_id', 'cc1.name, cc2.name, ' . . "WHERE creator = ? OR (is_public = 1 AND cgm.category_id IS NULL) "
'series.name'), . $dbh->sql_group_by(
undef, Bugzilla->user->id); 'series.series_id', 'cc1.name, cc2.name, ' . 'series.name'
),
undef,
Bugzilla->user->id
);
foreach my $series (@$serieses) { foreach my $series (@$serieses) {
my ($cat, $subcat, $name, $series_id) = @$series; my ($cat, $subcat, $name, $series_id) = @$series;
$cats{$cat}{$subcat}{$name} = $series_id; $cats{$cat}{$subcat}{$name} = $series_id;
...@@ -408,7 +427,7 @@ sub generateDateProgression { ...@@ -408,7 +427,7 @@ sub generateDateProgression {
$dateto += (2 * $oneday) / 3; $dateto += (2 * $oneday) / 3;
while ($datefrom < $dateto) { while ($datefrom < $dateto) {
push (@progression, time2str("%Y-%m-%d", $datefrom)); push(@progression, time2str("%Y-%m-%d", $datefrom));
$datefrom += $oneday; $datefrom += $oneday;
} }
......
...@@ -61,14 +61,16 @@ sub remove_from_db { ...@@ -61,14 +61,16 @@ sub remove_from_db {
$dbh->bz_start_transaction(); $dbh->bz_start_transaction();
# Reclassify products to the default classification, if needed. # Reclassify products to the default classification, if needed.
my $product_ids = $dbh->selectcol_arrayref( my $product_ids
'SELECT id FROM products WHERE classification_id = ?', undef, $self->id); = $dbh->selectcol_arrayref(
'SELECT id FROM products WHERE classification_id = ?',
undef, $self->id);
if (@$product_ids) { if (@$product_ids) {
$dbh->do('UPDATE products SET classification_id = 1 WHERE ' $dbh->do('UPDATE products SET classification_id = 1 WHERE '
. $dbh->sql_in('id', $product_ids)); . $dbh->sql_in('id', $product_ids));
foreach my $id (@$product_ids) { foreach my $id (@$product_ids) {
Bugzilla->memcached->clear({ table => 'products', id => $id }); Bugzilla->memcached->clear({table => 'products', id => $id});
} }
Bugzilla->memcached->clear_config(); Bugzilla->memcached->clear_config();
} }
...@@ -94,8 +96,10 @@ sub _check_name { ...@@ -94,8 +96,10 @@ sub _check_name {
} }
my $classification = new Bugzilla::Classification({name => $name}); my $classification = new Bugzilla::Classification({name => $name});
if ($classification && (!ref $invocant || $classification->id != $invocant->id)) { if ($classification && (!ref $invocant || $classification->id != $invocant->id))
ThrowUserError("classification_already_exists", { name => $classification->name }); {
ThrowUserError("classification_already_exists",
{name => $classification->name});
} }
return $name; return $name;
} }
...@@ -113,7 +117,8 @@ sub _check_sortkey { ...@@ -113,7 +117,8 @@ sub _check_sortkey {
$sortkey ||= 0; $sortkey ||= 0;
my $stored_sortkey = $sortkey; my $stored_sortkey = $sortkey;
if (!detaint_natural($sortkey) || $sortkey > MAX_SMALLINT) { if (!detaint_natural($sortkey) || $sortkey > MAX_SMALLINT) {
ThrowUserError('classification_invalid_sortkey', { 'sortkey' => $stored_sortkey }); ThrowUserError('classification_invalid_sortkey',
{'sortkey' => $stored_sortkey});
} }
return $sortkey; return $sortkey;
} }
...@@ -139,9 +144,11 @@ sub product_count { ...@@ -139,9 +144,11 @@ sub product_count {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
if (!defined $self->{'product_count'}) { if (!defined $self->{'product_count'}) {
$self->{'product_count'} = $dbh->selectrow_array(q{ $self->{'product_count'} = $dbh->selectrow_array(
q{
SELECT COUNT(*) FROM products SELECT COUNT(*) FROM products
WHERE classification_id = ?}, undef, $self->id) || 0; WHERE classification_id = ?}, undef, $self->id
) || 0;
} }
return $self->{'product_count'}; return $self->{'product_count'};
} }
...@@ -151,10 +158,12 @@ sub products { ...@@ -151,10 +158,12 @@ sub products {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
if (!$self->{'products'}) { if (!$self->{'products'}) {
my $product_ids = $dbh->selectcol_arrayref(q{ my $product_ids = $dbh->selectcol_arrayref(
q{
SELECT id FROM products SELECT id FROM products
WHERE classification_id = ? WHERE classification_id = ?
ORDER BY name}, undef, $self->id); ORDER BY name}, undef, $self->id
);
$self->{'products'} = Bugzilla::Product->new_from_list($product_ids); $self->{'products'} = Bugzilla::Product->new_from_list($product_ids);
} }
...@@ -182,17 +191,22 @@ sub sort_products_by_classification { ...@@ -182,17 +191,22 @@ sub sort_products_by_classification {
if (Bugzilla->params->{'useclassification'}) { if (Bugzilla->params->{'useclassification'}) {
my $class = {}; my $class = {};
# Get all classifications with at least one product. # Get all classifications with at least one product.
foreach my $product (@$products) { foreach my $product (@$products) {
$class->{$product->classification_id}->{'object'} ||= $class->{$product->classification_id}->{'object'}
new Bugzilla::Classification($product->classification_id); ||= new Bugzilla::Classification($product->classification_id);
# Nice way to group products per classification, without querying # Nice way to group products per classification, without querying
# the DB again. # the DB again.
push(@{$class->{$product->classification_id}->{'products'}}, $product); push(@{$class->{$product->classification_id}->{'products'}}, $product);
} }
$list = [sort {$a->{'object'}->sortkey <=> $b->{'object'}->sortkey $list = [
|| lc($a->{'object'}->name) cmp lc($b->{'object'}->name)} sort {
(values %$class)]; $a->{'object'}->sortkey <=> $b->{'object'}->sortkey
|| lc($a->{'object'}->name) cmp lc($b->{'object'}->name)
} (values %$class)
];
} }
else { else {
$list = [{object => undef, products => $products}]; $list = [{object => undef, products => $products}];
......
...@@ -34,7 +34,7 @@ use constant DB_TABLE => 'longdescs_tags_weights'; ...@@ -34,7 +34,7 @@ use constant DB_TABLE => 'longdescs_tags_weights';
use constant ID_FIELD => 'id'; use constant ID_FIELD => 'id';
use constant NAME_FIELD => 'tag'; use constant NAME_FIELD => 'tag';
use constant LIST_ORDER => 'weight DESC'; use constant LIST_ORDER => 'weight DESC';
use constant VALIDATORS => { }; use constant VALIDATORS => {};
# There's no gain to caching these objects # There's no gain to caching these objects
use constant USE_MEMCACHED => 0; use constant USE_MEMCACHED => 0;
......
...@@ -25,15 +25,14 @@ use File::Basename; ...@@ -25,15 +25,14 @@ use File::Basename;
# Don't export localvars by default - people should have to explicitly # Don't export localvars by default - people should have to explicitly
# ask for it, as a (probably futile) attempt to stop code using it # ask for it, as a (probably futile) attempt to stop code using it
# when it shouldn't # when it shouldn't
%Bugzilla::Config::EXPORT_TAGS = %Bugzilla::Config::EXPORT_TAGS
( = (admin => [qw(update_params SetParam write_params)],);
admin => [qw(update_params SetParam write_params)],
);
Exporter::export_ok_tags('admin'); Exporter::export_ok_tags('admin');
# INITIALISATION CODE # INITIALISATION CODE
# Perl throws a warning if we use bz_locations() directly after do. # Perl throws a warning if we use bz_locations() directly after do.
our %params; our %params;
# Load in the param definitions # Load in the param definitions
sub _load_params { sub _load_params {
my $panels = param_panels(); my $panels = param_panels();
...@@ -42,12 +41,12 @@ sub _load_params { ...@@ -42,12 +41,12 @@ sub _load_params {
my $module = $panels->{$panel}; my $module = $panels->{$panel};
eval("require $module") || die $@; eval("require $module") || die $@;
my @new_param_list = $module->get_param_list(); my @new_param_list = $module->get_param_list();
$hook_panels{lc($panel)} = { params => \@new_param_list }; $hook_panels{lc($panel)} = {params => \@new_param_list};
} }
# This hook is also called in editparams.cgi. This call here is required # This hook is also called in editparams.cgi. This call here is required
# to make SetParam work. # to make SetParam work.
Bugzilla::Hook::process('config_modify_panels', Bugzilla::Hook::process('config_modify_panels', {panels => \%hook_panels});
{ panels => \%hook_panels });
foreach my $panel (keys %hook_panels) { foreach my $panel (keys %hook_panels) {
foreach my $item (@{$hook_panels{$panel}->{params}}) { foreach my $item (@{$hook_panels{$panel}->{params}}) {
...@@ -55,6 +54,7 @@ sub _load_params { ...@@ -55,6 +54,7 @@ sub _load_params {
} }
} }
} }
# END INIT CODE # END INIT CODE
# Subroutines go here # Subroutines go here
...@@ -65,11 +65,12 @@ sub param_panels { ...@@ -65,11 +65,12 @@ sub param_panels {
foreach my $item ((glob "$libpath/Bugzilla/Config/*.pm")) { foreach my $item ((glob "$libpath/Bugzilla/Config/*.pm")) {
$item =~ m#/([^/]+)\.pm$#; $item =~ m#/([^/]+)\.pm$#;
my $module = $1; my $module = $1;
$param_panels->{$module} = "Bugzilla::Config::$module" unless $module eq 'Common'; $param_panels->{$module} = "Bugzilla::Config::$module"
unless $module eq 'Common';
} }
# Now check for any hooked params # Now check for any hooked params
Bugzilla::Hook::process('config_add_panels', Bugzilla::Hook::process('config_add_panels', {panel_modules => $param_panels});
{ panel_modules => $param_panels });
return $param_panels; return $param_panels;
} }
...@@ -114,7 +115,7 @@ sub update_params { ...@@ -114,7 +115,7 @@ sub update_params {
die "Error evaluating $old_file: $@" if $@; die "Error evaluating $old_file: $@" if $@;
# Now read the param back out from the sandbox. # Now read the param back out from the sandbox.
$param = \%{ $s->varglob('param') }; $param = \%{$s->varglob('param')};
} }
else { else {
# Rename params.js to params.json if checksetup.pl # Rename params.js to params.json if checksetup.pl
...@@ -152,9 +153,7 @@ sub update_params { ...@@ -152,9 +153,7 @@ sub update_params {
# Change from old product groups to controls for group_control_map # Change from old product groups to controls for group_control_map
# 2002-10-14 bug 147275 bugreport@peshkin.net # 2002-10-14 bug 147275 bugreport@peshkin.net
if (exists $param->{'usebuggroups'} && if (exists $param->{'usebuggroups'} && !exists $param->{'makeproductgroups'}) {
!exists $param->{'makeproductgroups'})
{
$new_params{'makeproductgroups'} = $param->{'usebuggroups'}; $new_params{'makeproductgroups'} = $param->{'usebuggroups'};
} }
...@@ -164,22 +163,20 @@ sub update_params { ...@@ -164,22 +163,20 @@ sub update_params {
} }
# set verify method to whatever loginmethod was # set verify method to whatever loginmethod was
if (exists $param->{'loginmethod'} if (exists $param->{'loginmethod'} && !exists $param->{'user_verify_class'}) {
&& !exists $param->{'user_verify_class'})
{
$new_params{'user_verify_class'} = $param->{'loginmethod'}; $new_params{'user_verify_class'} = $param->{'loginmethod'};
} }
# Remove quip-display control from parameters # Remove quip-display control from parameters
# and give it to users via User Settings (Bug 41972) # and give it to users via User Settings (Bug 41972)
if ( exists $param->{'enablequips'} if (exists $param->{'enablequips'}
&& !exists $param->{'quip_list_entry_control'}) && !exists $param->{'quip_list_entry_control'})
{ {
my $new_value; my $new_value;
($param->{'enablequips'} eq 'on') && do {$new_value = 'open';}; ($param->{'enablequips'} eq 'on') && do { $new_value = 'open'; };
($param->{'enablequips'} eq 'approved') && do {$new_value = 'moderated';}; ($param->{'enablequips'} eq 'approved') && do { $new_value = 'moderated'; };
($param->{'enablequips'} eq 'frozen') && do {$new_value = 'closed';}; ($param->{'enablequips'} eq 'frozen') && do { $new_value = 'closed'; };
($param->{'enablequips'} eq 'off') && do {$new_value = 'closed';}; ($param->{'enablequips'} eq 'off') && do { $new_value = 'closed'; };
$new_params{'quip_list_entry_control'} = $new_value; $new_params{'quip_list_entry_control'} = $new_value;
} }
...@@ -192,11 +189,14 @@ sub update_params { ...@@ -192,11 +189,14 @@ sub update_params {
'smtp' => 'SMTP', 'smtp' => 'SMTP',
'qmail' => 'Qmail', 'qmail' => 'Qmail',
'testfile' => 'Test', 'testfile' => 'Test',
'none' => 'None'); 'none' => 'None'
);
$param->{'mail_delivery_method'} = $translation{$mta}; $param->{'mail_delivery_method'} = $translation{$mta};
} }
# This will force the parameter to be reset to its default value. # This will force the parameter to be reset to its default value.
delete $param->{'mail_delivery_method'} if $param->{'mail_delivery_method'} eq 'Qmail'; delete $param->{'mail_delivery_method'}
if $param->{'mail_delivery_method'} eq 'Qmail';
} }
# Convert the old "ssl" parameter to the new "ssl_redirect" parameter. # Convert the old "ssl" parameter to the new "ssl_redirect" parameter.
...@@ -206,9 +206,10 @@ sub update_params { ...@@ -206,9 +206,10 @@ sub update_params {
$new_params{'ssl_redirect'} = 1; $new_params{'ssl_redirect'} = 1;
} }
# "specific_search_allow_empty_words" has been renamed to "search_allow_no_criteria". # "specific_search_allow_empty_words" has been renamed to "search_allow_no_criteria".
if (exists $param->{'specific_search_allow_empty_words'}) { if (exists $param->{'specific_search_allow_empty_words'}) {
$new_params{'search_allow_no_criteria'} = $param->{'specific_search_allow_empty_words'}; $new_params{'search_allow_no_criteria'}
= $param->{'specific_search_allow_empty_words'};
} }
# --- DEFAULTS FOR NEW PARAMS --- # --- DEFAULTS FOR NEW PARAMS ---
...@@ -238,6 +239,7 @@ sub update_params { ...@@ -238,6 +239,7 @@ sub update_params {
# --- REMOVE OLD PARAMS --- # --- REMOVE OLD PARAMS ---
my %oldparams; my %oldparams;
# Remove any old params # Remove any old params
foreach my $item (keys %$param) { foreach my $item (keys %$param) {
if (!exists $params{$item}) { if (!exists $params{$item}) {
...@@ -308,22 +310,27 @@ sub read_param_file { ...@@ -308,22 +310,27 @@ sub read_param_file {
# to all users in its error message, so we have to eval'uate it. # to all users in its error message, so we have to eval'uate it.
%params = eval { %{JSON::XS->new->decode($data)} }; %params = eval { %{JSON::XS->new->decode($data)} };
if ($@) { if ($@) {
my $error_msg = (basename($0) eq 'checksetup.pl') ? my $error_msg
$@ : 'run checksetup.pl to see the details.'; = (basename($0) eq 'checksetup.pl')
? $@
: 'run checksetup.pl to see the details.';
die "Error parsing $file: $error_msg"; die "Error parsing $file: $error_msg";
} }
# JSON::XS doesn't detaint data for us. # JSON::XS doesn't detaint data for us.
foreach my $key (keys %params) { foreach my $key (keys %params) {
if (ref($params{$key}) eq "ARRAY") { if (ref($params{$key}) eq "ARRAY") {
foreach my $item (@{$params{$key}}) { foreach my $item (@{$params{$key}}) {
trick_taint($item); trick_taint($item);
} }
} else { }
else {
trick_taint($params{$key}) if defined $params{$key}; trick_taint($params{$key}) if defined $params{$key};
} }
} }
} }
elsif ($ENV{'SERVER_SOFTWARE'}) { elsif ($ENV{'SERVER_SOFTWARE'}) {
# We're in a CGI, but the params file doesn't exist. We can't # We're in a CGI, but the params file doesn't exist. We can't
# Template Toolkit, or even install_string, since checksetup # Template Toolkit, or even install_string, since checksetup
# might not have thrown an error. Bugzilla::CGI->new # might not have thrown an error. Bugzilla::CGI->new
...@@ -332,7 +339,7 @@ sub read_param_file { ...@@ -332,7 +339,7 @@ sub read_param_file {
require CGI::Carp; require CGI::Carp;
CGI::Carp->import('fatalsToBrowser'); CGI::Carp->import('fatalsToBrowser');
die "The $file file does not exist." die "The $file file does not exist."
. ' You probably need to run checksetup.pl.', . ' You probably need to run checksetup.pl.',;
} }
return \%params; return \%params;
} }
......
...@@ -18,30 +18,19 @@ our $sortkey = 200; ...@@ -18,30 +18,19 @@ our $sortkey = 200;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'allowbugdeletion', type => 'b', default => 0},
name => 'allowbugdeletion',
type => 'b',
default => 0
},
{ {name => 'allowemailchange', type => 'b', default => 1},
name => 'allowemailchange',
type => 'b',
default => 1
},
{ {name => 'allowuserdeletion', type => 'b', default => 0},
name => 'allowuserdeletion',
type => 'b',
default => 0
},
{ {
name => 'last_visit_keep_days', name => 'last_visit_keep_days',
type => 't', type => 't',
default => 10, default => 10,
checker => \&check_numeric checker => \&check_numeric
}); }
);
return @param_list; return @param_list;
} }
......
...@@ -16,24 +16,11 @@ use Bugzilla::Config::Common; ...@@ -16,24 +16,11 @@ use Bugzilla::Config::Common;
our $sortkey = 1700; our $sortkey = 1700;
use constant get_param_list => ( use constant get_param_list => (
{ {name => 'cookiedomain', type => 't', default => ''},
name => 'cookiedomain',
type => 't',
default => ''
},
{ {name => 'inbound_proxies', type => 't', default => '', checker => \&check_ip},
name => 'inbound_proxies',
type => 't',
default => '',
checker => \&check_ip
},
{ {name => 'proxy_url', type => 't', default => ''},
name => 'proxy_url',
type => 't',
default => ''
},
{ {
name => 'strict_transport_security', name => 'strict_transport_security',
......
...@@ -18,11 +18,7 @@ our $sortkey = 400; ...@@ -18,11 +18,7 @@ our $sortkey = 400;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'allow_attachment_display', type => 'b', default => 0},
name => 'allow_attachment_display',
type => 'b',
default => 0
},
{ {
name => 'attachment_base', name => 'attachment_base',
...@@ -31,11 +27,7 @@ sub get_param_list { ...@@ -31,11 +27,7 @@ sub get_param_list {
checker => \&check_urlbase checker => \&check_urlbase
}, },
{ {name => 'allow_attachment_deletion', type => 'b', default => 0},
name => 'allow_attachment_deletion',
type => 'b',
default => 0
},
{ {
name => 'maxattachmentsize', name => 'maxattachmentsize',
...@@ -57,7 +49,8 @@ sub get_param_list { ...@@ -57,7 +49,8 @@ sub get_param_list {
type => 't', type => 't',
default => '0', default => '0',
checker => \&check_numeric checker => \&check_numeric
} ); }
);
return @param_list; return @param_list;
} }
......
...@@ -18,23 +18,11 @@ our $sortkey = 300; ...@@ -18,23 +18,11 @@ our $sortkey = 300;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'auth_env_id', type => 't', default => '',},
name => 'auth_env_id',
type => 't',
default => '',
},
{ {name => 'auth_env_email', type => 't', default => '',},
name => 'auth_env_email',
type => 't',
default => '',
},
{ {name => 'auth_env_realname', type => 't', default => '',},
name => 'auth_env_realname',
type => 't',
default => '',
},
# XXX in the future: # XXX in the future:
# #
...@@ -47,7 +35,7 @@ sub get_param_list { ...@@ -47,7 +35,7 @@ sub get_param_list {
{ {
name => 'user_info_class', name => 'user_info_class',
type => 's', type => 's',
choices => [ 'CGI', 'Env', 'Env,CGI' ], choices => ['CGI', 'Env', 'Env,CGI'],
default => 'CGI', default => 'CGI',
checker => \&check_multi checker => \&check_multi
}, },
...@@ -55,7 +43,7 @@ sub get_param_list { ...@@ -55,7 +43,7 @@ sub get_param_list {
{ {
name => 'user_verify_class', name => 'user_verify_class',
type => 'o', type => 'o',
choices => [ 'DB', 'RADIUS', 'LDAP' ], choices => ['DB', 'RADIUS', 'LDAP'],
default => 'DB', default => 'DB',
checker => \&check_user_verify_class checker => \&check_user_verify_class
}, },
...@@ -68,17 +56,9 @@ sub get_param_list { ...@@ -68,17 +56,9 @@ sub get_param_list {
checker => \&check_multi checker => \&check_multi
}, },
{ {name => 'requirelogin', type => 'b', default => '0'},
name => 'requirelogin',
type => 'b',
default => '0'
},
{ {name => 'webservice_email_filter', type => 'b', default => 0},
name => 'webservice_email_filter',
type => 'b',
default => 0
},
{ {
name => 'emailregexp', name => 'emailregexp',
...@@ -90,15 +70,11 @@ sub get_param_list { ...@@ -90,15 +70,11 @@ sub get_param_list {
{ {
name => 'emailregexpdesc', name => 'emailregexpdesc',
type => 'l', type => 'l',
default => 'A legal address must contain exactly one \'@\', and at least ' . default => 'A legal address must contain exactly one \'@\', and at least '
'one \'.\' after the @.' . 'one \'.\' after the @.'
}, },
{ {name => 'emailsuffix', type => 't', default => ''},
name => 'emailsuffix',
type => 't',
default => ''
},
{ {
name => 'createemailregexp', name => 'createemailregexp',
...@@ -110,17 +86,15 @@ sub get_param_list { ...@@ -110,17 +86,15 @@ sub get_param_list {
{ {
name => 'password_complexity', name => 'password_complexity',
type => 's', type => 's',
choices => [ 'no_constraints', 'mixed_letters', 'letters_numbers', choices => [
'letters_numbers_specialchars' ], 'no_constraints', 'mixed_letters',
'letters_numbers', 'letters_numbers_specialchars'
],
default => 'no_constraints', default => 'no_constraints',
checker => \&check_multi checker => \&check_multi
}, },
{ {name => 'password_check_on_login', type => 'b', default => '1'},
name => 'password_check_on_login',
type => 'b',
default => '1'
},
); );
return @param_list; return @param_list;
} }
......
...@@ -26,7 +26,8 @@ sub get_param_list { ...@@ -26,7 +26,8 @@ sub get_param_list {
# and bug_status.is_open is not yet defined (hence the eval), so we use # and bug_status.is_open is not yet defined (hence the eval), so we use
# the bug statuses above as they are still hardcoded. # the bug statuses above as they are still hardcoded.
eval { eval {
my @current_closed_states = map {$_->name} closed_bug_statuses(); my @current_closed_states = map { $_->name } closed_bug_statuses();
# If no closed state was found, use the default list above. # If no closed state was found, use the default list above.
@closed_bug_statuses = @current_closed_states if scalar(@current_closed_states); @closed_bug_statuses = @current_closed_states if scalar(@current_closed_states);
}; };
...@@ -40,41 +41,18 @@ sub get_param_list { ...@@ -40,41 +41,18 @@ sub get_param_list {
checker => \&check_bug_status checker => \&check_bug_status
}, },
{ {name => 'letsubmitterchoosepriority', type => 'b', default => 1},
name => 'letsubmitterchoosepriority',
type => 'b',
default => 1
},
{ {name => 'letsubmitterchoosemilestone', type => 'b', default => 1},
name => 'letsubmitterchoosemilestone',
type => 'b',
default => 1
},
{ {name => 'musthavemilestoneonaccept', type => 'b', default => 0},
name => 'musthavemilestoneonaccept',
type => 'b',
default => 0
},
{ {name => 'commentonchange_resolution', type => 'b', default => 0},
name => 'commentonchange_resolution',
type => 'b',
default => 0
},
{ {name => 'commentonduplicate', type => 'b', default => 0},
name => 'commentonduplicate',
type => 'b',
default => 0
},
{ {name => 'noresolveonopenblockers', type => 'b', default => 0,}
name => 'noresolveonopenblockers', );
type => 'b',
default => 0,
} );
return @param_list; return @param_list;
} }
......
...@@ -25,35 +25,15 @@ sub get_param_list { ...@@ -25,35 +25,15 @@ sub get_param_list {
my @legal_OS = @{get_legal_field_values('op_sys')}; my @legal_OS = @{get_legal_field_values('op_sys')};
my @param_list = ( my @param_list = (
{ {name => 'useclassification', type => 'b', default => 0},
name => 'useclassification',
type => 'b',
default => 0
},
{ {name => 'usetargetmilestone', type => 'b', default => 0},
name => 'usetargetmilestone',
type => 'b',
default => 0
},
{ {name => 'useqacontact', type => 'b', default => 0},
name => 'useqacontact',
type => 'b',
default => 0
},
{ {name => 'usestatuswhiteboard', type => 'b', default => 0},
name => 'usestatuswhiteboard',
type => 'b',
default => 0
},
{ {name => 'use_see_also', type => 'b', default => 1},
name => 'use_see_also',
type => 'b',
default => 1
},
{ {
name => 'defaultpriority', name => 'defaultpriority',
...@@ -87,11 +67,8 @@ sub get_param_list { ...@@ -87,11 +67,8 @@ sub get_param_list {
checker => \&check_opsys checker => \&check_opsys
}, },
{ {name => 'collapsed_comment_tags', type => 't', default => 'obsolete, spam',}
name => 'collapsed_comment_tags', );
type => 't',
default => 'obsolete, spam',
});
return @param_list; return @param_list;
} }
......
...@@ -16,31 +16,13 @@ use Bugzilla::Config::Common; ...@@ -16,31 +16,13 @@ use Bugzilla::Config::Common;
our $sortkey = 100; our $sortkey = 100;
use constant get_param_list => ( use constant get_param_list => (
{ {name => 'urlbase', type => 't', default => '', checker => \&check_urlbase},
name => 'urlbase',
type => 't', {name => 'ssl_redirect', type => 'b', default => 0},
default => '',
checker => \&check_urlbase {name => 'sslbase', type => 't', default => '', checker => \&check_sslbase},
},
{name => 'cookiepath', type => 't', default => '/'},
{
name => 'ssl_redirect',
type => 'b',
default => 0
},
{
name => 'sslbase',
type => 't',
default => '',
checker => \&check_sslbase
},
{
name => 'cookiepath',
type => 't',
default => '/'
},
); );
1; 1;
...@@ -25,12 +25,8 @@ sub get_param_list { ...@@ -25,12 +25,8 @@ sub get_param_list {
checker => \&check_webdotbase checker => \&check_webdotbase
}, },
{ {name => 'font_file', type => 't', default => '', checker => \&check_font_file}
name => 'font_file', );
type => 't',
default => '',
checker => \&check_font_file
});
return @param_list; return @param_list;
} }
......
...@@ -24,30 +24,19 @@ use constant get_param_list => ( ...@@ -24,30 +24,19 @@ use constant get_param_list => (
checker => \&check_email checker => \&check_email
}, },
{ {name => 'utf8', type => 'b', default => '0', checker => \&check_utf8},
name => 'utf8',
type => 'b',
default => '0',
checker => \&check_utf8
},
{ {name => 'shutdownhtml', type => 'l', default => ''},
name => 'shutdownhtml',
type => 'l',
default => ''
},
{ {name => 'announcehtml', type => 'l', default => ''},
name => 'announcehtml',
type => 'l',
default => ''
},
{ {
name => 'upgrade_notification', name => 'upgrade_notification',
type => 's', type => 's',
choices => ['development_snapshot', 'latest_stable_release', choices => [
'stable_branch_release', 'disabled'], 'development_snapshot', 'latest_stable_release',
'stable_branch_release', 'disabled'
],
default => 'latest_stable_release', default => 'latest_stable_release',
checker => \&check_notification checker => \&check_notification
}, },
......
...@@ -20,11 +20,7 @@ sub get_param_list { ...@@ -20,11 +20,7 @@ sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'makeproductgroups', type => 'b', default => 0},
name => 'makeproductgroups',
type => 'b',
default => 0
},
{ {
name => 'chartgroup', name => 'chartgroup',
...@@ -74,28 +70,17 @@ sub get_param_list { ...@@ -74,28 +70,17 @@ sub get_param_list {
checker => \&check_group checker => \&check_group
}, },
{ {name => 'usevisibilitygroups', type => 'b', default => 0},
name => 'usevisibilitygroups',
type => 'b',
default => 0
},
{ {name => 'strict_isolation', type => 'b', default => 0},
name => 'strict_isolation',
type => 'b',
default => 0
},
{ {name => 'or_groups', type => 'b', default => 0}
name => 'or_groups', );
type => 'b',
default => 0
} );
return @param_list; return @param_list;
} }
sub _get_all_group_names { sub _get_all_group_names {
my @group_names = map {$_->name} Bugzilla::Group->get_all; my @group_names = map { $_->name } Bugzilla::Group->get_all;
unshift(@group_names, ''); unshift(@group_names, '');
return \@group_names; return \@group_names;
} }
......
...@@ -18,47 +18,20 @@ our $sortkey = 1000; ...@@ -18,47 +18,20 @@ our $sortkey = 1000;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'LDAPserver', type => 't', default => ''},
name => 'LDAPserver',
type => 't',
default => ''
},
{ {name => 'LDAPstarttls', type => 'b', default => 0},
name => 'LDAPstarttls',
type => 'b',
default => 0
},
{ {name => 'LDAPbinddn', type => 't', default => ''},
name => 'LDAPbinddn',
type => 't',
default => ''
},
{ {name => 'LDAPBaseDN', type => 't', default => ''},
name => 'LDAPBaseDN',
type => 't',
default => ''
},
{ {name => 'LDAPuidattribute', type => 't', default => 'uid'},
name => 'LDAPuidattribute',
type => 't',
default => 'uid'
},
{ {name => 'LDAPmailattribute', type => 't', default => 'mail'},
name => 'LDAPmailattribute',
type => 't',
default => 'mail'
},
{ {name => 'LDAPfilter', type => 't', default => '',}
name => 'LDAPfilter', );
type => 't',
default => '',
} );
return @param_list; return @param_list;
} }
......
...@@ -26,11 +26,7 @@ sub get_param_list { ...@@ -26,11 +26,7 @@ sub get_param_list {
checker => \&check_mail_delivery_method checker => \&check_mail_delivery_method
}, },
{ {name => 'mailfrom', type => 't', default => 'bugzilla-daemon'},
name => 'mailfrom',
type => 't',
default => 'bugzilla-daemon'
},
{ {
name => 'use_mailer_queue', name => 'use_mailer_queue',
...@@ -51,33 +47,12 @@ sub get_param_list { ...@@ -51,33 +47,12 @@ sub get_param_list {
default => '', default => '',
checker => \&check_smtp_auth checker => \&check_smtp_auth
}, },
{ {name => 'smtp_password', type => 'p', default => ''},
name => 'smtp_password', {name => 'smtp_ssl', type => 'b', default => 0, checker => \&check_smtp_ssl},
type => 'p', {name => 'smtp_debug', type => 'b', default => 0},
default => '' {name => 'whinedays', type => 't', default => 7, checker => \&check_numeric},
}, {name => 'globalwatchers', type => 't', default => '',},
{ );
name => 'smtp_ssl',
type => 'b',
default => 0,
checker => \&check_smtp_ssl
},
{
name => 'smtp_debug',
type => 'b',
default => 0
},
{
name => 'whinedays',
type => 't',
default => 7,
checker => \&check_numeric
},
{
name => 'globalwatchers',
type => 't',
default => '',
}, );
return @param_list; return @param_list;
} }
......
...@@ -17,16 +17,8 @@ our $sortkey = 1550; ...@@ -17,16 +17,8 @@ our $sortkey = 1550;
sub get_param_list { sub get_param_list {
return ( return (
{ {name => 'memcached_servers', type => 't', default => ''},
name => 'memcached_servers', {name => 'memcached_namespace', type => 't', default => 'bugzilla:',},
type => 't',
default => ''
},
{
name => 'memcached_namespace',
type => 't',
default => 'bugzilla:',
},
); );
} }
......
...@@ -29,20 +29,18 @@ sub get_param_list { ...@@ -29,20 +29,18 @@ sub get_param_list {
{ {
name => 'mybugstemplate', name => 'mybugstemplate',
type => 't', type => 't',
default => 'buglist.cgi?resolution=---&amp;emailassigned_to1=1&amp;emailreporter1=1&amp;emailtype1=exact&amp;email1=%userid%' default =>
'buglist.cgi?resolution=---&amp;emailassigned_to1=1&amp;emailreporter1=1&amp;emailtype1=exact&amp;email1=%userid%'
}, },
{ {
name => 'defaultquery', name => 'defaultquery',
type => 't', type => 't',
default => 'resolution=---&emailassigned_to1=1&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailqa_contact2=1&emaillongdesc3=1&order=Importance&long_desc_type=substring' default =>
'resolution=---&emailassigned_to1=1&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailqa_contact2=1&emaillongdesc3=1&order=Importance&long_desc_type=substring'
}, },
{ {name => 'search_allow_no_criteria', type => 'b', default => 1},
name => 'search_allow_no_criteria',
type => 'b',
default => 1
},
{ {
name => 'default_search_limit', name => 'default_search_limit',
......
...@@ -18,29 +18,13 @@ our $sortkey = 1100; ...@@ -18,29 +18,13 @@ our $sortkey = 1100;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'RADIUS_server', type => 't', default => ''},
name => 'RADIUS_server',
type => 't', {name => 'RADIUS_secret', type => 't', default => ''},
default => ''
}, {name => 'RADIUS_NAS_IP', type => 't', default => ''},
{ {name => 'RADIUS_email_suffix', type => 't', default => ''},
name => 'RADIUS_secret',
type => 't',
default => ''
},
{
name => 'RADIUS_NAS_IP',
type => 't',
default => ''
},
{
name => 'RADIUS_email_suffix',
type => 't',
default => ''
},
); );
return @param_list; return @param_list;
} }
......
...@@ -18,11 +18,7 @@ our $sortkey = 1500; ...@@ -18,11 +18,7 @@ our $sortkey = 1500;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'shadowdbhost', type => 't', default => '',},
name => 'shadowdbhost',
type => 't',
default => '',
},
{ {
name => 'shadowdbport', name => 'shadowdbport',
...@@ -31,20 +27,12 @@ sub get_param_list { ...@@ -31,20 +27,12 @@ sub get_param_list {
checker => \&check_numeric, checker => \&check_numeric,
}, },
{ {name => 'shadowdbsock', type => 't', default => '',},
name => 'shadowdbsock',
type => 't',
default => '',
},
# This entry must be _after_ the shadowdb{host,port,sock} settings so that # This entry must be _after_ the shadowdb{host,port,sock} settings so that
# they can be used in the validation here # they can be used in the validation here
{ {name => 'shadowdb', type => 't', default => '', checker => \&check_shadowdb}
name => 'shadowdb', );
type => 't',
default => '',
checker => \&check_shadowdb
} );
return @param_list; return @param_list;
} }
......
...@@ -18,17 +18,9 @@ our $sortkey = 1600; ...@@ -18,17 +18,9 @@ our $sortkey = 1600;
sub get_param_list { sub get_param_list {
my $class = shift; my $class = shift;
my @param_list = ( my @param_list = (
{ {name => 'usemenuforusers', type => 'b', default => '0'},
name => 'usemenuforusers',
type => 'b',
default => '0'
},
{ {name => 'ajax_user_autocompletion', type => 'b', default => '1',},
name => 'ajax_user_autocompletion',
type => 'b',
default => '1',
},
{ {
name => 'maxusermatches', name => 'maxusermatches',
...@@ -37,11 +29,8 @@ sub get_param_list { ...@@ -37,11 +29,8 @@ sub get_param_list {
checker => \&check_numeric checker => \&check_numeric
}, },
{ {name => 'confirmuniqueusermatch', type => 'b', default => 1,}
name => 'confirmuniqueusermatch', );
type => 'b',
default => 1,
} );
return @param_list; return @param_list;
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -28,18 +28,18 @@ sub _initialize { ...@@ -28,18 +28,18 @@ sub _initialize {
$self = $self->SUPER::_initialize(@_); $self = $self->SUPER::_initialize(@_);
# Remove FULLTEXT index types from the schemas. # Remove FULLTEXT index types from the schemas.
foreach my $table (keys %{ $self->{schema} }) { foreach my $table (keys %{$self->{schema}}) {
if ($self->{schema}{$table}{INDEXES}) { if ($self->{schema}{$table}{INDEXES}) {
foreach my $index (@{ $self->{schema}{$table}{INDEXES} }) { foreach my $index (@{$self->{schema}{$table}{INDEXES}}) {
if (ref($index) eq 'HASH') { if (ref($index) eq 'HASH') {
delete($index->{TYPE}) if (exists $index->{TYPE} delete($index->{TYPE})
&& $index->{TYPE} eq 'FULLTEXT'); if (exists $index->{TYPE} && $index->{TYPE} eq 'FULLTEXT');
} }
} }
foreach my $index (@{ $self->{abstract_schema}{$table}{INDEXES} }) { foreach my $index (@{$self->{abstract_schema}{$table}{INDEXES}}) {
if (ref($index) eq 'HASH') { if (ref($index) eq 'HASH') {
delete($index->{TYPE}) if (exists $index->{TYPE} delete($index->{TYPE})
&& $index->{TYPE} eq 'FULLTEXT'); if (exists $index->{TYPE} && $index->{TYPE} eq 'FULLTEXT');
} }
} }
} }
...@@ -75,14 +75,16 @@ sub _initialize { ...@@ -75,14 +75,16 @@ sub _initialize {
return $self; return $self;
} #eosub--_initialize } #eosub--_initialize
#-------------------------------------------------------------------- #--------------------------------------------------------------------
sub get_create_database_sql { sub get_create_database_sql {
my ($self, $name) = @_; my ($self, $name) = @_;
# We only create as utf8 if we have no params (meaning we're doing # We only create as utf8 if we have no params (meaning we're doing
# a new installation) or if the utf8 param is on. # a new installation) or if the utf8 param is on.
my $create_utf8 = Bugzilla->params->{'utf8'} my $create_utf8
|| !defined Bugzilla->params->{'utf8'}; = Bugzilla->params->{'utf8'} || !defined Bugzilla->params->{'utf8'};
my $charset = $create_utf8 ? "ENCODING 'UTF8' TEMPLATE template0" : ''; my $charset = $create_utf8 ? "ENCODING 'UTF8' TEMPLATE template0" : '';
return ("CREATE DATABASE $name $charset"); return ("CREATE DATABASE $name $charset");
} }
...@@ -90,6 +92,7 @@ sub get_create_database_sql { ...@@ -90,6 +92,7 @@ sub get_create_database_sql {
sub get_rename_column_ddl { sub get_rename_column_ddl {
my ($self, $table, $old_name, $new_name) = @_; my ($self, $table, $old_name, $new_name) = @_;
if (lc($old_name) eq lc($new_name)) { if (lc($old_name) eq lc($new_name)) {
# if the only change is a case change, return an empty list, since Pg # if the only change is a case change, return an empty list, since Pg
# is case-insensitive and will return an error about a duplicate name # is case-insensitive and will return an error about a duplicate name
return (); return ();
...@@ -97,9 +100,12 @@ sub get_rename_column_ddl { ...@@ -97,9 +100,12 @@ sub get_rename_column_ddl {
my @sql = ("ALTER TABLE $table RENAME COLUMN $old_name TO $new_name"); my @sql = ("ALTER TABLE $table RENAME COLUMN $old_name TO $new_name");
my $def = $self->get_column_abstract($table, $old_name); my $def = $self->get_column_abstract($table, $old_name);
if ($def->{TYPE} =~ /SERIAL/i) { if ($def->{TYPE} =~ /SERIAL/i) {
# We have to rename the series also. # We have to rename the series also.
push(@sql, "ALTER SEQUENCE ${table}_${old_name}_seq push(
RENAME TO ${table}_${new_name}_seq"); @sql, "ALTER SEQUENCE ${table}_${old_name}_seq
RENAME TO ${table}_${new_name}_seq"
);
} }
return @sql; return @sql;
} }
...@@ -107,6 +113,7 @@ sub get_rename_column_ddl { ...@@ -107,6 +113,7 @@ sub get_rename_column_ddl {
sub get_rename_table_sql { sub get_rename_table_sql {
my ($self, $old_name, $new_name) = @_; my ($self, $old_name, $new_name) = @_;
if (lc($old_name) eq lc($new_name)) { if (lc($old_name) eq lc($new_name)) {
# if the only change is a case change, return an empty list, since Pg # if the only change is a case change, return an empty list, since Pg
# is case-insensitive and will return an error about a duplicate name # is case-insensitive and will return an error about a duplicate name
return (); return ();
...@@ -124,8 +131,10 @@ sub get_rename_table_sql { ...@@ -124,8 +131,10 @@ sub get_rename_table_sql {
my $old_seq = "${old_name}_${column}_seq"; my $old_seq = "${old_name}_${column}_seq";
my $new_seq = "${new_name}_${column}_seq"; my $new_seq = "${new_name}_${column}_seq";
push(@sql, "ALTER SEQUENCE $old_seq RENAME TO $new_seq"); push(@sql, "ALTER SEQUENCE $old_seq RENAME TO $new_seq");
push(@sql, "ALTER TABLE $new_name ALTER COLUMN $column push(
SET DEFAULT NEXTVAL('$new_seq')"); @sql, "ALTER TABLE $new_name ALTER COLUMN $column
SET DEFAULT NEXTVAL('$new_seq')"
);
} }
if ($def->{PRIMARYKEY}) { if ($def->{PRIMARYKEY}) {
my $old_pk = "${old_name}_pkey"; my $old_pk = "${old_name}_pkey";
...@@ -139,8 +148,10 @@ sub get_rename_table_sql { ...@@ -139,8 +148,10 @@ sub get_rename_table_sql {
sub get_set_serial_sql { sub get_set_serial_sql {
my ($self, $table, $column, $value) = @_; my ($self, $table, $column, $value) = @_;
return ("SELECT setval('${table}_${column}_seq', $value, false) return (
FROM $table"); "SELECT setval('${table}_${column}_seq', $value, false)
FROM $table"
);
} }
sub _get_alter_type_sql { sub _get_alter_type_sql {
...@@ -148,8 +159,7 @@ sub _get_alter_type_sql { ...@@ -148,8 +159,7 @@ sub _get_alter_type_sql {
my @statements; my @statements;
my $type = $new_def->{TYPE}; my $type = $new_def->{TYPE};
$type = $self->{db_specific}->{$type} $type = $self->{db_specific}->{$type} if exists $self->{db_specific}->{$type};
if exists $self->{db_specific}->{$type};
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) { if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
die("You cannot specify a DEFAULT on a SERIAL-type column.") die("You cannot specify a DEFAULT on a SERIAL-type column.")
...@@ -162,26 +172,38 @@ sub _get_alter_type_sql { ...@@ -162,26 +172,38 @@ sub _get_alter_type_sql {
# two identical indexes otherwise. # two identical indexes otherwise.
$type =~ s/unique//i if $new_def->{PRIMARYKEY}; $type =~ s/unique//i if $new_def->{PRIMARYKEY};
push(@statements, "ALTER TABLE $table ALTER COLUMN $column push(
TYPE $type"); @statements, "ALTER TABLE $table ALTER COLUMN $column
TYPE $type"
);
if ($new_def->{TYPE} =~ /serial/i && $old_def->{TYPE} !~ /serial/i) { if ($new_def->{TYPE} =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
push(@statements, "CREATE SEQUENCE ${table}_${column}_seq push(
OWNED BY $table.$column"); @statements, "CREATE SEQUENCE ${table}_${column}_seq
push(@statements, "SELECT setval('${table}_${column}_seq', OWNED BY $table.$column"
);
push(
@statements, "SELECT setval('${table}_${column}_seq',
MAX($table.$column)) MAX($table.$column))
FROM $table"); FROM $table"
push(@statements, "ALTER TABLE $table ALTER COLUMN $column );
SET DEFAULT nextval('${table}_${column}_seq')"); push(
@statements, "ALTER TABLE $table ALTER COLUMN $column
SET DEFAULT nextval('${table}_${column}_seq')"
);
} }
# If this column is no longer SERIAL, we need to drop the sequence # If this column is no longer SERIAL, we need to drop the sequence
# that went along with it. # that went along with it.
if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i) { if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i) {
push(@statements, "ALTER TABLE $table ALTER COLUMN $column push(
DROP DEFAULT"); @statements, "ALTER TABLE $table ALTER COLUMN $column
push(@statements, "ALTER SEQUENCE ${table}_${column}_seq DROP DEFAULT"
OWNED BY NONE"); );
push(
@statements, "ALTER SEQUENCE ${table}_${column}_seq
OWNED BY NONE"
);
push(@statements, "DROP SEQUENCE ${table}_${column}_seq"); push(@statements, "DROP SEQUENCE ${table}_${column}_seq");
} }
......
...@@ -62,7 +62,8 @@ sub _initialize { ...@@ -62,7 +62,8 @@ sub _initialize {
sub _sqlite_create_table { sub _sqlite_create_table {
my ($self, $table) = @_; my ($self, $table) = @_;
return scalar Bugzilla->dbh->selectrow_array( return
scalar Bugzilla->dbh->selectrow_array(
"SELECT sql FROM sqlite_master WHERE name = ? AND type = 'table'", "SELECT sql FROM sqlite_master WHERE name = ? AND type = 'table'",
undef, $table); undef, $table);
} }
...@@ -71,6 +72,7 @@ sub _sqlite_table_lines { ...@@ -71,6 +72,7 @@ sub _sqlite_table_lines {
my $self = shift; my $self = shift;
my $table_sql = $self->_sqlite_create_table(@_); my $table_sql = $self->_sqlite_create_table(@_);
$table_sql =~ s/\n*\)$//s; $table_sql =~ s/\n*\)$//s;
# The $ makes this work even if people some day add crazy stuff to their # The $ makes this work even if people some day add crazy stuff to their
# schema like multi-column foreign keys. # schema like multi-column foreign keys.
return split(/,\s*$/m, $table_sql); return split(/,\s*$/m, $table_sql);
...@@ -107,8 +109,8 @@ sub _sqlite_alter_schema { ...@@ -107,8 +109,8 @@ sub _sqlite_alter_schema {
my $insert_str = join(',', @insert_cols); my $insert_str = join(',', @insert_cols);
my $select_str = join(',', @select_cols); my $select_str = join(',', @select_cols);
my $copy_sql = "INSERT INTO $table ($insert_str)" my $copy_sql
. " SELECT $select_str FROM $rename_to"; = "INSERT INTO $table ($insert_str)" . " SELECT $select_str FROM $rename_to";
# We have to turn FKs off before doing this. Otherwise, when we rename # We have to turn FKs off before doing this. Otherwise, when we rename
# the table, all of the FKs in the other tables will be automatically # the table, all of the FKs in the other tables will be automatically
...@@ -120,7 +122,7 @@ sub _sqlite_alter_schema { ...@@ -120,7 +122,7 @@ sub _sqlite_alter_schema {
my @sql = ( my @sql = (
'PRAGMA foreign_keys = OFF', 'PRAGMA foreign_keys = OFF',
'BEGIN EXCLUSIVE TRANSACTION', 'BEGIN EXCLUSIVE TRANSACTION',
@{ $options->{pre_sql} || [] }, @{$options->{pre_sql} || []},
"ALTER TABLE $table RENAME TO $rename_to", "ALTER TABLE $table RENAME TO $rename_to",
$create_table, $create_table,
$copy_sql, $copy_sql,
...@@ -133,6 +135,7 @@ sub _sqlite_alter_schema { ...@@ -133,6 +135,7 @@ sub _sqlite_alter_schema {
# For finding a particular column's definition in a CREATE TABLE statement. # For finding a particular column's definition in a CREATE TABLE statement.
sub _sqlite_column_regex { sub _sqlite_column_regex {
my ($column) = @_; my ($column) = @_;
# 1 = Comma at start # 1 = Comma at start
# 2 = Column name + Space # 2 = Column name + Space
# 3 = Definition # 3 = Definition
...@@ -145,6 +148,7 @@ sub _sqlite_column_regex { ...@@ -145,6 +148,7 @@ sub _sqlite_column_regex {
############################# #############################
sub get_create_database_sql { sub get_create_database_sql {
# If we get here, it means there was some error creating the # If we get here, it means there was some error creating the
# database file during bz_create_database in Bugzilla::DB, # database file during bz_create_database in Bugzilla::DB,
# and we just want to display that error instead of doing # and we just want to display that error instead of doing
...@@ -182,6 +186,7 @@ sub get_type_ddl { ...@@ -182,6 +186,7 @@ sub get_type_ddl {
if ($def->{TYPE} =~ /text/i or $def->{TYPE} =~ /char/i) { if ($def->{TYPE} =~ /text/i or $def->{TYPE} =~ /char/i) {
$ddl .= " COLLATE bugzilla"; $ddl .= " COLLATE bugzilla";
} }
# Don't collate DATETIME fields. # Don't collate DATETIME fields.
if ($def->{TYPE} eq 'DATETIME') { if ($def->{TYPE} eq 'DATETIME') {
$ddl =~ s/\bDATETIME\b/text COLLATE BINARY/; $ddl =~ s/\bDATETIME\b/text COLLATE BINARY/;
...@@ -196,19 +201,20 @@ sub get_alter_column_ddl { ...@@ -196,19 +201,20 @@ sub get_alter_column_ddl {
my $table_sql = $self->_sqlite_create_table($table); my $table_sql = $self->_sqlite_create_table($table);
my $new_ddl = $self->get_type_ddl($new_def); my $new_ddl = $self->get_type_ddl($new_def);
# When we do ADD COLUMN, columns can show up all on one line separated # When we do ADD COLUMN, columns can show up all on one line separated
# by commas, so we have to account for that. # by commas, so we have to account for that.
my $column_regex = _sqlite_column_regex($column); my $column_regex = _sqlite_column_regex($column);
$table_sql =~ s/$column_regex/$1$2$new_ddl$4/ $table_sql =~ s/$column_regex/$1$2$new_ddl$4/
|| die "couldn't find $column in $table:\n$table_sql"; || die "couldn't find $column in $table:\n$table_sql";
my @pre_sql = $self->_set_nulls_sql(@_); my @pre_sql = $self->_set_nulls_sql(@_);
return $self->_sqlite_alter_schema($table, $table_sql, return $self->_sqlite_alter_schema($table, $table_sql, {pre_sql => \@pre_sql});
{ pre_sql => \@pre_sql });
} }
sub get_add_column_ddl { sub get_add_column_ddl {
my $self = shift; my $self = shift;
my ($table, $column, $definition, $init_value) = @_; my ($table, $column, $definition, $init_value) = @_;
# SQLite can use the normal ADD COLUMN when: # SQLite can use the normal ADD COLUMN when:
# * The column isn't a PK # * The column isn't a PK
if ($definition->{PRIMARYKEY}) { if ($definition->{PRIMARYKEY}) {
...@@ -216,12 +222,14 @@ sub get_add_column_ddl { ...@@ -216,12 +222,14 @@ sub get_add_column_ddl {
die "You can only add new SERIAL type PKs with SQLite"; die "You can only add new SERIAL type PKs with SQLite";
} }
my $table_sql = $self->_sqlite_new_column_sql(@_); my $table_sql = $self->_sqlite_new_column_sql(@_);
# This works because _sqlite_alter_schema will exclude the new column # This works because _sqlite_alter_schema will exclude the new column
# in its INSERT ... SELECT statement, meaning that when the "new" # in its INSERT ... SELECT statement, meaning that when the "new"
# table is populated, it will have AUTOINCREMENT values generated # table is populated, it will have AUTOINCREMENT values generated
# for it. # for it.
return $self->_sqlite_alter_schema($table, $table_sql); return $self->_sqlite_alter_schema($table, $table_sql);
} }
# * The column has a default one way or another. Either it # * The column has a default one way or another. Either it
# defaults to NULL (it lacks NOT NULL) or it has a DEFAULT # defaults to NULL (it lacks NOT NULL) or it has a DEFAULT
# clause. Since we also require this when doing bz_add_column (in # clause. Since we also require this when doing bz_add_column (in
...@@ -231,11 +239,10 @@ sub get_add_column_ddl { ...@@ -231,11 +239,10 @@ sub get_add_column_ddl {
if ($definition->{NOTNULL} and !defined $definition->{DEFAULT}) { if ($definition->{NOTNULL} and !defined $definition->{DEFAULT}) {
my %with_default = %$definition; my %with_default = %$definition;
$with_default{DEFAULT} = $init_value; $with_default{DEFAULT} = $init_value;
my @pre_sql = my @pre_sql = $self->SUPER::get_add_column_ddl($table, $column, \%with_default);
$self->SUPER::get_add_column_ddl($table, $column, \%with_default);
my $table_sql = $self->_sqlite_new_column_sql(@_); my $table_sql = $self->_sqlite_new_column_sql(@_);
return $self->_sqlite_alter_schema($table, $table_sql, return $self->_sqlite_alter_schema($table, $table_sql,
{ pre_sql => \@pre_sql, extra_column => $column }); {pre_sql => \@pre_sql, extra_column => $column});
} }
return $self->SUPER::get_add_column_ddl(@_); return $self->SUPER::get_add_column_ddl(@_);
...@@ -257,10 +264,11 @@ sub get_drop_column_ddl { ...@@ -257,10 +264,11 @@ sub get_drop_column_ddl {
my $column_regex = _sqlite_column_regex($column); my $column_regex = _sqlite_column_regex($column);
$table_sql =~ s/$column_regex/$1/ $table_sql =~ s/$column_regex/$1/
|| die "Can't find column $column: $table_sql"; || die "Can't find column $column: $table_sql";
# Make sure we don't end up with a comma at the end of the definition. # Make sure we don't end up with a comma at the end of the definition.
$table_sql =~ s/,\s+\)$/\n)/s; $table_sql =~ s/,\s+\)$/\n)/s;
return $self->_sqlite_alter_schema($table, $table_sql, return $self->_sqlite_alter_schema($table, $table_sql,
{ exclude_column => $column }); {exclude_column => $column});
} }
sub get_rename_column_ddl { sub get_rename_column_ddl {
...@@ -270,8 +278,7 @@ sub get_rename_column_ddl { ...@@ -270,8 +278,7 @@ sub get_rename_column_ddl {
$table_sql =~ s/$column_regex/$1\t$new_name\t$3$4/ $table_sql =~ s/$column_regex/$1\t$new_name\t$3$4/
|| die "Can't find $old_name: $table_sql"; || die "Can't find $old_name: $table_sql";
my %rename = ($old_name => $new_name); my %rename = ($old_name => $new_name);
return $self->_sqlite_alter_schema($table, $table_sql, return $self->_sqlite_alter_schema($table, $table_sql, {rename => \%rename});
{ rename => \%rename });
} }
################ ################
...@@ -292,8 +299,7 @@ sub get_drop_fk_sql { ...@@ -292,8 +299,7 @@ sub get_drop_fk_sql {
my $fk_name = $self->_get_fk_name($table, $column, $references); my $fk_name = $self->_get_fk_name($table, $column, $references);
my $line_re = qr/^\s+CONSTRAINT $fk_name /s; my $line_re = qr/^\s+CONSTRAINT $fk_name /s;
grep { $line_re } @clauses grep {$line_re} @clauses or die "Can't find $fk_name: " . join(',', @clauses);
or die "Can't find $fk_name: " . join(',', @clauses);
@clauses = grep { $_ !~ $line_re } @clauses; @clauses = grep { $_ !~ $line_re } @clauses;
return $self->_sqlite_alter_schema($table, \@clauses); return $self->_sqlite_alter_schema($table, \@clauses);
......
...@@ -75,6 +75,7 @@ sub new { ...@@ -75,6 +75,7 @@ sub new {
# Let people specify paths intead of data/ for the DB. # Let people specify paths intead of data/ for the DB.
if ($db_name and $db_name !~ m{[\\/]}) { if ($db_name and $db_name !~ m{[\\/]}) {
# When the DB is first created, there's a chance that the # When the DB is first created, there's a chance that the
# data directory doesn't exist at all, because the Install::Filesystem # data directory doesn't exist at all, because the Install::Filesystem
# code happens after DB creation. So we create the directory ourselves # code happens after DB creation. So we create the directory ourselves
...@@ -93,25 +94,31 @@ sub new { ...@@ -93,25 +94,31 @@ sub new {
my $dsn = "dbi:SQLite:dbname=$db_name"; my $dsn = "dbi:SQLite:dbname=$db_name";
my $attrs = { my $attrs = {
# XXX Should we just enforce this to be always on? # XXX Should we just enforce this to be always on?
sqlite_unicode => Bugzilla->params->{'utf8'}, sqlite_unicode => Bugzilla->params->{'utf8'},
}; };
my $self = $class->db_new({ dsn => $dsn, user => '', my $self
pass => '', attrs => $attrs }); = $class->db_new({dsn => $dsn, user => '', pass => '', attrs => $attrs});
# Needed by TheSchwartz # Needed by TheSchwartz
$self->{private_bz_dsn} = $dsn; $self->{private_bz_dsn} = $dsn;
my %pragmas = ( my %pragmas = (
# Make sure that the sqlite file doesn't grow without bound. # Make sure that the sqlite file doesn't grow without bound.
auto_vacuum => 1, auto_vacuum => 1,
encoding => "'UTF-8'", encoding => "'UTF-8'",
foreign_keys => 'ON', foreign_keys => 'ON',
# We want the latest file format. # We want the latest file format.
legacy_file_format => 'OFF', legacy_file_format => 'OFF',
# This guarantees that we get column names like "foo" # This guarantees that we get column names like "foo"
# instead of "table.foo" in selectrow_hashref. # instead of "table.foo" in selectrow_hashref.
short_column_names => 'ON', short_column_names => 'ON',
# The write-ahead log mode in SQLite 3.7 gets us better concurrency, # The write-ahead log mode in SQLite 3.7 gets us better concurrency,
# but breaks backwards-compatibility with older versions of # but breaks backwards-compatibility with older versions of
# SQLite. (Which is important because people may also want to use # SQLite. (Which is important because people may also want to use
...@@ -128,6 +135,7 @@ sub new { ...@@ -128,6 +135,7 @@ sub new {
$self->sqlite_create_collation('bugzilla', \&_sqlite_collate_ci); $self->sqlite_create_collation('bugzilla', \&_sqlite_collate_ci);
$self->sqlite_create_function('position', 2, \&_sqlite_position); $self->sqlite_create_function('position', 2, \&_sqlite_position);
$self->sqlite_create_function('iposition', 2, \&_sqlite_position_ci); $self->sqlite_create_function('iposition', 2, \&_sqlite_position_ci);
# SQLite has a "substr" function, but other DBs call it "SUBSTRING" # SQLite has a "substr" function, but other DBs call it "SUBSTRING"
# so that's what we use, and I don't know of any way in SQLite to # so that's what we use, and I don't know of any way in SQLite to
# alias the SQL "substr" function to be called "SUBSTRING". # alias the SQL "substr" function to be called "SUBSTRING".
...@@ -138,7 +146,7 @@ sub new { ...@@ -138,7 +146,7 @@ sub new {
$self->sqlite_create_function('localtimestamp', 1, \&_sqlite_now); $self->sqlite_create_function('localtimestamp', 1, \&_sqlite_now);
$self->sqlite_create_function('floor', 1, \&POSIX::floor); $self->sqlite_create_function('floor', 1, \&POSIX::floor);
bless ($self, $class); bless($self, $class);
return $self; return $self;
} }
...@@ -167,6 +175,7 @@ sub sql_group_by { ...@@ -167,6 +175,7 @@ sub sql_group_by {
sub sql_group_concat { sub sql_group_concat {
my ($self, $column, $separator, $sort) = @_; my ($self, $column, $separator, $sort) = @_;
$separator = $self->quote(', ') if !defined $separator; $separator = $self->quote(', ') if !defined $separator;
# In SQLite, a GROUP_CONCAT call with a DISTINCT argument can't # In SQLite, a GROUP_CONCAT call with a DISTINCT argument can't
# specify its separator, and has to accept the default of ",". # specify its separator, and has to accept the default of ",".
if ($column =~ /^DISTINCT/) { if ($column =~ /^DISTINCT/) {
...@@ -200,7 +209,8 @@ sub sql_limit { ...@@ -200,7 +209,8 @@ sub sql_limit {
if (defined($offset)) { if (defined($offset)) {
return "LIMIT $limit OFFSET $offset"; return "LIMIT $limit OFFSET $offset";
} else { }
else {
return "LIMIT $limit"; return "LIMIT $limit";
} }
} }
...@@ -225,6 +235,7 @@ sub sql_date_format { ...@@ -225,6 +235,7 @@ sub sql_date_format {
sub sql_date_math { sub sql_date_math {
my ($self, $date, $operator, $interval, $units) = @_; my ($self, $date, $operator, $interval, $units) = @_;
# We do the || thing (concatenation) so that placeholders work properly. # We do the || thing (concatenation) so that placeholders work properly.
return "DATETIME($date, '$operator' || $interval || ' $units')"; return "DATETIME($date, '$operator' || $interval || ' $units')";
} }
...@@ -240,11 +251,10 @@ sub bz_setup_database { ...@@ -240,11 +251,10 @@ sub bz_setup_database {
# If we created TheSchwartz tables with COLLATE bugzilla (during the # If we created TheSchwartz tables with COLLATE bugzilla (during the
# 4.1.x development series) re-create them without it. # 4.1.x development series) re-create them without it.
my @tables = $self->bz_table_list(); my @tables = $self->bz_table_list();
my @ts_tables = grep { /^ts_/ } @tables; my @ts_tables = grep {/^ts_/} @tables;
my $drop_ok; my $drop_ok;
foreach my $table (@ts_tables) { foreach my $table (@ts_tables) {
my $create_table = my $create_table = $self->_bz_real_schema->_sqlite_create_table($table);
$self->_bz_real_schema->_sqlite_create_table($table);
if ($create_table =~ /COLLATE bugzilla/) { if ($create_table =~ /COLLATE bugzilla/) {
if (!$drop_ok) { if (!$drop_ok) {
_sqlite_jobqueue_drop_message(); _sqlite_jobqueue_drop_message();
...@@ -257,6 +267,7 @@ sub bz_setup_database { ...@@ -257,6 +267,7 @@ sub bz_setup_database {
} }
sub _sqlite_jobqueue_drop_message { sub _sqlite_jobqueue_drop_message {
# This is not translated because this situation will only happen if # This is not translated because this situation will only happen if
# you are updating from a 4.1.x development version of Bugzilla using # you are updating from a 4.1.x development version of Bugzilla using
# SQLite, and we don't want to maintain this string in strings.txt.pl # SQLite, and we don't want to maintain this string in strings.txt.pl
...@@ -279,6 +290,7 @@ sub bz_explain { } ...@@ -279,6 +290,7 @@ sub bz_explain { }
sub bz_table_list_real { sub bz_table_list_real {
my $self = shift; my $self = shift;
my @tables = $self->SUPER::bz_table_list_real(@_); my @tables = $self->SUPER::bz_table_list_real(@_);
# SQLite includes a sqlite_sequence table in every database that isn't # SQLite includes a sqlite_sequence table in every database that isn't
# one of our real tables. We exclude any table that starts with sqlite_, # one of our real tables. We exclude any table that starts with sqlite_,
# just to be safe. # just to be safe.
......
...@@ -47,12 +47,13 @@ sub _throw_error { ...@@ -47,12 +47,13 @@ sub _throw_error {
$dbh->bz_rollback_transaction() if ($dbh->bz_in_transaction() && !_in_eval()); $dbh->bz_rollback_transaction() if ($dbh->bz_in_transaction() && !_in_eval());
my $datadir = bz_locations()->{'datadir'}; my $datadir = bz_locations()->{'datadir'};
# If a writable $datadir/errorlog exists, log error details there. # If a writable $datadir/errorlog exists, log error details there.
if (-w "$datadir/errorlog") { if (-w "$datadir/errorlog") {
require Bugzilla::Util; require Bugzilla::Util;
require Data::Dumper; require Data::Dumper;
my $mesg = ""; my $mesg = "";
for (1..75) { $mesg .= "-"; }; for (1 .. 75) { $mesg .= "-"; }
$mesg .= "\n[$$] " . time2str("%D %H:%M:%S ", time()); $mesg .= "\n[$$] " . time2str("%D %H:%M:%S ", time());
$mesg .= "$name $error "; $mesg .= "$name $error ";
$mesg .= Bugzilla::Util::remote_ip(); $mesg .= Bugzilla::Util::remote_ip();
...@@ -61,18 +62,21 @@ sub _throw_error { ...@@ -61,18 +62,21 @@ sub _throw_error {
$mesg .= "\n"; $mesg .= "\n";
my %params = Bugzilla->cgi->Vars; my %params = Bugzilla->cgi->Vars;
$Data::Dumper::Useqq = 1; $Data::Dumper::Useqq = 1;
for my $param (sort keys %params) { for my $param (sort keys %params) {
my $val = $params{$param}; my $val = $params{$param};
# obscure passwords # obscure passwords
$val = "*****" if $param =~ /password/i; $val = "*****" if $param =~ /password/i;
# limit line length # limit line length
$val =~ s/^(.{512}).*$/$1\[CHOP\]/; $val =~ s/^(.{512}).*$/$1\[CHOP\]/;
$mesg .= "[$$] " . Data::Dumper->Dump([$val],["param($param)"]); $mesg .= "[$$] " . Data::Dumper->Dump([$val], ["param($param)"]);
} }
for my $var (sort keys %ENV) { for my $var (sort keys %ENV) {
my $val = $ENV{$var}; my $val = $ENV{$var};
$val = "*****" if $val =~ /password|http_pass/i; $val = "*****" if $val =~ /password|http_pass/i;
$mesg .= "[$$] " . Data::Dumper->Dump([$val],["env($var)"]); $mesg .= "[$$] " . Data::Dumper->Dump([$val], ["env($var)"]);
} }
open(ERRORLOGFID, ">>", "$datadir/errorlog"); open(ERRORLOGFID, ">>", "$datadir/errorlog");
print ERRORLOGFID "$mesg\n"; print ERRORLOGFID "$mesg\n";
...@@ -81,6 +85,7 @@ sub _throw_error { ...@@ -81,6 +85,7 @@ sub _throw_error {
my $template = Bugzilla->template; my $template = Bugzilla->template;
my $message; my $message;
# There are some tests that throw and catch a lot of errors, # There are some tests that throw and catch a lot of errors,
# and calling $template->process over and over for those errors # and calling $template->process over and over for those errors
# is too slow. So instead, we just "die" with a dump of the arguments. # is too slow. So instead, we just "die" with a dump of the arguments.
...@@ -91,8 +96,8 @@ sub _throw_error { ...@@ -91,8 +96,8 @@ sub _throw_error {
# Let's call the hook first, so that extensions can override # Let's call the hook first, so that extensions can override
# or extend the default behavior, or add their own error codes. # or extend the default behavior, or add their own error codes.
Bugzilla::Hook::process('error_catch', { error => $error, vars => $vars, Bugzilla::Hook::process('error_catch',
message => \$message }); {error => $error, vars => $vars, message => \$message});
if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) { if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) {
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
...@@ -111,9 +116,8 @@ sub _throw_error { ...@@ -111,9 +116,8 @@ sub _throw_error {
|| Bugzilla->error_mode == ERROR_MODE_REST) || Bugzilla->error_mode == ERROR_MODE_REST)
{ {
# Clone the hash so we aren't modifying the constant. # Clone the hash so we aren't modifying the constant.
my %error_map = %{ WS_ERROR_CODE() }; my %error_map = %{WS_ERROR_CODE()};
Bugzilla::Hook::process('webservice_error_codes', Bugzilla::Hook::process('webservice_error_codes', {error_map => \%error_map});
{ error_map => \%error_map });
my $code = $error_map{$error}; my $code = $error_map{$error};
if (!$code) { if (!$code) {
$code = ERROR_UNKNOWN_FATAL if $name =~ /code/i; $code = ERROR_UNKNOWN_FATAL if $name =~ /code/i;
...@@ -128,17 +132,21 @@ sub _throw_error { ...@@ -128,17 +132,21 @@ sub _throw_error {
my $status_code = 0; my $status_code = 0;
if (Bugzilla->error_mode == ERROR_MODE_REST) { if (Bugzilla->error_mode == ERROR_MODE_REST) {
my %status_code_map = %{ REST_STATUS_CODE_MAP() }; my %status_code_map = %{REST_STATUS_CODE_MAP()};
$status_code = $status_code_map{$code} || $status_code_map{'_default'}; $status_code = $status_code_map{$code} || $status_code_map{'_default'};
} }
# Technically JSON-RPC isn't allowed to have error numbers # Technically JSON-RPC isn't allowed to have error numbers
# higher than 999, but we do this to avoid conflicts with # higher than 999, but we do this to avoid conflicts with
# the internal JSON::RPC error codes. # the internal JSON::RPC error codes.
$server->raise_error(code => 100000 + $code, $server->raise_error(
code => 100000 + $code,
status_code => $status_code, status_code => $status_code,
message => $message, message => $message,
id => $server->{_bz_request_id}, id => $server->{_bz_request_id},
version => $server->version); version => $server->version
);
# Most JSON-RPC Throw*Error calls happen within an eval inside # Most JSON-RPC Throw*Error calls happen within an eval inside
# of JSON::RPC. So, in that circumstance, instead of exiting, # of JSON::RPC. So, in that circumstance, instead of exiting,
# we die with no message. JSON::RPC checks raise_error before # we die with no message. JSON::RPC checks raise_error before
...@@ -160,6 +168,7 @@ sub ThrowCodeError { ...@@ -160,6 +168,7 @@ sub ThrowCodeError {
# Don't show function arguments, in case they contain # Don't show function arguments, in case they contain
# confidential data. # confidential data.
local $Carp::MaxArgNums = -1; local $Carp::MaxArgNums = -1;
# Don't show the error as coming from Bugzilla::Error, show it # Don't show the error as coming from Bugzilla::Error, show it
# as coming from the caller. # as coming from the caller.
local $Carp::CarpInternal{'Bugzilla::Error'} = 1; local $Carp::CarpInternal{'Bugzilla::Error'} = 1;
......
...@@ -54,8 +54,7 @@ sub load { ...@@ -54,8 +54,7 @@ sub load {
my $name = require $config_file; my $name = require $config_file;
if ($name =~ /^\d+$/) { if ($name =~ /^\d+$/) {
ThrowCodeError('extension_must_return_name', ThrowCodeError('extension_must_return_name',
{ extension => $config_file, {extension => $config_file, returned => $name});
returned => $name });
} }
$package = "${class}::$name"; $package = "${class}::$name";
} }
...@@ -71,7 +70,7 @@ sub load { ...@@ -71,7 +70,7 @@ sub load {
my $name = require $extension_file; my $name = require $extension_file;
if ($name =~ /^\d+$/) { if ($name =~ /^\d+$/) {
ThrowCodeError('extension_must_return_name', ThrowCodeError('extension_must_return_name',
{ extension => $extension_file, returned => $name }); {extension => $extension_file, returned => $name});
} }
$package = "${class}::$name"; $package = "${class}::$name";
$package->modify_inc($extension_file) if !$config_file; $package->modify_inc($extension_file) if !$config_file;
...@@ -94,14 +93,12 @@ sub _validate_package { ...@@ -94,14 +93,12 @@ sub _validate_package {
if (!eval { $package->NAME }) { if (!eval { $package->NAME }) {
ThrowCodeError('extension_no_name', ThrowCodeError('extension_no_name',
{ filename => $extension_file, package => $package }); {filename => $extension_file, package => $package});
} }
if (!$package->isa($class)) { if (!$package->isa($class)) {
ThrowCodeError('extension_must_be_subclass', ThrowCodeError('extension_must_be_subclass',
{ filename => $extension_file, {filename => $extension_file, package => $package, class => $class});
package => $package,
class => $class });
} }
} }
...@@ -116,9 +113,11 @@ sub load_all { ...@@ -116,9 +113,11 @@ sub load_all {
# Extensions from data/extensions/additional # Extensions from data/extensions/additional
foreach my $package (@$extra_packages) { foreach my $package (@$extra_packages) {
# Don't load an "additional" extension if we already have an extension # Don't load an "additional" extension if we already have an extension
# loaded with that name. # loaded with that name.
next if grep($_ eq $package, @packages); next if grep($_ eq $package, @packages);
# Untaint the package name # Untaint the package name
$package =~ /([\w:]+)/; $package =~ /([\w:]+)/;
$package = $1; $package = $1;
...@@ -139,6 +138,7 @@ sub modify_inc { ...@@ -139,6 +138,7 @@ sub modify_inc {
# Note that this package_dir call is necessary to set things up # Note that this package_dir call is necessary to set things up
# for my_inc, even if we didn't take its return value. # for my_inc, even if we didn't take its return value.
my $package_dir = __do_call($class, 'package_dir', $file); my $package_dir = __do_call($class, 'package_dir', $file);
# Don't modify @INC for extensions that are just files in the extensions/ # Don't modify @INC for extensions that are just files in the extensions/
# directory. We don't want Bugzilla's base lib/CGI.pm being loaded as # directory. We don't want Bugzilla's base lib/CGI.pm being loaded as
# Bugzilla::Extension::Foo::CGI or any other confusing thing like that. # Bugzilla::Extension::Foo::CGI or any other confusing thing like that.
...@@ -159,9 +159,11 @@ sub my_inc { ...@@ -159,9 +159,11 @@ sub my_inc {
my @class_parts = split('::', $class); my @class_parts = split('::', $class);
my ($vol, $dir, $file_name) = File::Spec->splitpath($file); my ($vol, $dir, $file_name) = File::Spec->splitpath($file);
my @dir_parts = File::Spec->splitdir($dir); my @dir_parts = File::Spec->splitdir($dir);
# File::Spec::Win32 (any maybe other OSes) add an empty directory at the # File::Spec::Win32 (any maybe other OSes) add an empty directory at the
# end of @dir_parts. # end of @dir_parts.
@dir_parts = grep { $_ ne '' } @dir_parts; @dir_parts = grep { $_ ne '' } @dir_parts;
# Validate that this is a sub-package of Bugzilla::Extension::Foo ($class). # Validate that this is a sub-package of Bugzilla::Extension::Foo ($class).
for (my $i = 0; $i < scalar(@class_parts); $i++) { for (my $i = 0; $i < scalar(@class_parts); $i++) {
return if !@dir_parts; return if !@dir_parts;
...@@ -173,6 +175,7 @@ sub my_inc { ...@@ -173,6 +175,7 @@ sub my_inc {
} }
shift(@dir_parts); shift(@dir_parts);
} }
# For Bugzilla::Extension::Foo::Bar, this would look something like # For Bugzilla::Extension::Foo::Bar, this would look something like
# extensions/Example/lib/Bar.pm # extensions/Example/lib/Bar.pm
my $resolved_path = File::Spec->catfile($lib_dir, @dir_parts, $file_name); my $resolved_path = File::Spec->catfile($lib_dir, @dir_parts, $file_name);
...@@ -189,6 +192,7 @@ use constant enabled => 1; ...@@ -189,6 +192,7 @@ use constant enabled => 1;
sub lib_dir { sub lib_dir {
my $invocant = shift; my $invocant = shift;
my $package_dir = __do_call($invocant, 'package_dir'); my $package_dir = __do_call($invocant, 'package_dir');
# For extensions that are just files in the extensions/ directory, # For extensions that are just files in the extensions/ directory,
# use the base lib/ dir as our "lib_dir". Note that Bugzilla never # use the base lib/ dir as our "lib_dir". Note that Bugzilla never
# uses lib_dir in this case, though, because modify_inc is prevented # uses lib_dir in this case, though, because modify_inc is prevented
......
...@@ -81,6 +81,7 @@ sub type { ...@@ -81,6 +81,7 @@ sub type {
my $field_name = $field_obj->name; my $field_name = $field_obj->name;
if (my $package = $class->CLASS_MAP->{$field_name}) { if (my $package = $class->CLASS_MAP->{$field_name}) {
# Callers expect the module to be already loaded. # Callers expect the module to be already loaded.
eval "require $package"; eval "require $package";
return $package; return $package;
...@@ -133,7 +134,7 @@ sub create { ...@@ -133,7 +134,7 @@ sub create {
my $class = shift; my $class = shift;
my ($params) = @_; my ($params) = @_;
foreach my $key (keys %$params) { foreach my $key (keys %$params) {
if (!grep {$_ eq $key} $class->_get_db_columns) { if (!grep { $_ eq $key } $class->_get_db_columns) {
delete $params->{$key}; delete $params->{$key};
} }
} }
...@@ -149,14 +150,12 @@ sub update { ...@@ -149,14 +150,12 @@ sub update {
my ($changes, $old_self) = $self->SUPER::update(@_); my ($changes, $old_self) = $self->SUPER::update(@_);
if (exists $changes->{value}) { if (exists $changes->{value}) {
my ($old, $new) = @{ $changes->{value} }; my ($old, $new) = @{$changes->{value}};
if ($self->field->type == FIELD_TYPE_MULTI_SELECT) { if ($self->field->type == FIELD_TYPE_MULTI_SELECT) {
$dbh->do("UPDATE bug_$fname SET value = ? WHERE value = ?", $dbh->do("UPDATE bug_$fname SET value = ? WHERE value = ?", undef, $new, $old);
undef, $new, $old);
} }
else { else {
$dbh->do("UPDATE bugs SET $fname = ? WHERE $fname = ?", $dbh->do("UPDATE bugs SET $fname = ? WHERE $fname = ?", undef, $new, $old);
undef, $new, $old);
} }
if ($old_self->is_default) { if ($old_self->is_default) {
...@@ -173,18 +172,22 @@ sub update { ...@@ -173,18 +172,22 @@ sub update {
sub remove_from_db { sub remove_from_db {
my $self = shift; my $self = shift;
if ($self->is_default) { if ($self->is_default) {
ThrowUserError('fieldvalue_is_default', ThrowUserError(
{ field => $self->field, value => $self, 'fieldvalue_is_default',
{
field => $self->field,
value => $self,
param_name => $self->DEFAULT_MAP->{$self->field->name}, param_name => $self->DEFAULT_MAP->{$self->field->name},
}); }
);
} }
if ($self->is_static) { if ($self->is_static) {
ThrowUserError('fieldvalue_not_deletable', ThrowUserError('fieldvalue_not_deletable',
{ field => $self->field, value => $self }); {field => $self->field, value => $self});
} }
if ($self->bug_count) { if ($self->bug_count) {
ThrowUserError("fieldvalue_still_has_bugs", ThrowUserError("fieldvalue_still_has_bugs",
{ field => $self->field, value => $self }); {field => $self->field, value => $self});
} }
$self->_check_if_controller(); # From ChoiceInterface. $self->_check_if_controller(); # From ChoiceInterface.
$self->SUPER::remove_from_db(); $self->SUPER::remove_from_db();
...@@ -197,6 +200,7 @@ sub remove_from_db { ...@@ -197,6 +200,7 @@ sub remove_from_db {
sub set_is_active { $_[0]->set('isactive', $_[1]); } sub set_is_active { $_[0]->set('isactive', $_[1]); }
sub set_name { $_[0]->set('value', $_[1]); } sub set_name { $_[0]->set('value', $_[1]); }
sub set_sortkey { $_[0]->set('sortkey', $_[1]); } sub set_sortkey { $_[0]->set('sortkey', $_[1]); }
sub set_visibility_value { sub set_visibility_value {
my ($self, $value) = @_; my ($self, $value) = @_;
$self->set('visibility_value_id', $value); $self->set('visibility_value_id', $value);
...@@ -213,14 +217,18 @@ sub _check_isactive { ...@@ -213,14 +217,18 @@ sub _check_isactive {
if (!$value and ref $invocant) { if (!$value and ref $invocant) {
if ($invocant->is_default) { if ($invocant->is_default) {
my $field = $invocant->field; my $field = $invocant->field;
ThrowUserError('fieldvalue_is_default', ThrowUserError(
{ value => $invocant, field => $field, 'fieldvalue_is_default',
{
value => $invocant,
field => $field,
param_name => $invocant->DEFAULT_MAP->{$field->name} param_name => $invocant->DEFAULT_MAP->{$field->name}
}); }
);
} }
if ($invocant->is_static) { if ($invocant->is_static) {
ThrowUserError('fieldvalue_not_deletable', ThrowUserError('fieldvalue_not_deletable',
{ value => $invocant, field => $invocant->field }); {value => $invocant, field => $invocant->field});
} }
} }
return $value; return $value;
...@@ -234,21 +242,19 @@ sub _check_value { ...@@ -234,21 +242,19 @@ sub _check_value {
$value = trim($value); $value = trim($value);
# Make sure people don't rename static values # Make sure people don't rename static values
if (blessed($invocant) && $value ne $invocant->name if (blessed($invocant) && $value ne $invocant->name && $invocant->is_static) {
&& $invocant->is_static)
{
ThrowUserError('fieldvalue_not_editable', ThrowUserError('fieldvalue_not_editable',
{ field => $field, old_value => $invocant }); {field => $field, old_value => $invocant});
} }
ThrowUserError('fieldvalue_undefined') if !defined $value || $value eq ""; ThrowUserError('fieldvalue_undefined') if !defined $value || $value eq "";
ThrowUserError('fieldvalue_name_too_long', { value => $value }) ThrowUserError('fieldvalue_name_too_long', {value => $value})
if length($value) > MAX_FIELD_VALUE_SIZE; if length($value) > MAX_FIELD_VALUE_SIZE;
my $exists = $invocant->type($field)->new({ name => $value }); my $exists = $invocant->type($field)->new({name => $value});
if ($exists && (!blessed($invocant) || $invocant->id != $exists->id)) { if ($exists && (!blessed($invocant) || $invocant->id != $exists->id)) {
ThrowUserError('fieldvalue_already_exists', ThrowUserError('fieldvalue_already_exists',
{ field => $field, value => $exists }); {field => $field, value => $exists});
} }
return $value; return $value;
...@@ -258,12 +264,12 @@ sub _check_sortkey { ...@@ -258,12 +264,12 @@ sub _check_sortkey {
my ($invocant, $value) = @_; my ($invocant, $value) = @_;
$value = trim($value); $value = trim($value);
return 0 if !$value; return 0 if !$value;
# Store for the error message in case detaint_natural clears it. # Store for the error message in case detaint_natural clears it.
my $orig_value = $value; my $orig_value = $value;
(detaint_natural($value) && $value <= MAX_SMALLINT) (detaint_natural($value) && $value <= MAX_SMALLINT)
|| ThrowUserError('fieldvalue_sortkey_invalid', || ThrowUserError('fieldvalue_sortkey_invalid',
{ sortkey => $orig_value, {sortkey => $orig_value, field => $invocant->field});
field => $invocant->field });
return $value; return $value;
} }
...@@ -272,8 +278,7 @@ sub _check_visibility_value_id { ...@@ -272,8 +278,7 @@ sub _check_visibility_value_id {
$value_id = trim($value_id); $value_id = trim($value_id);
my $field = $invocant->field->value_field; my $field = $invocant->field->value_field;
return undef if !$field || !$value_id; return undef if !$field || !$value_id;
my $value_obj = Bugzilla::Field::Choice->type($field) my $value_obj = Bugzilla::Field::Choice->type($field)->check({id => $value_id});
->check({ id => $value_id });
return $value_obj->id; return $value_obj->id;
} }
......
...@@ -30,9 +30,14 @@ sub _check_if_controller { ...@@ -30,9 +30,14 @@ sub _check_if_controller {
my $vis_fields = $self->controls_visibility_of_fields; my $vis_fields = $self->controls_visibility_of_fields;
my $values = $self->controlled_values_array; my $values = $self->controlled_values_array;
if (@$vis_fields || @$values) { if (@$vis_fields || @$values) {
ThrowUserError('fieldvalue_is_controller', ThrowUserError(
{ value => $self, fields => [map($_->name, @$vis_fields)], 'fieldvalue_is_controller',
vals => $self->controlled_values }); {
value => $self,
fields => [map($_->name, @$vis_fields)],
vals => $self->controlled_values
}
);
} }
} }
...@@ -51,13 +56,16 @@ sub bug_count { ...@@ -51,13 +56,16 @@ sub bug_count {
my $fname = $self->field->name; my $fname = $self->field->name;
my $count; my $count;
if ($self->field->type == FIELD_TYPE_MULTI_SELECT) { if ($self->field->type == FIELD_TYPE_MULTI_SELECT) {
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bug_$fname $count = $dbh->selectrow_array(
WHERE value = ?", undef, $self->name); "SELECT COUNT(*) FROM bug_$fname
WHERE value = ?", undef, $self->name
);
} }
else { else {
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM bugs $count = $dbh->selectrow_array(
WHERE $fname = ?", "SELECT COUNT(*) FROM bugs
undef, $self->name); WHERE $fname = ?", undef, $self->name
);
} }
$self->{bug_count} = $count; $self->{bug_count} = $count;
return $count; return $count;
...@@ -67,16 +75,17 @@ sub field { ...@@ -67,16 +75,17 @@ sub field {
my $invocant = shift; my $invocant = shift;
my $class = ref $invocant || $invocant; my $class = ref $invocant || $invocant;
my $cache = Bugzilla->request_cache; my $cache = Bugzilla->request_cache;
# This is just to make life easier for subclasses. Our auto-generated # This is just to make life easier for subclasses. Our auto-generated
# subclasses from Bugzilla::Field::Choice->type() already have this set. # subclasses from Bugzilla::Field::Choice->type() already have this set.
$cache->{"field_$class"} ||= $cache->{"field_$class"} ||= new Bugzilla::Field({name => $class->FIELD_NAME});
new Bugzilla::Field({ name => $class->FIELD_NAME });
return $cache->{"field_$class"}; return $cache->{"field_$class"};
} }
sub is_default { sub is_default {
my $self = shift; my $self = shift;
my $name = $self->DEFAULT_MAP->{$self->field->name}; my $name = $self->DEFAULT_MAP->{$self->field->name};
# If it doesn't exist in DEFAULT_MAP, then there is no parameter # If it doesn't exist in DEFAULT_MAP, then there is no parameter
# related to this field. # related to this field.
return 0 unless $name; return 0 unless $name;
...@@ -85,11 +94,11 @@ sub is_default { ...@@ -85,11 +94,11 @@ sub is_default {
sub is_static { sub is_static {
my $self = shift; my $self = shift;
# If we need to special-case Resolution for *anything* else, it should # If we need to special-case Resolution for *anything* else, it should
# get its own subclass. # get its own subclass.
if ($self->field->name eq 'resolution') { if ($self->field->name eq 'resolution') {
return grep($_ eq $self->name, ('', 'FIXED', 'DUPLICATE')) return grep($_ eq $self->name, ('', 'FIXED', 'DUPLICATE')) ? 1 : 0;
? 1 : 0;
} }
elsif ($self->field->custom) { elsif ($self->field->custom) {
return $self->name eq '---' ? 1 : 0; return $self->name eq '---' ? 1 : 0;
...@@ -106,11 +115,11 @@ sub controls_visibility_of_fields { ...@@ -106,11 +115,11 @@ sub controls_visibility_of_fields {
"SELECT id FROM fielddefs "SELECT id FROM fielddefs
INNER JOIN field_visibility INNER JOIN field_visibility
ON fielddefs.id = field_visibility.field_id ON fielddefs.id = field_visibility.field_id
WHERE value_id = ? AND visibility_field_id = ?", undef, WHERE value_id = ? AND visibility_field_id = ?", undef, $self->id,
$self->id, $self->field->id); $self->field->id
);
$self->{controls_visibility_of_fields} = $self->{controls_visibility_of_fields} = Bugzilla::Field->new_from_list($ids);
Bugzilla::Field->new_from_list($ids);
} }
return $self->{controls_visibility_of_fields}; return $self->{controls_visibility_of_fields};
...@@ -120,9 +129,9 @@ sub visibility_value { ...@@ -120,9 +129,9 @@ sub visibility_value {
my $self = shift; my $self = shift;
if ($self->{visibility_value_id}) { if ($self->{visibility_value_id}) {
require Bugzilla::Field::Choice; require Bugzilla::Field::Choice;
$self->{visibility_value} ||= $self->{visibility_value}
Bugzilla::Field::Choice->type($self->field->value_field)->new( ||= Bugzilla::Field::Choice->type($self->field->value_field)
$self->{visibility_value_id}); ->new($self->{visibility_value_id});
} }
return $self->{visibility_value}; return $self->{visibility_value};
} }
...@@ -134,9 +143,8 @@ sub controlled_values { ...@@ -134,9 +143,8 @@ sub controlled_values {
my %controlled_values; my %controlled_values;
require Bugzilla::Field::Choice; require Bugzilla::Field::Choice;
foreach my $field (@$fields) { foreach my $field (@$fields) {
$controlled_values{$field->name} = $controlled_values{$field->name} = Bugzilla::Field::Choice->type($field)
Bugzilla::Field::Choice->type($field) ->match({visibility_value_id => $self->id});
->match({ visibility_value_id => $self->id });
} }
$self->{controlled_values} = \%controlled_values; $self->{controlled_values} = \%controlled_values;
return $self->{controlled_values}; return $self->{controlled_values};
...@@ -145,7 +153,7 @@ sub controlled_values { ...@@ -145,7 +153,7 @@ sub controlled_values {
sub controlled_values_array { sub controlled_values_array {
my ($self) = @_; my ($self) = @_;
my $values = $self->controlled_values; my $values = $self->controlled_values;
return [map { @{ $values->{$_} } } keys %$values]; return [map { @{$values->{$_}} } keys %$values];
} }
sub is_visible_on_bug { sub is_visible_on_bug {
...@@ -169,13 +177,14 @@ sub is_visible_on_bug { ...@@ -169,13 +177,14 @@ sub is_visible_on_bug {
sub is_set_on_bug { sub is_set_on_bug {
my ($self, $bug) = @_; my ($self, $bug) = @_;
my $field_name = $self->FIELD_NAME; my $field_name = $self->FIELD_NAME;
# This allows bug/create/create.html.tmpl to pass in a hashref that # This allows bug/create/create.html.tmpl to pass in a hashref that
# looks like a bug object. # looks like a bug object.
my $value = blessed($bug) ? $bug->$field_name : $bug->{$field_name}; my $value = blessed($bug) ? $bug->$field_name : $bug->{$field_name};
$value = $value->name if blessed($value); $value = $value->name if blessed($value);
return 0 if !defined $value; return 0 if !defined $value;
if ($self->field->type == FIELD_TYPE_BUG_URLS if ( $self->field->type == FIELD_TYPE_BUG_URLS
or $self->field->type == FIELD_TYPE_MULTI_SELECT) or $self->field->type == FIELD_TYPE_MULTI_SELECT)
{ {
return grep($_ eq $self->name, @$value) ? 1 : 0; return grep($_ eq $self->name, @$value) ? 1 : 0;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment