From 8de2aaf9e213651afaf12ec10b1091c22b7a9c55 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 10 Jul 2018 15:17:08 -0400 Subject: Bug 1469911 - Make user autocompletion faster --- Bugzilla/DB.pm | 8 +++ Bugzilla/Install/DB.pm | 12 +++++ Bugzilla/User.pm | 35 +++++++++---- Bugzilla/WebService/Server/REST/Resources/User.pm | 5 ++ Bugzilla/WebService/User.pm | 62 +++++++++++++++++++++++ 5 files changed, 111 insertions(+), 11 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 33801e989..80404131a 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -350,6 +350,14 @@ sub import { $Exporter::ExportLevel-- if $is_exporter; } +sub sql_prefix_match { + my ($self, $column, $str) = @_; + my $must_escape = $str =~ s/([_%!])/!$1/g; + my $escape = $must_escape ? q/ESCAPE '!'/ : ''; + my $quoted_str = $self->quote("$str%"); + return "$column LIKE $quoted_str $escape"; +} + sub sql_istrcmp { my ($self, $left, $right, $op) = @_; $op ||= "="; diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index d81bcfbdc..2e5ae5ff2 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -773,6 +773,7 @@ sub update_table_definitions { $dbh->bz_add_index('profiles', 'profiles_realname_ft_idx', {TYPE => 'FULLTEXT', FIELDS => ['realname']}); + _migrate_nicknames(); ################################################################ # New --TABLE-- changes should go *** A B O V E *** this point # @@ -3911,6 +3912,17 @@ sub _migrate_group_owners { $dbh->do('UPDATE groups SET owner_user_id = ?', undef, $nobody->id); } +sub _migrate_nicknames { + my $dbh = Bugzilla->dbh; + my $sth = $dbh->prepare('SELECT userid FROM profiles WHERE realname LIKE "%:%" AND is_enabled = 1 AND NOT nickname'); + $sth->execute(); + while (my ($user_id) = $sth->fetchrow_array) { + my $user = Bugzilla::User->new($user_id); + $user->set_name($user->name); + $user->update(); + } +} + sub _migrate_preference_categories { my $dbh = Bugzilla->dbh; return if $dbh->bz_column_info('setting', 'category'); diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index dc8f60565..9faed25cc 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -80,7 +80,8 @@ sub DB_COLUMNS { 'profiles.password_change_required', 'profiles.password_change_reason', 'profiles.mfa', - 'profiles.mfa_required_date' + 'profiles.mfa_required_date', + 'profiles.nickname' ), } @@ -94,6 +95,7 @@ use constant VALIDATORS => { disabledtext => \&_check_disabledtext, login_name => \&check_login_name_for_creation, realname => \&_check_realname, + nickname => \&_check_realname, extern_id => \&_check_extern_id, is_enabled => \&_check_is_enabled, password_change_required => \&Bugzilla::Object::check_boolean, @@ -114,6 +116,7 @@ sub UPDATE_COLUMNS { password_change_reason mfa mfa_required_date + nickname ); push(@cols, 'cryptpassword') if exists $self->{cryptpassword}; return @cols; @@ -478,10 +481,25 @@ sub set_login { delete $self->{nick}; } +sub _generate_nickname { + my ($name, $login) = @_; + my ($nick) = extract_nicks($name); + if (!$nick) { + $nick = ""; + } + return $nick; +} + sub set_name { my ($self, $name) = @_; $self->set('realname', $name); delete $self->{identity}; + $self->set('nickname', _generate_nickname($name, $self->login)); +} + +sub set_nick { + my ($self, $nick) = @_; + $self->set('nickname', $nick); } sub set_password { @@ -726,12 +744,8 @@ sub nick { my $self = shift; return "" unless $self->id; - - if (!defined $self->{nick}) { - $self->{nick} = (split(/@/, $self->login, 2))[0]; - } - - return $self->{nick}; + return $self->{nickname} if $self->{nickname}; + return $self->{nick} //= (split(/@/, $self->login, 2))[0]; } sub queries { @@ -2514,13 +2528,12 @@ sub get_userlist { } sub create { - my $invocant = shift; - my $class = ref($invocant) || $invocant; + my ($class, $params) = @_; my $dbh = Bugzilla->dbh; $dbh->bz_start_transaction(); - - my $user = $class->SUPER::create(@_); + $params->{nickname} = _generate_nickname($params->{realname}, $params->{login_name}); + my $user = $class->SUPER::create($params); # Turn on all email for the new user require Bugzilla::BugMail; diff --git a/Bugzilla/WebService/Server/REST/Resources/User.pm b/Bugzilla/WebService/Server/REST/Resources/User.pm index eb44e9d2d..6185237fb 100644 --- a/Bugzilla/WebService/Server/REST/Resources/User.pm +++ b/Bugzilla/WebService/Server/REST/Resources/User.pm @@ -20,6 +20,11 @@ BEGIN { sub _rest_resources { my $rest_resources = [ + qr{^/user/suggest$}, { + GET => { + method => 'suggest', + }, + }, qr{^/valid_login$}, { GET => { method => 'valid_login' diff --git a/Bugzilla/WebService/User.pm b/Bugzilla/WebService/User.pm index 5f9b54787..5bb5e32c1 100644 --- a/Bugzilla/WebService/User.pm +++ b/Bugzilla/WebService/User.pm @@ -24,6 +24,7 @@ use Bugzilla::WebService::Util qw(filter filter_wants validate use Bugzilla::Hook; use List::Util qw(first); +use Taint::Util qw(untaint); # Don't need auth to login use constant LOGIN_EXEMPT => { @@ -33,6 +34,7 @@ use constant LOGIN_EXEMPT => { use constant READ_ONLY => qw( get + suggest ); use constant PUBLIC_METHODS => qw( @@ -135,6 +137,66 @@ sub create { return { id => $self->type('int', $user->id) }; } +sub suggest { + my ($self, $params) = @_; + + Bugzilla->switch_to_shadow_db(); + + ThrowCodeError('params_required', { function => 'User.suggest', params => ['match'] }) + unless defined $params->{match}; + + ThrowUserError('user_access_by_match_denied') + unless Bugzilla->user->id; + + untaint($params->{match}); + my $s = $params->{match}; + trim($s); + return { users => [] } if length($s) < 3; + + my $dbh = Bugzilla->dbh; + my @select = ('realname AS real_name', 'login_name AS name'); + my $order = 'last_seen_date DESC'; + my $where; + state $have_mysql = $dbh->isa('Bugzilla::DB::Mysql'); + + if ($s =~ /^[:@](.+)$/s) { + $where = $dbh->sql_prefix_match(nickname => $1); + } + elsif ($s =~ /@/) { + $where = $dbh->sql_prefix_match(login_name => $s); + } + else { + if ($have_mysql && ( $s =~ /[[:space:]]/ || $s =~ /[^[:ascii:]]/ ) ) { + my $match = sprintf 'MATCH(realname) AGAINST (%s) ', $dbh->quote($s); + push @select, "$match AS relevance"; + $order = 'relevance DESC'; + $where = $match; + } + elsif ($have_mysql && $s =~ /^[[:upper:]]/) { + my $match = sprintf 'MATCH(realname) AGAINST (%s) ', $dbh->quote($s); + $where = join ' OR ', + $match, + $dbh->sql_prefix_match( nickname => $s ), + $dbh->sql_prefix_match( login_name => $s ); + } + else { + $where = join ' OR ', $dbh->sql_prefix_match( nickname => $s ), $dbh->sql_prefix_match( login_name => $s ); + } + } + $where = "($where) AND is_enabled = 1"; + + my $sql = 'SELECT ' . join(', ', @select) . " FROM profiles WHERE $where ORDER BY $order LIMIT 25"; + my $results = $dbh->selectall_arrayref($sql, { Slice => {} }); + + my @users = map { + { + real_name => $self->type(string => $_->{real_name}), + name => $self->type(email => $_->{name}), + } + } @$results; + + return { users => \@users }; +} # function to return user information by passing either user ids or # login names or both together: -- cgit v1.2.3-24-g4f1b From a671f4ebe0f80686d9d98d51c754a07f4bb68d31 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 12 Jul 2018 18:20:25 -0400 Subject: Bug 1328659 - Add support for utf8=utf8mb4 (switches to dynamic/compressed row format, and changes charset to utf8mb4) --- Bugzilla/Config/Common.pm | 14 +++--- Bugzilla/Config/General.pm | 5 +- Bugzilla/DB/Mysql.pm | 116 +++++++++++++++++++++++++++++++++++--------- Bugzilla/DB/Schema/Mysql.pm | 27 +++++------ 4 files changed, 116 insertions(+), 46 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Config/Common.pm b/Bugzilla/Config/Common.pm index fabf7c880..24b636099 100644 --- a/Bugzilla/Config/Common.pm +++ b/Bugzilla/Config/Common.pm @@ -83,14 +83,16 @@ sub check_email { sub check_utf8 { - my $utf8 = shift; + my ($utf8, $entry) = @_; - # You cannot turn off the UTF-8 parameter if you've already converted - # your tables to utf-8. - my $dbh = Bugzilla->dbh; - if ( $dbh->isa('Bugzilla::DB::Mysql') && $dbh->bz_db_is_utf8 && !$utf8 ) { - return "You cannot disable UTF-8 support, because your MySQL database" . " is encoded in UTF-8"; + # You cannot turn off the UTF-8 parameter. + if ( !$utf8 ) { + return "You cannot disable UTF-8 support."; } + elsif ($entry eq 'utf8mb4' && $utf8 ne 'utf8mb4') { + return "You cannot disable UTF8-MB4 support."; + } + return ""; } diff --git a/Bugzilla/Config/General.pm b/Bugzilla/Config/General.pm index 9d85aecaf..15688dfd3 100644 --- a/Bugzilla/Config/General.pm +++ b/Bugzilla/Config/General.pm @@ -33,8 +33,9 @@ use constant get_param_list => ( { name => 'utf8', - type => 'b', - default => '0', + type => 's', + choices => [ '1', 'utf8', 'utf8mb4' ], + default => 'utf8', checker => \&check_utf8 }, diff --git a/Bugzilla/DB/Mysql.pm b/Bugzilla/DB/Mysql.pm index d0b9724eb..4dd2620d3 100644 --- a/Bugzilla/DB/Mysql.pm +++ b/Bugzilla/DB/Mysql.pm @@ -32,8 +32,9 @@ use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::DB::Schema::Mysql; -use List::Util qw(max); +use List::Util qw(max any); use Text::ParseWords; +use Carp; # This is how many comments of MAX_COMMENT_LENGTH we expect on a single bug. # In reality, you could have a LOT more comments than this, because @@ -52,9 +53,7 @@ sub BUILDARGS { $dsn .= ";port=$port" if $port; $dsn .= ";mysql_socket=$sock" if $sock; - my %attrs = ( - mysql_enable_utf8 => Bugzilla->params->{'utf8'}, - ); + my %attrs = ( mysql_enable_utf8 => 1 ); return { dsn => $dsn, user => $user, pass => $pass, attrs => \%attrs }; } @@ -64,7 +63,9 @@ sub on_dbi_connected { # This makes sure that if the tables are encoded as UTF-8, we # return their data correctly. - $dbh->do("SET NAMES utf8") if Bugzilla->params->{'utf8'}; + my $charset = $class->utf8_charset; + my $collate = $class->utf8_collate; + $dbh->do("SET NAMES $charset COLLATE $collate"); # Bug 321645 - disable MySQL strict mode, if set my ($var, $sql_mode) = $dbh->selectrow_array( @@ -310,6 +311,26 @@ sub bz_setup_database { die install_string('mysql_innodb_disabled'); } + if ($self->utf8_charset eq 'utf8mb4') { + my %global = map { @$_ } @{ $self->selectall_arrayref(q(SHOW GLOBAL VARIABLES LIKE 'innodb_%')) }; + my $utf8mb4_supported + = $global{innodb_file_format} eq 'Barracuda' + && $global{innodb_file_per_table} eq 'ON' + && $global{innodb_large_prefix} eq 'ON'; + + die install_string('mysql_innodb_settings') unless $utf8mb4_supported; + + my $tables = $self->selectall_arrayref('SHOW TABLE STATUS'); + foreach my $table (@$tables) { + my ($table, undef, undef, $row_format) = @$table; + my $new_row_format = $self->default_row_format($table); + next if $new_row_format =~ /compact/i; + if (lc($new_row_format) ne lc($row_format)) { + print install_string('mysql_row_format_conversion', { table => $table, format => $new_row_format }), "\n"; + $self->do(sprintf 'ALTER TABLE %s ROW_FORMAT=%s', $table, $new_row_format); + } + } + } my ($sd_index_deleted, $longdescs_index_deleted); my @tables = $self->bz_table_list_real(); @@ -345,9 +366,6 @@ sub bz_setup_database { 'SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND ENGINE = ?', undef, $db_name, 'MyISAM'); - foreach my $should_be_myisam (Bugzilla::DB::Schema::Mysql::MYISAM_TABLES) { - @$myisam_tables = grep { $_ ne $should_be_myisam } @$myisam_tables; - } if (scalar @$myisam_tables) { print "Bugzilla now uses the InnoDB storage engine in MySQL for", @@ -520,9 +538,7 @@ sub bz_setup_database { # This kind of situation happens when people create the database # themselves, and if we don't do this they will get the big # scary WARNING statement about conversion to UTF8. - if ( !$self->bz_db_is_utf8 && !@tables - && (Bugzilla->params->{'utf8'} || !scalar keys %{Bugzilla->params}) ) - { + unless ( $self->bz_db_is_utf8 ) { $self->_alter_db_charset_to_utf8(); } @@ -559,11 +575,13 @@ sub bz_setup_database { # the table charsets. # # TABLE_COLLATION IS NOT NULL prevents us from trying to convert views. + my $charset = $self->utf8_charset; + my $collate = $self->utf8_collate; my $non_utf8_tables = $self->selectrow_array( "SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_COLLATION IS NOT NULL - AND TABLE_COLLATION NOT LIKE 'utf8%' - LIMIT 1", undef, $db_name); + AND TABLE_COLLATION != ? + LIMIT 1", undef, $db_name, $collate); if (Bugzilla->params->{'utf8'} && $non_utf8_tables) { print "\n", install_string('mysql_utf8_conversion'); @@ -580,8 +598,7 @@ sub bz_setup_database { } } - print "Converting table storage format to UTF-8. This may take a", - " while.\n"; + print "Converting table storage format to $charset (collate $collate). This may take a while.\n"; foreach my $table ($self->bz_table_list_real) { my $info_sth = $self->prepare("SHOW FULL COLUMNS FROM $table"); $info_sth->execute(); @@ -594,11 +611,11 @@ sub bz_setup_database { # If this particular column isn't stored in utf-8 if ($column->{Collation} && $column->{Collation} ne 'NULL' - && $column->{Collation} !~ /utf8/) + && $column->{Collation} ne $collate) { my $name = $column->{Field}; - print "$table.$name needs to be converted to UTF-8...\n"; + print "$table.$name needs to be converted to $charset (collate $collate)...\n"; # These will be automatically re-created at the end # of checksetup. @@ -618,7 +635,7 @@ sub bz_setup_database { my ($binary, $utf8) = ($sql_def, $sql_def); my $type = $self->_bz_schema->convert_type($col_info->{TYPE}); $binary =~ s/(\Q$type\E)/$1 CHARACTER SET binary/; - $utf8 =~ s/(\Q$type\E)/$1 CHARACTER SET utf8/; + $utf8 =~ s/(\Q$type\E)/$1 CHARACTER SET $charset COLLATE $collate/; push(@binary_sql, "MODIFY COLUMN $name $binary"); push(@utf8_sql, "MODIFY COLUMN $name $utf8"); } @@ -639,7 +656,7 @@ sub bz_setup_database { print "Converting the $table table to UTF-8...\n"; my $bin = "ALTER TABLE $table " . join(', ', @binary_sql); my $utf = "ALTER TABLE $table " . join(', ', @utf8_sql, - 'DEFAULT CHARACTER SET utf8'); + "DEFAULT CHARACTER SET $charset COLLATE $collate"); $self->do($bin); $self->do($utf); @@ -649,7 +666,7 @@ sub bz_setup_database { } } else { - $self->do("ALTER TABLE $table DEFAULT CHARACTER SET utf8"); + $self->do("ALTER TABLE $table DEFAULT CHARACTER SET $charset COLLATE $collate"); } } # foreach my $table (@tables) @@ -660,7 +677,7 @@ sub bz_setup_database { # a mysqldump.) So we have this change outside of the above block, # so that it just happens silently if no actual *table* conversion # needs to happen. - if (Bugzilla->params->{'utf8'} && !$self->bz_db_is_utf8) { + unless ($self->bz_db_is_utf8) { $self->_alter_db_charset_to_utf8(); } @@ -739,18 +756,69 @@ sub _fix_defaults { } } +sub utf8_charset { + return 'utf8' unless Bugzilla->params->{'utf8'}; + return Bugzilla->params->{'utf8'} eq 'utf8mb4' ? 'utf8mb4' : 'utf8'; +} + +sub utf8_collate { + my $charset = utf8_charset(); + if ($charset eq 'utf8') { + return 'utf8_general_ci'; + } + elsif ($charset eq 'utf8mb4') { + return 'utf8mb4_unicode_520_ci'; + } + else { + croak "invalid charset: $charset"; + } +} + +sub default_row_format { + my ($class, $table) = @_; + my $charset = utf8_charset(); + if ($charset eq 'utf8') { + return 'Compact'; + } + elsif ($charset eq 'utf8mb4') { + my @no_compress = qw( + bug_user_last_visit + cc + email_rates + logincookies + token_data + tokens + ts_error + ts_exitstatus + ts_funcmap + ts_job + ts_note + user_request_log + votes + ); + return 'Dynamic' if any { $table eq $_ } @no_compress; + return 'Compressed'; + } + else { + croak "invalid charset: $charset"; + } +} + sub _alter_db_charset_to_utf8 { my $self = shift; my $db_name = Bugzilla->localconfig->{db_name}; - $self->do("ALTER DATABASE $db_name CHARACTER SET utf8"); + my $charset = $self->utf8_charset; + my $collate = $self->utf8_collate; + $self->do("ALTER DATABASE $db_name CHARACTER SET $charset COLLATE $collate"); } sub bz_db_is_utf8 { my $self = shift; - my $db_collation = $self->selectrow_arrayref( + my $db_charset = $self->selectrow_arrayref( "SHOW VARIABLES LIKE 'character_set_database'"); # First column holds the variable name, second column holds the value. - return $db_collation->[1] =~ /utf8/ ? 1 : 0; + my $charset = $self->utf8_charset; + return $db_charset->[1] eq $charset ? 1 : 0; } diff --git a/Bugzilla/DB/Schema/Mysql.pm b/Bugzilla/DB/Schema/Mysql.pm index 0b88d94a6..79814140a 100644 --- a/Bugzilla/DB/Schema/Mysql.pm +++ b/Bugzilla/DB/Schema/Mysql.pm @@ -76,8 +76,6 @@ use constant REVERSE_MAPPING => { # as in their db-specific version, so no reverse mapping is needed. }; -use constant MYISAM_TABLES => qw(); - #------------------------------------------------------------------------------ sub _initialize { @@ -120,16 +118,18 @@ sub _initialize { } #eosub--_initialize #------------------------------------------------------------------------------ sub _get_create_table_ddl { - # Extend superclass method to specify the MYISAM storage engine. # Returns a "create table" SQL statement. - my($self, $table) = @_; - - my $charset = Bugzilla->dbh->bz_db_is_utf8 ? "CHARACTER SET utf8" : ''; - my $type = grep($_ eq $table, MYISAM_TABLES) ? 'MYISAM' : 'InnoDB'; - return($self->SUPER::_get_create_table_ddl($table) - . " ENGINE = $type $charset"); - + my $charset = Bugzilla::DB::Mysql->utf8_charset; + my $collate = Bugzilla::DB::Mysql->utf8_collate; + my $row_format = Bugzilla::DB::Mysql->default_row_format($table); + my @parts = ( + $self->SUPER::_get_create_table_ddl($table), + 'ENGINE = InnoDB', + "CHARACTER SET $charset COLLATE $collate", + "ROW_FORMAT=$row_format", + ); + return join(' ', @parts); } #eosub--_get_create_table_ddl #------------------------------------------------------------------------------ sub _get_create_index_ddl { @@ -153,10 +153,9 @@ sub get_create_database_sql { my ($self, $name) = @_; # We only create as utf8 if we have no params (meaning we're doing # a new installation) or if the utf8 param is on. - my $create_utf8 = Bugzilla->params->{'utf8'} - || !defined Bugzilla->params->{'utf8'}; - my $charset = $create_utf8 ? "CHARACTER SET utf8" : ''; - return ("CREATE DATABASE $name $charset"); + my $charset = Bugzilla::DB::Mysql->utf8_charset; + my $collate = Bugzilla::DB::Mysql->utf8_collate; + return ("CREATE DATABASE $name CHARACTER SET $charset COLLATE $collate"); } # MySQL has a simpler ALTER TABLE syntax than ANSI. -- cgit v1.2.3-24-g4f1b From ac995e4ebf84924b6be9dba3fe8358f0a5857ad1 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 16 Jul 2018 23:25:27 -0400 Subject: Bug 1476052 - Bugzilla mishandles diff attachments that are UTF-8 and contain U+FFFF --- Bugzilla/PatchReader/Raw.pm | 1 + Bugzilla/Template.pm | 2 ++ Bugzilla/Util.pm | 1 + 3 files changed, 4 insertions(+) (limited to 'Bugzilla') diff --git a/Bugzilla/PatchReader/Raw.pm b/Bugzilla/PatchReader/Raw.pm index 0a8387a15..bb5a6cefd 100644 --- a/Bugzilla/PatchReader/Raw.pm +++ b/Bugzilla/PatchReader/Raw.pm @@ -16,6 +16,7 @@ package Bugzilla::PatchReader::Raw; use 5.10.1; use strict; use warnings; +no warnings 'utf8'; use Bugzilla::PatchReader::Base; diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 9eea0d3dd..8cf91052e 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -624,6 +624,7 @@ sub create { # and newlines/carriage returns escaped for use in JS strings. js => sub { my ($var) = @_; + no warnings 'utf8'; $var =~ s/([\\\'\"\/])/\\$1/g; $var =~ s/\n/\\n/g; $var =~ s/\r/\\r/g; @@ -639,6 +640,7 @@ sub create { # for details. json => sub { my ($var) = @_; + no warnings 'utf8'; $var =~ s/([\\\"\/])/\\$1/g; $var =~ s/\n/\\n/g; $var =~ s/\r/\\r/g; diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index a1316c7ef..a8477a62d 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -105,6 +105,7 @@ my %html_quote = ( # Bug 319331: Handle BiDi disruptions. sub html_quote { my $var = shift; + no warnings 'utf8'; $var =~ s/([&<>"@])/$html_quote{$1}/g; state $use_utf8 = Bugzilla->params->{'utf8'}; -- cgit v1.2.3-24-g4f1b From 731deac85cfeb37f870ee7c95669b13be7ad759a Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Tue, 17 Jul 2018 09:09:22 -0400 Subject: Bug 1260096 - BugUserLastVisit api not working and throwing error code : 32614 --- Bugzilla/WebService/Server/REST/Resources/BugUserLastVisit.pm | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Bugzilla') diff --git a/Bugzilla/WebService/Server/REST/Resources/BugUserLastVisit.pm b/Bugzilla/WebService/Server/REST/Resources/BugUserLastVisit.pm index a434d4bef..12290e84e 100644 --- a/Bugzilla/WebService/Server/REST/Resources/BugUserLastVisit.pm +++ b/Bugzilla/WebService/Server/REST/Resources/BugUserLastVisit.pm @@ -32,6 +32,15 @@ sub _rest_resources { }, }, }, + # no bug-id + qr{^/bug_user_last_visit$}, { + GET => { + method => 'get', + }, + POST => { + method => 'update', + }, + }, ]; } -- cgit v1.2.3-24-g4f1b From 351b399991094e57e890a9291484c4ab69ca628b Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Wed, 18 Jul 2018 22:42:56 -0400 Subject: Bug 1476288 - Replace moz_nick with (new, revised) nick and also attempt to disallow duplicate nicks --- Bugzilla/User.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 9faed25cc..4a58043a0 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -482,11 +482,15 @@ sub set_login { } sub _generate_nickname { - my ($name, $login) = @_; + my ($name, $login, $id) = @_; my ($nick) = extract_nicks($name); if (!$nick) { $nick = ""; } + my ($count) = Bugzilla->dbh->selectrow_array('SELECT COUNT(*) FROM profiles WHERE nickname = ? AND userid != ?', undef, $nick, $id); + if ($count) { + $nick = ""; + } return $nick; } @@ -494,7 +498,7 @@ sub set_name { my ($self, $name) = @_; $self->set('realname', $name); delete $self->{identity}; - $self->set('nickname', _generate_nickname($name, $self->login)); + $self->set('nickname', _generate_nickname($name, $self->login, $self->id)); } sub set_nick { @@ -2532,7 +2536,7 @@ sub create { my $dbh = Bugzilla->dbh; $dbh->bz_start_transaction(); - $params->{nickname} = _generate_nickname($params->{realname}, $params->{login_name}); + $params->{nickname} = _generate_nickname($params->{realname}, $params->{login_name}, 0); my $user = $class->SUPER::create($params); # Turn on all email for the new user -- cgit v1.2.3-24-g4f1b From 345e686485e484e087ea58c988e4831209c39554 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 23 Jul 2018 11:25:43 -0400 Subject: Bug 1476841 - Various code cleanups ahead of the Mojolicious patch --- Bugzilla/CPAN.pm | 2 +- Bugzilla/Constants.pm | 9 +++------ Bugzilla/DaemonControl.pm | 19 +++++++++---------- Bugzilla/Template.pm | 11 +++-------- 4 files changed, 16 insertions(+), 25 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/CPAN.pm b/Bugzilla/CPAN.pm index d765d2901..1b6fb93b9 100644 --- a/Bugzilla/CPAN.pm +++ b/Bugzilla/CPAN.pm @@ -103,7 +103,7 @@ sub feature { my $meta = $class->cpan_meta; my $feature = $meta->feature($feature_name); my @modules = $feature->prereqs->merged_requirements(['runtime'], ['requires'])->required_modules; - Module::Runtime::require_module($_) foreach @modules; + Module::Runtime::require_module($_) foreach grep { !/^Test::Taint$/ } @modules; return $FEATURE_LOADED{$feature_name} = 1; } diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 3aa7b0fa4..185a30390 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -15,6 +15,7 @@ use base qw(Exporter); # For bz_locations use File::Basename; +use Cwd qw(realpath); use Memoize; @Bugzilla::Constants::EXPORT = qw( @@ -644,12 +645,8 @@ sub _bz_locations { # directory under both mod_cgi and mod_perl. We call dirname twice # to get the name of the directory above the "Bugzilla/" directory. # - # Calling dirname twice like that won't work on VMS or AmigaOS - # but I doubt anybody runs Bugzilla on those. - # - # On mod_cgi this will be a relative path. On mod_perl it will be an - # absolute path. - my $libpath = dirname(dirname($INC{'Bugzilla/Constants.pm'})); + # Always use an absolute path, based on the location of this file. + my $libpath = realpath(dirname(dirname(__FILE__))); # We have to detaint $libpath, but we can't use Bugzilla::Util here. $libpath =~ /(.*)/; $libpath = $1; diff --git a/Bugzilla/DaemonControl.pm b/Bugzilla/DaemonControl.pm index 2c6df1b87..6ff883af0 100644 --- a/Bugzilla/DaemonControl.pm +++ b/Bugzilla/DaemonControl.pm @@ -14,7 +14,7 @@ use Bugzilla::Logging; use Bugzilla::Constants qw(bz_locations); use Cwd qw(realpath); use English qw(-no_match_vars $PROGRAM_NAME); -use File::Spec::Functions qw(catfile); +use File::Spec::Functions qw(catfile catdir); use Future::Utils qw(repeat try_repeat); use Future; use IO::Async::Loop; @@ -40,12 +40,11 @@ our %EXPORT_TAGS = ( utils => [qw(catch_signal on_exception on_finish)], ); -use constant { - JOBQUEUE_BIN => realpath( catfile( bz_locations->{cgi_path}, 'jobqueue.pl' ) ), - CEREAL_BIN => realpath( catfile( bz_locations->{cgi_path}, 'scripts', 'cereal.pl' ) ), - HTTPD_BIN => '/usr/sbin/httpd', - HTTPD_CONFIG => realpath( catfile( bz_locations->{confdir}, 'httpd.conf' ) ), -}; +my $BUGZILLA_DIR = bz_locations->{cgi_path}; +my $JOBQUEUE_BIN = catfile( $BUGZILLA_DIR, 'jobqueue.pl' ); +my $CEREAL_BIN = catfile( $BUGZILLA_DIR, 'scripts', 'cereal.pl' ); +my $HTTPD_BIN = '/usr/sbin/httpd'; +my $HTTPD_CONFIG = catfile( bz_locations->{confdir}, 'httpd.conf' ); sub catch_signal { my ($name, @done) = @_; @@ -76,7 +75,7 @@ sub run_cereal { my $loop = IO::Async::Loop->new; my $exit_f = $loop->new_future; my $cereal = IO::Async::Process->new( - command => [CEREAL_BIN], + command => [$CEREAL_BIN], on_finish => on_finish($exit_f), on_exception => on_exception( 'cereal', $exit_f ), ); @@ -103,7 +102,7 @@ sub run_httpd { # we have to setsid() to make a new process group # or else apache will kill its parent. setsid(); - my @command = ( HTTPD_BIN, '-DFOREGROUND', '-f' => HTTPD_CONFIG, @args ); + my @command = ( $HTTPD_BIN, '-DFOREGROUND', '-f' => $HTTPD_CONFIG, @args ); exec @command or die "failed to exec $command[0] $!"; }, @@ -122,7 +121,7 @@ sub run_jobqueue { my $loop = IO::Async::Loop->new; my $exit_f = $loop->new_future; my $jobqueue = IO::Async::Process->new( - command => [ JOBQUEUE_BIN, 'start', '-f', '-d', @args ], + command => [ $JOBQUEUE_BIN, 'start', '-f', '-d', @args ], on_finish => on_finish($exit_f), on_exception => on_exception( 'httpd', $exit_f ), ); diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 8cf91052e..299734d64 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -565,13 +565,8 @@ sub create { PRE_CHOMP => 1, TRIM => 1, - # Bugzilla::Template::Plugin::Hook uses the absolute (in mod_perl) - # or relative (in mod_cgi) paths of hook files to explicitly compile - # a specific file. Also, these paths may be absolute at any time - # if a packager has modified bz_locations() to contain absolute - # paths. ABSOLUTE => 1, - RELATIVE => $ENV{MOD_PERL} ? 0 : 1, + RELATIVE => 0, # Only use an on-disk template cache if we're running as the web # server. This ensures the permissions of the cache remain correct. @@ -584,7 +579,7 @@ sub create { # Initialize templates (f.e. by loading plugins like Hook). PRE_PROCESS => ["global/initialize.none.tmpl"], - ENCODING => Bugzilla->params->{'utf8'} ? 'UTF-8' : undef, + ENCODING => 'UTF-8', # Functions for processing text within templates in various ways. # IMPORTANT! When adding a filter here that does not override a @@ -1042,7 +1037,7 @@ sub create { # under mod_perl, use a provider (template loader) that preloads all templates into memory my $provider_class - = $ENV{MOD_PERL} + = $opts{preload} ? 'Bugzilla::Template::PreloadProvider' : 'Template::Provider'; -- cgit v1.2.3-24-g4f1b From acde857e4c2b250210a7dc7fe26ce6c382616c76 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Fri, 27 Jul 2018 10:03:40 -0400 Subject: Bug 1419636 - Make Google Analytics use beacon/XHR instead of img tag --- Bugzilla/CGI.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 03805ad1e..dbcb3ef68 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -39,11 +39,13 @@ sub DEFAULT_CSP { script_src => [ 'self', 'nonce', 'unsafe-inline', 'https://www.google-analytics.com' ], frame_src => [ 'none', ], worker_src => [ 'none', ], - img_src => [ 'self', 'https://secure.gravatar.com', 'https://www.google-analytics.com' ], + img_src => [ 'self', 'https://secure.gravatar.com' ], style_src => [ 'self', 'unsafe-inline' ], object_src => [ 'none' ], connect_src => [ 'self', + # This is for extensions/GoogleAnalytics using beacon or XHR + 'https://www.google-analytics.com', # This is from extensions/OrangeFactor/web/js/orange_factor.js 'https://treeherder.mozilla.org/api/failurecount/', ], @@ -70,9 +72,11 @@ sub SHOW_BUG_MODAL_CSP { my ($bug_id) = @_; my %policy = ( script_src => ['self', 'nonce', 'unsafe-inline', 'unsafe-eval', 'https://www.google-analytics.com' ], - img_src => [ 'self', 'https://secure.gravatar.com', 'https://www.google-analytics.com' ], + img_src => [ 'self', 'https://secure.gravatar.com' ], connect_src => [ 'self', + # This is for extensions/GoogleAnalytics using beacon or XHR + 'https://www.google-analytics.com', # This is from extensions/OrangeFactor/web/js/orange_factor.js 'https://treeherder.mozilla.org/api/failurecount/', ], -- cgit v1.2.3-24-g4f1b From f3f85f5bb8328a7264534a121c412938febda824 Mon Sep 17 00:00:00 2001 From: Kohei Yoshino Date: Fri, 27 Jul 2018 13:26:55 -0400 Subject: Bug 1320977 - Add review/feedback/needinfo request counts and block statuses to /rest/user and /rest/user/suggest responses --- Bugzilla/WebService/User.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Bugzilla') diff --git a/Bugzilla/WebService/User.pm b/Bugzilla/WebService/User.pm index 5bb5e32c1..c569cf9d8 100644 --- a/Bugzilla/WebService/User.pm +++ b/Bugzilla/WebService/User.pm @@ -154,7 +154,7 @@ sub suggest { return { users => [] } if length($s) < 3; my $dbh = Bugzilla->dbh; - my @select = ('realname AS real_name', 'login_name AS name'); + my @select = ('userid AS id', 'realname AS real_name', 'login_name AS name'); my $order = 'last_seen_date DESC'; my $where; state $have_mysql = $dbh->isa('Bugzilla::DB::Mysql'); @@ -190,11 +190,15 @@ sub suggest { my @users = map { { + id => $self->type(int => $_->{id}), real_name => $self->type(string => $_->{real_name}), name => $self->type(email => $_->{name}), } } @$results; + Bugzilla::Hook::process('webservice_user_suggest', + { webservice => $self, params => $params, users => \@users }); + return { users => \@users }; } -- cgit v1.2.3-24-g4f1b From 6df945da5900da86203e0527816690cb1d52c574 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 3 Apr 2018 23:05:04 -0400 Subject: Bug 1455495 - Replace apache with Mojolicious --- Bugzilla/Attachment/PatchReader.pm | 15 +-- Bugzilla/CGI.pm | 32 ++++-- Bugzilla/DaemonControl.pm | 26 +++-- Bugzilla/Error.pm | 4 +- Bugzilla/Install/Filesystem.pm | 127 +-------------------- Bugzilla/ModPerl.pm | 118 ------------------- Bugzilla/ModPerl/BasicAuth.pm | 65 ----------- Bugzilla/ModPerl/BlockIP.pm | 65 ----------- Bugzilla/ModPerl/Hostage.pm | 71 ------------ Bugzilla/ModPerl/StartupFix.pm | 51 --------- Bugzilla/Quantum.pm | 214 +++++++++++++++++++++++++++++++++++ Bugzilla/Quantum/CGI.pm | 162 ++++++++++++++++++++++++++ Bugzilla/Quantum/Plugin/BasicAuth.pm | 40 +++++++ Bugzilla/Quantum/Plugin/BlockIP.pm | 43 +++++++ Bugzilla/Quantum/Plugin/Glue.pm | 114 +++++++++++++++++++ Bugzilla/Quantum/Plugin/Hostage.pm | 80 +++++++++++++ Bugzilla/Quantum/SES.pm | 203 +++++++++++++++++++++++++++++++++ Bugzilla/Quantum/Static.pm | 30 +++++ Bugzilla/Quantum/Stdout.pm | 59 ++++++++++ Bugzilla/Quantum/Template.pm | 39 +++++++ Bugzilla/Template.pm | 1 + Bugzilla/WebService/Server/XMLRPC.pm | 6 +- Bugzilla/WebService/Util.pm | 8 +- 23 files changed, 1035 insertions(+), 538 deletions(-) delete mode 100644 Bugzilla/ModPerl.pm delete mode 100644 Bugzilla/ModPerl/BasicAuth.pm delete mode 100644 Bugzilla/ModPerl/BlockIP.pm delete mode 100644 Bugzilla/ModPerl/Hostage.pm delete mode 100644 Bugzilla/ModPerl/StartupFix.pm create mode 100644 Bugzilla/Quantum.pm create mode 100644 Bugzilla/Quantum/CGI.pm create mode 100644 Bugzilla/Quantum/Plugin/BasicAuth.pm create mode 100644 Bugzilla/Quantum/Plugin/BlockIP.pm create mode 100644 Bugzilla/Quantum/Plugin/Glue.pm create mode 100644 Bugzilla/Quantum/Plugin/Hostage.pm create mode 100644 Bugzilla/Quantum/SES.pm create mode 100644 Bugzilla/Quantum/Static.pm create mode 100644 Bugzilla/Quantum/Stdout.pm create mode 100644 Bugzilla/Quantum/Template.pm (limited to 'Bugzilla') diff --git a/Bugzilla/Attachment/PatchReader.pm b/Bugzilla/Attachment/PatchReader.pm index 2cfbf2c6b..8025f5b82 100644 --- a/Bugzilla/Attachment/PatchReader.pm +++ b/Bugzilla/Attachment/PatchReader.pm @@ -116,18 +116,9 @@ sub process_interdiff { $ENV{'PATH'} = $lc->{diffpath}; my ($pid, $interdiff_stdout, $interdiff_stderr); - if ($ENV{MOD_PERL}) { - 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] - ); - } else { - $interdiff_stderr = gensym; - my $pid = open3(gensym, $interdiff_stdout, $interdiff_stderr, - $lc->{interdiffbin}, $old_filename, $new_filename); - } + $interdiff_stderr = gensym; + $pid = open3(gensym, $interdiff_stdout, $interdiff_stderr, + $lc->{interdiffbin}, $old_filename, $new_filename); binmode $interdiff_stdout; # Check for errors diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index dbcb3ef68..01d610db1 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -123,7 +123,7 @@ sub new { # Under mod_perl, CGI's global variables get reset on each request, # so we need to set them up again every time. - $class->_init_bz_cgi_globals() if $ENV{MOD_PERL}; + $class->_init_bz_cgi_globals(); my $self = $class->SUPER::new(@args); @@ -481,11 +481,6 @@ sub _prevent_unsafe_response { print $self->SUPER::header(-type => 'text/html', -status => '403 Forbidden'); if ($content_type ne 'text/html') { print "Untrusted Referer Header\n"; - if ($ENV{MOD_PERL}) { - my $r = $self->r; - $r->rflush; - $r->status(200); - } } exit; } @@ -603,8 +598,25 @@ sub header { $headers{'-link'} .= ', ; rel="preconnect"; crossorigin'; } } - - return $self->SUPER::header(%headers) || ""; + my $headers = $self->SUPER::header(%headers) || ''; + if ($self->server_software eq 'Bugzilla::Quantum::CGI') { + my $c = $Bugzilla::Quantum::CGI::C; + $c->res->headers->parse($headers); + my $status = $c->res->headers->status; + if ($status && $status =~ /^([0-9]+)/) { + $c->res->code($1); + } + elsif ($c->res->headers->location) { + $c->res->code(302); + } + else { + $c->res->code(200); + } + return ''; + } + else { + LOGDIE("Bugzilla::CGI->header() should only be called from inside Bugzilla::Quantum::CGI!"); + } } sub param { @@ -721,6 +733,7 @@ sub redirect { return $self->SUPER::redirect(@_); } +use Bugzilla::Logging; # This helps implement Bugzilla::Search::Recent, and also shortens search # URLs that get POSTed to buglist.cgi. sub redirect_search_url { @@ -791,9 +804,6 @@ sub redirect_to_https { # and do not work with 302. Our redirect really is permanent anyhow, so # it doesn't hurt to make it a 301. print $self->redirect(-location => $url, -status => 301); - - # When using XML-RPC with mod_perl, we need the headers sent immediately. - $self->r->rflush if $ENV{MOD_PERL}; exit; } diff --git a/Bugzilla/DaemonControl.pm b/Bugzilla/DaemonControl.pm index 6ff883af0..5cb32973f 100644 --- a/Bugzilla/DaemonControl.pm +++ b/Bugzilla/DaemonControl.pm @@ -23,7 +23,8 @@ use IO::Async::Protocol::LineStream; use IO::Async::Signal; use IO::Socket; use LWP::Simple qw(get); -use POSIX qw(setsid WEXITSTATUS); +use JSON::MaybeXS qw(encode_json); +use POSIX qw(WEXITSTATUS); use base qw(Exporter); @@ -43,8 +44,14 @@ our %EXPORT_TAGS = ( my $BUGZILLA_DIR = bz_locations->{cgi_path}; my $JOBQUEUE_BIN = catfile( $BUGZILLA_DIR, 'jobqueue.pl' ); my $CEREAL_BIN = catfile( $BUGZILLA_DIR, 'scripts', 'cereal.pl' ); -my $HTTPD_BIN = '/usr/sbin/httpd'; -my $HTTPD_CONFIG = catfile( bz_locations->{confdir}, 'httpd.conf' ); +my $BUGZILLA_BIN = catfile( $BUGZILLA_DIR, 'bugzilla.pl' ); +my $HYPNOTOAD_BIN = catfile( $BUGZILLA_DIR, 'local', 'bin', 'hypnotoad' ); +my @PERL5LIB = ( $BUGZILLA_DIR, catdir($BUGZILLA_DIR, 'lib'), catdir($BUGZILLA_DIR, 'local', 'lib', 'perl5') ); + +my %HTTP_BACKENDS = ( + hypnotoad => [ $HYPNOTOAD_BIN, $BUGZILLA_BIN, '-f' ], + simple => [ $BUGZILLA_BIN, 'daemon' ], +); sub catch_signal { my ($name, @done) = @_; @@ -98,13 +105,12 @@ sub run_httpd { my $exit_f = $loop->new_future; my $httpd = IO::Async::Process->new( code => sub { - - # we have to setsid() to make a new process group - # or else apache will kill its parent. - setsid(); - my @command = ( $HTTPD_BIN, '-DFOREGROUND', '-f' => $HTTPD_CONFIG, @args ); - exec @command - or die "failed to exec $command[0] $!"; + $ENV{BUGZILLA_HTTPD_ARGS} = encode_json(\@args); + $ENV{PERL5LIB} = join(':', @PERL5LIB); + my $backend = $ENV{HTTP_BACKEND} // 'hypnotoad'; + my $command = $HTTP_BACKENDS{ $backend }; + exec @$command + or die "failed to exec $command->[0] $!"; }, on_finish => on_finish($exit_f), on_exception => on_exception( 'httpd', $exit_f ), diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index 9fcd16386..f932294b0 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -31,7 +31,7 @@ use Scalar::Util qw(blessed); sub _in_eval { my $in_eval = 0; for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) { - last if $sub =~ /^ModPerl/; + last if $sub =~ /^Bugzilla::Quantum::CGI::try/; $in_eval = 1 if $sub =~ /^\(eval\)/; } return $in_eval; @@ -196,7 +196,7 @@ sub ThrowTemplateError { # mod_perl overrides exit to call die with this string # we never want to display this to the user - exit if $template_err =~ /\bModPerl::Util::exit\b/; + die $template_err if ref($template_err) eq 'ARRAY' && $template_err->[0] eq "EXIT\n"; state $logger = Log::Log4perl->get_logger('Bugzilla.Error.Template'); $logger->error($template_err); diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm index 003be22e4..f520d3d56 100644 --- a/Bugzilla/Install/Filesystem.pm +++ b/Bugzilla/Install/Filesystem.pm @@ -41,56 +41,11 @@ use English qw(-no_match_vars $OSNAME); use base qw(Exporter); our @EXPORT = qw( update_filesystem - create_htaccess fix_all_file_permissions fix_dir_permissions fix_file_permissions ); -use constant HT_DEFAULT_DENY => <<'EOT'; -# nothing in this directory is retrievable unless overridden by an .htaccess -# in a subdirectory -deny from all -EOT - -use constant HT_GRAPHS_DIR => <<'EOT'; -# Allow access to .png and .gif files. - - Allow from all - - -# And no directory listings, either. -Deny from all -EOT - -use constant HT_WEBDOT_DIR => <<'EOT'; -# Restrict access to .dot files to the public webdot server at research.att.com -# if research.att.com ever changes their IP, or if you use a different -# webdot server, you'll need to edit this - - Allow from 192.20.225.0/24 - Deny from all - - -# Allow access to .png files created by a local copy of 'dot' - - Allow from all - - -# And no directory listings, either. -Deny from all -EOT - -use constant HT_ASSETS_DIR => <<'EOT'; -# Allow access to .css and js files - - Allow from all - - -# And no directory listings, either. -Deny from all -EOT - use constant INDEX_HTML => <<'EOT'; @@ -230,13 +185,13 @@ sub FILESYSTEM { 'jobqueue-worker.pl' => { perms => OWNER_EXECUTE }, 'clean-bug-user-last-visit.pl' => { perms => WS_EXECUTE }, + 'bugzilla.pl' => { perms => OWNER_EXECUTE }, 'Bugzilla.pm' => { perms => CGI_READ }, "$localconfig*" => { perms => CGI_READ }, 'META.*' => { perms => CGI_READ }, 'MYMETA.*' => { perms => CGI_READ }, 'bugzilla.dtd' => { perms => WS_SERVE }, 'mod_perl.pl' => { perms => WS_SERVE }, - '.htaccess' => { perms => WS_SERVE }, 'cvs-update.log' => { perms => WS_SERVE }, 'scripts/sendunsentbugmail.pl' => { perms => WS_EXECUTE }, 'docs/bugzilla.ent' => { perms => OWNER_WRITE }, @@ -438,54 +393,15 @@ sub FILESYSTEM { 'index.html' => { perms => WS_SERVE, contents => INDEX_HTML } ); - # Because checksetup controls the .htaccess creation separately - # by a localconfig variable, these go in a separate variable from - # %create_files. - # - # Note that these get WS_SERVE as their permission - # because they're *read* by the webserver, even though they're not - # actually, themselves, served. - my %htaccess = ( - "$attachdir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$libdir/Bugzilla/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$extlib/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$templatedir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 'contrib/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 'scripts/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 't/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - 'xt/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - '.circleci/.htaccess' => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$confdir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$datadir/.htaccess" => { perms => WS_SERVE, - contents => HT_DEFAULT_DENY }, - "$graphsdir/.htaccess" => { perms => WS_SERVE, - contents => HT_GRAPHS_DIR }, - "$webdotdir/.htaccess" => { perms => WS_SERVE, - contents => HT_WEBDOT_DIR }, - "$assetsdir/.htaccess" => { perms => WS_SERVE, - contents => HT_ASSETS_DIR }, - ); - Bugzilla::Hook::process('install_filesystem', { files => \%files, create_dirs => \%create_dirs, non_recurse_dirs => \%non_recurse_dirs, recurse_dirs => \%recurse_dirs, create_files => \%create_files, - htaccess => \%htaccess, }); - my %all_files = (%create_files, %htaccess, %index_html, %files); + my %all_files = (%create_files, %index_html, %files); my %all_dirs = (%create_dirs, %non_recurse_dirs); return { @@ -494,7 +410,6 @@ sub FILESYSTEM { all_dirs => \%all_dirs, create_files => \%create_files, - htaccess => \%htaccess, index_html => \%index_html, all_files => \%all_files, }; @@ -542,13 +457,6 @@ sub update_filesystem { _rename_file($oldparamsfile, "$datadir/$oldparamsfile"); } - # Remove old assets htaccess file to force recreation with correct values. - if (-e "$assetsdir/.htaccess") { - if (read_file("$assetsdir/.htaccess") =~ //) { - unlink("$assetsdir/.htaccess"); - } - } - _create_files(%files); if ($params->{index_html}) { _create_files(%{$fs->{index_html}}); @@ -653,27 +561,6 @@ sub _convert_single_file_skins { } } -sub create_htaccess { - _create_files(%{FILESYSTEM()->{htaccess}}); - - # Repair old .htaccess files - - my $webdot_dir = bz_locations()->{'webdotdir'}; - # The public webdot IP address changed. - my $webdot = new IO::File("$webdot_dir/.htaccess", 'r') - || die "$webdot_dir/.htaccess: $!"; - my $webdot_data; - { local $/; $webdot_data = <$webdot>; } - $webdot->close; - if ($webdot_data =~ /192\.20\.225\.10/) { - print "Repairing $webdot_dir/.htaccess...\n"; - $webdot_data =~ s/192\.20\.225\.10/192.20.225.0\/24/g; - $webdot = new IO::File("$webdot_dir/.htaccess", 'w') || die $!; - print $webdot $webdot_data; - $webdot->close; - } -} - sub _rename_file { my ($from, $to) = @_; print install_string('file_rename', { from => $from, to => $to }), "\n"; @@ -984,16 +871,6 @@ Params: C - Whether or not we should create Returns: nothing -=item C - -Description: Creates all of the .htaccess files for Apache, - in the various Bugzilla directories. Also updates - the .htaccess files if they need updating. - -Params: none - -Returns: nothing - =item C Description: Sets all the file permissions on all of Bugzilla's files diff --git a/Bugzilla/ModPerl.pm b/Bugzilla/ModPerl.pm deleted file mode 100644 index 19cd1128f..000000000 --- a/Bugzilla/ModPerl.pm +++ /dev/null @@ -1,118 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::ModPerl; - -use 5.10.1; -use strict; -use warnings; - -use File::Find (); -use Cwd (); -use Carp (); - -# We don't need (or want) to use Bugzilla's template subclass. -# it is easier to reason with the code without all the extra things Bugzilla::Template adds -# (and there might be side-effects, since this code is loaded very early in the httpd startup) -use Template (); - -use Bugzilla::ModPerl::BlockIP; -use Bugzilla::ModPerl::Hostage; - -sub apache_config { - my ($class, $cgi_path) = @_; - - Carp::croak "\$cgi_path is required" unless $cgi_path; - - my %htaccess; - $cgi_path = Cwd::realpath($cgi_path); - my $wanted = sub { - package File::Find; - our ($name, $dir); - - if ($name =~ m#/\.htaccess$#) { - open my $fh, '<', $name or die "cannot open $name $!"; - my $contents = do { - local $/ = undef; - <$fh>; - }; - close $fh; - $htaccess{$dir} = { file => $name, contents => $contents, dir => $dir }; - } - }; - - File::Find::find( { wanted => $wanted, no_chdir => 1 }, $cgi_path ); - my $template = Template->new; - my $conf; - my %vars = ( - root_htaccess => delete $htaccess{$cgi_path}, - htaccess_files => [ map { $htaccess{$_} } sort { length $a <=> length $b } keys %htaccess ], - cgi_path => $cgi_path, - ); - $template->process(\*DATA, \%vars, \$conf); - my $apache_version = Apache2::ServerUtil::get_server_version(); - if ($apache_version =~ m!Apache/(\d+)\.(\d+)\.(\d+)!) { - my ($major, $minor, $patch) = ($1, $2, $3); - if ($major > 2 || $major == 2 && $minor >= 4) { - $conf =~ s{^\s+deny\s+from\s+all.*$}{Require all denied}gmi; - $conf =~ s{^\s+allow\s+from\s+all.*$}{Require all granted}gmi; - $conf =~ s{^\s+allow\s+from\s+(\S+).*$}{Require host $1}gmi; - } - } - - return $conf; -} - -1; - -__DATA__ -# Make sure each httpd child receives a different random seed (bug 476622). -# Bugzilla::RNG has one srand that needs to be called for -# every process, and Perl has another. (Various Perl modules still use -# the built-in rand(), even though we never use it in Bugzilla itself, -# so we need to srand() both of them.) -PerlChildInitHandler "sub { Bugzilla::RNG::srand(); srand(); eval { Bugzilla->dbh->ping } }" -PerlInitHandler Bugzilla::ModPerl::Hostage -PerlAccessHandler Bugzilla::ModPerl::BlockIP - -# It is important to specify ErrorDocuments outside of all directories. -# These used to be in .htaccess, but then things like "AllowEncodedSlashes no" -# mean that urls containing %2f are unstyled. -ErrorDocument 401 /errors/401.html -ErrorDocument 403 /errors/403.html -ErrorDocument 404 /errors/404.html -ErrorDocument 500 /errors/500.html - - - AddHandler perl-script .cgi - # No need to PerlModule these because they're already defined in mod_perl.pl - PerlResponseHandler Bugzilla::ModPerl::ResponseHandler - PerlCleanupHandler Bugzilla::ModPerl::CleanupHandler Apache2::SizeLimit - PerlOptions +ParseHeaders - Options +ExecCGI +FollowSymLinks - DirectoryIndex index.cgi index.html - AllowOverride none - # from [% root_htaccess.file %] - [% root_htaccess.contents FILTER indent %] - - -# AWS SES endpoint for handling mail bounces/complaints - - PerlSetEnv AUTH_VAR_NAME ses_username - PerlSetEnv AUTH_VAR_PASS ses_password - PerlAuthenHandler Bugzilla::ModPerl::BasicAuth - AuthName SES - AuthType Basic - require valid-user - - -# directory rules for all the other places we have .htaccess files -[% FOREACH htaccess IN htaccess_files %] -# from [% htaccess.file %] - - [% htaccess.contents FILTER indent %] - -[% END %] diff --git a/Bugzilla/ModPerl/BasicAuth.pm b/Bugzilla/ModPerl/BasicAuth.pm deleted file mode 100644 index 7248a19f3..000000000 --- a/Bugzilla/ModPerl/BasicAuth.pm +++ /dev/null @@ -1,65 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::ModPerl::BasicAuth; -use 5.10.1; -use strict; -use warnings; - -# Protects a mod_perl with Basic HTTP authentication. -# -# Example use: -# -# -# PerlAuthenHandler Bugzilla::ModPerl::BasicAuth -# PerlSetEnv AUTH_VAR_NAME ses_username -# PerlSetEnv AUTH_VAR_PASS ses_password -# AuthName SES -# AuthType Basic -# require valid-user -# -# -# AUTH_VAR_NAME and AUTH_VAR_PASS are the names of variables defined in -# `localconfig` which hold the authentication credentials. - -use Apache2::Const -compile => qw(OK HTTP_UNAUTHORIZED); ## no critic (Freenode::ModPerl) -use Bugzilla::Logging; -use Bugzilla (); - -sub handler { - my $r = shift; - my ($status, $password) = $r->get_basic_auth_pw; - if ($status != Apache2::Const::OK) { - WARN("Got non-OK status: $status when trying to get password"); - return $status - } - - my $auth_var_name = $ENV{AUTH_VAR_NAME}; - my $auth_var_pass = $ENV{AUTH_VAR_PASS}; - unless ($auth_var_name && $auth_var_pass) { - ERROR('AUTH_VAR_NAME and AUTH_VAR_PASS environmental vars not set'); - $r->note_basic_auth_failure; - return Apache2::Const::HTTP_UNAUTHORIZED; - } - - my $auth_user = Bugzilla->localconfig->{$auth_var_name}; - my $auth_pass = Bugzilla->localconfig->{$auth_var_pass}; - unless ($auth_user && $auth_pass) { - ERROR("$auth_var_name and $auth_var_pass not configured"); - $r->note_basic_auth_failure; - return Apache2::Const::HTTP_UNAUTHORIZED; - } - - unless ($r->user eq $auth_user && $password eq $auth_pass) { - $r->note_basic_auth_failure; - WARN('username and password do not match'); - return Apache2::Const::HTTP_UNAUTHORIZED; - } - - return Apache2::Const::OK; -} - -1; diff --git a/Bugzilla/ModPerl/BlockIP.pm b/Bugzilla/ModPerl/BlockIP.pm deleted file mode 100644 index 4e9a4be5c..000000000 --- a/Bugzilla/ModPerl/BlockIP.pm +++ /dev/null @@ -1,65 +0,0 @@ -package Bugzilla::ModPerl::BlockIP; -use 5.10.1; -use strict; -use warnings; - -use Apache2::RequestRec (); -use Apache2::Connection (); - -use Apache2::Const -compile => qw(OK); -use Cache::Memcached::Fast; - -use constant BLOCK_TIMEOUT => 60*60; - -my $MEMCACHED = Bugzilla::Memcached->_new()->{memcached}; -my $STATIC_URI = qr{ - ^/ - (?: extensions/[^/]+/web - | robots\.txt - | __heartbeat__ - | __lbheartbeat__ - | __version__ - | images - | skins - | js - | errors - ) -}xms; - -sub block_ip { - my ($class, $ip) = @_; - $MEMCACHED->set("block_ip:$ip" => 1, BLOCK_TIMEOUT) if $MEMCACHED; -} - -sub unblock_ip { - my ($class, $ip) = @_; - $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; -} - -sub handler { - my $r = shift; - return Apache2::Const::OK if $r->uri =~ $STATIC_URI; - - my $ip = $r->headers_in->{'X-Forwarded-For'}; - if ($ip) { - $ip = (split(/\s*,\s*/ms, $ip))[-1]; - } - else { - $ip = $r->connection->remote_ip; - } - - if ($MEMCACHED && $MEMCACHED->get("block_ip:$ip")) { - __PACKAGE__->block_ip($ip); - $r->status_line("429 Too Many Requests"); - # 500 is used here because apache 2.2 doesn't understand 429. - # the above line and the return value together mean we produce 429. - # Any other variation doesn't work. - $r->custom_response(500, "Too Many Requests"); - return 429; - } - else { - return Apache2::Const::OK; - } -} - -1; diff --git a/Bugzilla/ModPerl/Hostage.pm b/Bugzilla/ModPerl/Hostage.pm deleted file mode 100644 index a3bdfac58..000000000 --- a/Bugzilla/ModPerl/Hostage.pm +++ /dev/null @@ -1,71 +0,0 @@ -package Bugzilla::ModPerl::Hostage; -use 5.10.1; -use strict; -use warnings; - -use Apache2::Const qw(:common); ## no critic (Freenode::ModPerl) - -sub _attachment_root { - my ($base) = @_; - return undef unless $base; - return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} - ? $1 - : undef; -} - -sub _attachment_host_regex { - my ($base) = @_; - return undef unless $base; - my $val = $base; - $val =~ s{^https?://}{}s; - $val =~ s{/$}{}s; - my $regex = quotemeta $val; - $regex =~ s/\\\%bugid\\\%/\\d+/g; - return qr/^$regex$/s; -} - -sub handler { - my $r = shift; - state $urlbase = Bugzilla->localconfig->{urlbase}; - state $urlbase_uri = URI->new($urlbase); - state $urlbase_host = $urlbase_uri->host; - state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/; - state $attachment_base = Bugzilla->localconfig->{attachment_base}; - state $attachment_root = _attachment_root($attachment_base); - state $attachment_host_regex = _attachment_host_regex($attachment_base); - - my $hostname = $r->hostname; - return OK if $hostname eq $urlbase_host; - - my $path = $r->uri; - return OK if $path eq '/__lbheartbeat__'; - - if ($attachment_base && $hostname eq $attachment_root) { - $r->headers_out->set(Location => $urlbase); - return REDIRECT; - } - elsif ($attachment_base && $hostname =~ $attachment_host_regex) { - if ($path =~ m{^/attachment\.cgi}s) { - return OK; - } else { - my $new_uri = URI->new($r->unparsed_uri); - $new_uri->scheme($urlbase_uri->scheme); - $new_uri->host($urlbase_host); - $r->headers_out->set(Location => $new_uri); - return REDIRECT; - } - } - elsif (my ($id) = $hostname =~ $urlbase_host_regex) { - my $new_uri = $urlbase_uri->clone; - $new_uri->path('/show_bug.cgi'); - $new_uri->query_form(id => $id); - $r->headers_out->set(Location => $new_uri); - return REDIRECT; - } - else { - $r->headers_out->set(Location => $urlbase); - return REDIRECT; - } -} - -1; \ No newline at end of file diff --git a/Bugzilla/ModPerl/StartupFix.pm b/Bugzilla/ModPerl/StartupFix.pm deleted file mode 100644 index bcc467e9f..000000000 --- a/Bugzilla/ModPerl/StartupFix.pm +++ /dev/null @@ -1,51 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. -package Bugzilla::ModPerl::StartupFix; -use 5.10.1; -use strict; -use warnings; - -use Filter::Util::Call; -use Apache2::ServerUtil (); - -# This module is a source filter that removes every subsequent line -# if this is the first time apache has started, -# as reported by Apache2::ServerUtil::restart_count(), which is 1 -# on the first start. - -my $FIRST_STARTUP = <<'CODE'; -warn "Bugzilla::ModPerl::StartupFix: Skipping first startup using source filter\n"; -1; -CODE - -sub import { - my ($class) = @_; - my ($ref) = {}; - filter_add( bless $ref, $class ); -} - -# this will be called for each line. -# For the first line replaced, we insert $FIRST_STARTUP. -# Every subsequent line is replaced with an empty string. -sub filter { - my ($self) = @_; - my ($status); - if ($status = filter_read() > 0) { - if (Apache2::ServerUtil::restart_count() < 2) { - if (!$self->{did_it}) { - $self->{did_it} = 1; - $_ = $FIRST_STARTUP; - } - else { - $_ = ""; - } - } - } - return $status; -} - -1; \ No newline at end of file diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm new file mode 100644 index 000000000..e4c75726c --- /dev/null +++ b/Bugzilla/Quantum.pm @@ -0,0 +1,214 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum; +use Mojo::Base 'Mojolicious'; + +use CGI::Compile; # Needed for its exit() overload +use Bugzilla::Logging; +use Bugzilla::Quantum::Template; +use Bugzilla::Quantum::CGI; +use Bugzilla::Quantum::SES; +use Bugzilla::Quantum::Static; + +use Bugzilla (); +use Bugzilla::Constants qw(bz_locations); +use Bugzilla::BugMail (); +use Bugzilla::CGI (); +use Bugzilla::Extension (); +use Bugzilla::Install::Requirements (); +use Bugzilla::Util (); +use Cwd qw(realpath); + +use MojoX::Log::Log4perl::Tiny; + +has 'static' => sub { Bugzilla::Quantum::Static->new }; + +sub startup { + my ($self) = @_; + + DEBUG("Starting up"); + $self->plugin('Bugzilla::Quantum::Plugin::Glue'); + $self->plugin('Bugzilla::Quantum::Plugin::Hostage'); + $self->plugin('Bugzilla::Quantum::Plugin::BlockIP'); + $self->plugin('Bugzilla::Quantum::Plugin::BasicAuth'); + + if ( $self->mode ne 'development' ) { + $self->hook( + after_static => sub { + my ($c) = @_; + $c->res->headers->cache_control('public, max-age=31536000'); + } + ); + } + + my $r = $self->routes; + Bugzilla::Quantum::CGI->load_all($r); + Bugzilla::Quantum::CGI->load_one('bzapi_cgi', 'extensions/BzAPI/bin/rest.cgi'); + + $r->any('/')->to('CGI#index_cgi'); + $r->any('/rest')->to('CGI#rest_cgi'); + $r->any('/rest.cgi/*PATH_INFP')->to('CGI#rest_cgi' => { PATH_INFO => '' }); + $r->any('/rest/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' }); + $r->any('/bug/:id')->to('CGI#show_bug_cgi'); + $r->any('/extensions/BzAPI/bin/rest.cgi/*PATH_INFO')->to('CGI#bzapi_cgi'); + + $r->get( + '/__lbheartbeat__' => sub { + my $c = shift; + $c->reply->file( $c->app->home->child('__lbheartbeat__') ); + }, + ); + + $r->get('/__heartbeat__')->to( 'CGI#heartbeat_cgi'); + $r->get('/robots.txt')->to( 'CGI#robots_cgi' ); + + $r->any('/review')->to( 'CGI#page_cgi' => {'id' => 'splinter.html'}); + $r->any('/user_profile')->to( 'CGI#page_cgi' => {'id' => 'user_profile.html'}); + $r->any('/userprofile')->to( 'CGI#page_cgi' => {'id' => 'user_profile.html'}); + $r->any('/request_defer')->to( 'CGI#page_cgi' => {'id' => 'request_defer.html'}); + $r->any('/login')->to( 'CGI#index_cgi' => { 'GoAheadAndLogIn' => '1' }); + + $r->any('/:new_bug' => [new_bug => qr{new[-_]bug}])->to( 'CGI#new_bug_cgi'); + + my $ses_auth = $r->under( + '/ses' => sub { + my ($c) = @_; + my $lc = Bugzilla->localconfig; + + return $c->basic_auth( 'SES', $lc->{ses_username}, $lc->{ses_password} ); + } + ); + $ses_auth->any('/index.cgi')->to('SES#main'); + + $r->any('/:REWRITE_itrequest' => [REWRITE_itrequest => qr{form[\.:]itrequest}])->to( + 'CGI#enter_bug_cgi' => { 'product' => 'Infrastructure & Operations', 'format' => 'itrequest' } + ); + $r->any('/:REWRITE_mozlist' => [REWRITE_mozlist => qr{form[\.:]mozlist}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'mozlist'} + ); + $r->any('/:REWRITE_poweredby' => [REWRITE_poweredby => qr{form[\.:]poweredby}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'poweredby'} + ); + $r->any('/:REWRITE_presentation' => [REWRITE_presentation => qr{form[\.:]presentation}])->to( + 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'presentation'} + ); + $r->any('/:REWRITE_trademark' => [REWRITE_trademark => qr{form[\.:]trademark}])->to( + 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'trademark'} + ); + $r->any('/:REWRITE_recoverykey' => [REWRITE_recoverykey => qr{form[\.:]recoverykey}])->to( + 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'recoverykey'} + ); + $r->any('/:REWRITE_legal' => [REWRITE_legal => qr{form[\.:]legal}])->to( + 'CGI#enter_bug_cgi' => { 'product' => 'Legal', 'format' => 'legal' }, + ); + $r->any('/:REWRITE_recruiting' => [REWRITE_recruiting => qr{form[\.:]recruiting}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Recruiting', 'format' => 'recruiting'} + ); + $r->any('/:REWRITE_intern' => [REWRITE_intern => qr{form[\.:]intern}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Recruiting', 'format' => 'intern'} + ); + $r->any('/:REWRITE_mozpr' => [REWRITE_mozpr => qr{form[\.:]mozpr}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Mozilla PR', 'format' => 'mozpr' }, + ); + $r->any('/:REWRITE_reps_mentorship' => [REWRITE_reps_mentorship => qr{form[\.:]reps[\.:]mentorship}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'mozreps' }, + ); + $r->any('/:REWRITE_reps_budget' => [REWRITE_reps_budget => qr{form[\.:]reps[\.:]budget}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'remo-budget'} + ); + $r->any('/:REWRITE_reps_swag' => [REWRITE_reps_swag => qr{form[\.:]reps[\.:]swag}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'remo-swag'} + ); + $r->any('/:REWRITE_reps_it' => [REWRITE_reps_it => qr{form[\.:]reps[\.:]it}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'remo-it'} + ); + $r->any('/:REWRITE_reps_payment' => [REWRITE_reps_payment => qr{form[\.:]reps[\.:]payment}])->to( + 'CGI#page_cgi' => {'id' => 'remo-form-payment.html'} + ); + $r->any('/:REWRITE_csa_discourse' => [REWRITE_csa_discourse => qr{form[\.:]csa[\.:]discourse}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Infrastructure & Operations', 'format' => 'csa-discourse'} + ); + $r->any('/:REWRITE_employee_incident' => [REWRITE_employee_incident => qr{form[\.:]employee[\.\-:]incident}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'employee-incident'} + ); + $r->any('/:REWRITE_brownbag' => [REWRITE_brownbag => qr{form[\.:]brownbag}])->to( + 'CGI#https_air_mozilla_org_requests' => {} + ); + $r->any('/:REWRITE_finance' => [REWRITE_finance => qr{form[\.:]finance}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Finance','format' => 'finance'} + ); + $r->any('/:REWRITE_moz_project_review' => [REWRITE_moz_project_review => qr{form[\.:]moz[\.\-:]project[\.\-:]review}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org','format' => 'moz-project-review'} + ); + $r->any('/:REWRITE_docs' => [REWRITE_docs => qr{form[\.:]docs?}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Developer Documentation','format' => 'doc'} + ); + $r->any('/:REWRITE_mdn' => [REWRITE_mdn => qr{form[\.:]mdn?}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'mdn','product' => 'developer.mozilla.org'} + ); + $r->any('/:REWRITE_swag_gear' => [REWRITE_swag_gear => qr{form[\.:](swag|gear)}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'swag','product' => 'Marketing'} + ); + $r->any('/:REWRITE_costume' => [REWRITE_costume => qr{form[\.:]costume}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Marketing','format' => 'costume'} + ); + $r->any('/:REWRITE_ipp' => [REWRITE_ipp => qr{form[\.:]ipp}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Internet Public Policy','format' => 'ipp'} + ); + $r->any('/:REWRITE_creative' => [REWRITE_creative => qr{form[\.:]creative}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'creative','product' => 'Marketing'} + ); + $r->any('/:REWRITE_user_engagement' => [REWRITE_user_engagement => qr{form[\.:]user[\.\-:]engagement}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'user-engagement','product' => 'Marketing'} + ); + $r->any('/:REWRITE_dev_engagement_event' => [REWRITE_dev_engagement_event => qr{form[\.:]dev[\.\-:]engagement[\.\-\:]event}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Developer Engagement','format' => 'dev-engagement-event'} + ); + $r->any('/:REWRITE_mobile_compat' => [REWRITE_mobile_compat => qr{form[\.:]mobile[\.\-:]compat}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Tech Evangelism','format' => 'mobile-compat'} + ); + $r->any('/:REWRITE_web_bounty' => [REWRITE_web_bounty => qr{form[\.:]web[\.:]bounty}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'web-bounty','product' => 'mozilla.org'} + ); + $r->any('/:REWRITE_automative' => [REWRITE_automative => qr{form[\.:]automative}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Testing','format' => 'automative'} + ); + $r->any('/:REWRITE_comm_newsletter' => [REWRITE_comm_newsletter => qr{form[\.:]comm[\.:]newsletter}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'comm-newsletter','product' => 'Marketing'} + ); + $r->any('/:REWRITE_screen_share_whitelist' => [REWRITE_screen_share_whitelist => qr{form[\.:]screen[\.:]share[\.:]whitelist}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'screen-share-whitelist','product' => 'Firefox'} + ); + $r->any('/:REWRITE_data_compliance' => [REWRITE_data_compliance => qr{form[\.:]data[\.\-:]compliance}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Data Compliance','format' => 'data-compliance'} + ); + $r->any('/:REWRITE_fsa_budget' => [REWRITE_fsa_budget => qr{form[\.:]fsa[\.:]budget}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'FSA','format' => 'fsa-budget'} + ); + $r->any('/:REWRITE_triage_request' => [REWRITE_triage_request => qr{form[\.:]triage[\.\-]request}])->to( + 'CGI#page_cgi' => {'id' => 'triage_request.html'} + ); + $r->any('/:REWRITE_crm_CRM' => [REWRITE_crm_CRM => qr{form[\.:](crm|CRM)}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'crm','product' => 'Marketing'} + ); + $r->any('/:REWRITE_nda' => [REWRITE_nda => qr{form[\.:]nda}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Legal','format' => 'nda'} + ); + $r->any('/:REWRITE_name_clearance' => [REWRITE_name_clearance => qr{form[\.:]name[\.:]clearance}])->to( + 'CGI#enter_bug_cgi' => {'format' => 'name-clearance','product' => 'Legal'} + ); + $r->any('/:REWRITE_shield_studies' => [REWRITE_shield_studies => qr{form[\.:]shield[\.:]studies}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Shield','format' => 'shield-studies'} + ); + $r->any('/:REWRITE_client_bounty' => [REWRITE_client_bounty => qr{form[\.:]client[\.:]bounty}])->to( + 'CGI#enter_bug_cgi' => {'product' => 'Firefox','format' => 'client-bounty'} + ); + +} + +1; diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm new file mode 100644 index 000000000..85f14cf83 --- /dev/null +++ b/Bugzilla/Quantum/CGI.pm @@ -0,0 +1,162 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::CGI; +use Mojo::Base 'Mojolicious::Controller'; + +use CGI::Compile; +use Bugzilla::Constants qw(bz_locations); +use Bugzilla::Quantum::Stdout; +use File::Slurper qw(read_text); +use File::Spec::Functions qw(catfile); +use Sub::Name; +use Sub::Quote 2.005000; +use Try::Tiny; +use Taint::Util qw(untaint); +use Socket qw(AF_INET inet_aton); +use Sys::Hostname; +use English qw(-no_match_vars); + +our $C; +my %SEEN; + +sub load_all { + my ($class, $r) = @_; + + foreach my $file (glob '*.cgi') { + my $name = _file_to_method($file); + $class->load_one($name, $file); + $r->any("/$file")->to("CGI#$name"); + } +} + +sub load_one { + my ($class, $name, $file) = @_; + my $package = __PACKAGE__ . "::$name", + my $inner_name = "_$name"; + my $content = read_text( catfile( bz_locations->{cgi_path}, $file ) ); + $content = "package $package; $content"; + untaint($content); + my %options = ( + package => $package, + file => $file, + line => 1, + no_defer => 1, + ); + die "Tried to load $file more than once" if $SEEN{$file}++; + my $inner = quote_sub $inner_name, $content, {}, \%options; + my $wrapper = sub { + my ($c) = @_; + my $stdin = $c->_STDIN; + my $stdout = ''; + local $C = $c; + local %ENV = $c->_ENV($file); + local *STDIN; ## no critic (local) + local $CGI::Compile::USE_REAL_EXIT = 0; + local $PROGRAM_NAME = $file; + open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; + tie *STDOUT, 'Bugzilla::Quantum::Stdout', controller => $c; ## no critic (tie) + try { + Bugzilla->init_page(); + $inner->(); + } + catch { + die $_ unless ref $_ eq 'ARRAY' && $_->[0] eq "EXIT\n"; + } + finally { + untie *STDOUT; + $c->finish; + Bugzilla->_cleanup; ## no critic (private) + CGI::initialize_globals(); + }; + }; + + no strict 'refs'; ## no critic (strict) + *{$name} = subname($name, $wrapper); + return 1; +} + +sub _ENV { + my ($c, $script_name) = @_; + my $tx = $c->tx; + my $req = $tx->req; + my $headers = $req->headers; + my $content_length = $req->content->is_multipart ? $req->body_size : $headers->content_length; + my %env_headers = ( HTTP_COOKIE => '', HTTP_REFERER => '' ); + + for my $name ( @{ $headers->names } ) { + my $key = uc "http_$name"; + $key =~ s!\W!_!g; + $env_headers{$key} = $headers->header($name); + } + + my $remote_user; + if ( my $userinfo = $c->req->url->to_abs->userinfo ) { + $remote_user = $userinfo =~ /([^:]+)/ ? $1 : ''; + } + elsif ( my $authenticate = $headers->authorization ) { + $remote_user = $authenticate =~ /Basic\s+(.*)/ ? b64_decode $1 : ''; + $remote_user = $remote_user =~ /([^:]+)/ ? $1 : ''; + } + my $path_info = $c->param('PATH_INFO'); + my %captures = %{ $c->stash->{'mojo.captures'} // {} }; + foreach my $key (keys %captures) { + if ($key eq 'controller' || $key eq 'action' || $key eq 'PATH_INFO' || $key =~ /^REWRITE_/) { + delete $captures{$key}; + } + } + my $cgi_query = Mojo::Parameters->new(%captures); + $cgi_query->append($req->url->query); + my $prefix = $c->stash->{bmo_prefix} ? '/bmo/' : '/'; + + return ( + %ENV, + CONTENT_LENGTH => $content_length || 0, + CONTENT_TYPE => $headers->content_type || '', + GATEWAY_INTERFACE => 'CGI/1.1', + HTTPS => $req->is_secure ? 'YES' : 'NO', + %env_headers, + QUERY_STRING => $cgi_query->to_string, + PATH_INFO => $path_info ? "/$path_info" : '', + REMOTE_ADDR => $tx->original_remote_address, + REMOTE_HOST => $tx->original_remote_address, + REMOTE_PORT => $tx->remote_port, + REMOTE_USER => $remote_user || '', + REQUEST_METHOD => $req->method, + SCRIPT_NAME => "$prefix$script_name", + SERVER_NAME => hostname, + SERVER_PORT => $tx->local_port, + SERVER_PROTOCOL => $req->is_secure ? 'HTTPS' : 'HTTP', # TODO: Version is missing + SERVER_SOFTWARE => __PACKAGE__, + ); +} + +sub _STDIN { + my $c = shift; + my $stdin; + + if ( $c->req->content->is_multipart ) { + $stdin = Mojo::Asset::File->new; + $stdin->add_chunk( $c->req->build_body ); + } + else { + $stdin = $c->req->content->asset; + } + + return $stdin if $stdin->isa('Mojo::Asset::File'); + return Mojo::Asset::File->new->add_chunk( $stdin->slurp ); +} + +sub _file_to_method { + my ($name) = @_; + $name =~ s/\./_/s; + $name =~ s/\W+/_/gs; + return $name; +} + + +1; diff --git a/Bugzilla/Quantum/Plugin/BasicAuth.pm b/Bugzilla/Quantum/Plugin/BasicAuth.pm new file mode 100644 index 000000000..bc79c89ef --- /dev/null +++ b/Bugzilla/Quantum/Plugin/BasicAuth.pm @@ -0,0 +1,40 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. +package Bugzilla::Quantum::Plugin::BasicAuth; +use 5.10.1; +use Mojo::Base qw(Mojolicious::Plugin); + +use Bugzilla::Logging; +use Carp; + +sub register { + my ( $self, $app, $conf ) = @_; + + $app->renderer->add_helper( + basic_auth => sub { + my ($c, $realm, $auth_user, $auth_pass) = @_; + my $req = $c->req; + my ($user, $password) = $req->url->to_abs->userinfo =~ /^([^:]+):(.*)/; + + unless ($realm && $auth_user && $auth_pass) { + croak 'basic_auth() called with missing parameters.'; + } + + unless ($user eq $auth_user && $password eq $auth_pass) { + WARN('username and password do not match'); + $c->res->headers->www_authenticate("Basic realm=\"$realm\""); + $c->res->code(401); + $c->rendered; + return 0; + } + + return 1; + } + ); +} + +1; diff --git a/Bugzilla/Quantum/Plugin/BlockIP.pm b/Bugzilla/Quantum/Plugin/BlockIP.pm new file mode 100644 index 000000000..fbfffad66 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/BlockIP.pm @@ -0,0 +1,43 @@ +package Bugzilla::Quantum::Plugin::BlockIP; +use 5.10.1; +use Mojo::Base 'Mojolicious::Plugin'; + +use Bugzilla::Memcached; + +use constant BLOCK_TIMEOUT => 60*60; + +my $MEMCACHED = Bugzilla::Memcached->_new()->{memcached}; + +sub register { + my ( $self, $app, $conf ) = @_; + + $app->hook(before_routes => \&_before_routes); + $app->helper(block_ip => \&_block_ip); + $app->helper(unblock_ip => \&_unblock_ip); +} + +sub _block_ip { + my ($class, $ip) = @_; + $MEMCACHED->set("block_ip:$ip" => 1, BLOCK_TIMEOUT) if $MEMCACHED; +} + +sub _unblock_ip { + my ($class, $ip) = @_; + $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; +} + +sub _before_routes { + my ( $c ) = @_; + return if $c->stash->{'mojo.static'}; + + my $ip = $c->tx->remote_address; + if ($MEMCACHED && $MEMCACHED->get("block_ip:$ip")) { + $c->block_ip($ip); + $c->res->code(429); + $c->res->message("Too Many Requests"); + $c->res->body("Too Many Requests"); + $c->finish; + } +} + +1; diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm new file mode 100644 index 000000000..4261d6729 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -0,0 +1,114 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Plugin::Glue; +use 5.10.1; +use Mojo::Base 'Mojolicious::Plugin'; + +use Try::Tiny; +use Bugzilla::Constants; +use Bugzilla::Quantum::Template; +use Bugzilla::Logging; +use Bugzilla::RNG (); +use JSON::MaybeXS qw(decode_json); + +sub register { + my ( $self, $app, $conf ) = @_; + + my %D; + if ($ENV{BUGZILLA_HTTPD_ARGS}) { + my $args = decode_json($ENV{BUGZILLA_HTTPD_ARGS}); + foreach my $arg (@$args) { + if ($arg =~ /^-D(\w+)$/) { + $D{$1} = 1; + } + else { + die "Unknown httpd arg: $arg"; + } + } + } + + # hypnotoad is weird and doesn't look for MOJO_LISTEN itself. + $app->config( + hypnotoad => { + listen => [ $ENV{MOJO_LISTEN} ], + }, + ); + + # Make sure each httpd child receives a different random seed (bug 476622). + # Bugzilla::RNG has one srand that needs to be called for + # every process, and Perl has another. (Various Perl modules still use + # the built-in rand(), even though we never use it in Bugzilla itself, + # so we need to srand() both of them.) + # Also, ping the dbh to force a reconnection. + Mojo::IOLoop->next_tick( + sub { + Bugzilla::RNG::srand(); + srand(); + eval { Bugzilla->dbh->ping }; + } + ); + + $app->hook( + before_dispatch => sub { + my ($c) = @_; + if ($D{HTTPD_IN_SUBDIR}) { + my $path = $c->req->url->path; + if ($path =~ s{^/bmo}{}s) { + $c->stash->{bmo_prefix} = 1; + $c->req->url->path($path); + } + } + Log::Log4perl::MDC->put(request_id => $c->req->request_id); + } + ); + + Bugzilla::Extension->load_all(); + if ($app->mode ne 'development') { + Bugzilla->preload_features(); + DEBUG("preloading templates"); + Bugzilla->preload_templates(); + DEBUG("done preloading templates"); + } + $app->secrets([Bugzilla->localconfig->{side_wide_secret}]); + + $app->renderer->add_handler( + 'bugzilla' => sub { + my ( $renderer, $c, $output, $options ) = @_; + my $vars = delete $c->stash->{vars}; + + # Helpers + my %helper; + foreach my $method ( grep {m/^\w+\z/} keys %{ $renderer->helpers } ) { + my $sub = $renderer->helpers->{$method}; + $helper{$method} = sub { $c->$sub(@_) }; + } + $vars->{helper} = \%helper; + + # The controller + $vars->{c} = $c; + my $name = $options->{template}; + unless ($name =~ /\./) { + $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; + } + my $template = Bugzilla->template; + $template->process( $name, $vars, $output ) + or die $template->error; + } + ); + + $app->log( + MojoX::Log::Log4perl::Tiny->new( + logger => Log::Log4perl->get_logger(ref $app) + ) + ); +} + + + + +1; diff --git a/Bugzilla/Quantum/Plugin/Hostage.pm b/Bugzilla/Quantum/Plugin/Hostage.pm new file mode 100644 index 000000000..42a05a910 --- /dev/null +++ b/Bugzilla/Quantum/Plugin/Hostage.pm @@ -0,0 +1,80 @@ +package Bugzilla::Quantum::Plugin::Hostage; +use 5.10.1; +use Mojo::Base 'Mojolicious::Plugin'; + +sub _attachment_root { + my ($base) = @_; + return undef unless $base; + return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} + ? $1 + : undef; +} + +sub _attachment_host_regex { + my ($base) = @_; + return undef unless $base; + my $val = $base; + $val =~ s{^https?://}{}s; + $val =~ s{/$}{}s; + my $regex = quotemeta $val; + $regex =~ s/\\\%bugid\\\%/\\d+/g; + return qr/^$regex$/s; +} + +sub register { + my ( $self, $app, $conf ) = @_; + + $app->hook(before_routes => \&_before_routes); +} + +sub _before_routes { + my ( $c ) = @_; + state $urlbase = Bugzilla->localconfig->{urlbase}; + state $urlbase_uri = URI->new($urlbase); + state $urlbase_host = $urlbase_uri->host; + state $urlbase_host_regex = qr/^bug(\d+)\.\Q$urlbase_host\E$/; + state $attachment_base = Bugzilla->localconfig->{attachment_base}; + state $attachment_root = _attachment_root($attachment_base); + state $attachment_host_regex = _attachment_host_regex($attachment_base); + + my $stash = $c->stash; + my $req = $c->req; + my $url = $req->url->to_abs; + + return if $stash->{'mojo.static'}; + + my $hostname = $url->host; + return if $hostname eq $urlbase_host; + + my $path = $url->path; + return if $path eq '/__lbheartbeat__'; + + if ($attachment_base && $hostname eq $attachment_root) { + $c->redirect_to($urlbase); + return; + } + elsif ($attachment_base && $hostname =~ $attachment_host_regex) { + if ($path =~ m{^/attachment\.cgi}s) { + return; + } else { + my $new_uri = $url->clone; + $new_uri->scheme($urlbase_uri->scheme); + $new_uri->host($urlbase_host); + $c->redirect_to($new_uri); + return; + } + } + elsif (my ($id) = $hostname =~ $urlbase_host_regex) { + my $new_uri = $urlbase_uri->clone; + $new_uri->path('/show_bug.cgi'); + $new_uri->query_form(id => $id); + $c->redirect_to($new_uri); + return; + } + else { + $c->redirect_to($urlbase); + return; + } +} + +1; \ No newline at end of file diff --git a/Bugzilla/Quantum/SES.pm b/Bugzilla/Quantum/SES.pm new file mode 100644 index 000000000..47c591fb5 --- /dev/null +++ b/Bugzilla/Quantum/SES.pm @@ -0,0 +1,203 @@ +package Bugzilla::Quantum::SES; +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use 5.10.1; +use Mojo::Base qw( Mojolicious::Controller ); + +use Bugzilla::Constants qw(ERROR_MODE_DIE); +use Bugzilla::Logging; +use Bugzilla::Mailer qw(MessageToMTA); +use Bugzilla::User (); +use Bugzilla::Util qw(html_quote remote_ip); +use JSON::MaybeXS qw(decode_json); +use LWP::UserAgent (); +use Try::Tiny qw(catch try); + +sub main { + my ($self) = @_; + Bugzilla->error_mode(ERROR_MODE_DIE); + my $message = $self->_decode_json_wrapper( $self->req->body ) // return; + my $message_type = $self->req->headers->header('X-Amz-SNS-Message-Type') // '(missing)'; + + if ( $message_type eq 'SubscriptionConfirmation' ) { + $self->_confirm_subscription($message); + } + + elsif ( $message_type eq 'Notification' ) { + my $notification = $self->_decode_json_wrapper( $message->{Message} ) // return; + unless ( + # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html + $self->_handle_notification( $notification, 'eventType' ) + + # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html + || $self->_handle_notification( $notification, 'notificationType' ) + ) + { + WARN('Failed to find notification type'); + $self->_respond( 400 => 'Bad Request' ); + } + } + + else { + WARN("Unsupported message-type: $message_type"); + $self->_respond( 200 => 'OK' ); + } +} + +sub _confirm_subscription { + my ($self, $message) = @_; + + my $subscribe_url = $message->{SubscribeURL}; + if ( !$subscribe_url ) { + WARN('Bad SubscriptionConfirmation request: missing SubscribeURL'); + $self->_respond( 400 => 'Bad Request' ); + return; + } + + my $ua = ua(); + my $res = $ua->get( $message->{SubscribeURL} ); + if ( !$res->is_success ) { + WARN( 'Bad response from SubscribeURL: ' . $res->status_line ); + $self->_respond( 400 => 'Bad Request' ); + return; + } + + $self->_respond( 200 => 'OK' ); +} + +sub _handle_notification { + my ( $self, $notification, $type_field ) = @_; + + if ( !exists $notification->{$type_field} ) { + return 0; + } + my $type = $notification->{$type_field}; + + if ( $type eq 'Bounce' ) { + $self->_process_bounce($notification); + } + elsif ( $type eq 'Complaint' ) { + $self->_process_complaint($notification); + } + else { + WARN("Unsupported notification-type: $type"); + $self->_respond( 200 => 'OK' ); + } + return 1; +} + +sub _process_bounce { + my ($self, $notification) = @_; + + # disable each account that is bouncing + foreach my $recipient ( @{ $notification->{bounce}->{bouncedRecipients} } ) { + my $address = $recipient->{emailAddress}; + my $reason = sprintf '(%s) %s', $recipient->{action} // 'error', $recipient->{diagnosticCode} // 'unknown'; + + my $user = Bugzilla::User->new( { name => $address, cache => 1 } ); + if ($user) { + + # never auto-disable admin accounts + if ( $user->in_group('admin') ) { + Bugzilla->audit("ignoring bounce for admin <$address>: $reason"); + } + + else { + my $template = Bugzilla->template_inner(); + my $vars = { + mta => $notification->{bounce}->{reportingMTA} // 'unknown', + reason => $reason, + }; + my $disable_text; + $template->process( 'admin/users/bounce-disabled.txt.tmpl', $vars, \$disable_text ) + || die $template->error(); + + $user->set_disabledtext($disable_text); + $user->set_disable_mail(1); + $user->update(); + Bugzilla->audit( "bounce for <$address> disabled userid-" . $user->id . ": $reason" ); + } + } + + else { + Bugzilla->audit("bounce for <$address> has no user: $reason"); + } + } + + $self->_respond( 200 => 'OK' ); +} + +sub _process_complaint { + my ($self) = @_; + + # email notification to bugzilla admin + my ($notification) = @_; + my $template = Bugzilla->template_inner(); + my $json = JSON::MaybeXS->new( + pretty => 1, + utf8 => 1, + canonical => 1, + ); + + foreach my $recipient ( @{ $notification->{complaint}->{complainedRecipients} } ) { + my $reason = $notification->{complaint}->{complaintFeedbackType} // 'unknown'; + my $address = $recipient->{emailAddress}; + Bugzilla->audit("complaint for <$address> for '$reason'"); + my $vars = { + email => $address, + user => Bugzilla::User->new( { name => $address, cache => 1 } ), + reason => $reason, + notification => $json->encode($notification), + }; + my $message; + $template->process( 'email/ses-complaint.txt.tmpl', $vars, \$message ) + || die $template->error(); + MessageToMTA($message); + } + + $self->_respond( 200 => 'OK' ); +} + +sub _respond { + my ( $self, $code, $message ) = @_; + $self->render(text => "$message\n", status => $code); +} + +sub _decode_json_wrapper { + my ($self, $json) = @_; + my $result; + if ( !defined $json ) { + WARN( 'Missing JSON from ' . $self->tx->remote_address ); + $self->_respond( 400 => 'Bad Request' ); + return undef; + } + my $ok = try { + $result = decode_json($json); + } + catch { + WARN( 'Malformed JSON from ' . $self->tx->remote_address ); + $self->_respond( 400 => 'Bad Request' ); + return undef; + }; + return $ok ? $result : undef; +} + +sub ua { + my $ua = LWP::UserAgent->new(); + $ua->timeout(10); + $ua->protocols_allowed( [ 'http', 'https' ] ); + if ( my $proxy_url = Bugzilla->params->{'proxy_url'} ) { + $ua->proxy( [ 'http', 'https' ], $proxy_url ); + } + else { + $ua->env_proxy; + } + return $ua; +} + +1; \ No newline at end of file diff --git a/Bugzilla/Quantum/Static.pm b/Bugzilla/Quantum/Static.pm new file mode 100644 index 000000000..2bb54990e --- /dev/null +++ b/Bugzilla/Quantum/Static.pm @@ -0,0 +1,30 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Static; +use Mojo::Base 'Mojolicious::Static'; +use Bugzilla::Constants qw(bz_locations); + +my $LEGACY_RE = qr{ + ^ (?:static/v[0-9]+\.[0-9]+/) ? + ( (?:extensions/[^/]+/web|(?:image|skin|j)s)/.+) + $ +}xs; + +sub file { + my ($self, $rel) = @_; + + if (my ($legacy_rel) = $rel =~ $LEGACY_RE) { + local $self->{paths} = [ bz_locations->{cgi_path} ]; + return $self->SUPER::file($legacy_rel); + } + else { + return $self->SUPER::file($rel); + } +} + +1; diff --git a/Bugzilla/Quantum/Stdout.pm b/Bugzilla/Quantum/Stdout.pm new file mode 100644 index 000000000..2cdba9160 --- /dev/null +++ b/Bugzilla/Quantum/Stdout.pm @@ -0,0 +1,59 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Stdout; +use 5.10.1; +use Moo; + +use Bugzilla::Logging; +use Encode; + +has 'controller' => ( + is => 'ro', + required => 1, +); + +has '_encoding' => ( + is => 'rw', + default => '', +); + +sub TIEHANDLE { ## no critic (unpack) + my $class = shift; + + return $class->new(@_); +} + +sub PRINTF { ## no critic (unpack) + my $self = shift; + $self->PRINT(sprintf @_); +} + +sub PRINT { ## no critic (unpack) + my $self = shift; + my $c = $self->controller; + my $bytes = join '', @_; + return unless $bytes; + if ($self->_encoding) { + $bytes = encode($self->_encoding, $bytes); + } + $c->write($bytes); +} + +sub BINMODE { + my ($self, $mode) = @_; + if ($mode) { + if ($mode eq ':bytes' or $mode eq ':raw') { + $self->_encoding(''); + } + elsif ($mode eq ':utf8') { + $self->_encoding('utf8'); + } + } +} + +1; diff --git a/Bugzilla/Quantum/Template.pm b/Bugzilla/Quantum/Template.pm new file mode 100644 index 000000000..2442f1134 --- /dev/null +++ b/Bugzilla/Quantum/Template.pm @@ -0,0 +1,39 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Quantum::Template; +use 5.10.1; +use Moo; + +has 'controller' => ( + is => 'ro', + required => 1, +); + +has 'template' => ( + is => 'ro', + required => 1, + handles => ['error', 'get_format'], +); + +sub process { + my ($self, $file, $vars, $output) = @_; + + if (@_ < 4) { + $self->controller->stash->{vars} = $vars; + $self->controller->render(template => $file, handler => 'bugzilla'); + return 1; + } + elsif (@_ == 4) { + return $self->template->process($file, $vars, $output); + } + else { + die __PACKAGE__ . '->process() called with too many arguments'; + } +} + +1; \ No newline at end of file diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 299734d64..39272a538 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -12,6 +12,7 @@ use 5.10.1; use strict; use warnings; +use Bugzilla::Logging; use Bugzilla::Template::PreloadProvider; use Bugzilla::Bug; use Bugzilla::Constants; diff --git a/Bugzilla/WebService/Server/XMLRPC.pm b/Bugzilla/WebService/Server/XMLRPC.pm index 6bb73af01..5ad50e91c 100644 --- a/Bugzilla/WebService/Server/XMLRPC.pm +++ b/Bugzilla/WebService/Server/XMLRPC.pm @@ -14,11 +14,7 @@ use warnings; use Bugzilla::Logging; use XMLRPC::Transport::HTTP; use Bugzilla::WebService::Server; -if ($ENV{MOD_PERL}) { - our @ISA = qw(XMLRPC::Transport::HTTP::Apache Bugzilla::WebService::Server); -} else { - our @ISA = qw(XMLRPC::Transport::HTTP::CGI Bugzilla::WebService::Server); -} +our @ISA = qw(XMLRPC::Transport::HTTP::CGI Bugzilla::WebService::Server); use Bugzilla::WebService::Constants; use Bugzilla::Error; diff --git a/Bugzilla/WebService/Util.pm b/Bugzilla/WebService/Util.pm index 29ff05448..d462c884a 100644 --- a/Bugzilla/WebService/Util.pm +++ b/Bugzilla/WebService/Util.pm @@ -23,7 +23,7 @@ use base qw(Exporter); # We have to "require", not "use" this, because otherwise it tries to # use features of Test::More during import(). -require Test::Taint; +require Test::Taint if ${^TAINT}; our @EXPORT_OK = qw( extract_flags @@ -193,8 +193,10 @@ sub taint_data { # 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. - Test::Taint::_deeply_traverse(\&_delete_bad_keys, \@params); - Test::Taint::taint_deeply(\@params); + if (${^TAINT}) { + Test::Taint::_deeply_traverse(\&_delete_bad_keys, \@params); + Test::Taint::taint_deeply(\@params); + } } sub _delete_bad_keys { -- cgit v1.2.3-24-g4f1b From 5cf8f186b1f2dbda01cd63a0feb12ea0bdfa02af Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 19 Jul 2018 11:22:00 -0400 Subject: fix lint errors --- Bugzilla/Quantum/CGI.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index 85f14cf83..0e09d795e 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -90,12 +90,12 @@ sub _ENV { for my $name ( @{ $headers->names } ) { my $key = uc "http_$name"; - $key =~ s!\W!_!g; + $key =~ s/\W/_/g; $env_headers{$key} = $headers->header($name); } my $remote_user; - if ( my $userinfo = $c->req->url->to_abs->userinfo ) { + if ( my $userinfo = $req->url->to_abs->userinfo ) { $remote_user = $userinfo =~ /([^:]+)/ ? $1 : ''; } elsif ( my $authenticate = $headers->authorization ) { -- cgit v1.2.3-24-g4f1b From f7c2f14988dc1638cb68f4a96cae4bc387688809 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Thu, 19 Jul 2018 12:18:47 -0400 Subject: fix errors from group review --- Bugzilla/Quantum.pm | 2 +- Bugzilla/Quantum/CGI.pm | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index e4c75726c..b5ee83d8e 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -52,7 +52,7 @@ sub startup { $r->any('/')->to('CGI#index_cgi'); $r->any('/rest')->to('CGI#rest_cgi'); - $r->any('/rest.cgi/*PATH_INFP')->to('CGI#rest_cgi' => { PATH_INFO => '' }); + $r->any('/rest.cgi/*PATH_INFO')->to('CGI#rest_cgi' => { PATH_INFO => '' }); $r->any('/rest/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' }); $r->any('/bug/:id')->to('CGI#show_bug_cgi'); $r->any('/extensions/BzAPI/bin/rest.cgi/*PATH_INFO')->to('CGI#bzapi_cgi'); diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index 0e09d795e..ba09dded6 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -52,7 +52,6 @@ sub load_one { my $wrapper = sub { my ($c) = @_; my $stdin = $c->_STDIN; - my $stdout = ''; local $C = $c; local %ENV = $c->_ENV($file); local *STDIN; ## no critic (local) @@ -102,7 +101,7 @@ sub _ENV { $remote_user = $authenticate =~ /Basic\s+(.*)/ ? b64_decode $1 : ''; $remote_user = $remote_user =~ /([^:]+)/ ? $1 : ''; } - my $path_info = $c->param('PATH_INFO'); + my $path_info = $c->stash->{'mojo.captures'}{'PATH_INFO'}; my %captures = %{ $c->stash->{'mojo.captures'} // {} }; foreach my $key (keys %captures) { if ($key eq 'controller' || $key eq 'action' || $key eq 'PATH_INFO' || $key =~ /^REWRITE_/) { -- cgit v1.2.3-24-g4f1b From 29c7ca6bb526ce2cc9eb99815b6fc1252b7870cf Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 23 Jul 2018 11:16:47 -0400 Subject: some cleanups --- Bugzilla/Quantum.pm | 169 +++++++----------------------------------------- Bugzilla/Quantum/CGI.pm | 71 ++++++++++---------- 2 files changed, 58 insertions(+), 182 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index b5ee83d8e..1f6dce8da 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -8,22 +8,22 @@ package Bugzilla::Quantum; use Mojo::Base 'Mojolicious'; -use CGI::Compile; # Needed for its exit() overload +# Needed for its exit() overload, must happen early in execution. +use CGI::Compile; + +use Bugzilla (); +use Bugzilla::BugMail (); +use Bugzilla::CGI (); +use Bugzilla::Constants qw(bz_locations); +use Bugzilla::Extension (); +use Bugzilla::Install::Requirements (); use Bugzilla::Logging; -use Bugzilla::Quantum::Template; use Bugzilla::Quantum::CGI; use Bugzilla::Quantum::SES; use Bugzilla::Quantum::Static; - -use Bugzilla (); -use Bugzilla::Constants qw(bz_locations); -use Bugzilla::BugMail (); -use Bugzilla::CGI (); -use Bugzilla::Extension (); -use Bugzilla::Install::Requirements (); +use Bugzilla::Quantum::Template; use Bugzilla::Util (); use Cwd qw(realpath); - use MojoX::Log::Log4perl::Tiny; has 'static' => sub { Bugzilla::Quantum::Static->new }; @@ -31,7 +31,7 @@ has 'static' => sub { Bugzilla::Quantum::Static->new }; sub startup { my ($self) = @_; - DEBUG("Starting up"); + DEBUG('Starting up'); $self->plugin('Bugzilla::Quantum::Plugin::Glue'); $self->plugin('Bugzilla::Quantum::Plugin::Hostage'); $self->plugin('Bugzilla::Quantum::Plugin::BlockIP'); @@ -48,12 +48,12 @@ sub startup { my $r = $self->routes; Bugzilla::Quantum::CGI->load_all($r); - Bugzilla::Quantum::CGI->load_one('bzapi_cgi', 'extensions/BzAPI/bin/rest.cgi'); + Bugzilla::Quantum::CGI->load_one( 'bzapi_cgi', 'extensions/BzAPI/bin/rest.cgi' ); $r->any('/')->to('CGI#index_cgi'); $r->any('/rest')->to('CGI#rest_cgi'); - $r->any('/rest.cgi/*PATH_INFO')->to('CGI#rest_cgi' => { PATH_INFO => '' }); - $r->any('/rest/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' }); + $r->any('/rest.cgi/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' } ); + $r->any('/rest/*PATH_INFO')->to( 'CGI#rest_cgi' => { PATH_INFO => '' } ); $r->any('/bug/:id')->to('CGI#show_bug_cgi'); $r->any('/extensions/BzAPI/bin/rest.cgi/*PATH_INFO')->to('CGI#bzapi_cgi'); @@ -64,16 +64,16 @@ sub startup { }, ); - $r->get('/__heartbeat__')->to( 'CGI#heartbeat_cgi'); - $r->get('/robots.txt')->to( 'CGI#robots_cgi' ); + $r->get('/__heartbeat__')->to('CGI#heartbeat_cgi'); + $r->get('/robots.txt')->to('CGI#robots_cgi'); - $r->any('/review')->to( 'CGI#page_cgi' => {'id' => 'splinter.html'}); - $r->any('/user_profile')->to( 'CGI#page_cgi' => {'id' => 'user_profile.html'}); - $r->any('/userprofile')->to( 'CGI#page_cgi' => {'id' => 'user_profile.html'}); - $r->any('/request_defer')->to( 'CGI#page_cgi' => {'id' => 'request_defer.html'}); - $r->any('/login')->to( 'CGI#index_cgi' => { 'GoAheadAndLogIn' => '1' }); + $r->any('/review')->to( 'CGI#page_cgi' => { 'id' => 'splinter.html' } ); + $r->any('/user_profile')->to( 'CGI#page_cgi' => { 'id' => 'user_profile.html' } ); + $r->any('/userprofile')->to( 'CGI#page_cgi' => { 'id' => 'user_profile.html' } ); + $r->any('/request_defer')->to( 'CGI#page_cgi' => { 'id' => 'request_defer.html' } ); + $r->any('/login')->to( 'CGI#index_cgi' => { 'GoAheadAndLogIn' => '1' } ); - $r->any('/:new_bug' => [new_bug => qr{new[-_]bug}])->to( 'CGI#new_bug_cgi'); + $r->any( '/:new_bug' => [ new_bug => qr{new[-_]bug} ] )->to('CGI#new_bug_cgi'); my $ses_auth = $r->under( '/ses' => sub { @@ -85,130 +85,7 @@ sub startup { ); $ses_auth->any('/index.cgi')->to('SES#main'); - $r->any('/:REWRITE_itrequest' => [REWRITE_itrequest => qr{form[\.:]itrequest}])->to( - 'CGI#enter_bug_cgi' => { 'product' => 'Infrastructure & Operations', 'format' => 'itrequest' } - ); - $r->any('/:REWRITE_mozlist' => [REWRITE_mozlist => qr{form[\.:]mozlist}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'mozlist'} - ); - $r->any('/:REWRITE_poweredby' => [REWRITE_poweredby => qr{form[\.:]poweredby}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'poweredby'} - ); - $r->any('/:REWRITE_presentation' => [REWRITE_presentation => qr{form[\.:]presentation}])->to( - 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'presentation'} - ); - $r->any('/:REWRITE_trademark' => [REWRITE_trademark => qr{form[\.:]trademark}])->to( - 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'trademark'} - ); - $r->any('/:REWRITE_recoverykey' => [REWRITE_recoverykey => qr{form[\.:]recoverykey}])->to( - 'cgi#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'recoverykey'} - ); - $r->any('/:REWRITE_legal' => [REWRITE_legal => qr{form[\.:]legal}])->to( - 'CGI#enter_bug_cgi' => { 'product' => 'Legal', 'format' => 'legal' }, - ); - $r->any('/:REWRITE_recruiting' => [REWRITE_recruiting => qr{form[\.:]recruiting}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Recruiting', 'format' => 'recruiting'} - ); - $r->any('/:REWRITE_intern' => [REWRITE_intern => qr{form[\.:]intern}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Recruiting', 'format' => 'intern'} - ); - $r->any('/:REWRITE_mozpr' => [REWRITE_mozpr => qr{form[\.:]mozpr}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Mozilla PR', 'format' => 'mozpr' }, - ); - $r->any('/:REWRITE_reps_mentorship' => [REWRITE_reps_mentorship => qr{form[\.:]reps[\.:]mentorship}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'mozreps' }, - ); - $r->any('/:REWRITE_reps_budget' => [REWRITE_reps_budget => qr{form[\.:]reps[\.:]budget}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'remo-budget'} - ); - $r->any('/:REWRITE_reps_swag' => [REWRITE_reps_swag => qr{form[\.:]reps[\.:]swag}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'remo-swag'} - ); - $r->any('/:REWRITE_reps_it' => [REWRITE_reps_it => qr{form[\.:]reps[\.:]it}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Mozilla Reps','format' => 'remo-it'} - ); - $r->any('/:REWRITE_reps_payment' => [REWRITE_reps_payment => qr{form[\.:]reps[\.:]payment}])->to( - 'CGI#page_cgi' => {'id' => 'remo-form-payment.html'} - ); - $r->any('/:REWRITE_csa_discourse' => [REWRITE_csa_discourse => qr{form[\.:]csa[\.:]discourse}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Infrastructure & Operations', 'format' => 'csa-discourse'} - ); - $r->any('/:REWRITE_employee_incident' => [REWRITE_employee_incident => qr{form[\.:]employee[\.\-:]incident}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org', 'format' => 'employee-incident'} - ); - $r->any('/:REWRITE_brownbag' => [REWRITE_brownbag => qr{form[\.:]brownbag}])->to( - 'CGI#https_air_mozilla_org_requests' => {} - ); - $r->any('/:REWRITE_finance' => [REWRITE_finance => qr{form[\.:]finance}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Finance','format' => 'finance'} - ); - $r->any('/:REWRITE_moz_project_review' => [REWRITE_moz_project_review => qr{form[\.:]moz[\.\-:]project[\.\-:]review}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'mozilla.org','format' => 'moz-project-review'} - ); - $r->any('/:REWRITE_docs' => [REWRITE_docs => qr{form[\.:]docs?}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Developer Documentation','format' => 'doc'} - ); - $r->any('/:REWRITE_mdn' => [REWRITE_mdn => qr{form[\.:]mdn?}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'mdn','product' => 'developer.mozilla.org'} - ); - $r->any('/:REWRITE_swag_gear' => [REWRITE_swag_gear => qr{form[\.:](swag|gear)}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'swag','product' => 'Marketing'} - ); - $r->any('/:REWRITE_costume' => [REWRITE_costume => qr{form[\.:]costume}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Marketing','format' => 'costume'} - ); - $r->any('/:REWRITE_ipp' => [REWRITE_ipp => qr{form[\.:]ipp}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Internet Public Policy','format' => 'ipp'} - ); - $r->any('/:REWRITE_creative' => [REWRITE_creative => qr{form[\.:]creative}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'creative','product' => 'Marketing'} - ); - $r->any('/:REWRITE_user_engagement' => [REWRITE_user_engagement => qr{form[\.:]user[\.\-:]engagement}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'user-engagement','product' => 'Marketing'} - ); - $r->any('/:REWRITE_dev_engagement_event' => [REWRITE_dev_engagement_event => qr{form[\.:]dev[\.\-:]engagement[\.\-\:]event}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Developer Engagement','format' => 'dev-engagement-event'} - ); - $r->any('/:REWRITE_mobile_compat' => [REWRITE_mobile_compat => qr{form[\.:]mobile[\.\-:]compat}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Tech Evangelism','format' => 'mobile-compat'} - ); - $r->any('/:REWRITE_web_bounty' => [REWRITE_web_bounty => qr{form[\.:]web[\.:]bounty}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'web-bounty','product' => 'mozilla.org'} - ); - $r->any('/:REWRITE_automative' => [REWRITE_automative => qr{form[\.:]automative}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Testing','format' => 'automative'} - ); - $r->any('/:REWRITE_comm_newsletter' => [REWRITE_comm_newsletter => qr{form[\.:]comm[\.:]newsletter}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'comm-newsletter','product' => 'Marketing'} - ); - $r->any('/:REWRITE_screen_share_whitelist' => [REWRITE_screen_share_whitelist => qr{form[\.:]screen[\.:]share[\.:]whitelist}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'screen-share-whitelist','product' => 'Firefox'} - ); - $r->any('/:REWRITE_data_compliance' => [REWRITE_data_compliance => qr{form[\.:]data[\.\-:]compliance}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Data Compliance','format' => 'data-compliance'} - ); - $r->any('/:REWRITE_fsa_budget' => [REWRITE_fsa_budget => qr{form[\.:]fsa[\.:]budget}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'FSA','format' => 'fsa-budget'} - ); - $r->any('/:REWRITE_triage_request' => [REWRITE_triage_request => qr{form[\.:]triage[\.\-]request}])->to( - 'CGI#page_cgi' => {'id' => 'triage_request.html'} - ); - $r->any('/:REWRITE_crm_CRM' => [REWRITE_crm_CRM => qr{form[\.:](crm|CRM)}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'crm','product' => 'Marketing'} - ); - $r->any('/:REWRITE_nda' => [REWRITE_nda => qr{form[\.:]nda}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Legal','format' => 'nda'} - ); - $r->any('/:REWRITE_name_clearance' => [REWRITE_name_clearance => qr{form[\.:]name[\.:]clearance}])->to( - 'CGI#enter_bug_cgi' => {'format' => 'name-clearance','product' => 'Legal'} - ); - $r->any('/:REWRITE_shield_studies' => [REWRITE_shield_studies => qr{form[\.:]shield[\.:]studies}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Shield','format' => 'shield-studies'} - ); - $r->any('/:REWRITE_client_bounty' => [REWRITE_client_bounty => qr{form[\.:]client[\.:]bounty}])->to( - 'CGI#enter_bug_cgi' => {'product' => 'Firefox','format' => 'client-bounty'} - ); - + Bugzilla::Hook::process( 'app_startup', { app => $self } ); } 1; diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index ba09dded6..ab092140c 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -25,40 +25,39 @@ our $C; my %SEEN; sub load_all { - my ($class, $r) = @_; + my ( $class, $r ) = @_; - foreach my $file (glob '*.cgi') { - my $name = _file_to_method($file); - $class->load_one($name, $file); + foreach my $file ( glob '*.cgi' ) { + my $name = _file_to_method($file); + $class->load_one( $name, $file ); $r->any("/$file")->to("CGI#$name"); } } sub load_one { - my ($class, $name, $file) = @_; - my $package = __PACKAGE__ . "::$name", - my $inner_name = "_$name"; - my $content = read_text( catfile( bz_locations->{cgi_path}, $file ) ); + my ( $class, $name, $file ) = @_; + my $package = __PACKAGE__ . "::$name", my $inner_name = "_$name"; + my $content = read_text( catfile( bz_locations->{cgi_path}, $file ) ); $content = "package $package; $content"; untaint($content); my %options = ( - package => $package, - file => $file, - line => 1, + package => $package, + file => $file, + line => 1, no_defer => 1, ); die "Tried to load $file more than once" if $SEEN{$file}++; my $inner = quote_sub $inner_name, $content, {}, \%options; my $wrapper = sub { my ($c) = @_; - my $stdin = $c->_STDIN; - local $C = $c; - local %ENV = $c->_ENV($file); - local *STDIN; ## no critic (local) + my $stdin = $c->_STDIN; + local $C = $c; + local %ENV = $c->_ENV($file); local $CGI::Compile::USE_REAL_EXIT = 0; - local $PROGRAM_NAME = $file; + local $PROGRAM_NAME = $file; + local *STDIN; ## no critic (local) open STDIN, '<', $stdin->path or die "STDIN @{[$stdin->path]}: $!" if -s $stdin->path; - tie *STDOUT, 'Bugzilla::Quantum::Stdout', controller => $c; ## no critic (tie) + tie *STDOUT, 'Bugzilla::Quantum::Stdout', controller => $c; ## no critic (tie) try { Bugzilla->init_page(); $inner->(); @@ -69,23 +68,24 @@ sub load_one { finally { untie *STDOUT; $c->finish; - Bugzilla->_cleanup; ## no critic (private) + Bugzilla->cleanup; CGI::initialize_globals(); }; }; - no strict 'refs'; ## no critic (strict) - *{$name} = subname($name, $wrapper); + no strict 'refs'; ## no critic (strict) + *{$name} = subname( $name, $wrapper ); return 1; } + sub _ENV { - my ($c, $script_name) = @_; - my $tx = $c->tx; - my $req = $tx->req; - my $headers = $req->headers; + my ( $c, $script_name ) = @_; + my $tx = $c->tx; + my $req = $tx->req; + my $headers = $req->headers; my $content_length = $req->content->is_multipart ? $req->body_size : $headers->content_length; - my %env_headers = ( HTTP_COOKIE => '', HTTP_REFERER => '' ); + my %env_headers = ( HTTP_COOKIE => '', HTTP_REFERER => '' ); for my $name ( @{ $headers->names } ) { my $key = uc "http_$name"; @@ -103,13 +103,13 @@ sub _ENV { } my $path_info = $c->stash->{'mojo.captures'}{'PATH_INFO'}; my %captures = %{ $c->stash->{'mojo.captures'} // {} }; - foreach my $key (keys %captures) { - if ($key eq 'controller' || $key eq 'action' || $key eq 'PATH_INFO' || $key =~ /^REWRITE_/) { + foreach my $key ( keys %captures ) { + if ( $key eq 'controller' || $key eq 'action' || $key eq 'PATH_INFO' || $key =~ /^REWRITE_/ ) { delete $captures{$key}; } } my $cgi_query = Mojo::Parameters->new(%captures); - $cgi_query->append($req->url->query); + $cgi_query->append( $req->url->query ); my $prefix = $c->stash->{bmo_prefix} ? '/bmo/' : '/'; return ( @@ -119,17 +119,17 @@ sub _ENV { GATEWAY_INTERFACE => 'CGI/1.1', HTTPS => $req->is_secure ? 'YES' : 'NO', %env_headers, - QUERY_STRING => $cgi_query->to_string, - PATH_INFO => $path_info ? "/$path_info" : '', - REMOTE_ADDR => $tx->original_remote_address, - REMOTE_HOST => $tx->original_remote_address, - REMOTE_PORT => $tx->remote_port, - REMOTE_USER => $remote_user || '', + QUERY_STRING => $cgi_query->to_string, + PATH_INFO => $path_info ? "/$path_info" : '', + REMOTE_ADDR => $tx->original_remote_address, + REMOTE_HOST => $tx->original_remote_address, + REMOTE_PORT => $tx->remote_port, + REMOTE_USER => $remote_user || '', REQUEST_METHOD => $req->method, SCRIPT_NAME => "$prefix$script_name", SERVER_NAME => hostname, SERVER_PORT => $tx->local_port, - SERVER_PROTOCOL => $req->is_secure ? 'HTTPS' : 'HTTP', # TODO: Version is missing + SERVER_PROTOCOL => $req->is_secure ? 'HTTPS' : 'HTTP', # TODO: Version is missing SERVER_SOFTWARE => __PACKAGE__, ); } @@ -157,5 +157,4 @@ sub _file_to_method { return $name; } - 1; -- cgit v1.2.3-24-g4f1b From 0840339ee8a77fac943338f7ca354261c3d1066c Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 23 Jul 2018 11:20:47 -0400 Subject: more tidy --- Bugzilla/Quantum.pm | 1 - Bugzilla/Quantum/Plugin/Glue.pm | 30 +++++++++++------------------- Bugzilla/Quantum/Static.pm | 4 ++-- Bugzilla/Quantum/Stdout.pm | 22 +++++++++++----------- Bugzilla/Quantum/Template.pm | 39 --------------------------------------- 5 files changed, 24 insertions(+), 72 deletions(-) delete mode 100644 Bugzilla/Quantum/Template.pm (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index 1f6dce8da..083dcaf55 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -21,7 +21,6 @@ use Bugzilla::Logging; use Bugzilla::Quantum::CGI; use Bugzilla::Quantum::SES; use Bugzilla::Quantum::Static; -use Bugzilla::Quantum::Template; use Bugzilla::Util (); use Cwd qw(realpath); use MojoX::Log::Log4perl::Tiny; diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm index 4261d6729..f99026690 100644 --- a/Bugzilla/Quantum/Plugin/Glue.pm +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -11,7 +11,6 @@ use Mojo::Base 'Mojolicious::Plugin'; use Try::Tiny; use Bugzilla::Constants; -use Bugzilla::Quantum::Template; use Bugzilla::Logging; use Bugzilla::RNG (); use JSON::MaybeXS qw(decode_json); @@ -20,10 +19,10 @@ sub register { my ( $self, $app, $conf ) = @_; my %D; - if ($ENV{BUGZILLA_HTTPD_ARGS}) { - my $args = decode_json($ENV{BUGZILLA_HTTPD_ARGS}); + if ( $ENV{BUGZILLA_HTTPD_ARGS} ) { + my $args = decode_json( $ENV{BUGZILLA_HTTPD_ARGS} ); foreach my $arg (@$args) { - if ($arg =~ /^-D(\w+)$/) { + if ( $arg =~ /^-D(\w+)$/ ) { $D{$1} = 1; } else { @@ -56,25 +55,25 @@ sub register { $app->hook( before_dispatch => sub { my ($c) = @_; - if ($D{HTTPD_IN_SUBDIR}) { + if ( $D{HTTPD_IN_SUBDIR} ) { my $path = $c->req->url->path; - if ($path =~ s{^/bmo}{}s) { + if ( $path =~ s{^/bmo}{}s ) { $c->stash->{bmo_prefix} = 1; $c->req->url->path($path); } } - Log::Log4perl::MDC->put(request_id => $c->req->request_id); + Log::Log4perl::MDC->put( request_id => $c->req->request_id ); } ); Bugzilla::Extension->load_all(); - if ($app->mode ne 'development') { + if ( $app->mode ne 'development' ) { Bugzilla->preload_features(); DEBUG("preloading templates"); Bugzilla->preload_templates(); DEBUG("done preloading templates"); } - $app->secrets([Bugzilla->localconfig->{side_wide_secret}]); + $app->secrets( [ Bugzilla->localconfig->{side_wide_secret} ] ); $app->renderer->add_handler( 'bugzilla' => sub { @@ -92,23 +91,16 @@ sub register { # The controller $vars->{c} = $c; my $name = $options->{template}; - unless ($name =~ /\./) { + unless ( $name =~ /\./ ) { $name = sprintf '%s.%s.tmpl', $options->{template}, $options->{format}; } my $template = Bugzilla->template; $template->process( $name, $vars, $output ) - or die $template->error; + or die $template->error; } ); - $app->log( - MojoX::Log::Log4perl::Tiny->new( - logger => Log::Log4perl->get_logger(ref $app) - ) - ); + $app->log( MojoX::Log::Log4perl::Tiny->new( logger => Log::Log4perl->get_logger( ref $app ) ) ); } - - - 1; diff --git a/Bugzilla/Quantum/Static.pm b/Bugzilla/Quantum/Static.pm index 2bb54990e..d687873ab 100644 --- a/Bugzilla/Quantum/Static.pm +++ b/Bugzilla/Quantum/Static.pm @@ -16,9 +16,9 @@ my $LEGACY_RE = qr{ }xs; sub file { - my ($self, $rel) = @_; + my ( $self, $rel ) = @_; - if (my ($legacy_rel) = $rel =~ $LEGACY_RE) { + if ( my ($legacy_rel) = $rel =~ $LEGACY_RE ) { local $self->{paths} = [ bz_locations->{cgi_path} ]; return $self->SUPER::file($legacy_rel); } diff --git a/Bugzilla/Quantum/Stdout.pm b/Bugzilla/Quantum/Stdout.pm index 2cdba9160..699ec164a 100644 --- a/Bugzilla/Quantum/Stdout.pm +++ b/Bugzilla/Quantum/Stdout.pm @@ -22,35 +22,35 @@ has '_encoding' => ( default => '', ); -sub TIEHANDLE { ## no critic (unpack) +sub TIEHANDLE { ## no critic (unpack) my $class = shift; return $class->new(@_); } -sub PRINTF { ## no critic (unpack) +sub PRINTF { ## no critic (unpack) my $self = shift; - $self->PRINT(sprintf @_); + $self->PRINT( sprintf @_ ); } -sub PRINT { ## no critic (unpack) - my $self = shift; - my $c = $self->controller; +sub PRINT { ## no critic (unpack) + my $self = shift; + my $c = $self->controller; my $bytes = join '', @_; return unless $bytes; - if ($self->_encoding) { - $bytes = encode($self->_encoding, $bytes); + if ( $self->_encoding ) { + $bytes = encode( $self->_encoding, $bytes ); } $c->write($bytes); } sub BINMODE { - my ($self, $mode) = @_; + my ( $self, $mode ) = @_; if ($mode) { - if ($mode eq ':bytes' or $mode eq ':raw') { + if ( $mode eq ':bytes' or $mode eq ':raw' ) { $self->_encoding(''); } - elsif ($mode eq ':utf8') { + elsif ( $mode eq ':utf8' ) { $self->_encoding('utf8'); } } diff --git a/Bugzilla/Quantum/Template.pm b/Bugzilla/Quantum/Template.pm deleted file mode 100644 index 2442f1134..000000000 --- a/Bugzilla/Quantum/Template.pm +++ /dev/null @@ -1,39 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Quantum::Template; -use 5.10.1; -use Moo; - -has 'controller' => ( - is => 'ro', - required => 1, -); - -has 'template' => ( - is => 'ro', - required => 1, - handles => ['error', 'get_format'], -); - -sub process { - my ($self, $file, $vars, $output) = @_; - - if (@_ < 4) { - $self->controller->stash->{vars} = $vars; - $self->controller->render(template => $file, handler => 'bugzilla'); - return 1; - } - elsif (@_ == 4) { - return $self->template->process($file, $vars, $output); - } - else { - die __PACKAGE__ . '->process() called with too many arguments'; - } -} - -1; \ No newline at end of file -- cgit v1.2.3-24-g4f1b From 8ca2304eea840e37bcf182f21e2c14bd21ad9cd7 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 23 Jul 2018 11:24:13 -0400 Subject: more tidy --- Bugzilla/Memcached.pm | 2 ++ Bugzilla/Quantum/Plugin/BasicAuth.pm | 10 +++++----- Bugzilla/Quantum/Plugin/BlockIP.pm | 20 ++++++++++---------- Bugzilla/Quantum/Plugin/Hostage.pm | 29 +++++++++++++++-------------- 4 files changed, 32 insertions(+), 29 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Memcached.pm b/Bugzilla/Memcached.pm index 40755aa29..6bbef080a 100644 --- a/Bugzilla/Memcached.pm +++ b/Bugzilla/Memcached.pm @@ -25,6 +25,8 @@ use Sys::Syslog qw(:DEFAULT); use constant MAX_KEY_LENGTH => 250; use constant RATE_LIMIT_PREFIX => "rate:"; +*new = \&_new; + sub _new { my $invocant = shift; my $class = ref($invocant) || $invocant; diff --git a/Bugzilla/Quantum/Plugin/BasicAuth.pm b/Bugzilla/Quantum/Plugin/BasicAuth.pm index bc79c89ef..e17273404 100644 --- a/Bugzilla/Quantum/Plugin/BasicAuth.pm +++ b/Bugzilla/Quantum/Plugin/BasicAuth.pm @@ -16,15 +16,15 @@ sub register { $app->renderer->add_helper( basic_auth => sub { - my ($c, $realm, $auth_user, $auth_pass) = @_; + my ( $c, $realm, $auth_user, $auth_pass ) = @_; my $req = $c->req; - my ($user, $password) = $req->url->to_abs->userinfo =~ /^([^:]+):(.*)/; + my ( $user, $password ) = $req->url->to_abs->userinfo =~ /^([^:]+):(.*)/; - unless ($realm && $auth_user && $auth_pass) { + unless ( $realm && $auth_user && $auth_pass ) { croak 'basic_auth() called with missing parameters.'; } - unless ($user eq $auth_user && $password eq $auth_pass) { + unless ( $user eq $auth_user && $password eq $auth_pass ) { WARN('username and password do not match'); $c->res->headers->www_authenticate("Basic realm=\"$realm\""); $c->res->code(401); @@ -37,4 +37,4 @@ sub register { ); } -1; +1; \ No newline at end of file diff --git a/Bugzilla/Quantum/Plugin/BlockIP.pm b/Bugzilla/Quantum/Plugin/BlockIP.pm index fbfffad66..ebeb2a4aa 100644 --- a/Bugzilla/Quantum/Plugin/BlockIP.pm +++ b/Bugzilla/Quantum/Plugin/BlockIP.pm @@ -4,34 +4,34 @@ use Mojo::Base 'Mojolicious::Plugin'; use Bugzilla::Memcached; -use constant BLOCK_TIMEOUT => 60*60; +use constant BLOCK_TIMEOUT => 60 * 60; -my $MEMCACHED = Bugzilla::Memcached->_new()->{memcached}; +my $MEMCACHED = Bugzilla::Memcached->new()->{memcached}; sub register { my ( $self, $app, $conf ) = @_; - $app->hook(before_routes => \&_before_routes); - $app->helper(block_ip => \&_block_ip); - $app->helper(unblock_ip => \&_unblock_ip); + $app->hook( before_routes => \&_before_routes ); + $app->helper( block_ip => \&_block_ip ); + $app->helper( unblock_ip => \&_unblock_ip ); } sub _block_ip { - my ($class, $ip) = @_; - $MEMCACHED->set("block_ip:$ip" => 1, BLOCK_TIMEOUT) if $MEMCACHED; + my ( $class, $ip ) = @_; + $MEMCACHED->set( "block_ip:$ip" => 1, BLOCK_TIMEOUT ) if $MEMCACHED; } sub _unblock_ip { - my ($class, $ip) = @_; + my ( $class, $ip ) = @_; $MEMCACHED->delete("block_ip:$ip") if $MEMCACHED; } sub _before_routes { - my ( $c ) = @_; + my ($c) = @_; return if $c->stash->{'mojo.static'}; my $ip = $c->tx->remote_address; - if ($MEMCACHED && $MEMCACHED->get("block_ip:$ip")) { + if ( $MEMCACHED && $MEMCACHED->get("block_ip:$ip") ) { $c->block_ip($ip); $c->res->code(429); $c->res->message("Too Many Requests"); diff --git a/Bugzilla/Quantum/Plugin/Hostage.pm b/Bugzilla/Quantum/Plugin/Hostage.pm index 42a05a910..19c77995e 100644 --- a/Bugzilla/Quantum/Plugin/Hostage.pm +++ b/Bugzilla/Quantum/Plugin/Hostage.pm @@ -6,14 +6,14 @@ sub _attachment_root { my ($base) = @_; return undef unless $base; return $base =~ m{^https?://(?:bug)?\%bugid\%\.([a-zA-Z\.-]+)} - ? $1 - : undef; + ? $1 + : undef; } sub _attachment_host_regex { my ($base) = @_; return undef unless $base; - my $val = $base; + my $val = $base; $val =~ s{^https?://}{}s; $val =~ s{/$}{}s; my $regex = quotemeta $val; @@ -24,11 +24,11 @@ sub _attachment_host_regex { sub register { my ( $self, $app, $conf ) = @_; - $app->hook(before_routes => \&_before_routes); + $app->hook( before_routes => \&_before_routes ); } sub _before_routes { - my ( $c ) = @_; + my ($c) = @_; state $urlbase = Bugzilla->localconfig->{urlbase}; state $urlbase_uri = URI->new($urlbase); state $urlbase_host = $urlbase_uri->host; @@ -43,31 +43,32 @@ sub _before_routes { return if $stash->{'mojo.static'}; - my $hostname = $url->host; + my $hostname = $url->host; return if $hostname eq $urlbase_host; my $path = $url->path; return if $path eq '/__lbheartbeat__'; - if ($attachment_base && $hostname eq $attachment_root) { + if ( $attachment_base && $hostname eq $attachment_root ) { $c->redirect_to($urlbase); return; } - elsif ($attachment_base && $hostname =~ $attachment_host_regex) { - if ($path =~ m{^/attachment\.cgi}s) { + elsif ( $attachment_base && $hostname =~ $attachment_host_regex ) { + if ( $path =~ m{^/attachment\.cgi}s ) { return; - } else { + } + else { my $new_uri = $url->clone; - $new_uri->scheme($urlbase_uri->scheme); + $new_uri->scheme( $urlbase_uri->scheme ); $new_uri->host($urlbase_host); $c->redirect_to($new_uri); return; } } - elsif (my ($id) = $hostname =~ $urlbase_host_regex) { + elsif ( my ($id) = $hostname =~ $urlbase_host_regex ) { my $new_uri = $urlbase_uri->clone; $new_uri->path('/show_bug.cgi'); - $new_uri->query_form(id => $id); + $new_uri->query_form( id => $id ); $c->redirect_to($new_uri); return; } @@ -77,4 +78,4 @@ sub _before_routes { } } -1; \ No newline at end of file +1; -- cgit v1.2.3-24-g4f1b From a108778bc2784cdadfc6b2df4ec989b46dc162ca Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Mon, 23 Jul 2018 11:41:14 -0400 Subject: more tidy --- Bugzilla/Quantum/CGI.pm | 14 +++++++------- Bugzilla/Quantum/Plugin/BlockIP.pm | 4 ++-- Bugzilla/Quantum/Plugin/Glue.pm | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index ab092140c..7585d888a 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -9,17 +9,17 @@ package Bugzilla::Quantum::CGI; use Mojo::Base 'Mojolicious::Controller'; use CGI::Compile; -use Bugzilla::Constants qw(bz_locations); -use Bugzilla::Quantum::Stdout; -use File::Slurper qw(read_text); -use File::Spec::Functions qw(catfile); -use Sub::Name; -use Sub::Quote 2.005000; use Try::Tiny; use Taint::Util qw(untaint); -use Socket qw(AF_INET inet_aton); use Sys::Hostname; +use Sub::Quote 2.005000; +use Sub::Name; +use Socket qw(AF_INET inet_aton); +use File::Spec::Functions qw(catfile); +use File::Slurper qw(read_text); use English qw(-no_match_vars); +use Bugzilla::Quantum::Stdout; +use Bugzilla::Constants qw(bz_locations); our $C; my %SEEN; diff --git a/Bugzilla/Quantum/Plugin/BlockIP.pm b/Bugzilla/Quantum/Plugin/BlockIP.pm index ebeb2a4aa..058ecbf64 100644 --- a/Bugzilla/Quantum/Plugin/BlockIP.pm +++ b/Bugzilla/Quantum/Plugin/BlockIP.pm @@ -34,8 +34,8 @@ sub _before_routes { if ( $MEMCACHED && $MEMCACHED->get("block_ip:$ip") ) { $c->block_ip($ip); $c->res->code(429); - $c->res->message("Too Many Requests"); - $c->res->body("Too Many Requests"); + $c->res->message('Too Many Requests'); + $c->res->body('Too Many Requests'); $c->finish; } } diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm index f99026690..02514a0f8 100644 --- a/Bugzilla/Quantum/Plugin/Glue.pm +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -48,7 +48,7 @@ sub register { sub { Bugzilla::RNG::srand(); srand(); - eval { Bugzilla->dbh->ping }; + try { Bugzilla->dbh->ping }; } ); @@ -69,9 +69,9 @@ sub register { Bugzilla::Extension->load_all(); if ( $app->mode ne 'development' ) { Bugzilla->preload_features(); - DEBUG("preloading templates"); + DEBUG('preloading templates'); Bugzilla->preload_templates(); - DEBUG("done preloading templates"); + DEBUG('done preloading templates'); } $app->secrets( [ Bugzilla->localconfig->{side_wide_secret} ] ); -- cgit v1.2.3-24-g4f1b From eec09d8bec17b7f11a5d204562c4646959696d97 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 31 Jul 2018 13:06:08 -0400 Subject: review nits --- Bugzilla/Config/General.pm | 6 ------ 1 file changed, 6 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Config/General.pm b/Bugzilla/Config/General.pm index 15688dfd3..c870c7376 100644 --- a/Bugzilla/Config/General.pm +++ b/Bugzilla/Config/General.pm @@ -39,12 +39,6 @@ use constant get_param_list => ( checker => \&check_utf8 }, - { - name => 'shutdownhtml', - type => 'l', - default => '' - }, - { name => 'announcehtml', type => 'l', -- cgit v1.2.3-24-g4f1b From 72734c6f4f14d763b4588c3d61e9190393a28c6d Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 31 Jul 2018 17:12:20 -0400 Subject: hypnotoad should always expect a proxy --- Bugzilla/Quantum/Plugin/Glue.pm | 1 + 1 file changed, 1 insertion(+) (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum/Plugin/Glue.pm b/Bugzilla/Quantum/Plugin/Glue.pm index 02514a0f8..ea21429bd 100644 --- a/Bugzilla/Quantum/Plugin/Glue.pm +++ b/Bugzilla/Quantum/Plugin/Glue.pm @@ -34,6 +34,7 @@ sub register { # hypnotoad is weird and doesn't look for MOJO_LISTEN itself. $app->config( hypnotoad => { + proxy => 1, listen => [ $ENV{MOJO_LISTEN} ], }, ); -- cgit v1.2.3-24-g4f1b From c54dcbbe9b9ec1199e6b7e8903ff65ac7448995d Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 31 Jul 2018 21:37:33 -0400 Subject: add version --- Bugzilla/Quantum.pm | 14 ++++++++++++++ Bugzilla/Quantum/Stdout.pm | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Quantum.pm b/Bugzilla/Quantum.pm index 083dcaf55..135ff94a9 100644 --- a/Bugzilla/Quantum.pm +++ b/Bugzilla/Quantum.pm @@ -63,6 +63,20 @@ sub startup { }, ); + $r->get( + '/__version__' => sub { + my $c = shift; + $c->reply->file( $c->app->home->child('version.json') ); + }, + ); + + $r->get( + '/version.json' => sub { + my $c = shift; + $c->reply->file( $c->app->home->child('version.json') ); + }, + ); + $r->get('/__heartbeat__')->to('CGI#heartbeat_cgi'); $r->get('/robots.txt')->to('CGI#robots_cgi'); diff --git a/Bugzilla/Quantum/Stdout.pm b/Bugzilla/Quantum/Stdout.pm index 699ec164a..be7b546ea 100644 --- a/Bugzilla/Quantum/Stdout.pm +++ b/Bugzilla/Quantum/Stdout.pm @@ -41,7 +41,7 @@ sub PRINT { ## no critic (unpack) if ( $self->_encoding ) { $bytes = encode( $self->_encoding, $bytes ); } - $c->write($bytes); + $c->write($bytes.$\); } sub BINMODE { -- cgit v1.2.3-24-g4f1b From 01309f9f36d1767c8d6984364c47539fed3946e6 Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Tue, 31 Jul 2018 23:49:38 -0400 Subject: add more debugging but also probably fix redirect problem --- Bugzilla/CGI.pm | 4 ++++ Bugzilla/Quantum/CGI.pm | 2 +- Bugzilla/Quantum/Plugin/Hostage.pm | 5 +++++ Bugzilla/Util.pm | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 01d610db1..1bf0dd54a 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -141,6 +141,7 @@ sub new { # apache collapses // to / in $ENV{PATH_INFO} but not in $self->path_info. # url() requires the full path in ENV in order to generate the correct url. $ENV{PATH_INFO} = $path; + DEBUG("redirecting because we see PATH_INFO and don't like it"); print $self->redirect($self->url(-path => 0, -query => 1)); exit; } @@ -151,6 +152,7 @@ sub new { # Redirect to urlbase if we are not viewing an attachment. if ($self->url_is_attachment_base and $script ne 'attachment.cgi') { + DEBUG("Redirecting to urlbase because the url is in the attachment base and not attachment.cgi"); $self->redirect_to_urlbase(); } @@ -782,6 +784,7 @@ sub redirect_search_url { # are only redirected if they're under the CGI_URI_LIMIT though. my $self_url = $self->self_url(); if ($self->request_method() ne 'POST' or length($self_url) < CGI_URI_LIMIT) { + DEBUG("Redirecting search url"); print $self->redirect(-url => $self_url); exit; } @@ -803,6 +806,7 @@ sub redirect_to_https { # XML-RPC clients (SOAP::Lite at least) require a 301 to redirect properly # and do not work with 302. Our redirect really is permanent anyhow, so # it doesn't hurt to make it a 301. + DEBUG("Redirecting to https"); print $self->redirect(-location => $url, -status => 301); exit; } diff --git a/Bugzilla/Quantum/CGI.pm b/Bugzilla/Quantum/CGI.pm index 7585d888a..0a74f1ee5 100644 --- a/Bugzilla/Quantum/CGI.pm +++ b/Bugzilla/Quantum/CGI.pm @@ -117,7 +117,7 @@ sub _ENV { CONTENT_LENGTH => $content_length || 0, CONTENT_TYPE => $headers->content_type || '', GATEWAY_INTERFACE => 'CGI/1.1', - HTTPS => $req->is_secure ? 'YES' : 'NO', + HTTPS => $req->is_secure ? 'on' : 'off', %env_headers, QUERY_STRING => $cgi_query->to_string, PATH_INFO => $path_info ? "/$path_info" : '', diff --git a/Bugzilla/Quantum/Plugin/Hostage.pm b/Bugzilla/Quantum/Plugin/Hostage.pm index 19c77995e..cbde7b5ee 100644 --- a/Bugzilla/Quantum/Plugin/Hostage.pm +++ b/Bugzilla/Quantum/Plugin/Hostage.pm @@ -1,6 +1,7 @@ package Bugzilla::Quantum::Plugin::Hostage; use 5.10.1; use Mojo::Base 'Mojolicious::Plugin'; +use Bugzilla::Logging; sub _attachment_root { my ($base) = @_; @@ -50,6 +51,7 @@ sub _before_routes { return if $path eq '/__lbheartbeat__'; if ( $attachment_base && $hostname eq $attachment_root ) { + DEBUG("redirecting to $urlbase because $hostname is $attachment_root"); $c->redirect_to($urlbase); return; } @@ -61,6 +63,7 @@ sub _before_routes { my $new_uri = $url->clone; $new_uri->scheme( $urlbase_uri->scheme ); $new_uri->host($urlbase_host); + DEBUG("redirecting to $new_uri because $hostname matches attachment regex"); $c->redirect_to($new_uri); return; } @@ -69,10 +72,12 @@ sub _before_routes { my $new_uri = $urlbase_uri->clone; $new_uri->path('/show_bug.cgi'); $new_uri->query_form( id => $id ); + DEBUG("redirecting to $new_uri because $hostname includes bug id"); $c->redirect_to($new_uri); return; } else { + DEBUG("redirecting to $urlbase because $hostname doesn't make sense"); $c->redirect_to($urlbase); return; } diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index a8477a62d..aa524b263 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -29,7 +29,7 @@ use base qw(Exporter); get_text template_var disable_utf8 enable_utf8 detect_encoding email_filter round extract_nicks); - +use Bugzilla::Logging; use Bugzilla::Constants; use Bugzilla::RNG qw(irand); @@ -317,6 +317,7 @@ sub do_ssl_redirect_if_required { # If we're already running under SSL, never redirect. return if $ENV{HTTPS} && $ENV{HTTPS} eq 'on'; + DEBUG("Redirect to HTTPS because \$ENV{HTTPS}=$ENV{HTTPS}"); Bugzilla->cgi->redirect_to_https(); } -- cgit v1.2.3-24-g4f1b