Error.pm 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # -*- Mode: perl; indent-tabs-mode: nil -*-
  2. #
  3. # The contents of this file are subject to the Mozilla Public
  4. # License Version 1.1 (the "License"); you may not use this file
  5. # except in compliance with the License. You may obtain a copy of
  6. # the License at http://www.mozilla.org/MPL/
  7. #
  8. # Software distributed under the License is distributed on an "AS
  9. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10. # implied. See the License for the specific language governing
  11. # rights and limitations under the License.
  12. #
  13. # The Original Code is the Bugzilla Bug Tracking System.
  14. #
  15. # The Initial Developer of the Original Code is Netscape Communications
  16. # Corporation. Portions created by Netscape are
  17. # Copyright (C) 1998 Netscape Communications Corporation. All
  18. # Rights Reserved.
  19. #
  20. # Contributor(s): Bradley Baetz <bbaetz@acm.org>
  21. # Marc Schumann <wurblzap@gmail.com>
  22. # Frédéric Buclin <LpSolit@gmail.com>
  23. package Bugzilla::Error;
  24. use strict;
  25. use base qw(Exporter);
  26. @Bugzilla::Error::EXPORT = qw(ThrowCodeError ThrowTemplateError ThrowUserError);
  27. use Bugzilla::Constants;
  28. use Bugzilla::WebService::Constants;
  29. use Bugzilla::Util;
  30. use Date::Format;
  31. # We cannot use $^S to detect if we are in an eval(), because mod_perl
  32. # already eval'uates everything, so $^S = 1 in all cases under mod_perl!
  33. sub _in_eval {
  34. my $in_eval = 0;
  35. for (my $stack = 1; my $sub = (caller($stack))[3]; $stack++) {
  36. last if $sub =~ /^ModPerl/;
  37. $in_eval = 1 if $sub =~ /^\(eval\)/;
  38. }
  39. return $in_eval;
  40. }
  41. sub _throw_error {
  42. my ($name, $error, $vars) = @_;
  43. my $dbh = Bugzilla->dbh;
  44. $vars ||= {};
  45. $vars->{error} = $error;
  46. # Make sure any transaction is rolled back (if supported).
  47. # If we are within an eval(), do not roll back transactions as we are
  48. # eval'uating some test on purpose.
  49. $dbh->bz_rollback_transaction() if ($dbh->bz_in_transaction() && !_in_eval());
  50. my $datadir = bz_locations()->{'datadir'};
  51. # If a writable $datadir/errorlog exists, log error details there.
  52. if (-w "$datadir/errorlog") {
  53. require Data::Dumper;
  54. my $mesg = "";
  55. for (1..75) { $mesg .= "-"; };
  56. $mesg .= "\n[$$] " . time2str("%D %H:%M:%S ", time());
  57. $mesg .= "$name $error ";
  58. $mesg .= "$ENV{REMOTE_ADDR} " if $ENV{REMOTE_ADDR};
  59. $mesg .= Bugzilla->user->login;
  60. $mesg .= (' actually ' . Bugzilla->sudoer->login) if Bugzilla->sudoer;
  61. $mesg .= "\n";
  62. my %params = Bugzilla->cgi->Vars;
  63. $Data::Dumper::Useqq = 1;
  64. for my $param (sort keys %params) {
  65. my $val = $params{$param};
  66. # obscure passwords
  67. $val = "*****" if $param =~ /password/i;
  68. # limit line length
  69. $val =~ s/^(.{512}).*$/$1\[CHOP\]/;
  70. $mesg .= "[$$] " . Data::Dumper->Dump([$val],["param($param)"]);
  71. }
  72. for my $var (sort keys %ENV) {
  73. my $val = $ENV{$var};
  74. $val = "*****" if $val =~ /password|http_pass/i;
  75. $mesg .= "[$$] " . Data::Dumper->Dump([$val],["env($var)"]);
  76. }
  77. open(ERRORLOGFID, ">>$datadir/errorlog");
  78. print ERRORLOGFID "$mesg\n";
  79. close ERRORLOGFID;
  80. }
  81. my $template = Bugzilla->template;
  82. if (Bugzilla->error_mode == ERROR_MODE_WEBPAGE) {
  83. print Bugzilla->cgi->header();
  84. $template->process($name, $vars)
  85. || ThrowTemplateError($template->error());
  86. }
  87. else {
  88. my $message;
  89. $template->process($name, $vars, \$message)
  90. || ThrowTemplateError($template->error());
  91. if (Bugzilla->error_mode == ERROR_MODE_DIE) {
  92. die("$message\n");
  93. }
  94. elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) {
  95. # Clone the hash so we aren't modifying the constant.
  96. my %error_map = %{ WS_ERROR_CODE() };
  97. require Bugzilla::Hook;
  98. Bugzilla::Hook::process('webservice-error_codes',
  99. { error_map => \%error_map });
  100. my $code = $error_map{$error};
  101. if (!$code) {
  102. $code = ERROR_UNKNOWN_FATAL if $name =~ /code/i;
  103. $code = ERROR_UNKNOWN_TRANSIENT if $name =~ /user/i;
  104. }
  105. die SOAP::Fault->faultcode($code)->faultstring($message);
  106. }
  107. }
  108. exit;
  109. }
  110. sub ThrowUserError {
  111. _throw_error("global/user-error.html.tmpl", @_);
  112. }
  113. sub ThrowCodeError {
  114. _throw_error("global/code-error.html.tmpl", @_);
  115. }
  116. sub ThrowTemplateError {
  117. my ($template_err) = @_;
  118. my $dbh = Bugzilla->dbh;
  119. # Make sure the transaction is rolled back (if supported).
  120. $dbh->bz_rollback_transaction() if $dbh->bz_in_transaction();
  121. my $vars = {};
  122. if (Bugzilla->error_mode == ERROR_MODE_DIE) {
  123. die("error: template error: $template_err");
  124. }
  125. $vars->{'template_error_msg'} = $template_err;
  126. $vars->{'error'} = "template_error";
  127. my $template = Bugzilla->template;
  128. # Try a template first; but if this one fails too, fall back
  129. # on plain old print statements.
  130. if (!$template->process("global/code-error.html.tmpl", $vars)) {
  131. my $maintainer = Bugzilla->params->{'maintainer'};
  132. my $error = html_quote($vars->{'template_error_msg'});
  133. my $error2 = html_quote($template->error());
  134. print <<END;
  135. <tt>
  136. <p>
  137. Bugzilla has suffered an internal error. Please save this page and
  138. send it to $maintainer with details of what you were doing at the
  139. time this message appeared.
  140. </p>
  141. <script type="text/javascript"> <!--
  142. document.write("<p>URL: " +
  143. document.location.href.replace(/&/g,"&amp;")
  144. .replace(/</g,"&lt;")
  145. .replace(/>/g,"&gt;") + "</p>");
  146. // -->
  147. </script>
  148. <p>Template->process() failed twice.<br>
  149. First error: $error<br>
  150. Second error: $error2</p>
  151. </tt>
  152. END
  153. }
  154. exit;
  155. }
  156. 1;
  157. __END__
  158. =head1 NAME
  159. Bugzilla::Error - Error handling utilities for Bugzilla
  160. =head1 SYNOPSIS
  161. use Bugzilla::Error;
  162. ThrowUserError("error_tag",
  163. { foo => 'bar' });
  164. =head1 DESCRIPTION
  165. Various places throughout the Bugzilla codebase need to report errors to the
  166. user. The C<Throw*Error> family of functions allow this to be done in a
  167. generic and localizable manner.
  168. These functions automatically unlock the database tables, if there were any
  169. locked. They will also roll back the transaction, if it is supported by
  170. the underlying DB.
  171. =head1 FUNCTIONS
  172. =over 4
  173. =item C<ThrowUserError>
  174. This function takes an error tag as the first argument, and an optional hashref
  175. of variables as a second argument. These are used by the
  176. I<global/user-error.html.tmpl> template to format the error, using the passed
  177. in variables as required.
  178. =item C<ThrowCodeError>
  179. This function is used when an internal check detects an error of some sort.
  180. This usually indicates a bug in Bugzilla, although it can occur if the user
  181. manually constructs urls without correct parameters.
  182. This function's behaviour is similar to C<ThrowUserError>, except that the
  183. template used to display errors is I<global/code-error.html.tmpl>. In addition
  184. if the hashref used as the optional second argument contains a key I<variables>
  185. then the contents of the hashref (which is expected to be another hashref) will
  186. be displayed after the error message, as a debugging aid.
  187. =item C<ThrowTemplateError>
  188. This function should only be called if a C<template-<gt>process()> fails.
  189. It tries another template first, because often one template being
  190. broken or missing doesn't mean that they all are. But it falls back to
  191. a print statement as a last-ditch error.
  192. =back
  193. =head1 SEE ALSO
  194. L<Bugzilla|Bugzilla>