From d181e03c34ca9b37ffb1d865dbdd4863392deb4f Mon Sep 17 00:00:00 2001 From: AntonV1211 Date: Mon, 28 Sep 2026 17:29:45 +0700 Subject: [PATCH] Fix. FW. Blocking the launch of parallel updates with Direct Upd https://app.doboard.com/1/task/58028 --- inc/fw-update.php | 63 ++++++++++++++++++++++++- lib/CleantalkSP/SpbctWP/Firewall/FW.php | 25 ++++++++-- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/inc/fw-update.php b/inc/fw-update.php index a7d03fc5e..5a0af2513 100644 --- a/inc/fw-update.php +++ b/inc/fw-update.php @@ -30,6 +30,11 @@ function spbc_security_firewall_update__init($delay = null) sleep((int)$delay); + // A direct update lives longer than the cron task lock, so it has to be checked before anything is wiped + if ( spbc_security_firewall_update_direct_lock__is_active() ) { + return true; + } + $spbc->update_logger::clearStorage(); $spbc->update_logger->writeLog('UPDATE INIT START'); @@ -584,7 +589,8 @@ static function ($hash) { SPBC_TBL_FIREWALL_DATA . '_temp', // Write to the main table for daughter blogs SPBC_TBL_FIREWALL_DATA__IPS . '_temp', SPBC_TBL_FIREWALL_DATA__COUNTRIES . '_temp', - $path + $path, + $current_file_content ); $single_file_result = empty($result['error']) @@ -844,6 +850,40 @@ function spbc_security_firewall_update__checker() return true; } +/** + * Heartbeat option name of a running direct Security FireWall update. + */ +if ( ! defined('SPBC_SECFW_DIRECT_UPDATE_LOCK_OPTION') ) { + define('SPBC_SECFW_DIRECT_UPDATE_LOCK_OPTION', 'spbc_secfw_direct_update_lock'); +} + +/** + * Seconds without a heartbeat after which a direct update is considered dead. + */ +if ( ! defined('SPBC_SECFW_DIRECT_UPDATE_LOCK_TTL') ) { + define('SPBC_SECFW_DIRECT_UPDATE_LOCK_TTL', 120); +} + +/** + * @return bool + */ +function spbc_security_firewall_update_direct_lock__is_active() +{ + $heartbeat = (int)get_option(SPBC_SECFW_DIRECT_UPDATE_LOCK_OPTION, 0); + + return $heartbeat > 0 && time() - $heartbeat < SPBC_SECFW_DIRECT_UPDATE_LOCK_TTL; +} + +function spbc_security_firewall_update_direct_lock__touch() +{ + update_option(SPBC_SECFW_DIRECT_UPDATE_LOCK_OPTION, time(), false); +} + +function spbc_security_firewall_update_direct_lock__release() +{ + update_option(SPBC_SECFW_DIRECT_UPDATE_LOCK_OPTION, 0, false); +} + /** * Update security firewall in single thread * @@ -851,6 +891,26 @@ function spbc_security_firewall_update__checker() * @throws Exception */ function spbc_security_firewall_update_direct() +{ + spbc_security_firewall_update_direct_lock__touch(); + + try { + $result = spbc_security_firewall_update_direct__run(); + } catch ( Exception $e ) { + spbc_security_firewall_update_direct_lock__release(); + throw $e; + } + + spbc_security_firewall_update_direct_lock__release(); + + return $result; +} + +/** + * @return bool|string[]|array[] + * @throws Exception + */ +function spbc_security_firewall_update_direct__run() { global $spbc; @@ -873,6 +933,7 @@ function spbc_security_firewall_update_direct() // process_file foreach ( $urls as $url ) { + spbc_security_firewall_update_direct_lock__touch(); $result_process_file = spbc_security_firewall_update__process_file($url, true); if ( ! empty($result_process_file['error']) ) { $spbc->update_logger->writeLog('DIRECT UPDATE ERROR: processing file: ' . @json_encode($result_process_file['error'])); diff --git a/lib/CleantalkSP/SpbctWP/Firewall/FW.php b/lib/CleantalkSP/SpbctWP/Firewall/FW.php index c4f96eed4..7744bb39e 100644 --- a/lib/CleantalkSP/SpbctWP/Firewall/FW.php +++ b/lib/CleantalkSP/SpbctWP/Firewall/FW.php @@ -549,6 +549,7 @@ public static function firewallUpdateGetMultifiles($spbc_key) * @param string $data_table__personal Table name with personal IPs * @param string $data_table__personal_countries Table name with with personal country list * @param string $file_url Local or remote URL + * @param string|null $raw_gz_content Already fetched GZ content, saves a repeated download when passed * * @return array|bool|int|mixed|string */ @@ -557,10 +558,18 @@ public static function updateWriteToDb( $data_table__common, $data_table__personal, $data_table__personal_countries, - $file_url + $file_url, + $raw_gz_content = null ) { - // Check if the URL is remote address or not, and use a proper function to extract data - $data = HTTP::getDataFromGZ($file_url); + if ( is_string($raw_gz_content) && $raw_gz_content !== '' ) { + $data = function_exists('gzdecode') ? @gzdecode($raw_gz_content) : false; + if ( $data === false ) { + $data = array('error' => 'Can not unpack datafile'); + } + } else { + // Check if the URL is remote address or not, and use a proper function to extract data + $data = HTTP::getDataFromGZ($file_url); + } if ( empty($data['error']) ) { $inserted = 0; @@ -812,11 +821,17 @@ public static function dataTablesCreateTemporaryTablesForTables($db, $table_name if ( ! $db->execute( 'CREATE TABLE IF NOT EXISTS `' . $table_name__temp . '` LIKE `' . $table_name . '`; ' ) ) { - return array('error' => 'CREATE TABLES: COULD NOT CREATE ' . $table_name__temp); + return array( + 'error' => 'CREATE TABLES: COULD NOT CREATE ' . $table_name__temp + . ' DB Error: ' . substr($db->getLastError(), 0, 1000), + ); } if ( ! $db->execute('TRUNCATE `' . $table_name__temp . '`; ') ) { - return array('error' => 'CREATE TABLES: COULD NOT TRUNCATE ' . $table_name__temp); + return array( + 'error' => 'CREATE TABLES: COULD NOT TRUNCATE ' . $table_name__temp + . ' DB Error: ' . substr($db->getLastError(), 0, 1000), + ); } }