Commit e43a00ac authored by Per Cederqvist's avatar Per Cederqvist
Browse files

Imported Bugzilla 2.23.4.

parent f36ab7b3
......@@ -53,6 +53,7 @@ our $_request_cache = {};
use constant SHUTDOWNHTML_EXEMPT => [
'editparams.cgi',
'checksetup.pl',
'recode.pl',
];
# Non-cgi scripts that should silently exit.
......@@ -81,6 +82,9 @@ sub init_page {
# Some environment variables are not taint safe
delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Some modules throw undefined errors (notably File::Spec::Win32) if
# PATH is undefined.
$ENV{'PATH'} = '';
# If Bugzilla is shut down, do not allow anything to run, just display a
# message to the user about the downtime and log out. Scripts listed in
......@@ -142,14 +146,17 @@ init_page() if !$ENV{MOD_PERL};
sub template {
my $class = shift;
request_cache()->{language} = "";
request_cache()->{template} ||= Bugzilla::Template->create();
return request_cache()->{template};
}
sub template_inner {
my $class = shift;
$class->request_cache->{template_inner} ||= Bugzilla::Template->create();
return $class->request_cache->{template_inner};
my ($class, $lang) = @_;
$lang = defined($lang) ? $lang : (request_cache()->{language} || "");
request_cache()->{language} = $lang;
request_cache()->{"template_inner_$lang"} ||= Bugzilla::Template->create();
return request_cache()->{"template_inner_$lang"};
}
sub cgi {
......@@ -175,6 +182,11 @@ sub user {
return request_cache()->{user};
}
sub set_user {
my ($class, $user) = @_;
$class->request_cache->{user} = $user;
}
sub sudoer {
my $class = shift;
return request_cache()->{sudoer};
......@@ -196,6 +208,8 @@ sub sudo_request {
sub login {
my ($class, $type) = @_;
return Bugzilla->user if Bugzilla->usage_mode == USAGE_MODE_EMAIL;
my $authorizer = new Bugzilla::Auth();
$type = LOGIN_REQUIRED if Bugzilla->cgi->param('GoAheadAndLogIn');
if (!defined $type || $type == LOGIN_NORMAL) {
......@@ -222,7 +236,7 @@ sub login {
!($sudo_target->in_group('bz_sudo_protect'))
)
{
request_cache()->{user} = $sudo_target;
Bugzilla->set_user($sudo_target);
request_cache()->{sudoer} = $authenticated_user;
# And make sure that both users have the same Auth object,
# since we never call Auth::login for the sudo target.
......@@ -231,10 +245,10 @@ sub login {
# NOTE: If you want to do any special logging, do it here.
}
else {
request_cache()->{user} = $authenticated_user;
Bugzilla->set_user($authenticated_user);
}
return request_cache()->{user};
return Bugzilla->user;
}
sub logout {
......@@ -303,6 +317,9 @@ sub usage_mode {
elsif ($newval == USAGE_MODE_WEBSERVICE) {
$class->error_mode(ERROR_MODE_DIE_SOAP_FAULT);
}
elsif ($newval == USAGE_MODE_EMAIL) {
$class->error_mode(ERROR_MODE_DIE);
}
else {
ThrowCodeError('usage_mode_invalid',
{'invalid_usage_mode', $newval});
......@@ -313,6 +330,28 @@ sub usage_mode {
|| Bugzilla::Constants::USAGE_MODE_BROWSER;
}
sub installation_mode {
my ($class, $newval) = @_;
($class->request_cache->{installation_mode} = $newval) if defined $newval;
return $class->request_cache->{installation_mode}
|| INSTALLATION_MODE_INTERACTIVE;
}
sub installation_answers {
my ($class, $filename) = @_;
if ($filename) {
my $s = new Safe;
$s->rdo($filename);
die "Error reading $filename: $!" if $!;
die "Error evaluating $filename: $@" if $@;
# Now read the param back out from the sandbox
$class->request_cache->{installation_answers} = $s->varglob('answer');
}
return $class->request_cache->{installation_answers} || {};
}
sub switch_to_shadow_db {
my $class = shift;
......@@ -461,7 +500,10 @@ The current C<Template> object, to be used for output
=item C<template_inner>
If you ever need a L<Bugzilla::Template> object while you're already
processing a template, use this.
processing a template, use this. Also use it if you want to specify
the language to use. If no argument is passed, it uses the last
language set. If the argument is "" (empty string), the language is
reset to the current one (the one used by Bugzilla->template).
=item C<cgi>
......@@ -476,6 +518,12 @@ yet been run. If an sudo session is in progress, the C<Bugzilla::User>
corresponding to the person who is being impersonated. If no session is in
progress, the current C<Bugzilla::User>.
=item C<set_user>
Allows you to directly set what L</user> will return. You can use this
if you want to bypass L</login> for some reason and directly "log in"
a specific L<Bugzilla::User>. Be careful with it, though!
=item C<sudoer>
C<undef> if there is no currently logged in user, the currently logged in user
......@@ -550,6 +598,16 @@ usage mode.
C<Bugzilla->usage_mode> will return the current state of this flag.
=item C<installation_mode>
Determines whether or not installation should be silent. See
L<Bugzilla::Constants> for the C<INSTALLATION_MODE> constants.
=item C<installation_answers>
Returns a hashref representing any "answers" file passed to F<checksetup.pl>,
used to automatically answer or skip prompts.
=item C<dbh>
The current database handle. See L<DBI>.
......
......@@ -609,19 +609,22 @@ sub validate_content_type {
=pod
=item C<validate_can_edit()>
=item C<validate_can_edit($attachment, $product_id)>
Description: validates if the user is allowed to view and edit the attachment.
Only the submitter or someone with editbugs privs can edit it.
Only the submitter and users in the insider group can view
private attachments.
Params: $attachment - the attachment object being edited.
$product_id - the product ID the attachment belongs to.
Returns: 1 on success. Else an error is thrown.
=cut
sub validate_can_edit {
my $attachment = shift;
my ($attachment, $product_id) = @_;
my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
......@@ -634,27 +637,27 @@ sub validate_can_edit {
}
# Users with editbugs privs can edit all attachments.
return if $user->in_group('editbugs');
return if $user->in_group('editbugs', $product_id);
# If we come here, then this attachment cannot be seen by the user.
ThrowUserError('illegal_attachment_edit', { attach_id => $attachment->id });
}
=item C<validate_obsolete($bug_id)>
=item C<validate_obsolete($bug)>
Description: validates if attachments the user wants to mark as obsolete
really belong to the given bug and are not already obsolete.
Moreover, a user cannot mark an attachment as obsolete if
he cannot view it (due to restrictions on it).
Params: $bug_id - The bug ID obsolete attachments should belong to.
Params: $bug - The bug object obsolete attachments should belong to.
Returns: 1 on success. Else an error is thrown.
=cut
sub validate_obsolete {
my ($class, $bug_id) = @_;
my ($class, $bug) = @_;
my $cgi = Bugzilla->cgi;
# Make sure the attachment id is valid and the user has permissions to view
......@@ -674,12 +677,12 @@ sub validate_obsolete {
ThrowUserError('invalid_attach_id', $vars) unless $attachment;
# Check that the user can view and edit this attachment.
$attachment->validate_can_edit;
$attachment->validate_can_edit($bug->product_id);
$vars->{'description'} = $attachment->description;
if ($attachment->bug_id != $bug_id) {
$vars->{'my_bug_id'} = $bug_id;
if ($attachment->bug_id != $bug->bug_id) {
$vars->{'my_bug_id'} = $bug->bug_id;
$vars->{'attach_bug_id'} = $attachment->bug_id;
ThrowCodeError('mismatched_bug_ids_on_obsolete', $vars);
}
......@@ -746,7 +749,10 @@ sub insert_attachment_for_bug {
unless ($cgi->param('ispatch')) {
$class->validate_content_type($throw_error) || return 0;
}
$data = _validate_data($hr_vars, $throw_error) || return 0;
$data = _validate_data($throw_error, $hr_vars);
# If the attachment is stored locally, $data eq ''.
# If an error is thrown, $data eq '0'.
($data ne '0') || return 0;
$contenttype = $cgi->param('contenttype');
# These are inserted using placeholders so no need to panic
......@@ -758,7 +764,7 @@ sub insert_attachment_for_bug {
# Check attachments the user tries to mark as obsolete.
my @obsolete_attachments;
if ($cgi->param('obsolete')) {
@obsolete_attachments = $class->validate_obsolete($bug->bug_id);
@obsolete_attachments = $class->validate_obsolete($bug);
}
# The order of these function calls is important, as Flag::validate
......@@ -776,12 +782,6 @@ sub insert_attachment_for_bug {
$$hr_vars->{'message'} = 'user_match_multiple';
}
# Flag::validate() should not detect any reference to existing flags
# when creating a new attachment. Setting the third param to -1 will
# force this function to check this point.
# XXX needs $throw_error treatment
Bugzilla::Flag::validate($cgi, $bug->bug_id, -1);
# Escape characters in strings that will be used in SQL statements.
my $description = $cgi->param('description');
trick_taint($description);
......@@ -851,9 +851,28 @@ sub insert_attachment_for_bug {
$timestamp, $fieldid, 0, 1));
}
# Create flags.
my $attachment = Bugzilla::Attachment->get($attachid);
Bugzilla::Flag::process($bug, $attachment, $timestamp, $cgi);
# 1. Add flags, if any. To avoid dying if something goes wrong
# while processing flags, we will eval() flag validation.
# This requires errors to die().
# XXX: this can go away as soon as flag validation is able to
# fail without dying.
#
# 2. Flag::validate() should not detect any reference to existing flags
# when creating a new attachment. Setting the third param to -1 will
# force this function to check this point.
my $error_mode_cache = Bugzilla->error_mode;
Bugzilla->error_mode(ERROR_MODE_DIE);
eval {
Bugzilla::Flag::validate($cgi, $bug->bug_id, -1);
Bugzilla::Flag::process($bug, $attachment, $timestamp, $cgi);
};
Bugzilla->error_mode($error_mode_cache);
if ($@) {
$$hr_vars->{'message'} = 'flag_creation_failed';
$$hr_vars->{'flag_creation_error'} = $@;
}
# Return the ID of the new attachment.
return $attachid;
......
/PatchReader.pm/1.3/Sat Oct 14 21:04:55 2006//TBUGZILLA-2_23_3
/PatchReader.pm/1.4/Sat Dec 30 01:58:28 2006//TBUGZILLA-2_23_4
D
NBUGZILLA-2_23_3
NBUGZILLA-2_23_4
......@@ -222,6 +222,9 @@ sub setup_patch_readers {
&& Bugzilla->params->{'cvsroot_get'})
{
require PatchReader::AddCVSContext;
# We need to set $cvsbin as global, because PatchReader::CVSClient
# needs it in order to find 'cvs'.
$main::cvsbin = Bugzilla->localconfig->{cvsbin};
$last_reader->sends_data_to(
new PatchReader::AddCVSContext($context, Bugzilla->params->{'cvsroot_get'}));
$last_reader = $last_reader->sends_data_to;
......
/Login.pm/1.1/Fri May 12 02:41:05 2006//TBUGZILLA-2_23_3
/Verify.pm/1.5/Fri Aug 25 22:10:38 2006//TBUGZILLA-2_23_3
/Login.pm/1.1/Fri May 12 02:41:05 2006//TBUGZILLA-2_23_4
/Verify.pm/1.6/Fri Oct 20 18:52:24 2006//TBUGZILLA-2_23_4
D/Login////
D/Persist////
D/Verify////
NBUGZILLA-2_23_3
NBUGZILLA-2_23_4
/CGI.pm/1.7/Sat Aug 19 17:20:23 2006//TBUGZILLA-2_23_3
/Cookie.pm/1.5/Wed Jul 5 23:42:47 2006//TBUGZILLA-2_23_3
/Env.pm/1.4/Mon Jul 3 21:42:46 2006//TBUGZILLA-2_23_3
/Stack.pm/1.1/Fri May 12 02:41:06 2006//TBUGZILLA-2_23_3
/CGI.pm/1.7/Sat Aug 19 17:20:23 2006//TBUGZILLA-2_23_4
/Cookie.pm/1.5/Wed Jul 5 23:42:47 2006//TBUGZILLA-2_23_4
/Env.pm/1.4/Mon Jul 3 21:42:46 2006//TBUGZILLA-2_23_4
/Stack.pm/1.1/Fri May 12 02:41:06 2006//TBUGZILLA-2_23_4
D
A D/CGI////
A D/WWW////
R D/WWW////
R D/CGI////
NBUGZILLA-2_23_3
NBUGZILLA-2_23_4
/Cookie.pm/1.5/Mon Jul 3 21:42:46 2006//TBUGZILLA-2_23_3
/Cookie.pm/1.5/Mon Jul 3 21:42:46 2006//TBUGZILLA-2_23_4
D
......@@ -77,6 +77,11 @@ sub create_or_update_user {
|| return { failure => AUTH_ERROR,
error => 'auth_invalid_email',
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.
trick_taint($password);
# 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({
......@@ -111,9 +116,6 @@ sub create_or_update_user {
validate_email_syntax($username)
|| return { failure => AUTH_ERROR, error => 'auth_invalid_email',
details => {addr => $username} };
# Username is more than likely tainted, but we only use it in a
# placeholder, and we've already validated it, so it's safe.
trick_taint($username);
$dbh->do('UPDATE profiles SET login_name = ? WHERE userid = ?',
undef, $username, $user->id);
}
......
/DB.pm/1.7/Fri May 12 02:41:14 2006//TBUGZILLA-2_23_3
/LDAP.pm/1.14/Tue Jul 11 00:42:57 2006//TBUGZILLA-2_23_3
/Stack.pm/1.1/Fri May 12 02:41:14 2006//TBUGZILLA-2_23_3
/DB.pm/1.7/Fri May 12 02:41:14 2006//TBUGZILLA-2_23_4
/LDAP.pm/1.14/Tue Jul 11 00:42:57 2006//TBUGZILLA-2_23_4
/Stack.pm/1.1/Fri May 12 02:41:14 2006//TBUGZILLA-2_23_4
D
NBUGZILLA-2_23_3
NBUGZILLA-2_23_4
......@@ -40,6 +40,8 @@ use Bugzilla::User;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Product;
use Bugzilla::Component;
use Bugzilla::Group;
use List::Util qw(min);
......@@ -98,8 +100,8 @@ sub DB_COLUMNS {
use constant REQUIRED_CREATE_FIELDS => qw(
bug_severity
comment
component
creation_ts
op_sys
priority
product
......@@ -119,7 +121,6 @@ sub VALIDATORS {
commentprivacy => \&_check_commentprivacy,
deadline => \&_check_deadline,
estimated_time => \&_check_estimated_time,
keywords => \&_check_keywords,
op_sys => \&_check_op_sys,
priority => \&_check_priority,
product => \&_check_product,
......@@ -283,6 +284,8 @@ sub create {
$dbh->do('UPDATE bugs SET creation_ts = ? WHERE bug_id = ?', undef,
$timestamp, $bug->bug_id);
# Update the bug instance as well
$bug->{creation_ts} = $timestamp;
# Add the CCs
my $sth_cc = $dbh->prepare('INSERT INTO cc (bug_id, who) VALUES (?,?)');
......@@ -300,24 +303,37 @@ sub create {
# Set up dependencies (blocked/dependson)
my $sth_deps = $dbh->prepare(
'INSERT INTO dependencies (blocked, dependson) VALUES (?, ?)');
my $sth_bug_time = $dbh->prepare('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?');
foreach my $depends_on_id (@$depends_on) {
$sth_deps->execute($bug->bug_id, $depends_on_id);
# Log the reverse action on the other bug.
LogActivityEntry($depends_on_id, 'blocked', '', $bug->bug_id,
$bug->{reporter_id}, $timestamp);
$sth_bug_time->execute($timestamp, $depends_on_id);
}
foreach my $blocked_id (@$blocked) {
$sth_deps->execute($blocked_id, $bug->bug_id);
# Log the reverse action on the other bug.
LogActivityEntry($blocked_id, 'dependson', '', $bug->bug_id,
$bug->{reporter_id}, $timestamp);
$sth_bug_time->execute($timestamp, $blocked_id);
}
# And insert the comment. We always insert a comment on bug creation,
# but sometimes it's blank.
$dbh->do('INSERT INTO longdescs (bug_id, who, bug_when, thetext, isprivate)
VALUES (?, ?, ?, ?, ?)', undef,
$bug->bug_id, $bug->{reporter_id}, $timestamp, $comment, $privacy);
my @columns = qw(bug_id who bug_when thetext);
my @values = ($bug->bug_id, $bug->{reporter_id}, $timestamp, $comment);
# We don't include the "isprivate" column unless it was specified.
# This allows it to fall back to its database default.
if (defined $privacy) {
push(@columns, 'isprivate');
push(@values, $privacy);
}
my $qmarks = "?," x @columns;
chop($qmarks);
$dbh->do('INSERT INTO longdescs (' . join(',', @columns) . ")
VALUES ($qmarks)", undef, @values);
$dbh->bz_unlock_tables();
......@@ -341,6 +357,8 @@ sub run_create_validators {
$params->{version} = $class->_check_version($product, $params->{version});
$params->{keywords} = $class->_check_keywords($product, $params->{keywords});
$params->{groups} = $class->_check_groups($product,
$params->{groups});
......@@ -357,14 +375,24 @@ sub run_create_validators {
# Callers cannot set Reporter, currently.
$params->{reporter} = Bugzilla->user->id;
$params->{creation_ts} ||= Bugzilla->dbh->selectrow_array('SELECT NOW()');
$params->{delta_ts} = $params->{creation_ts};
$params->{remaining_time} = $params->{estimated_time};
if ($params->{estimated_time}) {
$params->{remaining_time} = $params->{estimated_time};
}
$class->_check_strict_isolation($product, $params->{cc},
$params->{assigned_to}, $params->{qa_contact});
($params->{dependson}, $params->{blocked}) =
$class->_check_dependencies($params->{dependson}, $params->{blocked});
$class->_check_dependencies($product, $params->{dependson}, $params->{blocked});
# You can't set these fields on bug creation (or sometimes ever).
delete $params->{resolution};
delete $params->{votes};
delete $params->{lastdiffed};
delete $params->{bug_id};
return $params;
}
......@@ -457,7 +485,7 @@ sub _check_assigned_to {
$name = trim($name);
# Default assignee is the component owner.
my $id;
if (!$user->in_group("editbugs") || !$name) {
if (!$user->in_group('editbugs', $component->product_id) || !$name) {
$id = $component->default_assignee->id;
} else {
$id = login_to_id($name, THROW_ERROR);
......@@ -485,7 +513,8 @@ sub _check_bug_status {
my @valid_statuses = VALID_ENTRY_STATUS;
if ($user->in_group('editbugs') || $user->in_group('canconfirm')) {
if ($user->in_group('editbugs', $product->id)
|| $user->in_group('canconfirm', $product->id)) {
# Default to NEW if the user with privs hasn't selected another status.
$status ||= 'NEW';
}
......@@ -558,9 +587,6 @@ sub _check_component {
$name = trim($name);
$name || ThrowUserError("require_component");
my $obj = Bugzilla::Component::check_component($product, $name);
# XXX Right now, post_bug needs this to return an object. However,
# when we move to Bugzilla::Bug->create, this should just return
# what it was passed.
return $obj;
}
......@@ -579,10 +605,10 @@ sub _check_deadline {
# Takes two comma/space-separated strings and returns arrayrefs
# of valid bug IDs.
sub _check_dependencies {
my ($invocant, $depends_on, $blocks) = @_;
my ($invocant, $product, $depends_on, $blocks) = @_;
# Only editbugs users can set dependencies on bug entry.
return ([], []) unless Bugzilla->user->in_group('editbugs');
return ([], []) unless Bugzilla->user->in_group('editbugs', $product->id);
$depends_on ||= '';
$blocks ||= '';
......@@ -656,9 +682,10 @@ sub _check_groups {
}
sub _check_keywords {
my ($invocant, $keyword_string) = @_;
my ($invocant, $product, $keyword_string) = @_;
$keyword_string = trim($keyword_string);
return [] if (!$keyword_string || !Bugzilla->user->in_group('editbugs'));
return [] if (!$keyword_string
|| !Bugzilla->user->in_group('editbugs', $product->id));
my %keywords;
foreach my $keyword (split(/[\s,]+/, $keyword_string)) {
......@@ -678,9 +705,6 @@ sub _check_product {
# can_enter_product already does everything that check_product
# would do for us, so we don't need to use it.
my $obj = new Bugzilla::Product({ name => $name });
# XXX Right now, post_bug needs this to return an object. However,
# when we move to Bugzilla::Bug->create, this should just return
# what it was passed.
return $obj;
}
......@@ -784,7 +808,7 @@ sub _check_qa_contact {
$name = trim($name);
my $id;
if (!$user->in_group("editbugs") || !$name) {
if (!$user->in_group('editbugs', $component->product_id) || !$name) {
# We want to insert NULL into the database if we get a 0.
$id = $component->default_qa_contact->id || undef;
} else {
......@@ -825,7 +849,7 @@ sub fields {
bug_status resolution dup_id
bug_file_loc status_whiteboard keywords
priority bug_severity target_milestone
dependson blocked votes
dependson blocked votes everconfirmed
reporter assigned_to cc),
# Conditional Fields
......@@ -1190,21 +1214,16 @@ sub user {
my $user = Bugzilla->user;
my $canmove = Bugzilla->params->{'move-enabled'} && $user->is_mover;
# In the below, if the person hasn't logged in, then we treat them
# as if they can do anything. That's because we don't know why they
# haven't logged in; it may just be because they don't use cookies.
# Display everything as if they have all the permissions in the
# world; their permissions will get checked when they log in and
# actually try to make the change.
my $unknown_privileges = !$user->id
|| $user->in_group("editbugs");
my $prod_id = $self->{'product_id'};
my $unknown_privileges = $user->in_group('editbugs', $prod_id);
my $canedit = $unknown_privileges
|| $user->id == $self->{assigned_to_id}
|| (Bugzilla->params->{'useqacontact'}
&& $self->{'qa_contact_id'}
&& $user->id == $self->{qa_contact_id});
my $canconfirm = $unknown_privileges
|| $user->in_group("canconfirm");
|| $user->in_group('canconfirm', $prod_id);
my $isreporter = $user->id
&& $user->id == $self->{reporter_id};
......@@ -1300,12 +1319,16 @@ sub bug_alias_to_id {
#####################################################################
sub Append