. // Copyright © 2007-2014 Erwan Briand // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, version 3 only. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public // License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . /** * @file * This file contains the ErrorHandler class * * Handle both PHP and CodingTeam errors, show them in a human-way to * the user, log them and finally exit. */ /** * ErrorHandler class */ class ErrorHandler { public $lang, $show_debug, $log_errors, $log_level, $show_notice; /* * Constructor * * Construct an instance of ErrorHandler. * @param $lang * The spoken language of the user. * @param $show_debug * A boolean to turn on or off the debug. This will show source code and * error context. It's only launched if the error status is 3. * @param $log_errors * A boolean to turn on or off errors logging. * @param $log_level * An int of the first level of error status to be logged. * If you want to log level 2 and 3 (critical and internal), set it to 2. * @param $show_notice * A boolean to turn on or off notice catch and show. */ function __construct($lang, $show_debug, $log_errors, $log_level, $show_notice, $catch_fatal) { $this->lang = $lang; $this->show_debug = $show_debug; $this->log_errors = $log_errors; $this->log_level = $log_level; $this->show_notice = $show_notice; if ($catch_fatal) ini_set('display_errors', FALSE); } /** * Load the view * * Generate a pretty textarea with optional Javascript content. * @param $error * The error message. * @param $status * The error status (integer). Different levels are: * - -1: Unlogged user, in login.tpl * (only if the forge is not public) * - 0: Banal, in warning.tpl * (eg. 404, 403, 401, non-existing resource…) * - 1: Important, in error.tpl * (eg. database request error…) * - 2: Critical, in error.tpl * (eg. database connection error…) * - 3: Internal error, in error.tpl * (only PHP errors, not trigged manually by displayError) * @param $http (optional) * HTTP Headers to send. */ function displayError($error, $status, $http='') { // If the configuration tell to ignore E_NOTICE, ignore them if (!$this->show_notice && $status == 3 && ($error[0] == 8 || $error[0] == 8192)) return FALSE; /* In this file, if an external server (for OpenForge external search) * is not online, we get an ugly warning, just ignore it (that's not * so bad, that's just a work-around: WARNING shouldn't be blocking, * but our class make them so). */ if (($error[0] == 2 || $error[0] == 8) && $error[2] == CT_BASEDIR. '/inc/modules/search/views/default.php') return FALSE; // Same work to prevent ugly errors when documenters are bad boys // FIXME: this is bug in textview.php, that sort of "fix" are ugly! if ($error[0] == 2 && $error[2] == CT_BASEDIR. '/inc/classes/textview.php') return FALSE; // Don't show errors for libs used by CodingTeam $libs = CT_BASEDIR.'/inc/libs'; $nlibs = mb_strlen($libs); if (($error[0] == 2048 || $error[0] == 8 || $error[0] == 2 || $error[0] == 8192) && mb_substr($error[2], 0, $nlibs) == $libs) return FALSE; /* Error logging. * * If you enable error logging, don't forget to create a error.log file * in the CodingTeam root directory and allow Apache HTTPD to write in * this file. */ if ($status == 3) { switch ($error[0]) { case 1: $errmsg = 'E_ERROR'; break; case 2: $errmsg = 'E_WARNING'; break; case 4: $errmsg = 'E_PARSE'; break; case 8: $errmsg = 'E_NOTICE'; break; case 2048: $errmsg = 'E_STRICT'; break; default: $errmsg = 'E_UNKNOWN'; } $errinfos = ' (file: '.$error[2].'@'.$error[3].')'; $errdesc = $error[1]; } elseif ($status == -1) { $errmsg = 'E_CODINGTEAM'; $errinfos = ''; $errdesc = $error[0]; } else { $errmsg = 'E_CODINGTEAM'; $errinfos = ''; $errdesc = $error; } // Log the error into a file (use log_errors/log_level). if ($status >= $this->log_level && $this->log_errors) { $file = CT_BASEDIR.'/error.log'; $data = '['.date('Y-m-d H:i:s').'] ['.$errmsg.'] [client '. $_SERVER['REMOTE_ADDR'].'] '.$errdesc. ' '. "'".$_SERVER['REQUEST_URI']."'".$errinfos."\n"; file_put_contents($file, $data, FILE_APPEND | LOCK_EX); } // If a HTTP header is specified, send it if (!empty($http)) Header('HTTP/1.0 '.$http); // Error status if ($status == -1) $errstat = i18n('Unlogged user'); elseif ($status == 0) $erstat = i18n('Banal'); elseif ($status == 1) $erstat = i18n('Important'); elseif ($status == 2) $erstat = i18n('Critical'); elseif ($status == 3) $erstat = i18n('Internal error'); else exit('Error.'); // Globals $temp['tpl:lang'] = $this->lang; $temp['tpl:baseurl'] = CT_BASEURL; $temp['tpl:cssdir'] = 'inc/templates/'; $temp['tpl:logosimages'] = 'public/images/logos/'; $temp['erstat'] = $status; // Error if ($status < 3) $temp['error'] = $error; else { $temp['error'] = i18n('An error occured.').'
'.nl2br($error[1]). ' ('.$error[0].' - '.$errmsg.')
'. i18n('In: %(file)s at line %(line)d', array('file' => $error[2], 'line' => $error[3])); if ($this->show_debug) { ob_start(); print_r($error[4]); $datas = htmlspecialchars(ob_get_clean()); if (empty($datas)) $datas = i18n('No context available for this error.'); if (is_file($error[2]) && is_readable($error[2])) { $output = ''; $lines = array(); $content = explode("\n", file_get_contents($error[2])); $line = $error[3]; for ($i=($line-5); $i<($line+4); $i++) if (array_key_exists($i, $content)) { $output .= $content[$i]."\n"; array_push($lines, ($i+1)); } $hlline = array($lines, $error[3]); $code = HTMLSourceView($output, 'php', $hlline); } else $code = ''; $dbimg = 'public/images/icons/alert.png'; $debug_output = '

'.i18n('Debug informations').'

'.i18n('Code').'

'.$error[2].'
'.$code.'

'.i18n('Memory dump').'

PHP '.PHP_VERSION.' ('.PHP_OS.')
'.i18n('You can see a memory dump and the source code because '. 'show_debug is activated in the configuration.').'
'; } } /* Load the template * * Load login.tpl, warning.tpl or error.tpl and fill it. */ if ($status == 0) { $html = file_get_contents(CT_BASEDIR.'/inc/templates/warning.tpl'); $temp['title'] = i18n('Something went wrong'); $temp['header'] = i18n('Warning! Something went wrong…'); $temp['error'] = $error; $temp['link-index'] = i18n('Go to the index'); $temp['url'] = $_SERVER['REQUEST_URI']; $in_array = array('projects' => i18n('Projects'), 'projects-bugs' => i18n('Bugs'), 'projects-doc' => i18n('Documentation'), 'projects-forum' => i18n('Forum'), 'projects-news' => i18n('News'), 'notepad' => i18n('Notepad'), 'users' => i18n('Users')); $str = ''; foreach ($in_array as $key => $value) { $explode = explode('-', $key); if (isset($explode[1])) $value = '  '.$value; $str .= ''; } $temp['header-search'] = i18n('Search'); $temp['select-search'] = $str; $temp['button-search'] = i18n('Search'); $temp['text-search'] = i18n('If you were trying to find something'. ' on the forge, you should try to search it.'); } elseif ($status == -1) { $html = file_get_contents(CT_BASEDIR.'/inc/templates/login.tpl'); require(CT_BASEDIR.'/inc/modules/head_member/head_member.php'); $hmember = new head_member($error[1], $error[2], array('index')); $hmember->treatForms(); $temp['header-login'] = $error[0]; ob_start(); $hmember->getPageContent(); $temp['form'] = ob_get_clean(); $temp['error'] = $errstat; $temp['title'] = i18n('Access denied'); $temp['header'] = i18n('This forge is not public!'); } else { $html = file_get_contents(CT_BASEDIR.'/inc/templates/error.tpl'); $temp['title'] = i18n('An error has occured'); $temp['header'] = i18n('An error has occured.'); $temp['header-report'] = i18n('Report'); $version = file_get_contents(CT_BASEDIR.'/VERSION'); $server = str_replace('address', 'strong', $_SERVER['SERVER_SIGNATURE']); $temp['table-bugstatus-key'] = i18n('Bug status:'); $temp['table-bugstatus-value'] = $erstat; $temp['table-affects-key'] = i18n('Affects:'); $temp['table-affects-value'] = $_SERVER['SERVER_NAME']; $temp['table-url-key'] = i18n('URL:'); $temp['table-url-value'] = $_SERVER['REQUEST_URI']; $temp['table-version-key'] = i18n('CodingTeam version:'); $temp['table-version-value'] = $version; $temp['table-server-key'] = i18n('Server identification:'); $temp['table-server-value'] = $server; $temp['table-date-key'] = i18n('Date/time:'); $temp['table-date-value'] = date('Y-m-d H:i:s'); $temp['header-reproduce'] = i18n('What to do if you can reproduce'. ' this bug'); $temp['contact-srvadmin'] = i18n('Contact your server admin'); $temp['contact-srvadmin-more'] = i18n('It is the first thing to '. 'do when you see a bug on '. 'your forge.'); $temp['report-upstream'] = i18n('Report this bug to upstream'); $temp['report-upstream-more'] = i18n('You can send all datas on '. 'this page to the CodingTeam bug tracker at this page: %(url)s.', array('url' => 'http://codingteam.net/project/codingteam/bugs')). '
'. i18n('Be careful not to transmit private data! Also, be sure that'. ' this bug concerns only the CodingTeam development team.'); if ($status < 3) $temp['debug'] = ''; else $temp['debug'] = $debug_output; } // Parse the template foreach ($temp as $key => $value) { $search[] = '{'.$key.'}'; $replace[] = $value; } $output = str_replace($search, $replace, $html); echo $output; exit(); } } ?>