<?php
if (!defined('__KIMS__')) exit;

function kimsq_is_https()
{
	return (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off')
		|| (!empty($_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443);
}

function kimsq_request_host()
{
	$host = strtolower(trim((string)($_SERVER['HTTP_HOST'] ?? '')));
	if (!preg_match('/^([a-z0-9](?:[a-z0-9.-]{0,251}[a-z0-9])?)(?::([0-9]{1,5}))?$/', $host, $matches)) return '';
	if (!empty($matches[2]) && ((int)$matches[2] < 1 || (int)$matches[2] > 65535)) return '';
	return $host;
}

function kimsq_site_origin()
{
	return (kimsq_is_https() ? 'https' : 'http').'://'.(kimsq_request_host() ?: 'localhost');
}

function kimsq_send_security_headers()
{
	if (headers_sent()) return;
	header_remove('X-Powered-By');
	header('X-Content-Type-Options: nosniff');
	header('X-Frame-Options: SAMEORIGIN');
	header('Referrer-Policy: strict-origin-when-cross-origin');
	header('Permissions-Policy: camera=(), geolocation=(), microphone=()');
	header("Content-Security-Policy: base-uri 'self'; frame-ancestors 'self'; object-src 'none'");
	if (kimsq_is_https()) header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
}

function kimsq_is_request_key($key)
{
	return is_string($key) && preg_match('/^[A-Za-z][A-Za-z0-9_]{0,63}$/', $key);
}

function kimsq_is_internal_request_key($key)
{
	/* Legacy JusQ controls intentionally use a leading underscore. Keep this
	 * list explicit so arbitrary internal/global-looking names stay blocked. */
	static $allowed = array(
		'_addfront','_addmodule','_clientr','_clientu','_csrf','_edittheme',
		'_layoutAction','_make','_menu','_page','_stop','_themeConfig',
		'_themePage','_toID','__target','__msg','__close'
	);
	return is_string($key) && in_array($key, $allowed, true);
}

function kimsq_import_request_variables()
{
	$reserved = array('GLOBALS','g','d','DB','table','DB_CONNECT','my','lang','date','_SESSION','_SERVER','_COOKIE','_FILES','_GET','_POST','_REQUEST');
	$values = array_replace((array)$_GET, (array)$_POST);

	foreach ($values as $key => $value)
	{
		if ((!kimsq_is_request_key($key) && !kimsq_is_internal_request_key($key)) || in_array($key, $reserved, true)) continue;
		$GLOBALS[$key] = $value;
	}
}

function kimsq_is_secret_field($key)
{
	return (bool)preg_match('/(^|_)(?:pw|password)(?:_|[0-9]|$)/i', (string)$key);
}

function kimsq_escape_request_value($value, $connection)
{
	if (is_array($value))
	{
		foreach ($value as $key => $item) $value[$key] = kimsq_escape_request_value($item, $connection);
		return $value;
	}
	if (!is_string($value)) return $value;
	return mysqli_real_escape_string($connection, str_replace("\0", '', $value));
}

function kimsq_escape_request_values($connection)
{
	if (!$connection) return;
	foreach (array('_GET','_POST') as $bag)
	{
		foreach ((array)$GLOBALS[$bag] as $key => $value)
		{
			if (!kimsq_is_request_key($key) && !kimsq_is_internal_request_key($key))
			{
				unset($GLOBALS[$bag][$key]);
				continue;
			}
			if (!kimsq_is_secret_field($key)) $GLOBALS[$bag][$key] = kimsq_escape_request_value($value, $connection);
		}
	}

	$values = array_replace((array)$_GET, (array)$_POST);
	foreach ($values as $key => $value) if (kimsq_is_request_key($key) || kimsq_is_internal_request_key($key)) $GLOBALS[$key] = $value;
	$_REQUEST = array_replace((array)$_COOKIE, (array)$_GET, (array)$_POST);
}

function kimsq_password_length($password)
{
	return function_exists('mb_strlen') ? mb_strlen($password, 'UTF-8') : strlen($password);
}

function kimsq_password_is_valid($password)
{
	return is_string($password) && kimsq_password_length($password) >= 12 && kimsq_password_length($password) <= 255;
}

function kimsq_hash_password($password)
{
	return kimsq_password_is_valid($password) ? password_hash($password, PASSWORD_DEFAULT) : false;
}

function kimsq_verify_password($password, $storedHash)
{
	if (!is_string($password) || !is_string($storedHash) || $storedHash === '') return false;
	if (preg_match('/^[a-f0-9]{32}$/i', $storedHash)) return hash_equals(strtolower($storedHash), md5($password));
	return password_verify($password, $storedHash);
}

function kimsq_password_needs_upgrade($storedHash)
{
	return preg_match('/^[a-f0-9]{32}$/i', (string)$storedHash) || password_needs_rehash((string)$storedHash, PASSWORD_DEFAULT);
}

function kimsq_csrf_token()
{
	if (empty($_SESSION['_kimsq_csrf']) || !preg_match('/^[a-f0-9]{64}$/', $_SESSION['_kimsq_csrf']))
	{
		try { $_SESSION['_kimsq_csrf'] = bin2hex(random_bytes(32)); }
		catch (Exception $e) { $_SESSION['_kimsq_csrf'] = hash('sha256', session_id().uniqid('', true)); }
	}
	return $_SESSION['_kimsq_csrf'];
}

function kimsq_origin_matches_site($value)
{
	$origin = parse_url((string)$value);
	$site = parse_url(kimsq_site_origin());
	if (empty($origin['scheme']) || empty($origin['host']) || empty($site['host'])) return false;
	$originPort = $origin['port'] ?? ($origin['scheme'] === 'https' ? 443 : 80);
	$sitePort = $site['port'] ?? ($site['scheme'] === 'https' ? 443 : 80);
	return strtolower($origin['scheme']) === strtolower($site['scheme'])
		&& strtolower($origin['host']) === strtolower($site['host'])
		&& (int)$originPort === (int)$sitePort;
}

function kimsq_valid_action_request()
{
	$provided = $_POST['_csrf'] ?? $_GET['_csrf'] ?? ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? '');
	if (is_string($provided) && hash_equals(kimsq_csrf_token(), $provided)) return true;
	if (strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) !== 'POST') return false;
	if (!empty($_SERVER['HTTP_ORIGIN'])) return kimsq_origin_matches_site($_SERVER['HTTP_ORIGIN']);
	if (!empty($_SERVER['HTTP_REFERER'])) return kimsq_origin_matches_site($_SERVER['HTTP_REFERER']);
	return false;
}

function kimsq_is_dangerous_upload_extension($extension)
{
	static $blocked = array('php','php3','php4','php5','php7','php8','phtml','pht','phar','cgi','pl','py','jsp','asp','aspx','shtml','shtm','sh','bash','bat','cmd','com','exe','dll','msi','htaccess','user.ini');
	return in_array(strtolower((string)$extension), $blocked, true);
}

function kimsq_legacy_ereg_match($pattern, $subject)
{
	$pattern = str_replace('~', '\\~', (string)$pattern);
	return @preg_match('~'.$pattern.'~', (string)$subject);
}

function kimsq_validate_upload($file, $maxBytes = 52428800)
{
	if (!is_array($file) || ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) return false;
	if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) return false;
	$size = (int)($file['size'] ?? 0);
	$maxBytes = max(1, (int)$maxBytes);
	if ($size < 1 || $size > $maxBytes) return false;
	$name = basename(str_replace('\\', '/', (string)($file['name'] ?? '')));
	$name = preg_replace('/[[:cntrl:]]/', '', $name);
	if ($name === '') return false;
	$extension = strtolower(pathinfo($name, PATHINFO_EXTENSION));
	if (kimsq_is_dangerous_upload_extension($extension)) return false;
	$image = @getimagesize($file['tmp_name']) !== false;
	return array('name' => $name, 'extension' => $extension, 'size' => $size, 'tmp_name' => $file['tmp_name'], 'is_image' => $image);
}
?>
