From 9a5a196d79ed8692c45595c4e3d42e34571bc3a7 Mon Sep 17 00:00:00 2001 From: Frédéric Buclin Date: Tue, 17 Apr 2012 20:56:41 +0200 Subject: Bug 745197: Add a hook in Bugzilla::Error::_throw_error() so that extensions can control the way to throw errors r=dkl a=LpSolit --- Bugzilla/Error.pm | 91 +++++++++++++------------ Bugzilla/Hook.pm | 31 +++++++++ extensions/Example/Extension.pm | 19 ++++++ template/en/default/global/code-error.html.tmpl | 6 +- 4 files changed, 100 insertions(+), 47 deletions(-) diff --git a/Bugzilla/Error.pm b/Bugzilla/Error.pm index ebe8decbd..e1df5ddbb 100644 --- a/Bugzilla/Error.pm +++ b/Bugzilla/Error.pm @@ -77,56 +77,61 @@ sub _throw_error { } my $template = Bugzilla->template; - if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) { - print Bugzilla->cgi->header(); - $template->process($name, $vars) - || ThrowTemplateError($template->error()); - } + my $message; # There are some tests that throw and catch a lot of errors, # and calling $template->process over and over for those errors # is too slow. So instead, we just "die" with a dump of the arguments. + if (Bugzilla->error_mode != ERROR_MODE_TEST) { + $template->process($name, $vars, \$message) + || ThrowTemplateError($template->error()); + } + + # Let's call the hook first, so that extensions can override + # or extend the default behavior, or add their own error codes. + Bugzilla::Hook::process('error_catch', { error => $error, vars => $vars, + message => \$message }); + + if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) { + print Bugzilla->cgi->header(); + print $message; + } elsif (Bugzilla->error_mode == ERROR_MODE_TEST) { die Dumper($vars); } - else { - my $message; - $template->process($name, $vars, \$message) - || ThrowTemplateError($template->error()); - if (Bugzilla->error_mode == ERROR_MODE_DIE) { - die("$message\n"); + elsif (Bugzilla->error_mode == ERROR_MODE_DIE) { + die("$message\n"); + } + elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT + || Bugzilla->error_mode == ERROR_MODE_JSON_RPC) + { + # Clone the hash so we aren't modifying the constant. + my %error_map = %{ WS_ERROR_CODE() }; + Bugzilla::Hook::process('webservice_error_codes', + { error_map => \%error_map }); + my $code = $error_map{$error}; + if (!$code) { + $code = ERROR_UNKNOWN_FATAL if $name =~ /code/i; + $code = ERROR_UNKNOWN_TRANSIENT if $name =~ /user/i; + } + + if (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) { + die SOAP::Fault->faultcode($code)->faultstring($message); } - elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT - || Bugzilla->error_mode == ERROR_MODE_JSON_RPC) - { - # Clone the hash so we aren't modifying the constant. - my %error_map = %{ WS_ERROR_CODE() }; - Bugzilla::Hook::process('webservice_error_codes', - { error_map => \%error_map }); - my $code = $error_map{$error}; - if (!$code) { - $code = ERROR_UNKNOWN_FATAL if $name =~ /code/i; - $code = ERROR_UNKNOWN_TRANSIENT if $name =~ /user/i; - } - - if (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) { - die SOAP::Fault->faultcode($code)->faultstring($message); - } - else { - my $server = Bugzilla->_json_server; - # Technically JSON-RPC isn't allowed to have error numbers - # higher than 999, but we do this to avoid conflicts with - # the internal JSON::RPC error codes. - $server->raise_error(code => 100000 + $code, - message => $message, - id => $server->{_bz_request_id}, - version => $server->version); - # Most JSON-RPC Throw*Error calls happen within an eval inside - # of JSON::RPC. So, in that circumstance, instead of exiting, - # we die with no message. JSON::RPC checks raise_error before - # it checks $@, so it returns the proper error. - die if _in_eval(); - $server->response($server->error_response_header); - } + else { + my $server = Bugzilla->_json_server; + # Technically JSON-RPC isn't allowed to have error numbers + # higher than 999, but we do this to avoid conflicts with + # the internal JSON::RPC error codes. + $server->raise_error(code => 100000 + $code, + message => $message, + id => $server->{_bz_request_id}, + version => $server->version); + # Most JSON-RPC Throw*Error calls happen within an eval inside + # of JSON::RPC. So, in that circumstance, instead of exiting, + # we die with no message. JSON::RPC checks raise_error before + # it checks $@, so it returns the proper error. + die if _in_eval(); + $server->response($server->error_response_header); } } exit; diff --git a/Bugzilla/Hook.pm b/Bugzilla/Hook.pm index 8eac561c0..27184c2e4 100644 --- a/Bugzilla/Hook.pm +++ b/Bugzilla/Hook.pm @@ -687,6 +687,37 @@ Params: =back +=head2 error_catch + +This hook allows extensions to catch errors thrown by Bugzilla and +take the appropriate actions. + +Params: + +=over + +=item C + +A string representing the error code thrown by Bugzilla. This string +matches the C variable in C and +C. + +=item C + +If the error mode is set to C, you get a reference to +the whole HTML page with the error message in it, including its header and +footer. If you need to extract the error message itself, you can do it by +looking at the content of the table cell whose ID is C. +If the error mode is not set to C, you get a reference +to the error message itself. + +=item C + +This hash contains all the data passed to the error template. Its content +depends on the error thrown. + +=back + =head2 flag_end_of_update This happens at the end of L, after all other diff --git a/extensions/Example/Extension.pm b/extensions/Example/Extension.pm index 5cd6a1d29..62fb345d9 100644 --- a/extensions/Example/Extension.pm +++ b/extensions/Example/Extension.pm @@ -340,6 +340,25 @@ sub enter_bug_entrydefaultvars { $vars->{'example'} = 1; } +sub error_catch { + my ($self, $args) = @_; + # Customize the error message displayed when someone tries to access + # page.cgi with an invalid page ID, and keep track of this attempt + # in the web server log. + return unless Bugzilla->error_mode == ERROR_MODE_WEBPAGE; + return unless $args->{error} eq 'bad_page_cgi_id'; + + my $page_id = $args->{vars}->{page_id}; + my $login = Bugzilla->user->identity || "Someone"; + warn "$login attempted to access page.cgi with id = $page_id"; + + my $page = $args->{message}; + my $new_error_msg = "Ah ah, you tried to access $page_id? Good try!"; + $new_error_msg = html_quote($new_error_msg); + # There are better tools to parse an HTML page, but it's just an example. + $$page =~ s/(?<=).*(?=<\/td>)/$new_error_msg/si; +} + sub flag_end_of_update { my ($self, $args) = @_; diff --git a/template/en/default/global/code-error.html.tmpl b/template/en/default/global/code-error.html.tmpl index 4a4720b2a..19d062841 100644 --- a/template/en/default/global/code-error.html.tmpl +++ b/template/en/default/global/code-error.html.tmpl @@ -508,10 +508,8 @@ -
- - [% error_message FILTER none %] - + + [% error_message FILTER none %]
-- cgit v1.2.3-24-g4f1b