+
+
+ +
From 5222d239f54fb44f6ee39213562865155c1655ee Mon Sep 17 00:00:00 2001 From: anonymoususer72041 <247563575+anonymoususer72041@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:01:32 +0200 Subject: [PATCH 1/5] Add schema migration status detection --- lib/SchemaMigrationStatus.php | 128 ++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 lib/SchemaMigrationStatus.php diff --git a/lib/SchemaMigrationStatus.php b/lib/SchemaMigrationStatus.php new file mode 100644 index 000000000..3ceb9992e --- /dev/null +++ b/lib/SchemaMigrationStatus.php @@ -0,0 +1,128 @@ +makeQueryString('install') + ); + $rs = $db->getAssoc($sql); + + if (empty($rs)) + { + self::$_storedInstallSchemaVersion = false; + } + else + { + self::$_storedInstallSchemaVersion = $rs['version']; + } + + self::$_storedInstallSchemaVersionLoaded = true; + + return self::$_storedInstallSchemaVersion; + } + + /** + * Returns whether install schema migrations are pending. + * + * @return boolean + */ + public static function hasPendingInstallMigrations() + { + if (self::$_hasPendingInstallMigrations !== NULL) + { + return self::$_hasPendingInstallMigrations; + } + + $storedVersion = self::getStoredInstallSchemaVersion(); + + if ($storedVersion === false || + $storedVersion === NULL || + $storedVersion === '') + { + /* Unknown versions require explicit install or maintenance finalization. */ + self::$_hasPendingInstallMigrations = true; + return true; + } + + self::$_hasPendingInstallMigrations = + (int) $storedVersion < self::getLatestInstallSchemaVersion(); + + return self::$_hasPendingInstallMigrations; + } + + /** + * Clears cached migration status after explicit maintenance updates. + * + * @return void + */ + public static function clearCache() + { + self::$_latestInstallSchemaVersion = NULL; + self::$_storedInstallSchemaVersion = NULL; + self::$_storedInstallSchemaVersionLoaded = false; + self::$_hasPendingInstallMigrations = NULL; + } +} + +?> From 5387fc848fd5ae28b3636c043cd291ae43a8bbc2 Mon Sep 17 00:00:00 2001 From: anonymoususer72041 <247563575+anonymoususer72041@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:02:00 +0200 Subject: [PATCH 2/5] Skip normal module loading while install migrations are pending --- lib/ModuleUtility.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/ModuleUtility.php b/lib/ModuleUtility.php index d35c42c0e..81720030e 100755 --- a/lib/ModuleUtility.php +++ b/lib/ModuleUtility.php @@ -35,6 +35,8 @@ * @package CATS * @subpackage Library */ +include_once(LEGACY_ROOT . '/lib/SchemaMigrationStatus.php'); + class ModuleUtility { /* Prevent this class from being instantiated. */ @@ -441,6 +443,14 @@ public static function getModuleSchemaVersions() */ private static function processModuleSchema($moduleName, $schema) { + global $maintPage; + + if ($moduleName === 'install' && + (!isset($maintPage) || $maintPage !== true)) + { + return; + } + if( ini_get('safe_mode') ) { //don't do anything in safe mode @@ -501,7 +511,11 @@ private static function processModuleSchema($moduleName, $schema) if ($moduleName === 'install' && ($currentVersion === NULL || $currentVersion === '')) { - /* A NULL install module version means the database came from cats_schema.sql and should not replay historical install migrations. */ + /* This explicit installer/maintenance finalization is only for + * snapshot databases whose schema already matches the bundled + * baseline. It must not run during normal requests and is not + * proof that an unknown historical database state is current. + */ $sql = sprintf( "UPDATE module_schema @@ -514,6 +528,7 @@ private static function processModuleSchema($moduleName, $schema) ); $db->query($sql); + SchemaMigrationStatus::clearCache(); return; } @@ -532,7 +547,6 @@ private static function processModuleSchema($moduleName, $schema) } /* if maintPage, execute 1 query, output the next query and progress, and terminate. */ - global $maintPage; if ((isset($maintPage) && $maintPage === true)) { if ($executedQuery == false) @@ -586,6 +600,11 @@ private static function processModuleSchema($moduleName, $schema) $rs = $db->query($sql); $currentVersion = $version; + + if ($moduleName === 'install') + { + SchemaMigrationStatus::clearCache(); + } } } } From 8076d22da5aa5435a768df730d4f6ba031dfce0b Mon Sep 17 00:00:00 2001 From: anonymoususer72041 <247563575+anonymoususer72041@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:02:29 +0200 Subject: [PATCH 3/5] Block the career portal during pending migrations --- index.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/index.php b/index.php index fe76ea812..64564ba3e 100644 --- a/index.php +++ b/index.php @@ -166,6 +166,18 @@ if (((isset($careerPage) && $careerPage) || (isset($_GET['showCareerPortal']) && $_GET['showCareerPortal'] == '1'))) { + if (SchemaMigrationStatus::hasPendingInstallMigrations()) + { + header('HTTP/1.1 503 Service Unavailable'); + header('Content-Type: text/html; charset=UTF-8'); + + echo '', + '
The career portal is temporarily unavailable while system maintenance is in progress. Please try again later.
', + ''; + die(); + } + ModuleUtility::loadModule('careers'); } From b9b98f8a15f0d24ffda17822bd19b18b941e1dd3 Mon Sep 17 00:00:00 2001 From: anonymoususer72041 <247563575+anonymoususer72041@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:03:56 +0200 Subject: [PATCH 4/5] Gate application requests during pending migrations --- ajax.php | 27 +++++++++++++++++ index.php | 24 +++++++++++++++ js/install.js | 26 ++++++++++++++-- modules/install/CATSUI.php | 28 ++++++++++++++++++ modules/install/Maintenance.tpl | 45 ++++++++++++++++++++++++++++ modules/install/ajax/maint.php | 46 +++++++++++++++++++++-------- modules/login/PendingMigrations.tpl | 41 +++++++++++++++++++++++++ 7 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 modules/install/Maintenance.tpl create mode 100644 modules/login/PendingMigrations.tpl diff --git a/ajax.php b/ajax.php index 6908707c0..6058aeab5 100644 --- a/ajax.php +++ b/ajax.php @@ -41,6 +41,7 @@ include_once(LEGACY_ROOT . '/lib/Session.php'); /* Depends: MRU, Users, DatabaseConnection. */ include_once(LEGACY_ROOT . '/lib/AJAXInterface.php'); include_once(LEGACY_ROOT . '/lib/CATSUtility.php'); +include_once(LEGACY_ROOT . '/lib/SchemaMigrationStatus.php'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); @@ -117,6 +118,8 @@ } } +$module = ''; + if (strpos($_REQUEST['f'], ':') === false) { $function = preg_replace("/[^A-Za-z0-9]/", "", $_REQUEST['f']); @@ -134,6 +137,30 @@ $filename = sprintf('modules/%s/ajax/%s.php', $module, $function); } +/* Fresh installer AJAX remains available. On installed systems, only the + * existing maintenance action may pass the pending-migration AJAX gate. + */ +$maintenanceAJAXAllowed = + $installerActive || + ($module === 'install' && $function === 'maint'); + +if (isset($_SESSION['CATS']) && + $_SESSION['CATS']->isLoggedIn() && + !$maintenanceAJAXAllowed && + SchemaMigrationStatus::hasPendingInstallMigrations()) +{ + header('Content-type: text/xml; charset=' . AJAX_ENCODING); + echo '', "\n"; + echo( + "\n" . + "Database migrations are pending. OpenCATS should be updated before normal use continues.
+ + + + + + +This maintenance action must be triggered via POST.
', - 'This page starts maintenance mode and related installer tasks.
', - '', - ''; + header('HTTP/1.1 405 Method Not Allowed'); + header('Allow: POST'); die(); } +$installerActive = !file_exists('INSTALL_BLOCK'); + +if (!$installerActive) +{ + /* Fresh installation uses the installer's existing access model. An + * installed system requires an authenticated site admin and CSRF token. + */ + include_once('./config.php'); + include_once(LEGACY_ROOT . '/constants.php'); + include_once(LEGACY_ROOT . '/lib/DatabaseConnection.php'); + include_once(LEGACY_ROOT . '/lib/Session.php'); + + @session_name(CATS_SESSION_NAME); + @session_start(); + + if (!isset($_SESSION['CATS']) || + !$_SESSION['CATS']->isLoggedIn() || + $_SESSION['CATS']->getAccessLevel(ACL::SECOBJ_ROOT) < ACCESS_LEVEL_SA || + !isset($_POST['csrfToken']) || + !$_SESSION['CATS']->isCSRFTokenValid($_POST['csrfToken'])) + { + header('HTTP/1.1 403 Forbidden'); + die('Access denied.'); + } +} + if (file_exists('./modules.cache')) { @unlink('./modules.cache'); } +if (isset($_SESSION['modules'])) +{ + unset($_SESSION['modules']); +} + $maintPage = true; include_once('index.php'); diff --git a/modules/login/PendingMigrations.tpl b/modules/login/PendingMigrations.tpl new file mode 100644 index 000000000..21f4bf04a --- /dev/null +++ b/modules/login/PendingMigrations.tpl @@ -0,0 +1,41 @@ + + + + +Database migrations are pending. OpenCATS should be updated before normal use continues.
+ + +Database maintenance is required before OpenCATS can be used normally. Please contact your administrator.
+ +