diff --git a/admin/templates/settings/settings_main.tpl b/admin/templates/settings/settings_main.tpl index 54f66ae..f41599f 100644 --- a/admin/templates/settings/settings_main.tpl +++ b/admin/templates/settings/settings_main.tpl @@ -57,7 +57,7 @@ {#SETTINGS_SITE_NAME#} -
+
diff --git a/class/class.settings.php b/class/class.settings.php index 68b6e1f..16b5787 100644 --- a/class/class.settings.php +++ b/class/class.settings.php @@ -52,52 +52,39 @@ function settingsShow() '%A, %d %B %Y (%H:%M)' ); - // 1. Получаем настройки (ПАРОЛЬ УЖЕ ДЕШИФРОВАН в кэше get_settings()) $settings_data_array = get_settings(); - // 2. Извлекаем строку настроек (обычно это $settings[0]) $row = (isset($settings_data_array[0]) && is_array($settings_data_array[0])) ? $settings_data_array[0] : (is_array($settings_data_array) ? $settings_data_array : array()); - // 3. КРИТИЧЕСКАЯ ЛОГИКА МАСКИРОВКИ: Заменяем дешифрованный пароль на '****' для отображения if (isset($row['mail_type']) && $row['mail_type'] === 'smtp' && !empty($row['mail_smtp_pass'])) { - // Дешифрованный пароль заменяется на '****' для поля формы $row['mail_smtp_pass'] = '****'; } - // ОПРЕДЕЛЯЕМ, КАКИЕ ИМЕННО ФУНКЦИИ ЗАБЛОКИРОВАНЫ $blocked_functions = []; - // Проверяем popen if (!function_exists('popen')) { $blocked_functions[] = 'popen()'; } - // Проверяем proc_open if (!function_exists('proc_open')) { $blocked_functions[] = 'proc_open()'; } - // Создаем строку для вывода (например: "popen() и proc_open()") if (count($blocked_functions) > 1) { - // Если заблокированы обе, объединяем их через " и " $blocked_string = implode(' и ', $blocked_functions); } elseif (count($blocked_functions) === 1) { - // Если заблокирована одна $blocked_string = $blocked_functions[0]; } else { - // Ничего не заблокировано $blocked_string = ''; } - // Передаем строку заблокированных функций в Smarty $AVE_Template->assign('blocked_sendmail_funcs', $blocked_string); $AVE_Template->assign('date_formats', $date_formats); $AVE_Template->assign('time_formats', $time_formats); - // 4. Передаем модифицированный массив с '****' в шаблон $AVE_Template->assign('row', $row); $AVE_Template->assign('available_countries', get_country_list(1)); @@ -112,22 +99,18 @@ function settingsShow() */ private function _logSwitchEvent($message, $level = 'INFO') { - // Абсолютный путь к папке логов. BASE_DIR должен быть определен. + // Абсолютный путь к папке логов. $log_dir = BASE_DIR . '/tmp/logs'; $log_file = $log_dir . '/query_switch.log'; - // Форматируем сообщение $timestamp = date('Y-m-d H:i:s'); $log_entry = "[" . $timestamp . "] [" . $level . "] " . $message . "\n"; - // Проверяем существование директории (и права, если есть возможность) if (!is_dir($log_dir) || !is_writable($log_dir)) { - // Если лог-директория недоступна, пытаемся записать в системный лог CMS, если это возможно. @reportLog("ОШИБКА ЛОГА: Не удалось записать событие. Директория '{$log_dir}' недоступна."); return; } - // Безопасная запись в файл @file_put_contents($log_file, $log_entry, FILE_APPEND | LOCK_EX); } @@ -157,7 +140,6 @@ private function _switchQueryFiles($use_safe_version, $clear_db_flag) $source_base = BASE_DIR . '/inc/query_variants/' . $source_dir_name; $error_count = 0; - // ИТЕРАЦИЯ И КОПИРОВАНИЕ foreach ($manifest as $relative_path) { $source_file = $source_base . '/' . $relative_path; $target_file = BASE_DIR . '/' . $relative_path; @@ -190,7 +172,6 @@ private function _switchQueryFiles($use_safe_version, $clear_db_flag) } } - // ЛОГИКА ОЧИСТКИ БАЗЫ ДАННЫХ if ($error_count > 0) { $this->_logSwitchEvent("ПРЕДУПРЕЖДЕНИЕ: Обнаружены ошибки копирования файлов ({$error_count} шт.). Очистка БД не будет выполнена.", 'WARNING'); } @@ -199,7 +180,6 @@ private function _switchQueryFiles($use_safe_version, $clear_db_flag) { $this->_logSwitchEvent("Очистка БД: Обнаружено фактическое переключение версии. Запуск DELETE FROM и ALTER TABLE.", 'INFO'); - // 1. Очистка ДОЧЕРНЕЙ таблицы if (@$AVE_DB->query("DELETE FROM `" . PREFIX . "_request_conditions`")) { $this->_logSwitchEvent('Очистка `_request_conditions`: Успех.', 'INFO'); } else { @@ -207,7 +187,6 @@ private function _switchQueryFiles($use_safe_version, $clear_db_flag) $error_count++; } - // 2. Очистка РОДИТЕЛЬСКОЙ таблицы if (@$AVE_DB->query("DELETE FROM `" . PREFIX . "_request`")) { $this->_logSwitchEvent('Очистка `_request`: Успех.', 'INFO'); } else { @@ -215,7 +194,6 @@ private function _switchQueryFiles($use_safe_version, $clear_db_flag) $error_count++; } - // 3. Сброс СЧЕТЧИКА if (@$AVE_DB->query("ALTER TABLE `" . PREFIX . "_request` AUTO_INCREMENT = 1")) { $this->_logSwitchEvent('Сброс AUTO_INCREMENT: Успех.', 'INFO'); } else { @@ -241,14 +219,10 @@ function settingsCase() { global $AVE_Template; -// 1. Читаем ТЕКУЩЕЕ (старое) значение прямо из файла config.inc.php. -// Если файл пуст, мы предполагаем, что используется версия по умолчанию "из коробки" (Safe/TRUE), -// так как файлы Safe-версии уже должны быть на месте после установки. $old_query_setting = true; $old_config_content = @file_get_contents(BASE_DIR . '/config/config.inc.php'); if ($old_config_content) { - // НАДЕЖНОЕ РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ: Ищет true, false, '1', '0' (в кавычках или без) if (preg_match("/define\('REQUEST_USE_VERSION',\s*(true|false|[']?\d+[']?)\s*\);/i", $old_config_content, $matches)) { $val = strtolower(trim($matches[1], "'")); @@ -261,21 +235,14 @@ function settingsCase() } } - // Сохраняем настройки if (isset($_REQUEST['more'])) { - // >>> ЗАПУСКАЕМ БУФЕРИЗАЦИЮ ВЫВОДА для чистого JSON-ответа (AJAX) if (isAjax()) { ob_start(); } - // --- ИНИЦИАЛИЗАЦИЯ ДЛЯ ОБРАБОТКИ НЕ-AJAX ЗАПРОСОВ --- - // Получаем текущее значение константы из глобального пространства. - // Это необходимо для того, чтобы при клике "Сохранить" (не-AJAX) без изменения чекбокса - // значение REQUEST_USE_VERSION не сбрасывалось и не циклилось. $current_constant_value = defined('REQUEST_USE_VERSION') ? (REQUEST_USE_VERSION ? '1' : '0') : '0'; $request_use_version_value = $current_constant_value; - // ---------------------------------------------------- $set = ' $v) { - // ИЗВЛЕКАЕМ НОВОЕ ЗНАЧЕНИЕ ДЛЯ ПЕРЕКЛЮЧЕНИЯ ФАЙЛОВ ИЗ ФОРМЫ if ($k == 'REQUEST_USE_VERSION') { - // Значение из формы всегда приоритетнее, даже если оно '0' (отмечено 'Нет') $request_use_version_value = $v; } @@ -319,10 +284,8 @@ function settingsCase() { $new_query_setting = (bool)$request_use_version_value; - // 2. Определяем, произошло ли фактическое переключение версий $is_a_version_switch = ($new_query_setting !== $old_query_setting); - // 3. Вызываем функцию, передавая НОВОЕ значение и ФЛАГ очистки $switch_success = $this->_switchQueryFiles($new_query_setting, $is_a_version_switch); if (!$switch_success) { @@ -343,7 +306,6 @@ function settingsCase() if (isAjax()) { - // ОЧИЩАЕМ ВЕСЬ ВЫВОД ПЕРЕД JSON ob_end_clean(); echo json_encode(array( @@ -354,17 +316,13 @@ function settingsCase() } else { - // 1. Принудительно очищаем кэш PHP-файлов для главного конфига (если доступно) - // Это обходной путь, который может понадобиться, если сервер использует Opcache if (function_exists('opcache_invalidate')) { @opcache_invalidate(BASE_DIR . '/config/config.inc.php', true); } - // 2. Добавляем "соль" (cache buster) и принудительно сбрасываем состояние $cache_bust = microtime(true); $redirect_url = 'Location:index.php?do=settings&sub=case&cp=' . SESSION . '&t=' . $cache_bust . '#settings'; - // 3. Добавляем заголовки, запрещающие кэширование прокси/сервером header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); @@ -391,12 +349,8 @@ function settingsSave() { global $AVE_DB, $AVE_Template; - // ДОБАВЛЕНО: Принудительно загружаем ключ шифрования. get_smtp_encryption_key(); - // ---------------------------------------------------- - // 1. ЛОГИКА ГЕНЕРАЦИИ УНИКАЛЬНОГО КЛЮЧА - // ---------------------------------------------------- if (isset($_REQUEST['mail_type']) && $_REQUEST['mail_type'] === 'smtp' && !file_exists(BASE_DIR . '/inc/smtp_key.php')) { $new_key = create_smtp_key_file(); @@ -416,33 +370,22 @@ function settingsSave() } } - // ---------------------------------------------------- - // 2. ОБРАБОТКА И ШИФРОВАНИЕ SMTP ПАРОЛЯ (С ИГНОРОМ ****) - // ---------------------------------------------------- $smtp_pass_encrypted = null; // Используем null, чтобы пропустить поле в SQL, если оно пустое или **** if (isset($_REQUEST['mail_smtp_pass'])) { $new_smtp_pass_raw = trim($_REQUEST['mail_smtp_pass']); - // ИСПРАВЛЕНО: Если пароль не пуст И не равен маске if (!empty($new_smtp_pass_raw) && $new_smtp_pass_raw !== '****') { - // ПАРОЛЬ БЫЛ ВВЕДЕН ИЛИ ИЗМЕНЕН: Шифруем новый пароль if (isset($_REQUEST['mail_type']) && $_REQUEST['mail_type'] === 'smtp') { $smtp_pass_encrypted = encrypt_smtp_pass($new_smtp_pass_raw); } else { $smtp_pass_encrypted = $new_smtp_pass_raw; } } - // Если $new_smtp_pass_raw === '****' ИЛИ ПУСТОЙ, поле mail_smtp_pass не будет добавлено в SQL. } - - // ---------------------------------------------------- - // 3. ФОРМИРОВАНИЕ УСЛОВНЫХ И ОБЯЗАТЕЛЬНЫХ ПОЛЕЙ - // ---------------------------------------------------- - $mandatory_fields = array( "mail_smtp_login" => $_REQUEST['mail_smtp_login'], "mail_smtp_encrypt" => $_REQUEST['mail_smtp_encrypt'], @@ -465,7 +408,6 @@ $mandatory_fields = array( "use_doctime" => intval($_REQUEST['use_doctime']) ); - // ДОБАВЛЯЕМ ПАРОЛЬ ТОЛЬКО ЕСЛИ ОН БЫЛ ВВЕДЕН ИЛИ ИЗМЕНЕН if ($smtp_pass_encrypted !== null) { $mandatory_fields["mail_smtp_pass"] = addslashes($smtp_pass_encrypted); } @@ -482,12 +424,12 @@ $mandatory_fields = array( $set_clauses = array(); - // 1. Формируем обязательные поля + // Формируем обязательные поля foreach ($mandatory_fields as $key => $value) { $set_clauses[] = "{$key} = '{$value}'"; } -// 2. Формируем условные поля +// Формируем условные поля foreach ($conditional_keys as $key) { if (isset($_REQUEST[$key])) { $value = $_REQUEST[$key]; @@ -505,7 +447,7 @@ foreach ($conditional_keys as $key) { $set_string = implode(",\r\n", $set_clauses); // ---------------------------------------------------- - // 4. ВЫПОЛНЕНИЕ SQL-ЗАПРОСА + // ВЫПОЛНЕНИЕ SQL-ЗАПРОСА // ---------------------------------------------------- $sql = $AVE_DB->Query(" UPDATE @@ -640,7 +582,7 @@ foreach ($conditional_keys as $key) { // Инициализируем $items как null, чтобы шаблон работал при добавлении $items = null; - // Безопасно получаем ID (используем 'id' или 'Id', если что-то передано) + // получаем ID (используем 'id' или 'Id', если что-то передано) $lang_id = (int)($_REQUEST['id'] ?? $_REQUEST['Id'] ?? 0); if ($lang_id > 0) @@ -654,7 +596,7 @@ foreach ($conditional_keys as $key) { Id = '" . $lang_id . "' "); - // Безопасное получение данных + // получение данных if (is_object($result)) { $items = $result->FetchRow(); } @@ -670,7 +612,7 @@ function settingsLanguageEditSave() { global $AVE_DB, $AVE_Template; - // 1. Сохранение/Обновление данных языка в БД (Ваш оригинальный код) + // 1. Сохранение/Обновление данных языка в БД if (! empty($_REQUEST["Id"])) { @@ -700,7 +642,7 @@ function settingsLanguageEditSave() } - // 2. Логика сохранения загруженного флага (ДОБАВЛЕНО) + // сохранение загруженного флага $lang_key = $_REQUEST['lang_key'] ?? ''; diff --git a/functions/func.common.php b/functions/func.common.php index f697748..f5e4cf5 100644 --- a/functions/func.common.php +++ b/functions/func.common.php @@ -289,7 +289,7 @@ function generate_encryption_key() { } /** - * Создает файл ключа inc/smtp_key.php с уникальным ключом. + * Создает файл ключа * @return string Сгенерированный ключ, или пустая строка в случае ошибки. */ function create_smtp_key_file() { @@ -307,7 +307,6 @@ function create_smtp_key_file() { return ''; } - // @chmod($key_path, 0400); // Была причиной ошибок, оставляем закомментированной // Определяем константу, чтобы не читать файл снова if (!defined('SMTP_ENCRYPTION_KEY')) { @@ -318,7 +317,7 @@ function create_smtp_key_file() { } /** - * Получает ключ шифрования SMTP, читая файл inc/smtp_key.php. + * Получает ключ шифрования SMTP * @return string Ключ шифрования (64 символа), или пустая строка, если ключ отсутствует. */ function get_smtp_encryption_key() { @@ -418,22 +417,16 @@ function get_settings($field = '') { get_smtp_encryption_key(); - // Включаем параметры кэша (-1, 'settings', true, '.settings'). $result = $AVE_DB->Query(" SELECT * FROM " . PREFIX . "_settings ", -1, 'settings', true, '.settings')->FetchAssocArray(); - // 2. НОРМАЛИЗАЦИЯ: Преобразуем результат в одномерный массив $settings. - // Если кэш вернул двумерный массив (Array([0] => Array(...))), берем индекс [0]. if (isset($result[0]) && is_array($result[0])) { $settings = $result[0]; } else { - // Если вернулся одномерный массив (например, при первом чтении из БД без кэша), используем его напрямую. $settings = $result; } - // --- ЛОГИКА ДЕШИФРОВАНИЯ --- - // Теперь $settings гарантированно одномерный массив. $row = $settings; if (isset($row['mail_type']) && $row['mail_type'] === 'smtp' && !empty($row['mail_smtp_pass'])) { @@ -442,9 +435,8 @@ function get_settings($field = '') $decrypted_pass = decrypt_smtp_pass($pass); - // Если дешифровка сработала, обновляем значение if ($decrypted_pass !== $pass) { - $settings['mail_smtp_pass'] = $decrypted_pass; // Обновляем в статическом кэше $settings + $settings['mail_smtp_pass'] = $decrypted_pass; } } }