error.class.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <?php
  2. /**
  3. * error.class.php
  4. *
  5. * This contains the custom error handler for SquirrelMail.
  6. *
  7. * @copyright 2005-2025 The SquirrelMail Project Team
  8. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  9. * @version $Id$
  10. * @package squirrelmail
  11. */
  12. /** Used defines */
  13. define('SQM_NOTICE',0);
  14. define('SQM_WARNING',1);
  15. define('SQM_ERROR',2);
  16. define('SQM_STRICT',3);
  17. // php5 E_STRICT constant (compatibility with php4)
  18. if (! defined('E_STRICT')) define('E_STRICT',2048);
  19. // Set docref_root (fixes URLs that link to php manual)
  20. if (ini_get('docref_root')=='') ini_set('docref_root','http://www.php.net/');
  21. /**
  22. * Error Handler class
  23. *
  24. * This class contains a custom error handler in order to display
  25. * user notices/warnings/errors and php notices and warnings in a template
  26. *
  27. * @author Marc Groot Koerkamp
  28. * @package squirrelmail
  29. */
  30. class ErrorHandler {
  31. /**
  32. * Constructor (PHP5 style, required in some future version of PHP)
  33. * @param object $oTemplate Template object
  34. * @param string $sTemplateFile Template containing the error template
  35. * @since 1.5.1
  36. */
  37. function __construct(&$oTemplate, $sTemplateFile) {
  38. # echo 'init error handler...';
  39. $this->TemplateName = $sTemplateFile;
  40. $this->Template =& $oTemplate;
  41. $this->aErrors = array();
  42. $this->header_sent = false;
  43. $this->delayed_errors = false;
  44. $this->Template->assign('delayed_errors', $this->delayed_errors);
  45. }
  46. /**
  47. * Constructor (PHP4 style, kept for compatibility reasons)
  48. * @param object $oTemplate Template object
  49. * @param string $sTemplateFile Template containing the error template
  50. * @since 1.5.1
  51. */
  52. function ErrorHandler(&$oTemplate, $sTemplateFile) {
  53. self::__construct($oTemplate, $sTemplateFile);
  54. }
  55. /**
  56. * Sets the error template
  57. * @since 1.5.1
  58. */
  59. function SetTemplateFile($sTemplateFile) {
  60. $this->TemplateFile = $sTemplateFile;
  61. }
  62. /**
  63. * Sets if the page header is already sent
  64. * @since 1.5.1
  65. */
  66. function HeaderSent() {
  67. $this->header_sent = true;
  68. $this->Template->assign('header_sent', true);
  69. }
  70. /**
  71. * Turn on/off delayed error handling
  72. * @since 1.5.2
  73. */
  74. function setDelayedErrors ($val = true) {
  75. $this->delayed_errors = $val===true;
  76. $this->Template->assign('delayed_errors', $this->delayed_errors);
  77. }
  78. /**
  79. * Store errors generated in a previous script but couldn't be displayed
  80. * due to a header redirect. This requires storing of aDelayedErrors in the session
  81. * @param array $aDelayedErrors array with errors stored in the $this->aErrors format.
  82. * @since 1.5.1
  83. */
  84. function AssignDelayedErrors(&$aDelayedErrors) {
  85. $aErrors = array_merge($this->aErrors,$aDelayedErrors);
  86. $this->aErrors = $aErrors;
  87. $this->Template->assign('aErrors',$this->aErrors);
  88. $aDelayedErrors = false;
  89. }
  90. /**
  91. * Custom Error handler (set with set_error_handler() )
  92. * @private
  93. * @since 1.5.1
  94. */
  95. function SquirrelMailErrorhandler($iErrNo, $sErrStr, $sErrFile, $iErrLine, $aContext=NULL) {
  96. $aError = array(
  97. 'type' => SQM_NOTICE,// Error type, notice, warning or fatal error;
  98. 'category' => NULL, // SquirrelMail error category;
  99. 'message' => NULL, // Error display message;
  100. 'extra' => NULL, // Key value based array with extra error info;
  101. 'link' => NULL, // Link to help location;
  102. 'tip' => NULL // User tip.
  103. );
  104. $iType = NULL;
  105. $aErrorCategory = array();
  106. /**
  107. * Get current error reporting level.
  108. *
  109. * PHP 4.1.2 does not return current error reporting level in ini_get (php 5.1b3 and
  110. * 4.3.10 does). Retrieve current error reporting level while setting error reporting
  111. * to ini value and reset it to retrieved value.
  112. */
  113. $iCurErrLevel = error_reporting(ini_get('error_reporting'));
  114. error_reporting($iCurErrLevel);
  115. /**
  116. * Check error_reporting value before logging error.
  117. * Don't log errors that are disabled by @ (error_reporting = 0). Some SquirrelMail scripts
  118. * (sq_mb_list_encodings(), ldap function calls in functions/abook_ldap_server.php)
  119. * handle errors themselves and @ is used to disable generic php error messages.
  120. */
  121. if ($iErrNo & $iCurErrLevel) {
  122. /*
  123. * The following errors cannot be handled by a user defined error handler:
  124. * E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
  125. */
  126. switch ($iErrNo) {
  127. case E_STRICT:
  128. $iType = (is_null($iType)) ? SQM_STRICT : $iType;
  129. case E_NOTICE:
  130. $iType = (is_null($iType)) ? SQM_NOTICE : $iType;
  131. case E_WARNING:
  132. $iType = (is_null($iType)) ? SQM_WARNING : $iType;
  133. $aErrorCategory[] = 'PHP';
  134. $aError['message'] = $sErrStr;
  135. $aError['extra'] = array(
  136. 'FILE' => $sErrFile,
  137. 'LINE' => $iErrLine) ;
  138. // what todo with $aContext?
  139. break;
  140. case E_USER_ERROR:
  141. $iType = (is_null($iType)) ? SQM_ERROR : $iType;
  142. case E_USER_NOTICE:
  143. $iType = (is_null($iType)) ? SQM_NOTICE : $iType;
  144. case E_USER_WARNING:
  145. $iType = (is_null($iType)) ? SQM_WARNING : $iType;
  146. if ($sErrFile == __FILE__) { // Error is triggered in this file and probably by sqm_trigger_error
  147. $aErrorTemp = @unserialize($sErrStr);
  148. if (!is_array($aErrorTemp)) {
  149. $aError['message'] = $sErrStr;
  150. $aErrorCategory[] = 'UNDEFINED';
  151. } else {
  152. $aError = array_merge($aError,$aErrorTemp);
  153. // special error handling below
  154. if ($aError['category'] & SQM_ERROR_IMAP) {
  155. $aErrorCategory[] = 'IMAP';
  156. // imap related error handling inside
  157. }
  158. if ($aError['category'] & SQM_ERROR_FS) {
  159. $aErrorCategory[] = 'FILESYSTEM';
  160. // filesystem related error handling inside
  161. }
  162. if ($aError['category'] & SQM_ERROR_SMTP) {
  163. $aErrorCategory[] = 'SMTP';
  164. // smtp related error handling inside
  165. }
  166. if ($aError['category'] & SQM_ERROR_LDAP) {
  167. $aErrorCategory[] = 'LDAP';
  168. // ldap related error handling inside
  169. }
  170. if ($aError['category'] & SQM_ERROR_DB) {
  171. $aErrorCategory[] = 'DATABASE';
  172. // db related error handling inside
  173. }
  174. if ($aError['category'] & SQM_ERROR_PLUGIN) {
  175. $aErrorCategory[] = 'PLUGIN';
  176. do_hook('error_handler_plugin', $aError);
  177. // plugin related error handling inside
  178. }
  179. //if ($aError['category'] & SQM_ERROR_X) {
  180. // $aErrorCategory[] = 'X';
  181. // place holder for a new category
  182. //}
  183. }
  184. unset($aErrorTemp);
  185. } else {
  186. $aError['message'] = $sErrStr;
  187. $aErrorCategory[] = 'SQM_NOTICE';
  188. }
  189. break;
  190. default: break;
  191. }
  192. /**
  193. * If delayed error handling is enabled, always record the location
  194. * and tag the error is delayed to make debugging easier.
  195. */
  196. if (isset($this->Template->values['delayed_errors']) && $this->Template->values['delayed_errors']) {
  197. $aErrorCategory[] = 'Delayed';
  198. $aError['extra'] = array(
  199. 'FILE' => $sErrFile,
  200. 'LINE' => $iErrLine) ;
  201. }
  202. $aErrorTpl = array(
  203. 'type' => $iType,
  204. 'category' => $aErrorCategory,
  205. 'message' => $aError['message'],
  206. 'link' => $aError['link'],
  207. 'tip' => $aError['tip'],
  208. 'extra' => $aError['extra']);
  209. // Add the notice/warning/error to the existing list of notices/warnings
  210. $this->aErrors[] = $aErrorTpl;
  211. $this->Template->assign('aErrors',$this->aErrors);
  212. }
  213. // Show the error immediate in case of fatal errors
  214. if ($iType == SQM_ERROR) {
  215. if (isset($this->Template->values['header_sent']) && !$this->Template->values['header_sent']) {
  216. // TODO replace this with template that can be assigned
  217. // UPDATE: displayHtmlHeader() no longer sends anything
  218. // directly to the browser itself and instead
  219. // displays all output through the template file
  220. // "protocol_header" as well as calls to the
  221. // template's header() method, so perhaps the
  222. // above TODO is alleviated?? (however, I don't fully
  223. // understand the problem behind the TODO comment myself (Paul))
  224. displayHtmlHeader(_("Error"),'',false);
  225. }
  226. $this->DisplayErrors();
  227. exit(_("Terminating SquirrelMail due to a fatal error"));
  228. }
  229. }
  230. /**
  231. * Force the delayed errors to be stored in the session in case
  232. * $this->displayErrors() never gets called, e.g. in compose.php
  233. */
  234. function saveDelayedErrors () {
  235. if($this->delayed_errors) {
  236. // Check for previous delayed errors...
  237. sqgetGlobalVar('delayed_errors', $delayed_errors, SQ_SESSION);
  238. if (is_array($delayed_errors)) {
  239. $this->AssignDelayedErrors($delayed_errors);
  240. sqsession_unregister("delayed_errors");
  241. }
  242. if (count($this->aErrors) > 0) {
  243. sqsession_register($this->aErrors,"delayed_errors");
  244. }
  245. }
  246. }
  247. /**
  248. * Display the error array in the error template
  249. * @return void
  250. * @since 1.5.1
  251. */
  252. function DisplayErrors() {
  253. // Check for delayed errors...
  254. if (!$this->delayed_errors) {
  255. sqgetGlobalVar('delayed_errors', $delayed_errors, SQ_SESSION);
  256. if (is_array($delayed_errors)) {
  257. $this->AssignDelayedErrors($delayed_errors);
  258. sqsession_unregister("delayed_errors");
  259. }
  260. }
  261. if (isset($this->Template->values['aErrors']) && count($this->Template->values['aErrors']) > 0) {
  262. foreach ($this->Template->values['aErrors'] as $err) {
  263. if (!in_array($err, $this->aErrors, true)) {
  264. $this->aErrors[] = $err;
  265. }
  266. }
  267. $this->Template->assign('aErrors',$this->aErrors);
  268. }
  269. if (count($this->aErrors) > 0) {
  270. if ($this->delayed_errors) {
  271. sqsession_register($this->aErrors,"delayed_errors");
  272. } else {
  273. $this->Template->display($this->TemplateName);
  274. }
  275. }
  276. }
  277. }
  278. /**
  279. * Custom Error handler for PHP version < 4.3.0 (set with set_error_handler() )
  280. * @author Marc Groot Koerkamp
  281. * @since 1.5.1
  282. */
  283. function SquirrelMailErrorhandler($iErrNo, $sErrStr, $sErrFile, $iErrLine, $aContext) {
  284. global $oTemplate;
  285. static $oErrorHandler;
  286. if (!isset($oErrorHandler)) {
  287. $oErrorHandler = new ErrorHandler($oTemplate,'error_message.tpl');
  288. }
  289. $oErrorHandler->SquirrelMailErrorhandler($iErrNo, $sErrStr, $sErrFile, $iErrLine, $aContext);
  290. }
  291. /**
  292. * Triggers an imap error. Utility function for sqm_trigger_error()
  293. * @param string $sErrNo error string defined in errors.php
  294. * @param string $sRequest imap request string
  295. * @param string $sResponse tagged imap response
  296. * @param string $sMessage tagged imap response message
  297. * @param array $aExtra optional associative array with extra error info
  298. * @return void
  299. * @author Marc Groot Koerkamp
  300. * @since 1.5.1
  301. */
  302. function sqm_trigger_imap_error($sErrNo,$sRequest,$sResponse, $sMessage, $aExtra=array()) {
  303. $aError = array(
  304. 'REQUEST' => $sRequest,
  305. 'RESPONSE' => $sResponse,
  306. 'MESSAGE' => $sMessage);
  307. $aError = array_merge($aExtra,$aError);
  308. sqm_trigger_error($sErrNo,$aError);
  309. }
  310. /**
  311. * Trigger an error.
  312. * @param string $sErrNo error string defined in errors.php
  313. * @param array $aExtra optional associative array with extra error info
  314. * @return void
  315. * @author Marc Groot Koerkamp
  316. * @since 1.5.1
  317. */
  318. function sqm_trigger_error($sErrNo,$aExtra=array()) {
  319. static $aErrors;
  320. if (!isset($aErrors)) {
  321. // Include the error definition file.
  322. include_once(SM_PATH.'include/errors.php');
  323. }
  324. $iPhpErr = E_USER_NOTICE;
  325. if (is_array($aErrors) && isset($aErrors[$sErrNo]['level'])) {
  326. if (is_array($aExtra) && count($aExtra)) {
  327. $aErrors[$sErrNo]['extra'] = $aExtra;
  328. }
  329. // because trigger_error can only handle a string argument for the error description
  330. // we serialize the result.
  331. $sErrString = serialize($aErrors[$sErrNo]);
  332. $iPhpErr = $aErrors[$sErrNo]['level'];
  333. } else {
  334. sm_print_r($aErrors);
  335. $sErrString = "Error <$sErrNo> does not exist, fix the code or update the errors.php file";
  336. $iPhpErr = E_USER_ERROR;
  337. }
  338. trigger_error($sErrString, $iPhpErr);
  339. }