Commit f297f575 authored by Per Cederqvist's avatar Per Cederqvist

Imported Bugzilla 4.3.3.

parent de18a0d1
No preview for this file type
# Don't allow people to retrieve non-cgi executable files or our private data
<FilesMatch ^(.*\.pm|.*\.pl|.*localconfig.*)$>
<FilesMatch (\.pm|\.pl|\.tmpl|localconfig.*)$>
deny from all
</FilesMatch>
Options -Indexes
<IfModule mod_expires.c>
<IfModule mod_headers.c>
<IfModule mod_env.c>
......
......@@ -18,6 +18,7 @@ use Bugzilla::User;
use Bugzilla::Util;
use Net::LDAP;
use Net::LDAP::Util qw(escape_filter_value);
use constant admin_can_create_account => 0;
use constant user_can_create_account => 0;
......@@ -121,6 +122,7 @@ sub check_credentials {
sub _bz_search_params {
my ($username) = @_;
$username = escape_filter_value($username);
return (base => Bugzilla->params->{"LDAPBaseDN"},
scope => "sub",
filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"}
......
......@@ -358,12 +358,14 @@ sub check {
# Bugzilla::Bug throws lots of special errors, so we don't call
# SUPER::check, we just call our new and do our own checks.
my $self = $class->new(trim($id));
# For error messages, use the id that was returned by new(), because
# it's cleaned up.
$id = $self->id;
$id = trim($id);
my $self = $class->new($id);
if ($self->{error}) {
# For error messages, use the id that was returned by new(), because
# it's cleaned up.
$id = $self->id;
if ($self->{error} eq 'NotFound') {
ThrowUserError("bug_id_does_not_exist", { bug_id => $id });
}
......@@ -375,7 +377,7 @@ sub check {
}
unless ($field && $field =~ /^(dependson|blocked|dup_id)$/) {
$self->check_is_visible;
$self->check_is_visible($id);
}
return $self;
}
......@@ -391,16 +393,19 @@ sub check_for_edit {
}
sub check_is_visible {
my $self = shift;
my ($self, $input_id) = @_;
$input_id ||= $self->id;
my $user = Bugzilla->user;
if (!$user->can_see_bug($self->id)) {
# The error the user sees depends on whether or not they are
# logged in (i.e. $user->id contains the user's positive integer ID).
# If we are validating an alias, then use it in the error message
# instead of its corresponding bug ID, to not disclose it.
if ($user->id) {
ThrowUserError("bug_access_denied", { bug_id => $self->id });
ThrowUserError("bug_access_denied", { bug_id => $input_id });
} else {
ThrowUserError("bug_access_query", { bug_id => $self->id });
ThrowUserError("bug_access_query", { bug_id => $input_id });
}
}
}
......@@ -1471,9 +1476,7 @@ sub _check_dependencies {
: split(/[\s,]+/, $deps_in{$type});
# Eliminate nulls.
@bug_ids = grep {$_} @bug_ids;
# We do this up here to make sure all aliases are converted to IDs.
@bug_ids = map { $invocant->check($_, $type)->id } @bug_ids;
my @check_access = @bug_ids;
# When we're updating a bug, only added or removed bug_ids are
# checked for whether or not we can see/edit those bugs.
......@@ -1503,7 +1506,8 @@ sub _check_dependencies {
}
}
}
# Replace all aliases by their corresponding bug ID.
@bug_ids = map { $_ =~ /^(\d+)$/ ? $1 : $invocant->check($_, $type)->id } @bug_ids;
$deps_in{$type} = \@bug_ids;
}
......@@ -1520,8 +1524,9 @@ sub _check_dependencies {
sub _check_dup_id {
my ($self, $dupe_of) = @_;
my $dbh = Bugzilla->dbh;
$dupe_of = trim($dupe_of);
# Store the bug ID/alias passed by the user for visibility checks.
my $orig_dupe_of = $dupe_of = trim($dupe_of);
$dupe_of || ThrowCodeError('undefined_field', { field => 'dup_id' });
# Validate the bug ID. The second argument will force check() to only
# make sure that the bug exists, and convert the alias to the bug ID
......@@ -1534,7 +1539,7 @@ sub _check_dup_id {
# If we come here, then the duplicate is new. We have to make sure
# that we can view/change it (issue A on bug 96085).
$dupe_of_bug->check_is_visible;
$dupe_of_bug->check_is_visible($orig_dupe_of);
# Make sure a loop isn't created when marking this bug
# as duplicate.
......
......@@ -16,15 +16,6 @@ use Bugzilla::Search::Recent;
use File::Basename;
BEGIN {
if (ON_WINDOWS) {
# Help CGI find the correct temp directory as the default list
# isn't Windows friendly (Bug 248988)
$ENV{'TMPDIR'} = $ENV{'TEMP'} || $ENV{'TMP'} || "$ENV{'WINDIR'}\\TEMP";
}
*AUTOLOAD = \&CGI::AUTOLOAD;
}
sub _init_bz_cgi_globals {
my $invocant = shift;
# We need to disable output buffering - see bug 179174
......@@ -59,14 +50,27 @@ sub new {
# Path-Info is of no use for Bugzilla and interacts badly with IIS.
# Moreover, it causes unexpected behaviors, such as totally breaking
# the rendering of pages. Skip it!
print $self->redirect($self->url(-path => 0, -query => 1)) if $self->path_info;
# the rendering of pages.
my $script = basename($0);
if (my $path_info = $self->path_info) {
my @whitelist;
Bugzilla::Hook::process('path_info_whitelist', { whitelist => \@whitelist });
if (!grep($_ eq $script, @whitelist)) {
# IIS includes the full path to the script in PATH_INFO,
# so we have to extract the real PATH_INFO from it,
# else we will be redirected outside Bugzilla.
my $script_name = $self->script_name;
$path_info =~ s/^\Q$script_name\E//;
if ($path_info) {
print $self->redirect($self->url(-path => 0, -query => 1));
}
}
}
# Send appropriate charset
$self->charset(Bugzilla->params->{'utf8'} ? 'UTF-8' : '');
# Redirect to urlbase/sslbase if we are not viewing an attachment.
my $script = basename($0);
if ($self->url_is_attachment_base and $script ne 'attachment.cgi') {
$self->redirect_to_urlbase();
}
......@@ -158,6 +162,16 @@ sub clean_search_url {
# Delete leftovers from the login form
$self->delete('Bugzilla_remember', 'GoAheadAndLogIn');
# Delete the token if we're not performing an action which needs it
unless ((defined $self->param('remtype')
&& ($self->param('remtype') eq 'asdefault'
|| $self->param('remtype') eq 'asnamed'))
|| (defined $self->param('remaction')
&& $self->param('remaction') eq 'forget'))
{
$self->delete("token");
}
foreach my $num (1,2,3) {
# If there's no value in the email field, delete the related fields.
if (!$self->param("email$num")) {
......@@ -344,7 +358,7 @@ sub param {
sub _fix_utf8 {
my $input = shift;
# The is_utf8 is here in case CGI gets smart about utf8 someday.
utf8::decode($input) if defined $input && !utf8::is_utf8($input);
utf8::decode($input) if defined $input && !ref $input && !utf8::is_utf8($input);
return $input;
}
......
......@@ -15,7 +15,8 @@ use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Product;
use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object);
use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object Exporter);
@Bugzilla::Classification::EXPORT = qw(sort_products_by_classification);
###############################
#### Initialization ####
......@@ -152,6 +153,38 @@ sub products {
sub description { return $_[0]->{'description'}; }
sub sortkey { return $_[0]->{'sortkey'}; }
###############################
#### Helpers ####
###############################
# This function is a helper to sort products to be listed
# in global/choose-product.html.tmpl.
sub sort_products_by_classification {
my $products = shift;
my $list;
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);
# 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)];
}
else {
$list = [{object => undef, products => $products}];
}
return $list;
}
1;
__END__
......@@ -208,4 +241,21 @@ A Classification is a higher-level grouping of Products.
=back
=head1 SUBROUTINES
=over
=item C<sort_products_by_classification>
Description: This is a helper which returns a list of products sorted
by classification in a form suitable to be passed to the
global/choose-product.html.tmpl template.
Params: An arrayref of product objects.
Returns: An arrayref of hashes suitable to be passed to
global/choose-product.html.tmpl.
=back
=cut
......@@ -161,6 +161,7 @@ use Memoize;
MAX_BUG_URL_LENGTH
MAX_POSSIBLE_DUPLICATES
MAX_ATTACH_FILENAME_LENGTH
MAX_QUIP_LENGTH
PASSWORD_DIGEST_ALGORITHM
PASSWORD_SALT_LENGTH
......@@ -181,7 +182,7 @@ use Memoize;
# CONSTANTS
#
# Bugzilla version
use constant BUGZILLA_VERSION => "4.3.2";
use constant BUGZILLA_VERSION => "4.3.3";
# Location of the remote and local XML files to track new releases.
use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml';
......@@ -556,6 +557,9 @@ use constant MAX_POSSIBLE_DUPLICATES => 25;
# necessary schema changes to store longer names.
use constant MAX_ATTACH_FILENAME_LENGTH => 255;
# Maximum length of a quip.
use constant MAX_QUIP_LENGTH => 512;
# This is the name of the algorithm used to hash passwords before storing
# them in the database. This can be any string that is valid to pass to
# Perl's "Digest" module. Note that if you change this, it won't take
......
......@@ -294,6 +294,9 @@ sub adjust_statement {
my $is_select = ($part =~ m/^\s*SELECT\b/io);
my $has_from = ($part =~ m/\bFROM\b/io) if $is_select;
# Oracle includes the time in CURRENT_DATE.
$part =~ s/\bCURRENT_DATE\b/TRUNC(CURRENT_DATE)/io;
# Oracle use SUBSTR instead of SUBSTRING
$part =~ s/\bSUBSTRING\b/SUBSTR/io;
......@@ -322,6 +325,9 @@ sub adjust_statement {
$has_from = ($nonstring =~ m/\bFROM\b/io)
if ($is_select and !$has_from);
# Oracle includes the time in CURRENT_DATE.
$nonstring =~ s/\bCURRENT_DATE\b/TRUNC(CURRENT_DATE)/io;
# Oracle use SUBSTR instead of SUBSTRING
$nonstring =~ s/\bSUBSTRING\b/SUBSTR/io;
......@@ -610,11 +616,25 @@ sub bz_setup_database {
$self->SUPER::bz_setup_database(@_);
my $sth = $self->prepare("SELECT OBJECT_NAME FROM USER_OBJECTS WHERE OBJECT_NAME = ?");
my @tables = $self->bz_table_list_real();
foreach my $table (@tables) {
my @columns = $self->bz_table_columns_real($table);
foreach my $column (@columns) {
my $def = $self->bz_column_info($table, $column);
# bz_add_column() before Bugzilla 4.2.3 didn't handle primary keys
# correctly (bug 731156). We have to add missing sequences and
# triggers ourselves.
if ($def->{TYPE} =~ /SERIAL/i) {
my $sequence = "${table}_${column}_SEQ";
my $exists = $self->selectrow_array($sth, undef, $sequence);
if (!$exists) {
my @sql = $self->_get_create_seq_ddl($table, $column);
$self->do($_) foreach @sql;
}
}
if ($def->{REFERENCES}) {
my $references = $def->{REFERENCES};
my $update = $references->{UPDATE} || 'CASCADE';
......@@ -628,15 +648,13 @@ sub bz_setup_database {
$to_table = 'tag';
}
if ( $update =~ /CASCADE/i ){
my $trigger_name = uc($fk_name . "_UC");
my $exist_trigger = $self->selectcol_arrayref(
"SELECT OBJECT_NAME FROM USER_OBJECTS
WHERE OBJECT_NAME = ?", undef, $trigger_name);
my $trigger_name = uc($fk_name . "_UC");
my $exist_trigger = $self->selectcol_arrayref($sth, undef, $trigger_name);
if(@$exist_trigger) {
$self->do("DROP TRIGGER $trigger_name");
}
my $tr_str = "CREATE OR REPLACE TRIGGER $trigger_name"
my $tr_str = "CREATE OR REPLACE TRIGGER $trigger_name"
. " AFTER UPDATE OF $to_column ON $to_table "
. " REFERENCING "
. " NEW AS NEW "
......@@ -647,22 +665,46 @@ sub bz_setup_database {
. " SET $column = :NEW.$to_column"
. " WHERE $column = :OLD.$to_column;"
. " END $trigger_name;";
$self->do($tr_str);
}
}
}
}
$self->do($tr_str);
}
}
}
}
# Drop the trigger which causes bug 541553
my $trigger_name = "PRODUCTS_MILESTONEURL";
my $exist_trigger = $self->selectcol_arrayref(
"SELECT OBJECT_NAME FROM USER_OBJECTS
WHERE OBJECT_NAME = ?", undef, $trigger_name);
my $exist_trigger = $self->selectcol_arrayref($sth, undef, $trigger_name);
if(@$exist_trigger) {
$self->do("DROP TRIGGER $trigger_name");
}
}
# These two methods have been copied from Bugzilla::DB::Schema::Oracle.
sub _get_create_seq_ddl {
my ($self, $table, $column) = @_;
my $seq_name = "${table}_${column}_SEQ";
my $seq_sql = "CREATE SEQUENCE $seq_name INCREMENT BY 1 START WITH 1 " .
"NOMAXVALUE NOCYCLE NOCACHE";
my $trigger_sql = $self->_get_create_trigger_ddl($table, $column, $seq_name);
return ($seq_sql, $trigger_sql);
}
sub _get_create_trigger_ddl {
my ($self, $table, $column, $seq_name) = @_;
my $trigger_sql = "CREATE OR REPLACE TRIGGER ${table}_${column}_TR "
. " BEFORE INSERT ON $table "
. " FOR EACH ROW "
. " BEGIN "
. " SELECT ${seq_name}.NEXTVAL "
. " INTO :NEW.$column FROM DUAL; "
. " END;";
return $trigger_sql;
}
############################################################################
package Bugzilla::DB::Oracle::st;
use base qw(DBI::st);
......
......@@ -341,7 +341,7 @@ use constant ABSTRACT_SCHEMA => {
COLUMN => 'id'}},
added => {TYPE => 'varchar(255)'},
removed => {TYPE => 'varchar(255)'},
comment_id => {TYPE => 'INT3',
comment_id => {TYPE => 'INT4',
REFERENCES => { TABLE => 'longdescs',
COLUMN => 'comment_id',
DELETE => 'CASCADE'}},
......@@ -376,7 +376,7 @@ use constant ABSTRACT_SCHEMA => {
longdescs => {
FIELDS => [
comment_id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
comment_id => {TYPE => 'INTSERIAL', NOTNULL => 1,
PRIMARYKEY => 1},
bug_id => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'bugs',
......@@ -416,7 +416,8 @@ use constant ABSTRACT_SCHEMA => {
DELETE => 'CASCADE'}},
],
INDEXES => [
dependencies_blocked_idx => ['blocked'],
dependencies_blocked_idx => {FIELDS => [qw(blocked dependson)],
TYPE => 'UNIQUE'},
dependencies_dependson_idx => ['dependson'],
],
},
......@@ -1025,6 +1026,23 @@ use constant ABSTRACT_SCHEMA => {
],
},
reports => {
FIELDS => [
id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1,
PRIMARYKEY => 1},
user_id => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'CASCADE'}},
name => {TYPE => 'varchar(64)', NOTNULL => 1},
query => {TYPE => 'LONGTEXT', NOTNULL => 1},
],
INDEXES => [
reports_user_id_idx => {FIELDS => [qw(user_id name)],
TYPE => 'UNIQUE'},
],
},
component_cc => {
FIELDS => [
......@@ -1466,7 +1484,7 @@ use constant ABSTRACT_SCHEMA => {
REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'SET NULL'}},
quip => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
quip => {TYPE => 'varchar(512)', NOTNULL => 1},
approved => {TYPE => 'BOOLEAN', NOTNULL => 1,
DEFAULT => 'TRUE'},
],
......
......@@ -184,6 +184,31 @@ sub _get_fk_name {
return $fk_name;
}
sub get_add_column_ddl {
my $self = shift;
my ($table, $column, $definition, $init_value) = @_;
my @sql;
# Create sequences and triggers to emulate SERIAL datatypes.
if ($definition->{TYPE} =~ /SERIAL/i) {
# Clone the definition to not alter the original one.
my %def = %$definition;
# Oracle requires to define the column is several steps.
my $pk = delete $def{PRIMARYKEY};
my $notnull = delete $def{NOTNULL};
@sql = $self->SUPER::get_add_column_ddl($table, $column, \%def, $init_value);
push(@sql, $self->_get_create_seq_ddl($table, $column));
push(@sql, "UPDATE $table SET $column = ${table}_${column}_SEQ.NEXTVAL");
push(@sql, "ALTER TABLE $table MODIFY $column NOT NULL") if $notnull;
push(@sql, "ALTER TABLE $table ADD PRIMARY KEY ($column)") if $pk;
}
else {
@sql = $self->SUPER::get_add_column_ddl(@_);
}
return @sql;
}
sub get_alter_column_ddl {
my ($self, $table, $column, $new_def, $set_nulls_to) = @_;
......@@ -349,6 +374,29 @@ sub get_rename_column_ddl {
return @sql;
}
sub get_drop_column_ddl {
my $self = shift;
my ($table, $column) = @_;
my @sql;
push(@sql, $self->SUPER::get_drop_column_ddl(@_));
my $dbh=Bugzilla->dbh;
my $trigger_name = uc($table . "_" . $column);
my $exist_trigger = $dbh->selectcol_arrayref(
"SELECT OBJECT_NAME FROM USER_OBJECTS
WHERE OBJECT_NAME = ?", undef, $trigger_name);
if(@$exist_trigger) {
push(@sql, "DROP TRIGGER $trigger_name");
}
# If this column is of type SERIAL, we need to drop the sequence
# and trigger that went along with it.
my $def = $self->get_column_abstract($table, $column);
if ($def->{TYPE} =~ /SERIAL/i) {
push(@sql, "DROP SEQUENCE ${table}_${column}_SEQ");
push(@sql, "DROP TRIGGER ${table}_${column}_TR");
}
return @sql;
}
sub get_rename_table_sql {
my ($self, $old_name, $new_name) = @_;
if (lc($old_name) eq lc($new_name)) {
......@@ -450,20 +498,4 @@ sub get_set_serial_sql {
return @sql;
}
sub get_drop_column_ddl {
my $self = shift;
my ($table, $column) = @_;
my @sql;
push(@sql, $self->SUPER::get_drop_column_ddl(@_));
my $dbh=Bugzilla->dbh;
my $trigger_name = uc($table . "_" . $column);
my $exist_trigger = $dbh->selectcol_arrayref(
"SELECT OBJECT_NAME FROM USER_OBJECTS
WHERE OBJECT_NAME = ?", undef, $trigger_name);
if(@$exist_trigger) {
push(@sql, "DROP TRIGGER $trigger_name");
}
return @sql;
}
1;
......@@ -74,6 +74,16 @@ sub _initialize {
} #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 $charset = $create_utf8 ? "ENCODING 'UTF8' TEMPLATE template0" : '';
return ("CREATE DATABASE $name $charset");
}
sub get_rename_column_ddl {
my ($self, $table, $old_name, $new_name) = @_;
if (lc($old_name) eq lc($new_name)) {
......
......@@ -1289,6 +1289,22 @@ your template.
=back
=head2 path_info_whitelist