Browse Source

simplify error handling; rate limit backend sanity checks

Andrew Dolgov 7 months ago
parent
commit
2c9d65038e
6 changed files with 126 additions and 71 deletions
  1. 7 1
      backend.php
  2. 1 0
      config.php-dist
  3. 0 40
      include/errors.php
  4. 32 16
      include/functions.php
  5. 79 13
      include/sanity_check.php
  6. 7 1
      index.php

+ 7 - 1
backend.php

@@ -31,7 +31,13 @@
 
 	header('Content-Type: text/json; charset=utf-8');
 
-	if (!sanity_check()) { return; }
+	if (!$_SESSION["uid"] || time() - $_SESSION["backend_sanity_check_last"] > 30) {
+
+		if (!sanity_check())
+			return;
+
+		$_SESSION["backend_sanity_check_last"] = time();
+	}
 
 	if (!$_SESSION["uid"] && $op != "fetch-profiles" && $op != "login" && $op != "imgproxy") {
 		print json_encode(array("error" => 6));

+ 1 - 0
config.php-dist

@@ -39,6 +39,7 @@
 	// >= SESSION_COOKIE_LIFETIME_REMEMBER
 
 	define('FETCH_URL_TITLES', false);
+	// Fetch titles of posted URLs and generate thumbnails (requires GD)
 
 	// ***************************************
 	// *** Other settings (less important) ***

+ 0 - 40
include/errors.php

@@ -1,40 +0,0 @@
-<?php
-
-	$ERRORS[0] = __("Unknown error");
-
-	$ERRORS[1] = __("This program requires XmlHttpRequest " .
-			"to function properly. Your browser doesn't seem to support it.");
-
-	$ERRORS[2] = __("This program requires cookies " .
-			"to function properly. Your browser doesn't seem to support them.");
-
-	$ERRORS[3] = __("Backend sanity check failed");
-
-	$ERRORS[4] = __("Frontend sanity check failed.");
-
-	$ERRORS[5] = __("Incorrect database schema version. &lt;a href='update.php'&gt;Please update&lt;/a&gt;.");
-
-	$ERRORS[6] = __("Request not authorized.");
-
-	$ERRORS[7] = __("No operation to perform.");
-
-	$ERRORS[8] = "[This error is not returned by server]";
-
-	$ERRORS[8] = __("Denied. Your access level is insufficient to access this page.");
-
-	$ERRORS[9] = __("Configuration check failed");
-
-	$ERRORS[10] = __("Your version of MySQL is not currently supported. Please see
-		official site for more information.");
-
-	$ERRORS[11] = "[This error is not returned by server]";
-
-	$ERRORS[12] = __("SQL escaping test failed, check your database and PHP configuration");
-	$ERRORS[13] = __("Backend connection daemon is not running.");
-
-	$ERRORS[14] = __("The application failed to initialize.");
-
-	$ERRORS[15] = __("Unable to connect to Redis server.");
-	$ERRORS[16] = __("Unable to connect to database server.");
-
-?>

+ 32 - 16
include/functions.php

@@ -17,12 +17,18 @@
 
 	$redis = new Credis_Client(REDIS_SERVER);
 
+	$ERROR_MESSAGES = [
+		0 => "Unknown error",
+		5 => "Incorrect database schema version.",
+		6 => "Request not authorized.",
+		15 => "Unable to connect to Redis server.",
+		16 => "Unable to connect to database server.",
+	];
+
 	define('SINGLE_USER_MODE', false);
 	define('EMOTICONS_MAP', 'emoticons/emoticons.json');
 	define('DEFAULT_BUFFER_SIZE', 128);
 
-	$connection_nick_cache = [];
-
 	if (DB_TYPE == 'mysql') {
 		define('DB_KEY_FIELD', 'param');
 	} else {
@@ -107,8 +113,6 @@
 		}
 	} // If translations are enabled.
 
-	require_once "errors.php";
-
 	function uniqid_short() {
 		return uniqid(base_convert(rand(), 10, 36));
 	}
@@ -1441,13 +1445,11 @@
 
 	function sanity_check($plaintext = false) {
 
-		global $ERRORS;
+		global $ERROR_MESSAGES;
 
 		$error_code = 0;
 
 		try {
-			$dbh = DB::get();
-
 			$schema_version = get_schema_version();
 
 			if ($schema_version != SCHEMA_VERSION) {
@@ -1458,13 +1460,6 @@
 			$error_code = 16;
 		}
 
-		/* $row = $dbh->query("SELECT value FROM ttirc_system WHERE	".DB_KEY_FIELD." = 'MASTER_RUNNING'")->fetch();
-		$master_running = $row['value'] == 'true'; # text field
-
-		if (!$master_running) {
-			$error_code = 13;
-		} */
-
 		global $redis;
 
 		try {
@@ -1475,10 +1470,31 @@
 
 		if ($error_code != 0) {
 			if ($plaintext) {
-				print "[E:$error_code] " . $ERRORS[$error_code];
+				?>
+					<!DOCTYPE html>
+					<html>
+					<head>
+					<title>Startup failed</title>
+						<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+						<style type="text/css">
+						body {
+							background : #900;
+							color : white;
+						}
+						</style>
+					</head>
+				<body>
+					<h1>Startup failed</h1>
+
+					<ul>
+					<li><?php echo $error_code . ": " . $ERROR_MESSAGES[$error_code] ?></li>
+					</ul>
+				</body>
+				</html>
+			<?php
 			} else {
 				print json_encode(array("error" => $error_code,
-					"errormsg" => $ERRORS[$error_code]));
+					"errormsg" => $ERROR_MESSAGES[$error_code]));
 			}
 			return false;
 		} else {

+ 79 - 13
include/sanity_check.php

@@ -4,23 +4,89 @@
 	define('SCHEMA_VERSION', 16);
 	define('EXPECTED_CONFIG_VERSION', 2);
 
-	$err_msg = "";
-
-	if (!file_exists("config.php")) {
-		print "<b>Fatal Error</b>: You forgot to copy
-		<b>config.php-dist</b> to <b>config.php</b> and edit it.\n";
-		exit;
-	}
+	$errors = [];
 
 	require_once "config.php";
 
-	if (CONFIG_VERSION != EXPECTED_CONFIG_VERSION) {
-		$err_msg = "config: your config file version is incorrect. See config.php-dist.\n";
-	}
+	function initial_sanity_check() {
+		if (CONFIG_VERSION != EXPECTED_CONFIG_VERSION) {
+			array_push($errors, "config: your config file version is incorrect. See config.php-dist.");
+		}
+
+		foreach (array_filter(glob("cache/*", "is_dir")) as $dir) {
+			if (!is_writable($dir)) {
+				array_push($error, "Cache directory is not writable: $dir");
+			}
+		}
+
+		if (!function_exists("json_encode")) {
+			array_push($errors, "PHP support for JSON is required, but was not found.");
+		}
+
+		if (!function_exists("pg_connect")) {
+			array_push($errors, "PHP support for PostgreSQL is required for configured DB_TYPE in config.php");
+		}
+
+		if (!class_exists("PDO")) {
+			array_push($errors, "PHP support for PDO is required but was not found.");
+		}
+
+		if (!function_exists("mb_strlen")) {
+			array_push($errors, "PHP support for mbstring functions is required but was not found.");
+		}
+
+		if (!function_exists("hash")) {
+			array_push($errors, "PHP support for hash() function is required but was not found.");
+		}
 
-	if ($err_msg) {
-		print "<b>Fatal Error</b>: $err_msg\n";
-		exit;
+		if (!function_exists("mime_content_type")) {
+			array_push($errors, "PHP function mime_content_type() is missing, try enabling fileinfo module.");
+		}
+
+		if (!class_exists("DOMDocument")) {
+			array_push($errors, "PHP support for DOMDocument is required, but was not found.");
+		}
+
+		if (!function_exists("imagecreatefromstring") && FETCH_URL_TITLES) {
+			array_push($errors, "PHP support for GD is required when using FETCH_URL_TITLES, but was not found.");
+		}
+
+		if (count($errors) > 0 && $_SERVER['REQUEST_URI']) { ?>
+			<!DOCTYPE html>
+			<html>
+			<head>
+			<title>Startup failed</title>
+				<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+				<style type="text/css">
+				body {
+					background : #900;
+					color : white;
+				}
+				</style>
+			</head>
+		<body>
+			<h1>Startup failed</h1>
+
+			<ul>
+			<?php foreach ($errors as $error) { echo "<li>" . format_error($error) . "</li>"; } ?>
+			</ul>
+		</body>
+		</html>
+
+		<?php
+			die;
+		} else if (count($errors) > 0) {
+			echo "Tiny Tiny IRC was unable to start properly. This usually means a misconfiguration or an incomplete upgrade.\n";
+			echo "Please fix errors indicated by the following messages:\n\n";
+
+			foreach ($errors as $error) {
+				echo " * $error\n";
+			}
+
+			exit(-1);
+		}
 	}
 
+	initial_sanity_check();
+
 ?>

+ 7 - 1
index.php

@@ -2,9 +2,15 @@
 	set_include_path(get_include_path() . PATH_SEPARATOR .
 		dirname(__FILE__) ."/include");
 
+	if (!file_exists("config.php")) {
+		print "<b>Fatal Error</b>: You forgot to copy
+		<b>config.php-dist</b> to <b>config.php</b> and edit it.\n";
+		exit;
+	}
+
 	require_once "functions.php";
-	require_once "sessions.php";
 	require_once "sanity_check.php";
+	require_once "sessions.php";
 	require_once "version.php";
 	require_once "config.php";