diff options
Diffstat (limited to 'Bugzilla.pm')
-rw-r--r-- | Bugzilla.pm | 227 |
1 files changed, 87 insertions, 140 deletions
diff --git a/Bugzilla.pm b/Bugzilla.pm index f5e64c371..3d7cb50d5 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -22,7 +22,7 @@ BEGIN { } } -our $VERSION = '20180330.1'; +our $VERSION = '20180703.1'; use Bugzilla::Auth; use Bugzilla::Auth::Persist::Cookie; @@ -38,6 +38,8 @@ use Bugzilla::Flag; use Bugzilla::Hook; use Bugzilla::Install::Localconfig qw(read_localconfig); use Bugzilla::Install::Util qw(init_console include_languages); +use Bugzilla::Markdown::GFM; +use Bugzilla::Markdown::GFM::Parser; use Bugzilla::Memcached; use Bugzilla::Template; use Bugzilla::Token; @@ -46,17 +48,12 @@ use Bugzilla::Util; use Bugzilla::CPAN; use Bugzilla::Bloomfilter; -use Bugzilla::Metrics::Collector; -use Bugzilla::Metrics::Template; -use Bugzilla::Metrics::Memcached; - use Date::Parse; use DateTime::TimeZone; use Encode; use File::Basename; use File::Spec::Functions; use Safe; -use Sys::Syslog qw(:DEFAULT); use JSON::XS qw(decode_json); use URI; @@ -83,6 +80,11 @@ use constant SHUTDOWNHTML_EXIT_SILENTLY => qw( # should search engines attempt to index the page again? use constant SHUTDOWNHTML_RETRY_AFTER => 3600; +# This is identical to Install::Util::_cache so that things loaded +# into Install::Util::_cache during installation can be read out +# of request_cache later in installation. +use constant request_cache => Bugzilla::Install::Util::_cache(); + ##################################################################### # Global Code ##################################################################### @@ -101,7 +103,7 @@ sub init_page { } if (i_am_cgi()) { - Log::Log4perl::MDC->put(remote_ip => remote_ip()); + Bugzilla::Logging->fields->{remote_ip} = remote_ip(); } if (${^TAINT}) { @@ -120,30 +122,6 @@ sub init_page { my $script = basename($0); - # BMO - init metrics collection if required - if (i_am_cgi() && $script eq 'show_bug.cgi') { - # we need to measure loading the params, so default to on - Bugzilla->metrics_enabled(1); - Bugzilla->metrics($script); - # we can now hit params to check if we really should be enabled. - # note - we can't use anything which uses templates or the database, as - # that would initialise those modules with metrics enabled. - if (!Bugzilla->params->{metrics_enabled}) { - Bugzilla->metrics_enabled(0); - } - else { - # to avoid generating massive amounts of data, we're only interested in - # a small subset of users - my $user_id = Bugzilla->cgi->cookie('Bugzilla_login'); - if (!$user_id - || !grep { $user_id == $_ } - split(/\s*,\s*/, Bugzilla->params->{metrics_user_ids})) - { - Bugzilla->metrics_enabled(0); - } - } - } - # Because of attachment_base, attachment.cgi handles this itself. if ($script ne 'attachment.cgi') { do_ssl_redirect_if_required(); @@ -212,28 +190,21 @@ sub init_page { ##################################################################### sub template { - # BMO - use metrics subclass if required - if (Bugzilla->metrics_enabled) { - $_[0]->request_cache->{template} ||= Bugzilla::Metrics::Template->create(); - } else { - $_[0]->request_cache->{template} ||= Bugzilla::Template->create(); - } - $_[0]->request_cache->{template}->{_is_main} = 1; + request_cache->{template} ||= Bugzilla::Template->create(); + request_cache->{template}->{_is_main} = 1; - return $_[0]->request_cache->{template}; + return request_cache->{template}; } sub template_inner { - my ($class, $lang) = @_; - my $cache = $class->request_cache; + my (undef, $lang) = @_; + my $cache = request_cache; my $current_lang = $cache->{template_current_lang}->[0]; $lang ||= $current_lang || ''; return $cache->{"template_inner_$lang"} ||= Bugzilla::Template->create(language => $lang); } sub extensions { - my ($class) = @_; - # Guard against extensions querying the extension list during initialization # (through this method or has_extension). # The extension list is not fully populated at that point, @@ -242,7 +213,7 @@ sub extensions { die "Recursive attempt to load/query extensions" if $recursive; $recursive = 1; - my $cache = $class->request_cache; + my $cache = request_cache; if (!$cache->{extensions}) { my $extension_packages = Bugzilla::Extension->load_all(); my @extensions; @@ -269,12 +240,12 @@ sub has_extension { } sub cgi { - return $_[0]->request_cache->{cgi} ||= new Bugzilla::CGI(); + return request_cache->{cgi} ||= new Bugzilla::CGI(); } sub input_params { my ($class, $params) = @_; - my $cache = $class->request_cache; + my $cache = request_cache; # This is how the WebService and other places set input_params. if (defined $params) { $cache->{input_params} = $params; @@ -300,7 +271,7 @@ sub urlbase { } sub params { - return $_[0]->request_cache->{params} ||= Bugzilla::Config::read_param_file(); + return request_cache->{params} ||= Bugzilla::Config::read_param_file(); } sub get_param_with_override { @@ -309,32 +280,32 @@ sub get_param_with_override { } sub user { - return $_[0]->request_cache->{user} ||= new Bugzilla::User; + return request_cache->{user} ||= new Bugzilla::User; } sub set_user { - my ($class, $user) = @_; - $class->request_cache->{user} = $user; + my (undef, $user) = @_; + request_cache->{user} = $user; } sub sudoer { - return $_[0]->request_cache->{sudoer}; + return request_cache->{sudoer}; } sub sudo_request { - my ($class, $new_user, $new_sudoer) = @_; - $class->request_cache->{user} = $new_user; - $class->request_cache->{sudoer} = $new_sudoer; + my (undef, $new_user, $new_sudoer) = @_; + request_cache->{user} = $new_user; + request_cache->{sudoer} = $new_sudoer; # NOTE: If you want to log the start of an sudo session, do it here. } sub page_requires_login { - return $_[0]->request_cache->{page_requires_login}; + return request_cache->{page_requires_login}; } sub github_secret { my ($class) = @_; - my $cache = $class->request_cache; + my $cache = request_cache; my $cgi = $class->cgi; $cache->{github_secret} //= $cgi->cookie('github_secret') // generate_random_password(256); @@ -346,7 +317,7 @@ sub passwdqc { my ($class) = @_; require Data::Password::passwdqc; - my $cache = $class->request_cache; + my $cache = request_cache; my $params = $class->params; return $cache->{passwdqc} if $cache->{passwdqc}; @@ -395,13 +366,13 @@ sub login { # Allow templates to know that we're in a page that always requires # login. if ($type == LOGIN_REQUIRED) { - $class->request_cache->{page_requires_login} = 1; + request_cache->{page_requires_login} = 1; } my $authenticated_user = $authorizer->login($type); - if (i_am_cgi()) { - Log::Log4perl::MDC->put(user_id => $authenticated_user->id); + if (i_am_cgi() && $authenticated_user->id) { + Bugzilla::Logging->fields->{user_id} = $authenticated_user->id; } # At this point, we now know if a real person is logged in. @@ -484,7 +455,7 @@ sub login { && !$sudo_target->in_group('bz_sudo_protect')) { $class->set_user($sudo_target); - $class->request_cache->{sudoer} = $authenticated_user; + request_cache->{sudoer} = $authenticated_user; # And make sure that both users have the same Auth object, # since we never call Auth::login for the sudo target. $sudo_target->set_authorizer($authenticated_user->authorizer); @@ -539,24 +510,25 @@ sub logout_user_by_id { # hack that invalidates credentials for a single request sub logout_request { my $class = shift; - delete $class->request_cache->{user}; - delete $class->request_cache->{sudoer}; + delete request_cache->{user}; + delete request_cache->{sudoer}; # We can't delete from $cgi->cookie, so logincookie data will remain # there. Don't rely on it: use Bugzilla->user->login instead! } sub job_queue { require Bugzilla::JobQueue; - return $_[0]->request_cache->{job_queue} ||= Bugzilla::JobQueue->new(); + return request_cache->{job_queue} ||= Bugzilla::JobQueue->new(); } sub dbh { + my ($class) = @_; # If we're not connected, then we must want the main db - return $_[0]->request_cache->{dbh} ||= $_[0]->dbh_main; + return request_cache->{dbh} ||= $class->dbh_main; } sub dbh_main { - return $_[0]->request_cache->{dbh_main} ||= Bugzilla::DB::connect_main(); + return request_cache->{dbh_main} ||= Bugzilla::DB::connect_main(); } sub languages { @@ -564,25 +536,25 @@ sub languages { } sub current_language { - return $_[0]->request_cache->{current_language} ||= (include_languages())[0]; + return request_cache->{current_language} ||= (include_languages())[0]; } sub error_mode { - my ($class, $newval) = @_; + my (undef, $newval) = @_; if (defined $newval) { - $class->request_cache->{error_mode} = $newval; + request_cache->{error_mode} = $newval; } - return $class->request_cache->{error_mode} + return request_cache->{error_mode} || (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE); } # This is used only by Bugzilla::Error to throw errors. sub _json_server { - my ($class, $newval) = @_; + my (undef, $newval) = @_; if (defined $newval) { - $class->request_cache->{_json_server} = $newval; + request_cache->{_json_server} = $newval; } - return $class->request_cache->{_json_server}; + return request_cache->{_json_server}; } sub usage_mode { @@ -613,21 +585,21 @@ sub usage_mode { ThrowCodeError('usage_mode_invalid', {'invalid_usage_mode', $newval}); } - $class->request_cache->{usage_mode} = $newval; + request_cache->{usage_mode} = $newval; } - return $class->request_cache->{usage_mode} + return request_cache->{usage_mode} || (i_am_cgi()? USAGE_MODE_BROWSER : USAGE_MODE_CMDLINE); } sub installation_mode { - my ($class, $newval) = @_; - ($class->request_cache->{installation_mode} = $newval) if defined $newval; - return $class->request_cache->{installation_mode} + my (undef, $newval) = @_; + (request_cache->{installation_mode} = $newval) if defined $newval; + return request_cache->{installation_mode} || INSTALLATION_MODE_INTERACTIVE; } sub installation_answers { - my ($class, $filename) = @_; + my (undef, $filename) = @_; if ($filename) { my $s = new Safe; $s->rdo($filename); @@ -636,23 +608,23 @@ sub installation_answers { die "Error evaluating $filename: $@" if $@; # Now read the param back out from the sandbox - $class->request_cache->{installation_answers} = $s->varglob('answer'); + request_cache->{installation_answers} = $s->varglob('answer'); } - return $class->request_cache->{installation_answers} || {}; + return request_cache->{installation_answers} || {}; } sub switch_to_shadow_db { my $class = shift; - if (!$class->request_cache->{dbh_shadow}) { + if (!request_cache->{dbh_shadow}) { if ($class->get_param_with_override('shadowdb')) { - $class->request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow(); + request_cache->{dbh_shadow} = Bugzilla::DB::connect_shadow(); } else { - $class->request_cache->{dbh_shadow} = $class->dbh_main; + request_cache->{dbh_shadow} = $class->dbh_main; } } - $class->request_cache->{dbh} = $class->request_cache->{dbh_shadow}; + request_cache->{dbh} = request_cache->{dbh_shadow}; # we have to return $class->dbh instead of {dbh} as # {dbh_shadow} may be undefined if no shadow DB is used # and no connection to the main DB has been established yet. @@ -662,7 +634,7 @@ sub switch_to_shadow_db { sub switch_to_main_db { my $class = shift; - $class->request_cache->{dbh} = $class->dbh_main; + request_cache->{dbh} = $class->dbh_main; return $class->dbh_main; } @@ -696,7 +668,7 @@ sub log_user_request { } eval { - local $class->request_cache->{dbh}; + local request_cache->{dbh}; $class->switch_to_main_db(); $class->dbh->do("INSERT INTO user_request_log (user_id, ip_address, user_agent, request_url, @@ -708,13 +680,13 @@ sub log_user_request { sub is_shadow_db { my $class = shift; - return $class->request_cache->{dbh} != $class->dbh_main; + return request_cache->{dbh} != $class->dbh_main; } sub fields { - my ($class, $criteria) = @_; + my (undef, $criteria) = @_; $criteria ||= {}; - my $cache = $class->request_cache; + my $cache = request_cache; # We create an advanced cache for fields by type, so that we # can avoid going back to the database for every fields() call. @@ -761,29 +733,28 @@ sub fields { } sub active_custom_fields { - my ($class, $params) = @_; + my (undef, $params) = @_; my $cache_id = 'active_custom_fields'; if ($params) { $cache_id .= ($params->{product} ? '_p' . $params->{product}->id : '') . ($params->{component} ? '_c' . $params->{component}->id : ''); $cache_id .= ':noext' if $params->{skip_extensions}; } - if (!exists $class->request_cache->{$cache_id}) { + if (!exists request_cache->{$cache_id}) { my $fields = Bugzilla::Field->match({ custom => 1, obsolete => 0, skip_extensions => 1 }); Bugzilla::Hook::process('active_custom_fields', { fields => \$fields, params => $params }); - $class->request_cache->{$cache_id} = $fields; + request_cache->{$cache_id} = $fields; } - return @{$class->request_cache->{$cache_id}}; + return @{request_cache->{$cache_id}}; } sub has_flags { - my $class = shift; - if (!defined $class->request_cache->{has_flags}) { - $class->request_cache->{has_flags} = Bugzilla::Flag->any_exist; + if (!defined request_cache->{has_flags}) { + request_cache->{has_flags} = Bugzilla::Flag->any_exist; } - return $class->request_cache->{has_flags}; + return request_cache->{has_flags}; } sub local_timezone { @@ -793,19 +764,13 @@ sub local_timezone { # Send messages to syslog for the auditing systems (eg. mozdef) to pick up. sub audit { - my ($class, $message) = @_; + my (undef, $message) = @_; state $logger = Log::Log4perl->get_logger("audit"); $logger->notice(encode_utf8($message)); } -# This creates the request cache for non-mod_perl installations. -# This is identical to Install::Util::_cache so that things loaded -# into Install::Util::_cache during installation can be read out -# of request_cache later in installation. -use constant request_cache => Bugzilla::Install::Util::_cache(); - sub clear_request_cache { - my ($class, %option) = @_; + my (undef, %option) = @_; my $request_cache = request_cache(); my @except = $option{except} ? @{ $option{except} } : (); @@ -821,37 +786,10 @@ sub process_cache { return $_process_cache; } -# BMO - Instrumentation - -sub metrics_enabled { - if (defined $_[1]) { - if (!$_[1] - && $_[0]->request_cache->{metrics_enabled} - && $_[0]->request_cache->{metrics}) - { - $_[0]->request_cache->{metrics}->cancel(); - delete $_[0]->request_cache->{metrics}; - } - $_[0]->request_cache->{metrics_enabled} = $_[1]; - } - else { - return $_[0]->request_cache->{metrics_enabled}; - } -} - -sub metrics { - return $_[0]->request_cache->{metrics} ||= Bugzilla::Metrics::Collector->new($_[1]); -} - # This is a memcached wrapper, which provides cross-process and cross-system # caching. sub memcached { - # BMO - use metrics subclass if required - if (Bugzilla->metrics_enabled) { - return $_[0]->request_cache->{memcached} ||= Bugzilla::Metrics::Memcached->_new(); - } else { - return $_[0]->request_cache->{memcached} ||= Bugzilla::Memcached->_new(); - } + return request_cache->{memcached} ||= Bugzilla::Memcached->_new(); } sub elastic { @@ -877,20 +815,25 @@ sub check_rate_limit { } my $limit = join("/", @$limit); Bugzilla->audit("[rate_limit] action=$action, ip=$ip, limit=$limit, name=$name"); - ThrowUserError("rate_limit") if $action eq 'block'; + if ($action eq 'block') { + Bugzilla::ModPerl::BlockIP->block_ip($ip); + ThrowUserError("rate_limit"); + } } } } +sub markdown_parser { + return request_cache->{markdown_parser} + ||= Bugzilla::Markdown::GFM::Parser->new( {extensions => [qw( autolink tagfilter table strikethrough)] } ); +} + # Private methods # Per-process cleanup. Note that this is a plain subroutine, not a method, # so we don't have $class available. sub _cleanup { - # BMO - finalise and report on metrics - if (Bugzilla->metrics_enabled) { - Bugzilla->metrics->finish(); - } + return if $^C; # BMO - allow "end of request" processing Bugzilla::Hook::process('request_cleanup'); @@ -901,7 +844,6 @@ sub _cleanup { foreach my $dbh ($main, $shadow) { next if !$dbh; $dbh->bz_rollback_transaction() if $dbh->bz_in_transaction; - $dbh->disconnect; } clear_request_cache(); @@ -1205,6 +1147,11 @@ of features, see C<OPTIONAL_MODULES> in C<Bugzilla::Install::Requirements>. Feeds the provided message into our centralised auditing system. +=item C<markdown_parser> + +Returns a L<Bugzilla::Markdown::GFM::Parser> with the default extensions +loaded (autolink, tagfilter, table, and strikethrough). + =back =head1 B<CACHING> |