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