Commit 425158e8 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 450ce307
use 5.006;
use strict;
use warnings;
package CPAN::Meta;
our $VERSION = '2.150005';
......@@ -92,7 +93,7 @@ BEGIN {
no strict 'refs';
for my $attr (@STRING_READERS) {
*$attr = sub { $_[0]{ $attr } };
*$attr = sub { $_[0]{$attr} };
}
}
......@@ -121,10 +122,9 @@ BEGIN {
no strict 'refs';
for my $attr (@LIST_READERS) {
*$attr = sub {
my $value = $_[0]{ $attr };
croak "$attr must be called in list context"
unless wantarray;
return @{ _dclone($value) } if ref $value;
my $value = $_[0]{$attr};
croak "$attr must be called in list context" unless wantarray;
return @{_dclone($value)} if ref $value;
return $value;
};
}
......@@ -163,7 +163,7 @@ BEGIN {
for my $attr (@MAP_READERS) {
(my $subname = $attr) =~ s/-/_/;
*$subname = sub {
my $value = $_[0]{ $attr };
my $value = $_[0]{$attr};
return _dclone($value) if $value;
return {};
};
......@@ -182,7 +182,7 @@ BEGIN {
#pod =cut
sub custom_keys {
return grep { /^x_/i } keys %{$_[0]};
return grep {/^x_/i} keys %{$_[0]};
}
sub custom {
......@@ -220,29 +220,29 @@ sub _new {
my ($class, $struct, $options) = @_;
my $self;
if ( $options->{lazy_validation} ) {
if ($options->{lazy_validation}) {
# try to convert to a valid structure; if succeeds, then return it
my $cmc = CPAN::Meta::Converter->new( $struct );
$self = $cmc->convert( version => 2 ); # valid or dies
my $cmc = CPAN::Meta::Converter->new($struct);
$self = $cmc->convert(version => 2); # valid or dies
return bless $self, $class;
}
else {
# validate original struct
my $cmv = CPAN::Meta::Validator->new( $struct );
unless ( $cmv->is_valid) {
die "Invalid metadata structure. Errors: "
. join(", ", $cmv->errors) . "\n";
my $cmv = CPAN::Meta::Validator->new($struct);
unless ($cmv->is_valid) {
die "Invalid metadata structure. Errors: " . join(", ", $cmv->errors) . "\n";
}
}
# up-convert older spec versions
my $version = $struct->{'meta-spec'}{version} || '1.0';
if ( $version == 2 ) {
if ($version == 2) {
$self = $struct;
}
else {
my $cmc = CPAN::Meta::Converter->new( $struct );
$self = $cmc->convert( version => 2 );
my $cmc = CPAN::Meta::Converter->new($struct);
$self = $cmc->convert(version => 2);
}
return bless $self, $class;
......@@ -268,10 +268,10 @@ sub new {
sub create {
my ($class, $struct, $options) = @_;
my $version = __PACKAGE__->VERSION || 2;
$struct->{generated_by} ||= __PACKAGE__ . " version $version" ;
$struct->{generated_by} ||= __PACKAGE__ . " version $version";
$struct->{'meta-spec'}{version} ||= int($version);
my $self = eval { $class->_new($struct, $options) };
croak ($@) if $@;
croak($@) if $@;
return $self;
}
......@@ -293,12 +293,11 @@ sub load_file {
my ($class, $file, $options) = @_;
$options->{lazy_validation} = 1 unless exists $options->{lazy_validation};
croak "load_file() requires a valid, readable filename"
unless -r $file;
croak "load_file() requires a valid, readable filename" unless -r $file;
my $self;
eval {
my $struct = Parse::CPAN::Meta->load_file( $file );
my $struct = Parse::CPAN::Meta->load_file($file);
$self = $class->_new($struct, $options);
};
croak($@) if $@;
......@@ -320,7 +319,7 @@ sub load_yaml_string {
my $self;
eval {
my ($struct) = Parse::CPAN::Meta->load_yaml_string( $yaml );
my ($struct) = Parse::CPAN::Meta->load_yaml_string($yaml);
$self = $class->_new($struct, $options);
};
croak($@) if $@;
......@@ -342,7 +341,7 @@ sub load_json_string {
my $self;
eval {
my $struct = Parse::CPAN::Meta->load_json_string( $json );
my $struct = Parse::CPAN::Meta->load_json_string($json);
$self = $class->_new($struct, $options);
};
croak($@) if $@;
......@@ -365,7 +364,7 @@ sub load_string {
my $self;
eval {
my $struct = Parse::CPAN::Meta->load_string( $string );
my $struct = Parse::CPAN::Meta->load_string($string);
$self = $class->_new($struct, $options);
};
croak($@) if $@;
......@@ -400,22 +399,18 @@ sub save {
my $version = $options->{version} || '2';
my $layer = $] ge '5.008001' ? ':utf8' : '';
if ( $version ge '2' ) {
carp "'$file' should end in '.json'"
unless $file =~ m{\.json$};
if ($version ge '2') {
carp "'$file' should end in '.json'" unless $file =~ m{\.json$};
}
else {
carp "'$file' should end in '.yml'"
unless $file =~ m{\.yml$};
carp "'$file' should end in '.yml'" unless $file =~ m{\.yml$};
}
my $data = $self->as_string( $options );
open my $fh, ">$layer", $file
or die "Error opening '$file' for writing: $!\n";
my $data = $self->as_string($options);
open my $fh, ">$layer", $file or die "Error opening '$file' for writing: $!\n";
print {$fh} $data;
close $fh
or die "Error closing '$file': $!\n";
close $fh or die "Error closing '$file': $!\n";
return 1;
}
......@@ -455,7 +450,7 @@ sub effective_prereqs {
return $prereq unless @$features;
my @other = map {; $self->feature($_)->prereqs } @$features;
my @other = map { ; $self->feature($_)->prereqs } @$features;
return $prereq->with_merged_prereqs(\@other);
}
......@@ -476,11 +471,11 @@ sub effective_prereqs {
sub should_index_file {
my ($self, $filename) = @_;
for my $no_index_file (@{ $self->no_index->{file} || [] }) {
for my $no_index_file (@{$self->no_index->{file} || []}) {
return if $filename eq $no_index_file;
}
for my $no_index_dir (@{ $self->no_index->{directory} }) {
for my $no_index_dir (@{$self->no_index->{directory}}) {
$no_index_dir =~ s{$}{/} unless $no_index_dir =~ m{/\z};
return if index($filename, $no_index_dir) == 0;
}
......@@ -502,11 +497,11 @@ sub should_index_file {
sub should_index_package {
my ($self, $package) = @_;
for my $no_index_pkg (@{ $self->no_index->{package} || [] }) {
for my $no_index_pkg (@{$self->no_index->{package} || []}) {
return if $package eq $no_index_pkg;
}
for my $no_index_ns (@{ $self->no_index->{namespace} }) {
for my $no_index_ns (@{$self->no_index->{namespace}}) {
return if index($package, "${no_index_ns}::") == 0;
}
......@@ -526,8 +521,8 @@ sub features {
my ($self) = @_;
my $opt_f = $self->optional_features;
my @features = map {; CPAN::Meta::Feature->new($_ => $opt_f->{ $_ }) }
keys %$opt_f;
my @features
= map { ; CPAN::Meta::Feature->new($_ => $opt_f->{$_}) } keys %$opt_f;
return @features;
}
......@@ -546,7 +541,7 @@ sub feature {
my ($self, $ident) = @_;
croak "no feature named $ident"
unless my $f = $self->optional_features->{ $ident };
unless my $f = $self->optional_features->{$ident};
return CPAN::Meta::Feature->new($ident, $f);
}
......@@ -567,9 +562,9 @@ sub feature {
sub as_struct {
my ($self, $options) = @_;
my $struct = _dclone($self);
if ( $options->{version} ) {
my $cmc = CPAN::Meta::Converter->new( $struct );
$struct = $cmc->convert( version => $options->{version} );
if ($options->{version}) {
my $cmc = CPAN::Meta::Converter->new($struct);
$struct = $cmc->convert(version => $options->{version});
}
return $struct;
}
......@@ -603,28 +598,28 @@ sub as_string {
my $version = $options->{version} || '2';
my $struct;
if ( $self->meta_spec_version ne $version ) {
my $cmc = CPAN::Meta::Converter->new( $self->as_struct );
$struct = $cmc->convert( version => $version );
if ($self->meta_spec_version ne $version) {
my $cmc = CPAN::Meta::Converter->new($self->as_struct);
$struct = $cmc->convert(version => $version);
}
else {
$struct = $self->as_struct;
}
my ($data, $backend);
if ( $version ge '2' ) {
if ($version ge '2') {
$backend = Parse::CPAN::Meta->json_backend();
local $struct->{x_serialization_backend} = sprintf '%s version %s',
$backend, $backend->VERSION;
local $struct->{x_serialization_backend} = sprintf '%s version %s', $backend,
$backend->VERSION;
$data = $backend->new->pretty->canonical->encode($struct);
}
else {
$backend = Parse::CPAN::Meta->yaml_backend();
local $struct->{x_serialization_backend} = sprintf '%s version %s',
$backend, $backend->VERSION;
local $struct->{x_serialization_backend} = sprintf '%s version %s', $backend,
$backend->VERSION;
$data = eval { no strict 'refs'; &{"$backend\::Dump"}($struct) };
if ( $@ ) {
croak $backend->can('errstr') ? $backend->errstr : $@
if ($@) {
croak $backend->can('errstr') ? $backend->errstr : $@;
}
}
......@@ -633,7 +628,7 @@ sub as_string {
# Used by JSON::PP, etc. for "convert_blessed"
sub TO_JSON {
return { %{ $_[0] } };
return {%{$_[0]}};
}
1;
......
use 5.006;
use strict;
use warnings;
package CPAN::Meta::Feature;
our $VERSION = '2.150005';
......
......@@ -2,6 +2,7 @@
use 5.006;
use strict;
use warnings;
package CPAN::Meta::History;
our $VERSION = '2.150005';
......
......@@ -11,15 +11,16 @@ use CPAN::Meta::Converter 2.141170;
sub _is_identical {
my ($left, $right) = @_;
return
(not defined $left and not defined $right)
return (not defined $left and not defined $right)
# if either of these are references, we compare the serialized value
|| (defined $left and defined $right and $left eq $right);
}
sub _identical {
my ($left, $right, $path) = @_;
croak sprintf "Can't merge attribute %s: '%s' does not equal '%s'", join('.', @{$path}), $left, $right
croak sprintf "Can't merge attribute %s: '%s' does not equal '%s'",
join('.', @{$path}), $left, $right
unless _is_identical($left, $right);
return $left;
}
......@@ -31,10 +32,10 @@ sub _merge {
$current->{$key} = $next->{$key};
}
elsif (my $merger = $mergers->{$key}) {
$current->{$key} = $merger->($current->{$key}, $next->{$key}, [ @{$path}, $key ]);
$current->{$key} = $merger->($current->{$key}, $next->{$key}, [@{$path}, $key]);
}
elsif ($merger = $mergers->{':default'}) {
$current->{$key} = $merger->($current->{$key}, $next->{$key}, [ @{$path}, $key ]);
$current->{$key} = $merger->($current->{$key}, $next->{$key}, [@{$path}, $key]);
}
else {
croak sprintf "Can't merge unknown attribute '%s'", join '.', @{$path}, $key;
......@@ -50,7 +51,7 @@ sub _uniq {
sub _set_addition {
my ($left, $right) = @_;
return [ +_uniq(@{$left}, @{$right}) ];
return [+_uniq(@{$left}, @{$right})];
}
sub _uniq_map {
......@@ -59,12 +60,13 @@ sub _uniq_map {
if (not exists $left->{$key}) {
$left->{$key} = $right->{$key};
}
# identical strings or references are merged identically
elsif (_is_identical($left->{$key}, $right->{$key})) {
1; # do nothing - keep left
}
elsif (ref $left->{$key} eq 'HASH' and ref $right->{$key} eq 'HASH') {
$left->{$key} = _uniq_map($left->{$key}, $right->{$key}, [ @{$path}, $key ]);
$left->{$key} = _uniq_map($left->{$key}, $right->{$key}, [@{$path}, $key]);
}
else {
croak 'Duplication of element ' . join '.', @{$path}, $key;
......@@ -98,20 +100,24 @@ sub _optional_features {
$left->{$key} = $right->{$key};
}
else {
for my $subkey (keys %{ $right->{$key} }) {
for my $subkey (keys %{$right->{$key}}) {
next if $subkey eq 'prereqs';
if (not exists $left->{$key}{$subkey}) {
$left->{$key}{$subkey} = $right->{$key}{$subkey};
}
else {
Carp::croak "Cannot merge two optional_features named '$key' with different '$subkey' values"
if do { no warnings 'uninitialized'; $left->{$key}{$subkey} ne $right->{$key}{$subkey} };
Carp::croak
"Cannot merge two optional_features named '$key' with different '$subkey' values"
if do {
no warnings 'uninitialized';
$left->{$key}{$subkey} ne $right->{$key}{$subkey};
};
}
}
require CPAN::Meta::Prereqs;
$left->{$key}{prereqs} =
CPAN::Meta::Prereqs->new($left->{$key}{prereqs})
$left->{$key}{prereqs}
= CPAN::Meta::Prereqs->new($left->{$key}{prereqs})
->with_merged_prereqs(CPAN::Meta::Prereqs->new($right->{$key}{prereqs}))
->as_string_hash;
}
......@@ -132,20 +138,18 @@ my %default = (
return join ', ', _uniq(split(/, /, $left), split(/, /, $right));
},
license => \&_set_addition,
'meta-spec' => {
version => \&_identical,
url => \&_identical
},
'meta-spec' => {version => \&_identical, url => \&_identical},
name => \&_identical,
release_status => \&_identical,
version => \&_identical,
description => \&_identical,
keywords => \&_set_addition,
no_index => { map { ($_ => \&_set_addition) } qw/file directory package namespace/ },
no_index =>
{map { ($_ => \&_set_addition) } qw/file directory package namespace/},
optional_features => \&_optional_features,
prereqs => sub {
require CPAN::Meta::Prereqs;
my ($left, $right) = map { CPAN::Meta::Prereqs->new($_) } @_[0,1];
my ($left, $right) = map { CPAN::Meta::Prereqs->new($_) } @_[0, 1];
return $left->with_merged_prereqs($right)->as_string_hash;
},
provides => \&_uniq_map,
......@@ -163,10 +167,10 @@ sub new {
my ($class, %arguments) = @_;
croak 'default version required' if not exists $arguments{default_version};
my %mapping = %default;
my %extra = %{ $arguments{extra_mappings} || {} };
my %extra = %{$arguments{extra_mappings} || {}};
for my $key (keys %extra) {
if (ref($mapping{$key}) eq 'HASH') {
$mapping{$key} = { %{ $mapping{$key} }, %{ $extra{$key} } };
$mapping{$key} = {%{$mapping{$key}}, %{$extra{$key}}};
}
else {
$mapping{$key} = $extra{$key};
......@@ -194,10 +198,10 @@ sub _coerce_mapping {
$ret{$key} = $value;
}
elsif (ref($value) eq 'HASH') {
my $mapping = _coerce_mapping($value, [ @{$map_path}, $key ]);
my $mapping = _coerce_mapping($value, [@{$map_path}, $key]);
$ret{$key} = sub {
my ($left, $right, $path) = @_;
return _merge($left, $right, $mapping, [ @{$path} ]);
return _merge($left, $right, $mapping, [@{$path}]);
};
}
elsif ($coderef_for{$value}) {
......@@ -214,13 +218,12 @@ sub merge {
my ($self, @items) = @_;
my $current = {};
for my $next (@items) {
if ( blessed($next) && $next->isa('CPAN::Meta') ) {
if (blessed($next) && $next->isa('CPAN::Meta')) {
$next = $next->as_struct;
}
elsif ( ref($next) eq 'HASH' ) {
my $cmc = CPAN::Meta::Converter->new(
$next, default_version => $self->{default_version}
);
elsif (ref($next) eq 'HASH') {
my $cmc = CPAN::Meta::Converter->new($next,
default_version => $self->{default_version});
$next = $cmc->upgrade_fragment;
}
else {
......
use 5.006;
use strict;
use warnings;
package CPAN::Meta::Prereqs;
our $VERSION = '2.150005';
......@@ -45,34 +46,33 @@ use CPAN::Meta::Requirements 2.121;
#pod
#pod =cut
sub __legal_phases { qw(configure build test runtime develop) }
sub __legal_types { qw(requires recommends suggests conflicts) }
sub __legal_phases {qw(configure build test runtime develop)}
sub __legal_types {qw(requires recommends suggests conflicts)}
# expect a prereq spec from META.json -- rjbs, 2010-04-11
sub new {
my ($class, $prereq_spec) = @_;
$prereq_spec ||= {};
my %is_legal_phase = map {; $_ => 1 } $class->__legal_phases;
my %is_legal_type = map {; $_ => 1 } $class->__legal_types;
my %is_legal_phase = map { ; $_ => 1 } $class->__legal_phases;
my %is_legal_type = map { ; $_ => 1 } $class->__legal_types;
my %guts;
PHASE: for my $phase (keys %$prereq_spec) {
PHASE: for my $phase (keys %$prereq_spec) {
next PHASE unless $phase =~ /\Ax_/i or $is_legal_phase{$phase};
my $phase_spec = $prereq_spec->{ $phase };
my $phase_spec = $prereq_spec->{$phase};
next PHASE unless keys %$phase_spec;
TYPE: for my $type (keys %$phase_spec) {
next TYPE unless $type =~ /\Ax_/i or $is_legal_type{$type};
my $spec = $phase_spec->{ $type };
my $spec = $phase_spec->{$type};
next TYPE unless keys %$spec;
$guts{prereqs}{$phase}{$type} = CPAN::Meta::Requirements->from_string_hash(
$spec
);
$guts{prereqs}{$phase}{$type}
= CPAN::Meta::Requirements->from_string_hash($spec);
}
}
......@@ -152,7 +152,7 @@ sub with_merged_prereqs {
next unless $req->required_modules;
$new_arg{ $phase }{ $type } = $req->as_string_hash;
$new_arg{$phase}{$type} = $req->as_string_hash;
}
}
......@@ -184,15 +184,15 @@ sub merged_requirements {
my $req = CPAN::Meta::Requirements->new;
for my $phase ( @$phases ) {
for my $phase (@$phases) {
unless ($phase =~ /\Ax_/i or grep { $phase eq $_ } $self->__legal_phases) {
confess "requested requirements for unknown phase: $phase";
}
for my $type ( @$types ) {
for my $type (@$types) {
unless ($type =~ /\Ax_/i or grep { $type eq $_ } $self->__legal_types) {
confess "requested requirements for unknown type: $type";
}
$req->add_requirements( $self->requirements_for($phase, $type) );
$req->add_requirements($self->requirements_for($phase, $type));
}
}
......@@ -220,7 +220,7 @@ sub as_string_hash {
my $req = $self->requirements_for($phase, $type);
next unless $req->required_modules;
$hash{ $phase }{ $type } = $req->as_string_hash;
$hash{$phase}{$type} = $req->as_string_hash;
}
}
......@@ -249,8 +249,8 @@ sub finalize {
$self->{finalized} = 1;
for my $phase (keys %{ $self->{prereqs} }) {
$_->finalize for values %{ $self->{prereqs}{$phase} };
for my $phase (keys %{$self->{prereqs}}) {
$_->finalize for values %{$self->{prereqs}{$phase}};
}
}
......@@ -268,7 +268,7 @@ sub finalize {
sub clone {
my ($self) = @_;
my $clone = (ref $self)->new( $self->as_string_hash );
my $clone = (ref $self)->new($self->as_string_hash);
}
1;
......
......@@ -6,6 +6,7 @@
use 5.006;
use strict;
use warnings;
package CPAN::Meta::Spec;
our $VERSION = '2.150005';
......
=head1 NAME
JSON::PP::Boolean - dummy module providing JSON::PP::Boolean
......
use 5.008001;
use strict;
package Parse::CPAN::Meta;
# ABSTRACT: Parse META.yml and META.json CPAN metadata files
our $VERSION = '1.4417';
......@@ -29,10 +31,10 @@ sub load_file {
sub load_string {
my ($class, $string) = @_;
if ( $string =~ /^---/ ) { # looks like YAML
if ($string =~ /^---/) { # looks like YAML
return $class->load_yaml_string($string);
}
elsif ( $string =~ /^\s*\{/ ) { # looks like JSON
elsif ($string =~ /^\s*\{/) { # looks like JSON
return $class->load_json_string($string);
}
else { # maybe doc-marker-free YAML
......@@ -56,15 +58,14 @@ sub load_json_string {
}
sub yaml_backend {
if (! defined $ENV{PERL_YAML_BACKEND} ) {
_can_load( 'CPAN::Meta::YAML', 0.011 )
if (!defined $ENV{PERL_YAML_BACKEND}) {
_can_load('CPAN::Meta::YAML', 0.011)
or croak "CPAN::Meta::YAML 0.011 is not available\n";
return "CPAN::Meta::YAML";
}
else {
my $backend = $ENV{PERL_YAML_BACKEND};
_can_load( $backend )
or croak "Could not load PERL_YAML_BACKEND '$backend'\n";
_can_load($backend) or croak "Could not load PERL_YAML_BACKEND '$backend'\n";
$backend->can("Load")
or croak "PERL_YAML_BACKEND '$backend' does not implement Load()\n";
return $backend;
......@@ -72,15 +73,14 @@ sub yaml_backend {
}
sub json_backend {
if (! $ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
_can_load( 'JSON::PP' => 2.27103 )
or croak "JSON::PP 2.27103 is not available\n";
if (!$ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
_can_load('JSON::PP' => 2.27103) or croak "JSON::PP 2.27103 is not available\n";
return 'JSON::PP';
}
else {
_can_load( 'JSON' => 2.5 )
or croak "JSON 2.5 is required for " .
"\$ENV{PERL_JSON_BACKEND} = '$ENV{PERL_JSON_BACKEND}'\n";
_can_load('JSON' => 2.5)
or croak "JSON 2.5 is required for "
. "\$ENV{PERL_JSON_BACKEND} = '$ENV{PERL_JSON_BACKEND}'\n";
return "JSON";
}
}
......@@ -100,11 +100,9 @@ sub _can_load {
$file .= ".pm";
return 1 if $INC{$file};
return 0 if exists $INC{$file}; # prior load failed
eval { require $file; 1 }
or return 0;
if ( defined $version ) {
eval { $module->VERSION($version); 1 }
or return 0;
eval { require $file; 1 } or return 0;
if (defined $version) {
eval { $module->VERSION($version); 1 } or return 0;
}
return 1;
}
......
......@@ -46,6 +46,7 @@ use File::Basename;
use File::Spec::Functions;
use DateTime::TimeZone;
use Date::Parse;
# Bug 1270550 - Tie::Hash::NamedCapture must be loaded before Safe.
use Tie::Hash::NamedCapture;
use Safe;
......@@ -88,12 +89,14 @@ sub init_page {
if (${^TAINT}) {
my $path = '';
if (ON_WINDOWS) {
# On Windows, these paths are tainted, preventing
# File::Spec::Win32->tmpdir from using them. But we need
# a place to temporary store attachments which are uploaded.
foreach my $temp (qw(TMPDIR TMP TEMP WINDIR)) {
trick_taint($ENV{$temp}) if $ENV{$temp};
}
# Some DLLs used by Strawberry Perl are also in c\bin,
# see https://rt.cpan.org/Public/Bug/Display.html?id=99104
if (!ON_ACTIVESTATE) {
......@@ -103,8 +106,10 @@ sub init_page {
trick_taint($path);
}
}
# 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'} = $path;
......@@ -144,11 +149,10 @@ sub init_page {
# Allow non-cgi scripts to exit silently (without displaying any
# message), if desired. At this point, no DBI call has been made
# yet, and no error will be returned if the DB is inaccessible.
if (!i_am_cgi()
&& grep { $_ eq $script } SHUTDOWNHTML_EXIT_SILENTLY)
{
if (!i_am_cgi() && grep { $_ eq $script } SHUTDOWNHTML_EXIT_SILENTLY) {
exit;
}
# Plack requires to exit differently.
return -1 if $ENV{BZ_PLACK};
_shutdown();
......@@ -156,10 +160,12 @@ sub init_page {
}
sub _shutdown {
# For security reasons, log out users when Bugzilla is down.
# Bugzilla->login() is required to catch the logincookie, if any.
my $user = eval { Bugzilla->login(LOGIN_OPTIONAL); };
if ($@) {
# The DB is not accessible. Use the default user object.
$user = Bugzilla->user;
$user->{settings} = {};
......@@ -173,10 +179,10 @@ sub _shutdown {
my $extension = 'txt';
if (i_am_cgi()) {
# Set the HTTP status to 503 when Bugzilla is down to avoid pages
# being indexed by search engines.
print $cgi->header(-status => 503,
-retry_after => SHUTDOWNHTML_RETRY_AFTER);
print $cgi->header(-status => 503, -retry_after => SHUTDOWNHTML_RETRY_AFTER);
if (!$cgi->param('ctype') || $cgi->param('ctype') eq 'html') {
$extension = 'html';
......@@ -184,7 +190,7 @@ sub _shutdown {
}
my $template = Bugzilla->template;
my $vars = { message => 'shutdown', userid => $userid };
my $vars = {message => 'shutdown', userid => $userid};
$template->process("global/message.$extension.tmpl", $vars)
or ThrowTemplateError($template->error);
......@@ -204,14 +210,17 @@ sub template_inner {
my $cache = $class->request_cache;
my $current_lang = $cache->{template_current_lang}->[0];
$lang ||= $current_lang || '';
return $cache->{"template_inner_$lang"} ||= Bugzilla::Template->create(language => $lang);
return $cache->{"template_inner_$lang"}
||= Bugzilla::Template->create(language => $lang);
}
our $extension_packages;
sub extensions {
my ($class) = @_;
my $cache = $class->request_cache;
if (!$cache->{extensions}) {
# Under mod_perl, mod_perl.pl populates $extension_packages for us.
if (!$extension_packages) {
$extension_packages = Bugzilla::Extension->load_all();
......@@ -249,8 +258,9 @@ sub feature {
my $cache = $class->process_cache;
my $feature = $cache->{cpan_meta}->feature($feature_name);
# Bugzilla expects this will also load all the modules.. so we have to do that.
# Later we should put a deprecation warning here, and favor calling has_feature().
# Bugzilla expects this will also load all the modules.. so we have to do that.
# Later we should put a deprecation warning here, and favor calling has_feature().
return 1 if $cache->{feature_loaded}{$feature_name};
my @modules = $feature->prereqs->merged_requirements->required_modules;
......@@ -270,7 +280,7 @@ sub has_feature {
my $meta = $cache->{cpan_meta} //= load_cpan_meta();
my $feature = eval { $meta->feature($feature_name) }
or ThrowCodeError('invalid_feature', { feature => $feature_name });
or ThrowCodeError('invalid_feature', {feature => $feature_name});
return $cache->{feature}{$feature_name} = check_cpan_feature($feature)->{ok};
}
......@@ -282,6 +292,7 @@ sub cgi {
sub input_params {
my ($class, $params) = @_;
my $cache = $class->request_cache;
# This is how the WebService and other places set input_params.
if (defined $params) {
$cache->{input_params} = $params;
......@@ -320,6 +331,7 @@ sub sudo_request {
my ($class, $new_user, $new_sudoer) = @_;
$class->request_cache->{user} = $new_user;
$class->request_cache->{sudoer} = $new_sudoer;
# NOTE: If you want to log the start of an sudo session, do it here.
}
......@@ -368,12 +380,13 @@ sub login {
}
my $sudo_target = new Bugzilla::User($sudo_target_id);
if ($authenticated_user->in_group('bz_sudoers')
if ( $authenticated_user->in_group('bz_sudoers')
&& defined $sudo_target
&& !$sudo_target->in_group('bz_sudo_protect'))
{
$class->set_user($sudo_target);
$class->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.
$sudo_target->set_authorizer($authenticated_user->authorizer);
......@@ -383,8 +396,8 @@ sub login {
else {
delete_token($token);
$class->cgi->remove_cookie('sudo');
ThrowUserError('sudo_illegal_action', { sudoer => $authenticated_user,
target_user => $sudo_target });
ThrowUserError('sudo_illegal_action',
{sudoer => $authenticated_user, target_user => $sudo_target});
}
}
else {
......@@ -393,7 +406,8 @@ sub login {
if ($class->sudoer) {
$class->sudoer->update_last_seen_date();
} else {
}
else {
$class->user->update_last_seen_date();
}
......@@ -413,6 +427,7 @@ sub logout {
sub logout_user {
my ($class, $user) = @_;
# When we're logging out another user we leave cookies alone, and
# therefore avoid calling Bugzilla->logout() directly.
Bugzilla::Auth::Persist::Cookie->logout({user => $user});
......@@ -430,6 +445,7 @@ sub logout_request {
my $class = shift;
delete $class->request_cache->{user};
delete $class->request_cache->{sudoer};
# We can't delete from $cgi->cookie, so logincookie data will remain
# there. Don't rely on it: use Bugzilla->user->login instead!
}
......@@ -447,6 +463,7 @@ sub job_queue {
}
sub dbh {
# If we're not connected, then we must want the main db
return $_[0]->request_cache->{dbh} ||= $_[0]->dbh_main;
}
......@@ -511,8 +528,7 @@ sub usage_mode {
$class->error_mode(ERROR_MODE_REST);
}
else {
ThrowCodeError('usage_mode_invalid',
{'invalid_usage_mode', $newval});
ThrowCodeError('usage_mode_invalid', {'invalid_usage_mode', $newval});
}
$class->request_cache->{usage_mode} = $newval;
}
......@@ -521,7 +537,7 @@ sub usage_mode {
return $class->request_cache->{usage_mode};
}
else {
return (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE);
return (i_am_cgi() ? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE);
}
}
......@@ -553,12 +569,14 @@ sub switch_to_shadow_db {
if (!$class->request_cache->{dbh_shadow}) {
if ($class->params->{'shadowdb'}) {
$class->request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow();
} else {
}
else {
$class->request_cache->{dbh_shadow} = $class->dbh_main;
}
}
$class->request_cache->{dbh} = $class->request_cache->{dbh_shadow};
# we have to return $class->dbh instead of {dbh} as
# {dbh_shadow} may be undefined if no shadow DB is used
# and no connection to the main DB has been established yet.
......@@ -597,17 +615,17 @@ sub fields {
$by_type{$field->type}->{$name} = $field;
$by_name{$name} = $field;
}
$cache->{fields} = { by_type => \%by_type, by_name => \%by_name };
$cache->{fields} = {by_type => \%by_type, by_name => \%by_name};
}
my $fields = $cache->{fields};
my %requested;
if (my $types = delete $criteria->{type}) {
$types = ref($types) ? $types : [$types];
%requested = map { %{ $fields->{by_type}->{$_} || {} } } @$types;
%requested = map { %{$fields->{by_type}->{$_} || {}} } @$types;
}
else {
%requested = %{ $fields->{by_name} };
%requested = %{$fields->{by_name}};
}
my $do_by_name = delete $criteria->{by_name};
......@@ -622,15 +640,17 @@ sub fields {
}
}
return $do_by_name ? \%requested
: [sort { $a->sortkey <=> $b->sortkey || $a->name cmp $b->name } values %requested];
return $do_by_name
? \%requested
: [sort { $a->sortkey <=> $b->sortkey || $a->name cmp $b->name }
values %requested];
}
sub active_custom_fields {
my $class = shift;
if (!exists $class->request_cache->{active_custom_fields}) {
$class->request_cache->{active_custom_fields} =
Bugzilla::Field->match({ custom => 1, obsolete => 0 });
$class->request_cache->{active_custom_fields}
= Bugzilla::Field->match({custom => 1, obsolete => 0});
}
return @{$class->request_cache->{active_custom_fields}};
}
......@@ -652,7 +672,7 @@ sub local_timezone {
use constant request_cache => Bugzilla::Install::Util::_cache();
sub clear_request_cache {
%{ request_cache() } = ();
%{request_cache()} = ();
}
# This is a per-process cache. Under mod_cgi it's identical to the
......@@ -696,6 +716,7 @@ sub _cleanup {
}
sub END {
# This is managed in mod_perl.pl and app.psgi when running
# in a persistent environment.
_cleanup() unless i_am_persistent();
......
......@@ -55,6 +55,7 @@ our @EXPORT_OK = qw(
# comment that it was retired. Also, if an error changes its name, you'll
# have to fix it here.
use constant WS_ERROR_CODE => {
# Generic errors (Bugzilla::Object and others) are 50-9
object_not_specified => 50,
reassign_to_empty => 50,
......@@ -70,68 +71,86 @@ use constant WS_ERROR_CODE => {
illegal_date => 56,
param_integer_required => 57,
param_scalar_array_required => 58,
# Bug errors usually occupy the 100-200 range.
improper_bug_id_field_value => 100,
bug_id_does_not_exist => 101,
bug_access_denied => 102,
bug_access_query => 102,
# These all mean "invalid alias"
alias_too_long => 103,
alias_in_use => 103,
alias_is_numeric => 103,
alias_has_comma_or_space => 103,
multiple_alias_not_allowed => 103,
# Misc. bug field errors
illegal_field => 104,
freetext_too_long => 104,
# Component errors
require_component => 105,
component_name_too_long => 105,
product_unknown_component => 105,
# Invalid Product
no_products => 106,
entry_access_denied => 106,
product_access_denied => 106,
product_disabled => 106,
# Invalid Summary
require_summary => 107,
# Invalid field name
invalid_field_name => 108,
# Not authorized to edit the bug
product_edit_denied => 109,
# Comment-related errors
comment_is_private => 110,
comment_id_invalid => 111,
comment_too_long => 114,
comment_invalid_isprivate => 117,
markdown_disabled => 140,
# Comment tagging
comment_tag_disabled => 125,
comment_tag_invalid => 126,
comment_tag_too_long => 127,
comment_tag_too_short => 128,
# See Also errors
bug_url_invalid => 112,
bug_url_too_long => 112,
# Insidergroup Errors
user_not_insider => 113,
# Note: 114 is above in the Comment-related section.
# Bug update errors
illegal_change => 115,
# Dependency errors
dependency_loop_single => 116,
dependency_loop_multi => 116,
# Note: 117 is above in the Comment-related section.
# Dup errors
dupe_loop_detected => 118,
dupe_id_required => 119,
# Bug-related group errors
group_invalid_removal => 120,
group_restriction_not_allowed => 120,
# Status/Resolution errors
missing_resolution => 121,
resolution_not_allowed => 122,
illegal_bug_status_transition => 123,
# Flag errors
flag_status_invalid => 129,
flag_update_denied => 130,
......@@ -162,8 +181,10 @@ use constant WS_ERROR_CODE => {
account_creation_disabled => 501,
account_creation_restricted => 501,
password_too_short => 502,
# Error 503 password_too_long no longer exists.
invalid_username => 504,
# This is from strict_isolation, but it also basically means
# "invalid user."
invalid_user_group => 504,
......@@ -173,9 +194,11 @@ use constant WS_ERROR_CODE => {
# Attachment errors are 600-700.
file_too_large => 600,
invalid_content_type => 601,
# Error 602 attachment_illegal_url no longer exists.
file_not_specified => 603,
missing_attachment_description => 604,
# Error 605 attachment_url_disabled no longer exists.
zero_length_file => 606,
......@@ -271,10 +294,10 @@ sub REST_STATUS_CODE_MAP {
};
Bugzilla::Hook::process('webservice_status_code_map',
{ status_code_map => $status_code_map });
{status_code_map => $status_code_map});
return $status_code_map;
};
}
# These are the fallback defaults for errors not in ERROR_CODE.
use constant ERROR_UNKNOWN_FATAL => -32000;
......
......@@ -27,7 +27,7 @@ use constant DATE_FIELDS => {};
use constant BASE64_FIELDS => {};
# For some methods, we shouldn't call Bugzilla->login before we call them
use constant LOGIN_EXEMPT => { };
use constant LOGIN_EXEMPT => {};
# Used to allow methods to be called in the JSON-RPC WebService via GET.
# Methods that can modify data MUST not be listed here.
......
......@@ -38,17 +38,18 @@ sub REST_RESOURCES {
use re '/a';
return [
# bug-id
qr{^/bug_user_last_visit/(\d+)$}, {
qr{^/bug_user_last_visit/(\d+)$},
{
GET => {
method => 'get',
params => sub {
return { ids => [$_[0]] };
return {ids => [$_[0]]};
},
},
POST => {
method => 'update',
params => sub {
return { ids => [$_[0]] };
return {ids => [$_[0]]};
},
},
},
......@@ -67,7 +68,7 @@ sub update {
$user->login(LOGIN_REQUIRED);
my $ids = $params->{ids} // [];
ThrowCodeError('param_required', { param => 'ids' }) unless @$ids;
ThrowCodeError('param_required', {param => 'ids'}) unless @$ids;
# Cache permissions for bugs. This highly reduces the number of calls to the
# DB. visible_bugs() is only able to handle bug IDs, so we have to skip
......@@ -78,18 +79,15 @@ sub update {
my @results;
my $last_visit_ts = $dbh->selectrow_array('SELECT NOW()');
foreach my $bug_id (@$ids) {
my $bug = Bugzilla::Bug->check({ id => $bug_id, cache => 1 });
my $bug = Bugzilla::Bug->check({id => $bug_id, cache => 1});
ThrowUserError('user_not_involved', { bug_id => $bug->id })
ThrowUserError('user_not_involved', {bug_id => $bug->id})
unless $user->is_involved_in_bug($bug);
$bug->update_user_last_visit($user, $last_visit_ts);
push(
@results,
_bug_user_last_visit_to_hash(
$bug->bug_id, $last_visit_ts, $params
));
push(@results,
_bug_user_last_visit_to_hash($bug->bug_id, $last_visit_ts, $params));
}
$dbh->bz_commit_transaction();
......@@ -105,18 +103,21 @@ sub get {
my @last_visits;
if ($ids) {
# Cache permissions for bugs. This highly reduces the number of calls to
# the DB. visible_bugs() is only able to handle bug IDs, so we have to
# skip aliases.
$user->visible_bugs([grep /^[0-9]+$/, @$ids]);
my %last_visit = map { $_->bug_id => $_->last_visit_ts } @{ $user->last_visited($ids) };
@last_visits = map { _bug_user_last_visit_to_hash($_, $last_visit{$_}, $params) } @$ids;
my %last_visit
= map { $_->bug_id => $_->last_visit_ts } @{$user->last_visited($ids)};
@last_visits
= map { _bug_user_last_visit_to_hash($_, $last_visit{$_}, $params) } @$ids;
}
else {
@last_visits = map {
_bug_user_last_visit_to_hash($_->bug_id, $_->last_visit_ts, $params)
} @{ $user->last_visited };
@last_visits
= map { _bug_user_last_visit_to_hash($_->bug_id, $_->last_visit_ts, $params) }
@{$user->last_visited};
}
return \@last_visits;
......@@ -125,8 +126,8 @@ sub get {
sub _bug_user_last_visit_to_hash {
my ($bug_id, $last_visit_ts, $params) = @_;
my %result = (id => as_int($bug_id),
last_visit_ts => as_datetime($last_visit_ts));
my %result
= (id => as_int($bug_id), last_visit_ts => as_datetime($last_visit_ts));
return filter($params, \%result);
}
......
......@@ -26,11 +26,7 @@ extends 'Bugzilla::API::1_0::Resource';
##############
# Basic info that is needed before logins
use constant LOGIN_EXEMPT => {
parameters => 1,
timezone => 1,
version => 1,
};
use constant LOGIN_EXEMPT => {parameters => 1, timezone => 1, version => 1,};
use constant READ_ONLY => qw(
extensions
......@@ -92,36 +88,12 @@ use constant PARAMETERS_LOGGED_IN => qw(
sub REST_RESOURCES {
my $rest_resources = [
qr{^/version$}, {
GET => {
method => 'version'
}
},
qr{^/extensions$}, {
GET => {
method => 'extensions'
}
},
qr{^/timezone$}, {
GET => {
method => 'timezone'
}
},
qr{^/time$}, {
GET => {
method => 'time'
}
},
qr{^/last_audit_time$}, {
GET => {
method => 'last_audit_time'
}
},
qr{^/parameters$}, {
GET => {
method => 'parameters'
}
}
qr{^/version$}, {GET => {method => 'version'}},
qr{^/extensions$}, {GET => {method => 'extensions'}},
qr{^/timezone$}, {GET => {method => 'timezone'}},
qr{^/time$}, {GET => {method => 'time'}},
qr{^/last_audit_time$}, {GET => {method => 'last_audit_time'}},
qr{^/parameters$}, {GET => {method => 'parameters'}}
];
return $rest_resources;
}
......@@ -132,29 +104,31 @@ sub REST_RESOURCES {
sub version {
my $self = shift;
return { version => as_string(BUGZILLA_VERSION) };
return {version => as_string(BUGZILLA_VERSION)};
}
sub extensions {
my $self = shift;
my %retval;
foreach my $extension (@{ Bugzilla->extensions }) {
foreach my $extension (@{Bugzilla->extensions}) {
my $version = $extension->VERSION || 0;
my $name = $extension->NAME;
$retval{$name}->{version} = as_string($version);
}
return { extensions => \%retval };
return {extensions => \%retval};
}
sub timezone {
my $self = shift;
# All Webservices return times in UTC; Use UTC here for backwards compat.
return { timezone => as_string("+0000") };
return {timezone => as_string("+0000")};
}
sub time {
my ($self) = @_;
# All Webservices return times in UTC; Use UTC here for backwards compat.
# Hardcode values where appropriate
my $dbh = Bugzilla->dbh;
......@@ -163,10 +137,7 @@ sub time {
$db_time = datetime_from($db_time, 'UTC');
my $now_utc = DateTime->now();
return {
db_time => as_datetime($db_time),
web_time => as_datetime($now_utc),
};
return {db_time => as_datetime($db_time), web_time => as_datetime($now_utc),};
}
sub last_audit_time {
......@@ -177,7 +148,7 @@ sub last_audit_time {
my $class_values = $params->{class};
my @class_values_quoted;
foreach my $class_value (@$class_values) {
push (@class_values_quoted, $dbh->quote($class_value))
push(@class_values_quoted, $dbh->quote($class_value))
if $class_value =~ /^Bugzilla(::[a-zA-Z0-9_]+)*$/;
}
......@@ -191,9 +162,7 @@ sub last_audit_time {
# Hardcode values where appropriate
$last_audit_time = datetime_from($last_audit_time, 'UTC');
return {
last_audit_time => as_datetime($last_audit_time)
};
return {last_audit_time => as_datetime($last_audit_time)};
}
sub parameters {
......@@ -202,9 +171,10 @@ sub parameters {
my $params = Bugzilla->params;
$args ||= {};
my @params_list = $user->in_group('tweakparams')
? keys(%$params)
: $user->id ? PARAMETERS_LOGGED_IN : PARAMETERS_LOGGED_OUT;
my @params_list
= $user->in_group('tweakparams') ? keys(%$params)
: $user->id ? PARAMETERS_LOGGED_IN
: PARAMETERS_LOGGED_OUT;
my %parameters;
foreach my $param (@params_list) {
......@@ -212,7 +182,7 @@ sub parameters {
$parameters{$param} = as_string($params->{$param});
}
return { parameters => \%parameters };
return {parameters => \%parameters};
}
1;
......
......@@ -34,12 +34,13 @@ use constant PUBLIC_METHODS => qw(
sub REST_RESOURCES {
my $rest_resources = [
qr{^/classification/([^/]+)$}, {
qr{^/classification/([^/]+)$},
{
GET => {
method => 'get',
params => sub {
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
return { $param => [ $_[0] ] };
return {$param => [$_[0]]};
}
}
}
......@@ -54,9 +55,10 @@ sub REST_RESOURCES {
sub get {
my ($self, $params) = validate(@_, 'names', 'ids');
defined $params->{names} || defined $params->{ids}
|| ThrowCodeError('params_required', { function => 'Classification.get',
params => ['names', 'ids'] });
defined $params->{names}
|| defined $params->{ids}
|| ThrowCodeError('params_required',
{function => 'Classification.get', params => ['names', 'ids']});
my $user = Bugzilla->user;
......@@ -66,43 +68,53 @@ sub get {
Bugzilla->switch_to_shadow_db;
my @classification_objs = @{ params_to_objects($params, 'Bugzilla::Classification') };
my @classification_objs
= @{params_to_objects($params, 'Bugzilla::Classification')};
unless ($user->in_group('editclassifications')) {
my %selectable_class = map { $_->id => 1 } @{$user->get_selectable_classifications};
my %selectable_class
= map { $_->id => 1 } @{$user->get_selectable_classifications};
@classification_objs = grep { $selectable_class{$_->id} } @classification_objs;
}
my @classifications = map { $self->_classification_to_hash($_, $params) } @classification_objs;
my @classifications
= map { $self->_classification_to_hash($_, $params) } @classification_objs;
return { classifications => \@classifications };
return {classifications => \@classifications};
}
sub _classification_to_hash {
my ($self, $classification, $params) = @_;
my $user = Bugzilla->user;
return unless (Bugzilla->params->{'useclassification'} || $user->in_group('editclassifications'));
return
unless (Bugzilla->params->{'useclassification'}
|| $user->in_group('editclassifications'));
my $products = $user->in_group('editclassifications') ?
$classification->products : $user->get_selectable_products($classification->id);
my $products
= $user->in_group('editclassifications')
? $classification->products
: $user->get_selectable_products($classification->id);
return filter $params, {
return filter $params,
{
id => as_int($classification->id),
name => as_string($classification->name),
description => as_string($classification->description),
sort_key => as_int($classification->sortkey),
products => [ map { $self->_product_to_hash($_, $params) } @$products ],
products => [map { $self->_product_to_hash($_, $params) } @$products],
};
}
sub _product_to_hash {
my ($self, $product, $params) = @_;
return filter $params, {
return filter $params,
{
id => as_int($product->id),
name => as_string($product->name),
description => as_string($product->description),
}, undef, 'products';
},
undef, 'products';
}
1;
......
......@@ -39,9 +39,7 @@ use constant CREATE_MAPPED_FIELDS => {
is_open => 'isactive',
};
use constant MAPPED_FIELDS => {
is_open => 'is_active',
};
use constant MAPPED_FIELDS => {is_open => 'is_active',};
use constant MAPPED_RETURNS => {
initialowner => 'default_assignee',
......@@ -52,37 +50,35 @@ use constant MAPPED_RETURNS => {
sub REST_RESOURCES {
my $rest_resources = [
qr{^/component$}, {
POST => {
method => 'create',
success_code => STATUS_CREATED
}
},
qr{^/component/(\d+)$}, {
qr{^/component$},
{POST => {method => 'create', success_code => STATUS_CREATED}},
qr{^/component/(\d+)$},
{
PUT => {
method => 'update',
params => sub {
return { ids => [ $_[0] ] };
return {ids => [$_[0]]};
}
},
DELETE => {
method => 'delete',
params => sub {
return { ids => [ $_[0] ] };
return {ids => [$_[0]]};
}
},
},
qr{^/component/([^/]+)/([^/]+)$}, {
qr{^/component/([^/]+)/([^/]+)$},
{
PUT => {
method => 'update',
params => sub {
return { names => [ { product => $_[0], component => $_[1] } ] };
return {names => [{product => $_[0], component => $_[1]}]};
}
},
DELETE => {
method => 'delete',
params => sub {
return { names => [ { product => $_[0], component => $_[1] } ] };
return {names => [{product => $_[0], component => $_[1]}]};
}
},
},
......@@ -100,10 +96,9 @@ sub create {
my $user = Bugzilla->login(LOGIN_REQUIRED);
$user->in_group('editcomponents')
|| scalar @{ $user->get_products_by_permission('editcomponents') }
|| ThrowUserError('auth_failure', { group => 'editcomponents',
action => 'edit',
object => 'components' });
|| scalar @{$user->get_products_by_permission('editcomponents')}
|| ThrowUserError('auth_failure',
{group => 'editcomponents', action => 'edit', object => 'components'});
my $product = $user->check_can_admin_product($params->{product});
......@@ -113,10 +108,11 @@ sub create {
# Create the component and return the newly created id.
my $component = Bugzilla::Component->create($values);
return { id => as_int($component->id) };
return {id => as_int($component->id)};
}
sub _component_params_to_objects {
# We can't use Util's _param_to_objects since name is a hash
my $params = shift;
my $user = Bugzilla->user;
......@@ -124,17 +120,18 @@ sub _component_params_to_objects {
my @components = ();
if (defined $params->{ids}) {
push @components, @{ Bugzilla::Component->new_from_list($params->{ids}) };
push @components, @{Bugzilla::Component->new_from_list($params->{ids})};
}
if (defined $params->{names}) {
# To get the component objects for product/component combination
# first obtain the product object from the passed product name
foreach my $name_hash (@{$params->{names}}) {
my $product = $user->check_can_admin_product($name_hash->{product});
push @components, @{ Bugzilla::Component->match({
product_id => $product->id,
name => $name_hash->{component}
push @components,
@{Bugzilla::Component->match({
product_id => $product->id, name => $name_hash->{component}
})};
}
}
......@@ -143,6 +140,7 @@ sub _component_params_to_objects {
my @accessible_components;
foreach my $component (@components) {
# Skip if we already included this component
next if $seen_component_ids{$component->id}++;
......@@ -162,14 +160,14 @@ sub update {
my $user = Bugzilla->login(LOGIN_REQUIRED);
$user->in_group('editcomponents')
|| scalar @{ $user->get_products_by_permission('editcomponents') }
|| ThrowUserError("auth_failure", { group => "editcomponents",
action => "edit",
object => "components" });
|| scalar @{$user->get_products_by_permission('editcomponents')}
|| ThrowUserError("auth_failure",
{group => "editcomponents", action => "edit", object => "components"});
defined($params->{names}) || defined($params->{ids})
defined($params->{names})
|| defined($params->{ids})
|| ThrowCodeError('params_required',
{ function => 'Component.update', params => ['ids', 'names'] });
{function => 'Component.update', params => ['ids', 'names']});
my $component_objects = _component_params_to_objects($params);
......@@ -178,7 +176,7 @@ sub update {
if ($params->{name}) {
my %unique_product_comps;
foreach my $comp (@$component_objects) {
if($unique_product_comps{$comp->product_id}) {
if ($unique_product_comps{$comp->product_id}) {
ThrowUserError("multiple_components_update_not_allowed");
}
else {
......@@ -207,26 +205,23 @@ sub update {
my @result;
foreach my $component (@$component_objects) {
my %hash = (
id => $component->id,
changes => {},
);
my %hash = (id => $component->id, changes => {},);
foreach my $field (keys %{ $changes{$component->id} }) {
foreach my $field (keys %{$changes{$component->id}}) {
my $change = $changes{$component->id}->{$field};
if ($field eq 'default_assignee'
if ( $field eq 'default_assignee'
|| $field eq 'default_qa_contact'
|| $field eq 'default_cc'
) {
|| $field eq 'default_cc')
{
# We need to convert user ids to login names
my @old_user_ids = split(/[,\s]+/, $change->[0]);
my @new_user_ids = split(/[,\s]+/, $change->[1]);
my @old_users = map { $_->login }
@{Bugzilla::User->new_from_list(\@old_user_ids)};
my @new_users = map { $_->login }
@{Bugzilla::User->new_from_list(\@new_user_ids)};
my @old_users
= map { $_->login } @{Bugzilla::User->new_from_list(\@old_user_ids)};
my @new_users
= map { $_->login } @{Bugzilla::User->new_from_list(\@new_user_ids)};
$hash{changes}{$field} = {
removed => as_string(join(', ', @old_users)),
......@@ -234,17 +229,15 @@ sub update {
};
}
else {
$hash{changes}{$field} = {
removed => as_string($change->[0]),
added => as_string($change->[1])
};
$hash{changes}{$field}
= {removed => as_string($change->[0]), added => as_string($change->[1])};
}
}
push(@result, \%hash);
}
return { components => \@result };
return {components => \@result};
}
sub delete {
......@@ -253,14 +246,14 @@ sub delete {
my $user = Bugzilla->login(LOGIN_REQUIRED);
$user->in_group('editcomponents')
|| scalar @{ $user->get_products_by_permission('editcomponents') }
|| ThrowUserError("auth_failure", { group => "editcomponents",
action => "edit",
object => "components" });
|| scalar @{$user->get_products_by_permission('editcomponents')}
|| ThrowUserError("auth_failure",
{group => "editcomponents", action => "edit", object => "components"});
defined($params->{names}) || defined($params->{ids})
defined($params->{names})
|| defined($params->{ids})
|| ThrowCodeError('params_required',
{ function => 'Component.delete', params => ['ids', 'names'] });
{function => 'Component.delete', params => ['ids', 'names']});
my $component_objects = _component_params_to_objects($params);
......@@ -273,10 +266,10 @@ sub delete {
my @result;
foreach my $component (@$component_objects) {
push @result, { id => $component->id };
push @result, {id => $component->id};
}
return { components => \@result };
return {components => \@result};
}
1;
......
......@@ -31,28 +31,23 @@ use constant PUBLIC_METHODS => qw(
update
);
use constant MAPPED_RETURNS => {
userregexp => 'user_regexp',
isactive => 'is_active'
};
use constant MAPPED_RETURNS =>
{userregexp => 'user_regexp', isactive => 'is_active'};
sub REST_RESOURCES {
my $rest_resources = [
qr{^/group$}, {
GET => {
method => 'get'
qr{^/group$},
{
GET => {method => 'get'},
POST => {method => 'create', success_code => STATUS_CREATED}
},
POST => {
method => 'create',
success_code => STATUS_CREATED
}
},
qr{^/group/([^/]+)$}, {
qr{^/group/([^/]+)$},
{
PUT => {
method => 'update',
params => sub {
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
return { $param => [ $_[0] ] };
return {$param => [$_[0]]};
}
}
}
......@@ -69,9 +64,9 @@ sub create {
Bugzilla->login(LOGIN_REQUIRED);
Bugzilla->user->in_group('creategroups')
|| ThrowUserError("auth_failure", { group => "creategroups",
action => "add",
object => "group"});
|| ThrowUserError("auth_failure",
{group => "creategroups", action => "add", object => "group"});
# Create group
my $group = Bugzilla::Group->create({
name => $params->{name},
......@@ -81,7 +76,7 @@ sub create {
isbuggroup => 1,
icon_url => $params->{icon_url}
});
return { id => as_int($group->id) };
return {id => as_int($group->id)};
}
sub update {
......@@ -91,13 +86,13 @@ sub update {
Bugzilla->login(LOGIN_REQUIRED);
Bugzilla->user->in_group('creategroups')
|| ThrowUserError("auth_failure", { group => "creategroups",
action => "edit",
object => "group" });
|| ThrowUserError("auth_failure",
{group => "creategroups", action => "edit", object => "group"});
defined($params->{names}) || defined($params->{ids})
defined($params->{names})
|| defined($params->{ids})
|| ThrowCodeError('params_required',
{ function => 'Group.update', params => ['ids', 'names'] });
{function => 'Group.update', params => ['ids', 'names']});
my $group_objects = params_to_objects($params, 'Bugzilla::Group');
......@@ -121,21 +116,16 @@ sub update {
my @result;
foreach my $group (@$group_objects) {
my %hash = (
id => $group->id,
changes => {},
);
foreach my $field (keys %{ $changes{$group->id} }) {
my %hash = (id => $group->id, changes => {},);
foreach my $field (keys %{$changes{$group->id}}) {
my $change = $changes{$group->id}->{$field};
$hash{changes}{$field} = {
removed => as_string($change->[0]),
added => as_string($change->[1])
};
$hash{changes}{$field}
= {removed => as_string($change->[0]), added => as_string($change->[1])};
}
push(@result, \%hash);
}
return { groups => \@result };
return {groups => \@result};
}
sub get {
......@@ -145,7 +135,8 @@ sub get {
# Reject access if there is no sense in continuing.
my $user = Bugzilla->user;
my $all_groups = $user->in_group('editusers') || $user->in_group('creategroups');
my $all_groups
= $user->in_group('editusers') || $user->in_group('creategroups');
if (!$all_groups && !$user->can_bless) {
ThrowUserError('group_cannot_view');
}
......@@ -155,17 +146,20 @@ sub get {
my $groups = [];
if (defined $params->{ids}) {
# Get the groups by id
$groups = Bugzilla::Group->new_from_list($params->{ids});
}
if (defined $params->{names}) {
# Get the groups by name. Check will throw an error if a bad name is given
foreach my $name (@{$params->{names}}) {
# Skip if we got this from params->{id}
next if grep { $_->name eq $name } @$groups;
push @$groups, Bugzilla::Group->check({ name => $name });
push @$groups, Bugzilla::Group->check({name => $name});
}
}
......@@ -181,7 +175,7 @@ sub get {
# Now create a result entry for each.
my @groups = map { $self->_group_to_hash($params, $_) } @$groups;
return { groups => \@groups };
return {groups => \@groups};
}
sub _group_to_hash {
......@@ -218,6 +212,7 @@ sub _get_group_membership {
my $visibleGroups;
if (!$editusers && Bugzilla->params->{'usevisibilitygroups'}) {
# Show only users in visible groups.
$visibleGroups = $user->visible_groups_inherited;
......@@ -227,7 +222,11 @@ sub _get_group_membership {
AND ugm.isbless = 0
AND } . $dbh->sql_in('ugm.group_id', $visibleGroups);
}
} elsif ($editusers || $user->can_bless($group->id) || $user->in_group('creategroups')) {
}
elsif ($editusers
|| $user->can_bless($group->id)
|| $user->in_group('creategroups'))
{
$visibleGroups = 1;
$query .= qq{, user_group_map AS ugm
WHERE ugm.user_id = profiles.userid
......@@ -235,7 +234,7 @@ sub _get_group_membership {
};
}
if (!$visibleGroups) {
ThrowUserError('group_not_visible', { group => $group });
ThrowUserError('group_not_visible', {group => $group});
}
my $grouplist = Bugzilla::Group->flatten_group_membership($group->id);
......@@ -243,8 +242,7 @@ sub _get_group_membership {
my $userids = $dbh->selectcol_arrayref($query);
my $user_objects = Bugzilla::User->new_from_list($userids);
my @users =
map {{
my @users = map { {
id => as_int($_->id),
real_name => as_string($_->name),
name => as_login($_->login),
......@@ -252,7 +250,7 @@ sub _get_group_membership {
can_login => as_boolean($_->is_enabled),
email_enabled => as_boolean($_->email_enabled),
login_denied_text => as_string($_->disabledtext),
}} @$user_objects;
} } @$user_objects;
return \@users;
}
......
......@@ -71,17 +71,18 @@ sub extract_flags {
if ($id) {
my $flag_obj = grep($id == $_->id, @$current_flags);
$flag_obj || ThrowUserError('object_does_not_exist',
{ class => 'Bugzilla::Flag', id => $id });
$flag_obj
|| ThrowUserError('object_does_not_exist',
{class => 'Bugzilla::Flag', id => $id});
}
elsif ($type_id) {
my $type_obj = grep($type_id == $_->id, @$flag_types);
$type_obj || ThrowUserError('object_does_not_exist',
{ class => 'Bugzilla::FlagType', id => $type_id });
$type_obj
|| ThrowUserError('object_does_not_exist',
{class => 'Bugzilla::FlagType', id => $type_id});
if (!$new) {
my @flag_matches = grep($type_id == $_->type->id, @$current_flags);
@flag_matches > 1 && ThrowUserError('flag_not_unique',
{ value => $type_id });
@flag_matches > 1 && ThrowUserError('flag_not_unique', {value => $type_id});
if (!@flag_matches) {
delete $flag->{id};
}
......@@ -93,17 +94,17 @@ sub extract_flags {
}
elsif ($name) {
my @type_matches = grep($name eq $_->name, @$flag_types);
@type_matches > 1 && ThrowUserError('flag_type_not_unique',
{ value => $name });
@type_matches || ThrowUserError('object_does_not_exist',
{ class => 'Bugzilla::FlagType', name => $name });
@type_matches > 1 && ThrowUserError('flag_type_not_unique', {value => $name});
@type_matches
|| ThrowUserError('object_does_not_exist',
{class => 'Bugzilla::FlagType', name => $name});
if ($new) {
delete $flag->{id};
$flag->{type_id} = $type_matches[0]->id;
}
else {
my @flag_matches = grep($name eq $_->type->name, @$current_flags);
@flag_matches > 1 && ThrowUserError('flag_not_unique', { value => $name });
@flag_matches > 1 && ThrowUserError('flag_not_unique', {value => $name});
if (@flag_matches) {
$flag->{id} = $flag_matches[0]->id;
}
......@@ -149,10 +150,11 @@ sub filter_wants($$;$$) {
}
# Mimic old behavior if no types provided
my %field_types = map { $_ => 1 } (ref $types ? @$types : ($types || 'default'));
my %field_types
= map { $_ => 1 } (ref $types ? @$types : ($types || 'default'));
my %include = map { $_ => 1 } @{ $params->{'include_fields'} || [] };
my %exclude = map { $_ => 1 } @{ $params->{'exclude_fields'} || [] };
my %include = map { $_ => 1 } @{$params->{'include_fields'} || []};
my %exclude = map { $_ => 1 } @{$params->{'exclude_fields'} || []};
my %include_types;
my %exclude_types;
......@@ -189,6 +191,7 @@ sub filter_wants($$;$$) {
my $wants = 0;
if ($prefix) {
# Include the field if the parent is include (and this one is not excluded)
$wants = 1 if $include{$prefix};
}
......@@ -205,6 +208,7 @@ sub filter_wants($$;$$) {
sub taint_data {
my @params = @_;
return if !@params;
# Though this is a private function, it hasn't changed since 2004 and
# should be safe to use, and prevents us from having to write it ourselves
# or require another module to do it.
......@@ -216,6 +220,7 @@ sub _delete_bad_keys {
foreach my $item (@_) {
next if ref $item ne 'HASH';
foreach my $key (keys %$item) {
# Making something a hash key always untaints it, in Perl.
# However, we need to validate our argument names in some way.
# We know that all hash keys passed in to the WebService wil
......@@ -233,10 +238,10 @@ sub api_include_exclude {
my ($params) = @_;
if ($params->{'include_fields'} && !ref $params->{'include_fields'}) {
$params->{'include_fields'} = [ split(/[\s+,]/, $params->{'include_fields'}) ];
$params->{'include_fields'} = [split(/[\s+,]/, $params->{'include_fields'})];
}
if ($params->{'exclude_fields'} && !ref $params->{'exclude_fields'}) {
$params->{'exclude_fields'} = [ split(/[\s+,]/, $params->{'exclude_fields'}) ];
$params->{'exclude_fields'} = [split(/[\s+,]/, $params->{'exclude_fields'})];
}
return $params;
......@@ -251,16 +256,17 @@ sub validate {
return ($self, undef) if (defined $params and !ref $params);
my @id_params = qw(ids comment_ids);
# If @keys is not empty then we convert any named
# parameters that have scalar values to arrayrefs
# that match.
foreach my $key (@keys) {
if (exists $params->{$key}) {
$params->{$key} = [ $params->{$key} ] unless ref $params->{$key};
$params->{$key} = [$params->{$key}] unless ref $params->{$key};
if (any { $key eq $_ } @id_params) {
my $ids = $params->{$key};
ThrowCodeError('param_scalar_array_required', { param => $key })
ThrowCodeError('param_scalar_array_required', {param => $key})
unless ref($ids) eq 'ARRAY' && none { ref $_ } @$ids;
}
}
......@@ -272,7 +278,7 @@ sub validate {
sub translate {
my ($params, $mapped) = @_;
my %changes;
while (my ($key,$value) = each (%$params)) {
while (my ($key, $value) = each(%$params)) {
my $new_field = $mapped->{$key} || $key;
$changes{$new_field} = $value;
}
......@@ -283,11 +289,10 @@ sub params_to_objects {
my ($params, $class) = @_;
my (@objects, @objects_by_ids);
@objects = map { $class->check($_) }
@{ $params->{names} } if $params->{names};
@objects = map { $class->check($_) } @{$params->{names}} if $params->{names};
@objects_by_ids = map { $class->check({ id => $_ }) }
@{ $params->{ids} } if $params->{ids};
@objects_by_ids = map { $class->check({id => $_}) } @{$params->{ids}}
if $params->{ids};
push(@objects, @objects_by_ids);
my %seen;
......@@ -302,8 +307,10 @@ sub fix_credentials {
# Allow user to pass in authentication details in X-Headers
# This allows callers to keep credentials out of GET request query-strings
if ($cgi) {
foreach my $field (keys %{ API_AUTH_HEADERS() }) {
next if exists $params->{API_AUTH_HEADERS->{$field}} || ($cgi->http($field) // '') eq '';
foreach my $field (keys %{API_AUTH_HEADERS()}) {
next
if exists $params->{API_AUTH_HEADERS->{$field}}
|| ($cgi->http($field) // '') eq '';
$params->{API_AUTH_HEADERS->{$field}} = uri_unescape($cgi->http($field));
}
}
......@@ -315,11 +322,13 @@ sub fix_credentials {
$params->{'Bugzilla_login'} = delete $params->{'login'};
$params->{'Bugzilla_password'} = delete $params->{'password'};
}
# Allow user to pass api_key=12345678 as a convenience which becomes
# "Bugzilla_api_key" which is what the auth code looks for.
if (exists $params->{api_key}) {
$params->{Bugzilla_api_key} = delete $params->{api_key};
}
# Allow user to pass token=12345678 as a convenience which becomes
# "Bugzilla_token" which is what the auth code looks for.
if (exists $params->{'token'}) {
......@@ -327,7 +336,7 @@ sub fix_credentials {
}
# Allow extensions to modify the credential data before login
Bugzilla::Hook::process('webservice_fix_credentials', { params => $params });
Bugzilla::Hook::process('webservice_fix_credentials', {params => $params});
}
sub datetime_format_inbound {
......@@ -335,10 +344,10 @@ sub datetime_format_inbound {
my $converted = datetime_from($time, Bugzilla->local_timezone);
if (!defined $converted) {
ThrowUserError('illegal_date', { date => $time });
ThrowUserError('illegal_date', {date => $time});
}
$time = $converted->ymd() . ' ' . $converted->hms();
return $time
return $time;
}
sub datetime_format_outbound {
......@@ -348,9 +357,11 @@ sub datetime_format_outbound {
my $time = $date;
if (blessed($date)) {
# We expect this to mean we were sent a datetime object
$time->set_time_zone('UTC');
} else {
}
else {
# We always send our time in UTC, for consistency.
# passed in value is likely a string, create a datetime object
$time = datetime_from($date, 'UTC');
......@@ -368,27 +379,27 @@ sub as_string { defined $_[0] ? $_[0] . '' : JSON::null }
# array types
sub as_int_array { [ map { as_int($_) } @{ $_[0] // [] } ] }
sub as_login_array { [ map { as_login($_) } @{ $_[0] // [] } ] }
sub as_name_array { [ map { as_string($_->name) } @{ $_[0] // [] } ] }
sub as_string_array { [ map { as_string($_) } @{ $_[0] // [] } ] }
sub as_int_array { [map { as_int($_) } @{$_[0] // []}] }
sub as_login_array { [map { as_login($_) } @{$_[0] // []}] }
sub as_name_array { [map { as_string($_->name) } @{$_[0] // []}] }
sub as_string_array { [map { as_string($_) } @{$_[0] // []}] }
# complex types
sub as_datetime {
return defined $_[0]
? datetime_from($_[0], 'UTC')->iso8601() . 'Z'
: JSON::null;
return
defined $_[0] ? datetime_from($_[0], 'UTC')->iso8601() . 'Z' : JSON::null;
}
sub as_login {
defined $_[0]
? ( Bugzilla->params->{use_email_as_login} ? email_filter($_[0]) : $_[0] . '' )
? (Bugzilla->params->{use_email_as_login} ? email_filter($_[0]) : $_[0] . '')
: JSON::null;
}
sub as_email {
defined($_[0]) && Bugzilla->user->in_group('editusers') ? $_[0] . '' : JSON::null;
defined($_[0])
&& Bugzilla->user->in_group('editusers') ? $_[0] . '' : JSON::null;
}
sub as_base64 {
......
......@@ -70,6 +70,7 @@ sub server {
# If we do not match /<namespace>/<version>/ then we assume legacy calls
# and use the default namespace and version.
if ($path_info =~ m|^/([^/]+)/(\d+\.\d+(?:\.\d+)?)/|) {
# First figure out the namespace we are accessing (core is native)
$api_namespace = $1 if $path_info =~ s|^/([^/]+)||;
$api_namespace = $self->_check_namespace($api_namespace);
......@@ -119,11 +120,13 @@ sub constants {
sub response_header {
my ($self, $code, $result) = @_;
# The HTTP body needs to be bytes (not a utf8 string) for recent
# versions of HTTP::Message, but JSON::RPC::Server doesn't handle this
# properly. $_[1] is the HTTP body content we're going to be sending.
if (utf8::is_utf8($result)) {
utf8::encode($result);
# Since we're going to just be sending raw bytes, we need to
# set STDOUT to not expect utf8.
disable_utf8();
......@@ -149,11 +152,8 @@ sub handle_login { }
sub return_error {
my ($self, $status_code, $message, $error_code) = @_;
if ($status_code && $message) {
$self->{_return_error} = {
status_code => $status_code,
error => JSON::true,
message => $message
};
$self->{_return_error}
= {status_code => $status_code, error => JSON::true, message => $message};
$self->{_return_error}->{code} = $error_code if $error_code;
}
return $self->{_return_error};
......@@ -163,11 +163,13 @@ sub callback {
my ($self, $value) = @_;
if (defined $value) {
$value = trim($value);
# We don't use \w because we don't want to allow Unicode here.
if ($value !~ /^[A-Za-z0-9_\.\[\]]+$/) {
ThrowUserError('json_rpc_invalid_callback', { callback => $value });
ThrowUserError('json_rpc_invalid_callback', {callback => $value});
}
$self->{_callback} = $value;
# JSONP needs to be parsed by a JS parser, not by a JSON parser.
$self->content_type('text/javascript');
}
......@@ -179,6 +181,7 @@ sub etag {
my ($self, $data) = @_;
my $cache = Bugzilla->request_cache;
if (defined $data) {
# Serialize the data if passed a reference
local $Storable::canonical = 1;
$data = freeze($data) if ref $data;
......@@ -202,16 +205,12 @@ sub etag {
# HACK: Allow error tag checking to work with t/012throwables.t
sub ThrowUserError {
my ($error, $self, $vars) = @_;
$self->load_error({ type => 'user',
error => $error,
vars => $vars });
$self->load_error({type => 'user', error => $error, vars => $vars});
}
sub ThrowCodeError {
my ($error, $self, $vars) = @_;
$self->load_error({ type => 'code',
error => $error,
vars => $vars });
$self->load_error({type => 'code', error => $error, vars => $vars});
}
###################
......@@ -223,12 +222,11 @@ sub _build_cgi {
}
sub _build_json {
# This may seem a little backwards to set utf8(0), but what this really
# means is "don't convert our utf8 into byte strings, just leave it as a
# utf8 string."
return JSON->new->utf8(0)
->allow_blessed(1)
->convert_blessed(1);
return JSON->new->utf8(0)->allow_blessed(1)->convert_blessed(1);
}
sub _build_request {
......@@ -243,13 +241,13 @@ sub _check_namespace {
# Check if namespace matches an extension name
my $found = 0;
foreach my $extension (@{ Bugzilla->extensions }) {
foreach my $extension (@{Bugzilla->extensions}) {
$found = 1 if lc($extension->NAME) eq lc($namespace);
}
# Make sure we have this namespace available
if (!$found) {
ThrowUserError('unknown_api_namespace', $self,
{ api_namespace => $namespace });
ThrowUserError('unknown_api_namespace', $self, {api_namespace => $namespace});
return DEFAULT_API_NAMESPACE;
}
......@@ -276,8 +274,7 @@ sub _check_version {
# Make sure we actual have this version installed
if (!-d $version_dir) {
ThrowUserError('unknown_api_version', $self,
{ api_version => $old_version,
api_namespace => $namespace });
{api_version => $old_version, api_namespace => $namespace});
return DEFAULT_API_VERSION;
}
......@@ -285,11 +282,9 @@ sub _check_version {
# the Core API it was written for.
if (lc($namespace) ne 'core') {
my $core_api_version;
foreach my $extension (@{ Bugzilla->extensions }) {
foreach my $extension (@{Bugzilla->extensions}) {
next if lc($extension->NAME) ne lc($namespace);
if ($extension->API_VERSION_MAP
&& $extension->API_VERSION_MAP->{$version})
{
if ($extension->API_VERSION_MAP && $extension->API_VERSION_MAP->{$version}) {
$self->api_ext_version($version);
$version = $extension->API_VERSION_MAP->{$version};
}
......@@ -302,12 +297,13 @@ sub _check_version {
sub _best_content_type {
my ($self, @types) = @_;
my @accept_types = $self->_get_content_prefs();
# Return the types as-is if no accept header sent, since sorting will be a no-op.
if (!@accept_types) {
return $types[0];
}
my $score = sub { $self->_score_type(shift, @accept_types) };
my @scored_types = sort {$score->($b) <=> $score->($a)} @types;
my @scored_types = sort { $score->($b) <=> $score->($a) } @types;
return $scored_types[0] || '*/*';
}
......@@ -333,18 +329,19 @@ sub _get_content_prefs {
my ($weight) = ($accept_type =~ /q=(\d\.\d+|\d+)/);
my ($name) = ($accept_type =~ m#(\S+/[^;]+)#);
next unless $name;
push @prefs, { name => $name, order => $order++};
push @prefs, {name => $name, order => $order++};
if (defined $weight) {
$prefs[-1]->{score} = $weight;
} else {
}
else {
$prefs[-1]->{score} = $default_weight;
$default_weight -= 0.001;
}
}
# Sort the types by score, subscore by order, and pull out just the name
@prefs = map {$_->{name}} sort {$b->{score} <=> $a->{score} ||
$a->{order} <=> $b->{order}} @prefs;
@prefs = map { $_->{name} }
sort { $b->{score} <=> $a->{score} || $a->{order} <=> $b->{order} } @prefs;
return @prefs;
}
......
......@@ -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, so we have
# to encode attachment data as utf8.
$attachment->data; # Populate ->{data}
......@@ -99,6 +109,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);
......@@ -116,21 +127,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') {
binmode $interdiff_stdout, ':utf8';
binmode $interdiff_stderr, ':utf8';
} else {
}
else {
binmode $interdiff_stdout;
binmode $interdiff_stderr;
}
......@@ -147,14 +160,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 //= '';
......@@ -162,8 +177,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: $!";
......@@ -182,6 +196,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();
......@@ -196,8 +211,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);
}
######################
......@@ -214,7 +229,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;
......@@ -228,6 +243,7 @@ sub get_unified_diff {
# Prints out to temporary file.
my ($fh, $filename) = File::Temp::tempfile();
if ($format ne 'raw') {
# The HTML page will be displayed with the UTF-8 encoding.
binmode $fh, ':utf8';
}
......@@ -247,15 +263,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})
......@@ -286,11 +301,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 {
......@@ -152,6 +152,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 persistence information can't be re-used again.
# (See Bugzilla::WebService::Server::JSONRPC for more info.)
......@@ -170,27 +171,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});
......@@ -202,25 +204,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,
......@@ -236,11 +240,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;
}
......@@ -59,14 +59,15 @@ 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});
}
}
elsif ($login_cookie && Bugzilla->usage_mode == USAGE_MODE_REST) {
# REST requires an api-token when using cookie authentication
# fall back to a non-authenticated request
$login_cookie = '';
......@@ -84,29 +85,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');
......@@ -118,7 +124,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 {
......@@ -141,10 +147,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);
$dbh->do("DELETE FROM tokens WHERE userid = ? AND tokentype = 'sudo'",
undef, $user->id);
return;
......@@ -109,7 +112,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);
......@@ -137,18 +140,24 @@ 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);
$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
);
my $token = $cgi->cookie('sudo');
delete_token($token);
} else {
}
else {
die("Invalid type $type supplied to logout()");
}
......
......@@ -49,30 +49,34 @@ 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 login, and they are
# not the same id, then we have a conflict.
if ($login_user_id && $extern_user_id
&& $login_user_id ne $extern_user_id)
{
if ($login_user_id && $extern_user_id && $login_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 => $login} };
return {
failure => AUTH_ERROR,
error => "extern_id_conflict",
details =>
{extern_id => $extern_id, extern_user => $extern_name, username => $login}
};
}
# If we have a valid login, but no valid id,
# then we have to create the user. This happens when we're
# passed only a login, and that login doesn't exist already.
if ($login && !$login_user_id && !$extern_user_id) {
validate_email_syntax($email)
|| return { failure => AUTH_ERROR,
validate_email_syntax($email) || return {
failure => AUTH_ERROR,
error => 'auth_invalid_email',
details => {addr => $email} };
details => {addr => $email}
};
# 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.
......@@ -84,7 +88,8 @@ sub create_or_update_user {
login_name => $login,
email => $email,
cryptpassword => $password,
realname => $real_name});
realname => $real_name
});
$login_user_id = $user->id;
}
......@@ -93,7 +98,7 @@ sub create_or_update_user {
if ($extern_id && $login_user_id && !$extern_user_id) {
$dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?',
undef, $extern_id, $login_user_id);
Bugzilla->memcached->clear({ table => 'profiles', id => $login_user_id });
Bugzilla->memcached->clear({table => 'profiles', id => $login_user_id});
}
# Finally, at this point, one of these will give us a valid user id.
......@@ -102,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);
......@@ -112,9 +121,11 @@ sub create_or_update_user {
my $changed = 0;
if ($email && lc($user->email) ne lc($email)) {
validate_email_syntax($email)
|| return { failure => AUTH_ERROR, error => 'auth_invalid_email',
details => {addr => $email} };
validate_email_syntax($email) || return {
failure => AUTH_ERROR,
error => 'auth_invalid_email',
details => {addr => $email}
};
$user->set_email($email);
$changed = 1;
}
......@@ -123,6 +134,7 @@ sub create_or_update_user {
$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);
......@@ -131,7 +143,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,10 +112,11 @@ sub check_credentials {
$params->{email} = $emails[0];
if (@emails > 1) {
# Cycle through the addresses and check if they're Bugzilla logins.
# Use the first one that returns a valid id.
foreach my $email (@emails) {
if ( email_to_id($email) ) {
if (email_to_id($email)) {
$params->{email} = $email;
last;
}
......@@ -125,21 +136,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();
......@@ -164,13 +176,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};
$params->{bz_username} = $username;
......
......@@ -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);
......
......@@ -25,11 +25,15 @@ sub should_handle {
# http://bugs.debian.org/1234
# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=123
# https://debbugs.gnu.org/123
return ((lc($uri->authority) eq 'bugs.debian.org'
or lc($uri->authority) eq 'debbugs.gnu.org')
and (($uri->path =~ /bugreport\.cgi$/
and $uri->query_param('bug') =~ m|^\d+$|a)
or $uri->path =~ m|^/\d+$|a)) ? 1 : 0;
return (
(
lc($uri->authority) eq 'bugs.debian.org'
or lc($uri->authority) eq 'debbugs.gnu.org'
)
and
(($uri->path =~ /bugreport\.cgi$/ and $uri->query_param('bug') =~ m|^\d+$|a)
or $uri->path =~ m|^/\d+$|a)
) ? 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+$|a) ? 1 : 0;
return ($uri->authority =~ /launchpad\.net$/ and $uri->path =~ m|bugs?/\d+$|a)
? 1
: 0;
}
sub _check_value {
......
......@@ -27,13 +27,20 @@ sub should_handle {
# http://sourceforge.net/p/project/feature-requests/12345/
# http://sourceforge.net/p/project/patches/12345/
# http://sourceforge.net/p/project/support-requests/12345/
return (lc($uri->authority) eq 'sourceforge.net'
and (($uri->path eq '/tracker/'
return (
lc($uri->authority) eq 'sourceforge.net'
and (
(
$uri->path eq '/tracker/'
and $uri->query_param('func') eq 'detail'
and $uri->query_param('aid')
and $uri->query_param('group_id')
and $uri->query_param('atid'))
or $uri->path =~ m!^/p/[^/]+/(?:bugs|feature-requests|patches|support-requests)/\d+/?$!)) ? 1 : 0;
and $uri->query_param('atid')
)
or $uri->path
=~ m!^/p/[^/]+/(?:bugs|feature-requests|patches|support-requests)/\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};
}
......
......@@ -17,7 +17,8 @@ use Type::Utils;
use Bugzilla::Util qw(generate_random_password);
my $SRC_KEYWORD = enum['none', 'self', 'unsafe-inline', 'unsafe-eval', 'nonce'];
my $SRC_KEYWORD
= enum ['none', 'self', 'unsafe-inline', 'unsafe-eval', 'nonce'];
my $SRC_URI = declare as Str, where {
$_ =~ m{
^(?: https?:// )? # optional http:// or https://
......@@ -26,11 +27,13 @@ my $SRC_URI = declare as Str, where {
}x;
};
my $SRC = $SRC_KEYWORD | $SRC_URI;
my $SOURCE_LIST = ArrayRef[$SRC];
my $REFERRER_KEYWORD = enum [qw(
my $SOURCE_LIST = ArrayRef [$SRC];
my $REFERRER_KEYWORD = enum [
qw(
no-referrer no-referrer-when-downgrade
origin origin-when-cross-origin unsafe-url
)];
)
];
my @ALL_BOOL = qw( sandbox upgrade_insecure_requests );
my @ALL_SRC = qw(
......@@ -39,15 +42,15 @@ my @ALL_SRC = qw(
object_src script_src style_src
);
has \@ALL_SRC => ( is => 'ro', isa => $SOURCE_LIST, predicate => 1 );
has \@ALL_BOOL => ( is => 'ro', isa => Bool, default => 0 );
has 'report_uri' => ( is => 'ro', isa => Str, predicate => 1 );
has 'base_uri' => ( is => 'ro', isa => Str, predicate => 1 );
has 'report_only' => ( is => 'ro', isa => Bool );
has 'referrer' => ( is => 'ro', isa => $REFERRER_KEYWORD, predicate => 1 );
has 'value' => ( is => 'lazy' );
has 'nonce' => ( is => 'lazy', init_arg => undef, predicate => 1 );
has 'disable' => ( is => 'ro', isa => Bool, default => 0 );
has \@ALL_SRC => (is => 'ro', isa => $SOURCE_LIST, predicate => 1);
has \@ALL_BOOL => (is => 'ro', isa => Bool, default => 0);
has 'report_uri' => (is => 'ro', isa => Str, predicate => 1);
has 'base_uri' => (is => 'ro', isa => Str, predicate => 1);
has 'report_only' => (is => 'ro', isa => Bool);
has 'referrer' => (is => 'ro', isa => $REFERRER_KEYWORD, predicate => 1);
has 'value' => (is => 'lazy');
has 'nonce' => (is => 'lazy', init_arg => undef, predicate => 1);
has 'disable' => (is => 'ro', isa => Bool, default => 0);
sub _has_directive {
my ($self, $directive) = @_;
......@@ -57,7 +60,8 @@ sub _has_directive {
sub header_names {
my ($self) = @_;
my @names = ('Content-Security-Policy', 'X-Content-Security-Policy', 'X-WebKit-CSP');
my @names
= ('Content-Security-Policy', 'X-Content-Security-Policy', 'X-WebKit-CSP');
if ($self->report_only) {
return map { $_ . '-Report-Only' } @names;
}
......@@ -84,7 +88,7 @@ sub _build_value {
foreach my $directive (@list_directives) {
next unless $self->_has_directive($directive);
my @values = map { $self->_quote($_) } @{ $self->$directive };
my @values = map { $self->_quote($_) } @{$self->$directive};
if (@values) {
push @result, join(' ', _name($directive), @values);
}
......@@ -132,7 +136,6 @@ sub _quote {
}
1;
__END__
......
......@@ -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->multi_param()) {
# Store all the lines
if ($param =~ /^line(\d+)$/a) {
foreach my $series_id ($cgi->multi_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;
......
......@@ -26,24 +26,23 @@ 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
%Bugzilla::Config::EXPORT_TAGS = (
admin => [
qw(update_params
SetParam
call_param_onchange_handlers
write_params)],
);
write_params)
],
);
Exporter::export_ok_tags('admin');
# new installs get these set of defaults (unless overridden by the answers file)
my %NEW_INSTALL_DEFAULT = (
or_groups => 1,
use_email_as_login => 0,
);
my %NEW_INSTALL_DEFAULT = (or_groups => 1, use_email_as_login => 0,);
# 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();
......@@ -52,12 +51,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}}) {
......@@ -65,6 +64,7 @@ sub _load_params {
}
}
}
# END INIT CODE
# Subroutines go here
......@@ -75,11 +75,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;
}
......@@ -137,7 +138,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
......@@ -179,22 +180,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;
}
......@@ -207,11 +206,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.
......@@ -221,13 +223,15 @@ 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'};
}
if (exists $param->{'noresolveonopenblockers'}) {
$new_params{'resolution_forbidden_with_open_blockers'} = $param->{'noresolveonopenblockers'} ? 'FIXED' : "";
$new_params{'resolution_forbidden_with_open_blockers'}
= $param->{'noresolveonopenblockers'} ? 'FIXED' : "";
}
# --- DEFAULTS FOR NEW PARAMS ---
......@@ -255,6 +259,7 @@ sub update_params {
# --- REMOVE OLD PARAMS ---
my %oldparams;
# Remove any old params
foreach my $item (keys %$param) {
if (!exists $params{$item}) {
......@@ -325,12 +330,15 @@ 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";
}
}
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
......@@ -341,7 +349,7 @@ sub read_param_file {
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;
}
......
......@@ -23,11 +23,7 @@ use constant get_param_list => (
checker => \&check_inbound_proxies
},
{
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 => 'xsendfile_header',
......@@ -65,7 +57,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,11 +56,7 @@ sub get_param_list {
checker => \&check_multi
},
{
name => 'requirelogin',
type => 'b',
default => '0'
},
{name => 'requirelogin', type => 'b', default => '0'},
{
name => 'emailregexp',
......@@ -84,8 +68,8 @@ 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 @.'
},
{
......@@ -105,22 +89,16 @@ 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 => 'auth_delegation',
type => 'b',
default => 0,
},
{name => 'password_check_on_login', type => 'b', default => '1'},
{name => 'auth_delegation', type => 'b', default => 0,},
);
return @param_list;
}
......
......@@ -27,7 +27,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);
};
......@@ -42,29 +43,13 @@ sub get_param_list {
onchange => \&change_duplicate_or_move_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 => '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 => 'resolution_forbidden_with_open_blockers',
......@@ -72,15 +57,17 @@ sub get_param_list {
choices => \&_get_resolutions,
default => '',
checker => \&check_resolution,
} );
}
);
return @param_list;
}
sub _get_resolutions {
my $resolution_field = Bugzilla::Field->new({ name => 'resolution', cache => 1 });
my $resolution_field = Bugzilla::Field->new({name => 'resolution', cache => 1});
# The empty resolution is included - it represents "no value".
return [ map { $_->name } @{ $resolution_field->legal_values } ];
return [map { $_->name } @{$resolution_field->legal_values}];
}
1;
......@@ -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;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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