Chris@76: '<', '>' => '>', '"' => '"'));
Chris@76: $error_message = strtr($error_message, array('<br />' => '
', '<b>' => '', '</b>' => '', "\n" => '
'));
Chris@76:
Chris@76: // Add a file and line to the error message?
Chris@76: // Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line
Chris@76: if ($file == null)
Chris@76: $file = '';
Chris@76: else
Chris@76: // Window style slashes don't play well, lets convert them to the unix style.
Chris@76: $file = str_replace('\\', '/', $file);
Chris@76:
Chris@76: if ($line == null)
Chris@76: $line = 0;
Chris@76: else
Chris@76: $line = (int) $line;
Chris@76:
Chris@76: // Just in case there's no id_member or IP set yet.
Chris@76: if (empty($user_info['id']))
Chris@76: $user_info['id'] = 0;
Chris@76: if (empty($user_info['ip']))
Chris@76: $user_info['ip'] = '';
Chris@76:
Chris@76: // Find the best query string we can...
Chris@76: $query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING'];
Chris@76:
Chris@76: // Don't log the session hash in the url twice, it's a waste.
Chris@76: $query_string = htmlspecialchars((SMF == 'SSI' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string));
Chris@76:
Chris@76: // Just so we know what board error messages are from.
Chris@76: if (isset($_POST['board']) && !isset($_GET['board']))
Chris@76: $query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board'];
Chris@76:
Chris@76: // What types of categories do we have?
Chris@76: $known_error_types = array(
Chris@76: 'general',
Chris@76: 'critical',
Chris@76: 'database',
Chris@76: 'undefined_vars',
Chris@76: 'user',
Chris@76: 'template',
Chris@76: 'debug',
Chris@76: );
Chris@76:
Chris@76: // Make sure the category that was specified is a valid one
Chris@76: $error_type = in_array($error_type, $known_error_types) && $error_type !== true ? $error_type : 'general';
Chris@76:
Chris@76: // Don't log the same error countless times, as we can get in a cycle of depression...
Chris@76: $error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, (string) $sc, $error_type, $file, $line);
Chris@76: if (empty($last_error) || $last_error != $error_info)
Chris@76: {
Chris@76: // Insert the error into the database.
Chris@76: $smcFunc['db_insert']('',
Chris@76: '{db_prefix}log_errors',
Chris@76: array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'string-16', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'),
Chris@76: $error_info,
Chris@76: array('id_error')
Chris@76: );
Chris@76: $last_error = $error_info;
Chris@76: }
Chris@76:
Chris@76: // Return the message to make things simpler.
Chris@76: return $error_message;
Chris@76: }
Chris@76:
Chris@76: // An irrecoverable error.
Chris@76: function fatal_error($error, $log = 'general')
Chris@76: {
Chris@76: global $txt, $context, $modSettings;
Chris@76:
Chris@76: // We don't have $txt yet, but that's okay...
Chris@76: if (empty($txt))
Chris@76: die($error);
Chris@76:
Chris@76: setup_fatal_error_context($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2) ? log_error($error, $log) : $error);
Chris@76: }
Chris@76:
Chris@76: // A fatal error with a message stored in the language file.
Chris@76: function fatal_lang_error($error, $log = 'general', $sprintf = array())
Chris@76: {
Chris@76: global $txt, $language, $modSettings, $user_info, $context;
Chris@76: static $fatal_error_called = false;
Chris@76:
Chris@76: // Try to load a theme if we don't have one.
Chris@76: if (empty($context['theme_loaded']) && empty($fatal_error_called))
Chris@76: {
Chris@76: $fatal_error_called = true;
Chris@76: loadTheme();
Chris@76: }
Chris@76:
Chris@76: // If we have no theme stuff we can't have the language file...
Chris@76: if (empty($context['theme_loaded']))
Chris@76: die($error);
Chris@76:
Chris@76: $reload_lang_file = true;
Chris@76: // Log the error in the forum's language, but don't waste the time if we aren't logging
Chris@76: if ($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2))
Chris@76: {
Chris@76: loadLanguage('Errors', $language);
Chris@76: $reload_lang_file = $language != $user_info['language'];
Chris@76: $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
Chris@76: log_error($error_message, $log);
Chris@76: }
Chris@76:
Chris@76: // Load the language file, only if it needs to be reloaded
Chris@76: if ($reload_lang_file)
Chris@76: {
Chris@76: loadLanguage('Errors');
Chris@76: $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
Chris@76: }
Chris@76:
Chris@76: setup_fatal_error_context($error_message);
Chris@76: }
Chris@76:
Chris@76: // Handler for standard error messages.
Chris@76: function error_handler($error_level, $error_string, $file, $line)
Chris@76: {
Chris@76: global $settings, $modSettings, $db_show_debug;
Chris@76:
Chris@76: // Ignore errors if we're ignoring them or they are strict notices from PHP 5 (which cannot be solved without breaking PHP 4.)
Chris@76: if (error_reporting() == 0 || (defined('E_STRICT') && $error_level == E_STRICT && (empty($modSettings['enableErrorLogging']) || $modSettings['enableErrorLogging'] != 2)))
Chris@76: return;
Chris@76:
Chris@76: if (strpos($file, 'eval()') !== false && !empty($settings['current_include_filename']))
Chris@76: {
Chris@76: if (function_exists('debug_backtrace'))
Chris@76: {
Chris@76: $array = debug_backtrace();
Chris@76: for ($i = 0; $i < count($array); $i++)
Chris@76: {
Chris@76: if ($array[$i]['function'] != 'loadSubTemplate')
Chris@76: continue;
Chris@76:
Chris@76: // This is a bug in PHP, with eval, it seems!
Chris@76: if (empty($array[$i]['args']))
Chris@76: $i++;
Chris@76: break;
Chris@76: }
Chris@76:
Chris@76: if (isset($array[$i]) && !empty($array[$i]['args']))
Chris@76: $file = realpath($settings['current_include_filename']) . ' (' . $array[$i]['args'][0] . ' sub template - eval?)';
Chris@76: else
Chris@76: $file = realpath($settings['current_include_filename']) . ' (eval?)';
Chris@76: }
Chris@76: else
Chris@76: $file = realpath($settings['current_include_filename']) . ' (eval?)';
Chris@76: }
Chris@76:
Chris@76: if (isset($db_show_debug) && $db_show_debug === true)
Chris@76: {
Chris@76: // Commonly, undefined indexes will occur inside attributes; try to show them anyway!
Chris@76: if ($error_level % 255 != E_ERROR)
Chris@76: {
Chris@76: $temporary = ob_get_contents();
Chris@76: if (substr($temporary, -2) == '="')
Chris@76: echo '"';
Chris@76: }
Chris@76:
Chris@76: // Debugging! This should look like a PHP error message.
Chris@76: echo '
Chris@76: ', $error_level % 255 == E_ERROR ? 'Error' : ($error_level % 255 == E_WARNING ? 'Warning' : 'Notice'), ': ', $error_string, ' in ', $file, ' on line ', $line, '
';
Chris@76: }
Chris@76:
Chris@76: $error_type = strpos(strtolower($error_string), 'undefined') !== false ? 'undefined_vars' : 'general';
Chris@76:
Chris@76: $message = log_error($error_level . ': ' . $error_string, $error_type, $file, $line);
Chris@76:
Chris@76: // Let's give integrations a chance to ouput a bit differently
Chris@76: call_integration_hook('integrate_output_error', array($message, $error_type, $error_level, $file, $line));
Chris@76:
Chris@76: // Dying on these errors only causes MORE problems (blank pages!)
Chris@76: if ($file == 'Unknown')
Chris@76: return;
Chris@76:
Chris@76: // If this is an E_ERROR or E_USER_ERROR.... die. Violently so.
Chris@76: if ($error_level % 255 == E_ERROR)
Chris@76: obExit(false);
Chris@76: else
Chris@76: return;
Chris@76:
Chris@76: // If this is an E_ERROR, E_USER_ERROR, E_WARNING, or E_USER_WARNING.... die. Violently so.
Chris@76: if ($error_level % 255 == E_ERROR || $error_level % 255 == E_WARNING)
Chris@76: fatal_error(allowedTo('admin_forum') ? $message : $error_string, false);
Chris@76:
Chris@76: // We should NEVER get to this point. Any fatal error MUST quit, or very bad things can happen.
Chris@76: if ($error_level % 255 == E_ERROR)
Chris@76: die('Hacking attempt...');
Chris@76: }
Chris@76:
Chris@76: function setup_fatal_error_context($error_message)
Chris@76: {
Chris@76: global $context, $txt, $ssi_on_error_method;
Chris@76: static $level = 0;
Chris@76:
Chris@76: // Attempt to prevent a recursive loop.
Chris@76: ++$level;
Chris@76: if ($level > 1)
Chris@76: return false;
Chris@76:
Chris@76: // Maybe they came from dlattach or similar?
Chris@76: if (SMF != 'SSI' && empty($context['theme_loaded']))
Chris@76: loadTheme();
Chris@76:
Chris@76: // Don't bother indexing errors mate...
Chris@76: $context['robot_no_index'] = true;
Chris@76:
Chris@76: if (!isset($context['error_title']))
Chris@76: $context['error_title'] = $txt['error_occured'];
Chris@76: $context['error_message'] = isset($context['error_message']) ? $context['error_message'] : $error_message;
Chris@76:
Chris@76: if (empty($context['page_title']))
Chris@76: $context['page_title'] = $context['error_title'];
Chris@76:
Chris@76: // Display the error message - wireless?
Chris@76: if (defined('WIRELESS') && WIRELESS)
Chris@76: $context['sub_template'] = WIRELESS_PROTOCOL . '_error';
Chris@76: // Load the template and set the sub template.
Chris@76: else
Chris@76: {
Chris@76: loadTemplate('Errors');
Chris@76: $context['sub_template'] = 'fatal_error';
Chris@76: }
Chris@76:
Chris@76: // If this is SSI, what do they want us to do?
Chris@76: if (SMF == 'SSI')
Chris@76: {
Chris@76: if (!empty($ssi_on_error_method) && $ssi_on_error_method !== true && is_callable($ssi_on_error_method))
Chris@76: $ssi_on_error_method();
Chris@76: elseif (empty($ssi_on_error_method) || $ssi_on_error_method !== true)
Chris@76: loadSubTemplate('fatal_error');
Chris@76:
Chris@76: // No layers?
Chris@76: if (empty($ssi_on_error_method) || $ssi_on_error_method !== true)
Chris@76: exit;
Chris@76: }
Chris@76:
Chris@76: // We want whatever for the header, and a footer. (footer includes sub template!)
Chris@76: obExit(null, true, false, true);
Chris@76:
Chris@76: /* DO NOT IGNORE:
Chris@76: If you are creating a bridge to SMF or modifying this function, you MUST
Chris@76: make ABSOLUTELY SURE that this function quits and DOES NOT RETURN TO NORMAL
Chris@76: PROGRAM FLOW. Otherwise, security error messages will not be shown, and
Chris@76: your forum will be in a very easily hackable state.
Chris@76: */
Chris@76: trigger_error('Hacking attempt...', E_USER_ERROR);
Chris@76: }
Chris@76:
Chris@76: // Show an error message for the connection problems.
Chris@76: function show_db_error($loadavg = false)
Chris@76: {
Chris@76: global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings;
Chris@76: global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
Chris@76:
Chris@76: // Just check we're not in any buffers, just in case.
Chris@76: for ($i = ob_get_level(); $i > 0; $i--)
Chris@76: @ob_end_clean();
Chris@76:
Chris@76: // Don't cache this page!
Chris@76: header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
Chris@76: header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
Chris@76: header('Cache-Control: no-cache');
Chris@76:
Chris@76: // Send the right error codes.
Chris@76: header('HTTP/1.1 503 Service Temporarily Unavailable');
Chris@76: header('Status: 503 Service Temporarily Unavailable');
Chris@76: header('Retry-After: 3600');
Chris@76:
Chris@76: if ($loadavg == false)
Chris@76: {
Chris@76: // For our purposes, we're gonna want this on if at all possible.
Chris@76: $modSettings['cache_enable'] = '1';
Chris@76:
Chris@76: if (($temp = cache_get_data('db_last_error', 600)) !== null)
Chris@76: $db_last_error = max($db_last_error, $temp);
Chris@76:
Chris@76: if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
Chris@76: {
Chris@76: require_once($sourcedir . '/Subs-Admin.php');
Chris@76:
Chris@76: // Avoid writing to the Settings.php file if at all possible; use shared memory instead.
Chris@76: cache_put_data('db_last_error', time(), 600);
Chris@76: if (($temp = cache_get_data('db_last_error', 600)) == null)
Chris@76: updateLastDatabaseError();
Chris@76:
Chris@76: // Language files aren't loaded yet :(.
Chris@76: $db_error = @$smcFunc['db_error']($db_connection);
Chris@76: @mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.');
Chris@76: }
Chris@76: }
Chris@76:
Chris@76: if (!empty($maintenance))
Chris@76: echo '
Chris@76:
Chris@76: