<commit_before><?php

/**
 * The purpose of this file is... errors. (hard to guess, I guess?)  It takes
 * care of logging, error messages, error handling, database errors, and
 * error log administration.
 *
 * Simple Machines Forum (SMF)
 *
 * @package SMF
 * @author Simple Machines http://www.simplemachines.org
 * @copyright 2016 Simple Machines and individual contributors
 * @license http://www.simplemachines.org/about/smf/license.php BSD
 *
 * @version 2.1 Beta 3
 */

if (!defined('SMF'))
	die('No direct access...');

/**
 * Log an error, if the error logging is enabled.
 * filename and line should be __FILE__ and __LINE__, respectively.
 * Example use:
 *  die(log_error($msg));
 *
 * @param string $error_message The message to log
 * @param string $error_type The type of error
 * @param string $file The name of the file where this error occurred
 * @param int $line The line where the error occurred
 * @return string The message that was logged
 */
function log_error($error_message, $error_type = 'general', $file = null, $line = null)
{
	global $modSettings, $sc, $user_info, $smcFunc, $scripturl, $last_error, $context;
	static $tried_hook = false;

	// Check if error logging is actually on.
	if (empty($modSettings['enableErrorLogging']))
		return $error_message;

	// Basically, htmlspecialchars it minus &. (for entities!)
	$error_message = strtr($error_message, array('<' => '&lt;', '>' => '&gt;', '"' => '&quot;'));
	$error_message = strtr($error_message, array('&lt;br /&gt;' => '<br>', '&lt;br&gt;' => '<br>', '&lt;b&gt;' => '<strong>', '&lt;/b&gt;' => '</strong>', "\n" => '<br>'));

	// Add a file and line to the error message?
	// Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line
	if ($file == null)
		$file = '';
	else
		// Window style slashes don't play well, lets convert them to the unix style.
		$file = str_replace('\\', '/', $file);

	if ($line == null)
		$line = 0;
	else
		$line = (int) $line;

	// Just in case there's no id_member or IP set yet.
	if (empty($user_info['id']))
		$user_info['id'] = 0;
	if (empty($user_info['ip']))
		$user_info['ip'] = '';

	// Find the best query string we can...
	$query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING'];

	// Don't log the session hash in the url twice, it's a waste.
	$query_string = $smcFunc['htmlspecialchars']((SMF == 'SSI' || SMF == 'BACKGROUND' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string));

	// Just so we know what board error messages are from.
	if (isset($_POST['board']) && !isset($_GET['board']))
		$query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board'];

	// What types of categories do we have?
	$known_error_types = array(
		'general',
		'critical',
		'database',
		'undefined_vars',
		'user',
		'ban',
		'template',
		'debug',
		'cron',
		'paidsubs',
		'backup',
	);

	// This prevents us from infinite looping if the hook or call produces an error.
	$other_error_types = array();
	if (empty($tried_hook))
	{
		$tried_hook = true;
		// Allow the hook to change the error_type and know about the error.
		call_integration_hook('integrate_error_types', array(&$other_error_types, &$error_type, $error_message, $file, $line));
		$known_error_types += $other_error_types;
	}
	// Make sure the category that was specified is a valid one
	$error_type = in_array($error_type, $known_error_types) && $error_type !== true ? $error_type : 'general';

	// Don't log the same error countless times, as we can get in a cycle of depression...
	$error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, (string) $sc, $error_type, $file, $line);
	if (empty($last_error) || $last_error != $error_info)
	{
		// Insert the error into the database.
		$smcFunc['db_insert']('',
			'{db_prefix}log_errors',
			array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'inet', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'),
			$error_info,
			array('id_error')
		);
		$last_error = $error_info;
	}

	// Increment our error count for the menu
	$context['num_errors']++;

	// Return the message to make things simpler.
	return $error_message;
}

/**
 * An irrecoverable error. This function stops execution and displays an error message.
 * It logs the error message if $log is specified.
 * @param string $error The error message
 * @param string $log = 'general' What type of error to log this as (false to not log it))
 * @param int $status The HTTP status code associated with this error
 */
function fatal_error($error, $log = 'general', $status = 500)
{
	global $txt;

	// Send the appropriate HTTP status header - set this to 0 or false if you don't want to send one at all
	if (!empty($status))
		send_http_status($status);

	// We don't have $txt yet, but that's okay...
	if (empty($txt))
		die($error);

	log_error_online($error, false);
	setup_fatal_error_context($log ? log_error($error, $log) : $error);
}

/**
 * Shows a fatal error with a message stored in the language file.
 *<commit_msg>Match the error count with whats been inserted on the log fixes #3432<commit_after><?php

/**
 * The purpose of this file is... errors. (hard to guess, I guess?)  It takes
 * care of logging, error messages, error handling, database errors, and
 * error log administration.
 *
 * Simple Machines Forum (SMF)
 *
 * @package SMF
 * @author Simple Machines http://www.simplemachines.org
 * @copyright 2016 Simple Machines and individual contributors
 * @license http://www.simplemachines.org/about/smf/license.php BSD
 *
 * @version 2.1 Beta 3
 */

if (!defined('SMF'))
	die('No direct access...');

/**
 * Log an error, if the error logging is enabled.
 * filename and line should be __FILE__ and __LINE__, respectively.
 * Example use:
 *  die(log_error($msg));
 *
 * @param string $error_message The message to log
 * @param string $error_type The type of error
 * @param string $file The name of the file where this error occurred
 * @param int $line The line where the error occurred
 * @return string The message that was logged
 */
function log_error($error_message, $error_type = 'general', $file = null, $line = null)
{
	global $modSettings, $sc, $user_info, $smcFunc, $scripturl, $last_error, $context;
	static $tried_hook = false;

	// Check if error logging is actually on.
	if (empty($modSettings['enableErrorLogging']))
		return $error_message;

	// Basically, htmlspecialchars it minus &. (for entities!)
	$error_message = strtr($error_message, array('<' => '&lt;', '>' => '&gt;', '"' => '&quot;'));
	$error_message = strtr($error_message, array('&lt;br /&gt;' => '<br>', '&lt;br&gt;' => '<br>', '&lt;b&gt;' => '<strong>', '&lt;/b&gt;' => '</strong>', "\n" => '<br>'));

	// Add a file and line to the error message?
	// Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line
	if ($file == null)
		$file = '';
	else
		// Window style slashes don't play well, lets convert them to the unix style.
		$file = str_replace('\\', '/', $file);

	if ($line == null)
		$line = 0;
	else
		$line = (int) $line;

	// Just in case there's no id_member or IP set yet.
	if (empty($user_info['id']))
		$user_info['id'] = 0;
	if (empty($user_info['ip']))
		$user_info['ip'] = '';

	// Find the best query string we can...
	$query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING'];

	// Don't log the session hash in the url twice, it's a waste.
	$query_string = $smcFunc['htmlspecialchars']((SMF == 'SSI' || SMF == 'BACKGROUND' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string));

	// Just so we know what board error messages are from.
	if (isset($_POST['board']) && !isset($_GET['board']))
		$query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board'];

	// What types of categories do we have?
	$known_error_types = array(
		'general',
		'critical',
		'database',
		'undefined_vars',
		'user',
		'ban',
		'template',
		'debug',
		'cron',
		'paidsubs',
		'backup',
	);

	// This prevents us from infinite looping if the hook or call produces an error.
	$other_error_types = array();
	if (empty($tried_hook))
	{
		$tried_hook = true;
		// Allow the hook to change the error_type and know about the error.
		call_integration_hook('integrate_error_types', array(&$other_error_types, &$error_type, $error_message, $file, $line));
		$known_error_types += $other_error_types;
	}
	// Make sure the category that was specified is a valid one
	$error_type = in_array($error_type, $known_error_types) && $error_type !== true ? $error_type : 'general';

	// Don't log the same error countless times, as we can get in a cycle of depression...
	$error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, (string) $sc, $error_type, $file, $line);
	if (empty($last_error) || $last_error != $error_info)
	{
		// Insert the error into the database.
		$smcFunc['db_insert']('',
			'{db_prefix}log_errors',
			array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'inet', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'),
			$error_info,
			array('id_error')
		);
		$last_error = $error_info;

		// Increment our error count for the menu
		$context['num_errors']++;
	}

	// Return the message to make things simpler.
	return $error_message;
}

/**
 * An irrecoverable error. This function stops execution and displays an error message.
 * It logs the error message if $log is specified.
 * @param string $error The error message
 * @param string $log = 'general' What type of error to log this as (false to not log it))
 * @param int $status The HTTP status code associated with this error
 */
function fatal_error($error, $log = 'general', $status = 500)
{
	global $txt;

	// Send the appropriate HTTP status header - set this to 0 or false if you don't want to send one at all
	if (!empty($status))
		send_http_status($status);

	// We don't have $txt yet, but that's okay...
	if (empty($txt))
		die($error);

	log_error_online($error, false);
	setup_fatal_error_context($log ? log_error($error, $log) : $error);
}

/**
 * Shows a fatal error with a message stored in the language file.
 *
{'fraction_non_alphanumeric': 0.1710353866317169, 'fraction_numerical': 0.005616925669350309, 'mean_word_length': 4.2393330063756745, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1829224', 'n_tokens_mistral': 3687, 'n_tokens_neox': 3516, 'n_words': 1164}