diff --git a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php index 5f7950c..9550b97 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/BackupEngine.php @@ -166,13 +166,13 @@ class BackupEngine continue; } - // Sanitize configuration.php — replace credentials with - // placeholders so backups are safe to store or transfer. - // MokoRestore prompts for these values during restore. + // Store configuration.php as .bak with credentials stripped. + // The restore process rebuilds a fresh configuration.php + // from user input + non-sensitive values from the .bak. if ($relativePath === 'configuration.php') { $sanitized = self::sanitizeConfiguration($fullPath); - $archiver->addFromString($relativePath, $sanitized); - $this->log('configuration.php sanitized (credentials replaced with placeholders)'); + $archiver->addFromString('configuration.php.bak', $sanitized); + $this->log('configuration.php saved as .bak (credentials stripped)'); } else { $archiver->addFile($fullPath, $relativePath); } diff --git a/source/packages/com_mokosuitebackup/src/Engine/FileRestorer.php b/source/packages/com_mokosuitebackup/src/Engine/FileRestorer.php index 57e99d4..5ffbbc3 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/FileRestorer.php +++ b/source/packages/com_mokosuitebackup/src/Engine/FileRestorer.php @@ -22,10 +22,11 @@ class FileRestorer /** * Files that should never be overwritten during restore. - * configuration.php is handled separately by the RestoreEngine. + * configuration.php is rebuilt from .bak + user input by RestoreEngine. */ private const SKIP_FILES = [ 'configuration.php', + 'configuration.php.bak', '.htaccess', 'web.config', ]; diff --git a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php index 044b931..44809cb 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php +++ b/source/packages/com_mokosuitebackup/src/Engine/MokoRestore.php @@ -258,10 +258,14 @@ function actionExtract(array $data): array $count = $zip->numFiles; $zip->close(); - // Try to read existing configuration.php for pre-filling. - // Sanitized backups contain [SANITIZED:field] placeholders — skip those. + // Pre-fill from configuration.php.bak (sanitized backup) or + // configuration.php (legacy/unsanitized backup). Skip [SANITIZED:] values. $existingConfig = []; - $configFile = RESTORE_DIR . '/configuration.php'; + $configFile = RESTORE_DIR . '/configuration.php.bak'; + + if (!is_file($configFile)) { + $configFile = RESTORE_DIR . '/configuration.php'; + } if (is_file($configFile)) { $content = file_get_contents($configFile); @@ -278,7 +282,6 @@ function actionExtract(array $data): array foreach ($fieldMap as $phpField => $configKey) { if (preg_match('/\$' . preg_quote($phpField, '/') . '\s*=\s*\'([^\']*)\'/', $content, $m)) { - // Skip sanitized placeholder values if (strpos($m[1], '[SANITIZED:') === false) { $existingConfig[$configKey] = $m[1]; } @@ -396,12 +399,18 @@ function actionConfig(array $data): array $tmpPath = RESTORE_DIR . '/tmp'; $logPath = RESTORE_DIR . '/administrator/logs'; - $configFile = RESTORE_DIR . '/configuration.php'; + $configPath = RESTORE_DIR . '/configuration.php'; + $bakPath = RESTORE_DIR . '/configuration.php.bak'; - if (is_file($configFile)) { - // Update existing configuration.php - $config = file_get_contents($configFile); + // Use .bak as the base template (preserves non-sensitive settings like + // debug, cache, SEF, editor, etc.). Fall back to existing config + // for legacy/unsanitized backups, or build from scratch if neither exists. + $basePath = is_file($bakPath) ? $bakPath : (is_file($configPath) ? $configPath : null); + if ($basePath !== null) { + $config = file_get_contents($basePath); + + // Replace all credential and server-specific fields with user input $replacements = [ '/\$host\s*=\s*\'[^\']*\'/' => "\$host = '{$host}'", '/\$db\s*=\s*\'[^\']*\'/' => "\$db = '{$dbName}'", @@ -418,24 +427,32 @@ function actionConfig(array $data): array $replacements['/\$live_site\s*=\s*\'[^\']*\'/'] = "\$live_site = '{$livesite}'"; } - // Replace SMTP credentials (only if provided — leave existing values if blank) - if ($smtpHost !== '') { - $replacements['/\$smtphost\s*=\s*\'[^\']*\'/'] = "\$smtphost = '{$smtpHost}'"; - } - if ($smtpUser !== '') { - $replacements['/\$smtpuser\s*=\s*\'[^\']*\'/'] = "\$smtpuser = '" . addcslashes($smtpUser, "'\\") . "'"; - } - if ($smtpPass !== '') { - $replacements['/\$smtppass\s*=\s*\'[^\']*\'/'] = "\$smtppass = '" . addcslashes($smtpPass, "'\\") . "'"; - } + // SMTP — always replace (clears sanitized placeholders even if blank) + $replacements['/\$smtphost\s*=\s*\'[^\']*\'/'] = "\$smtphost = '" . addcslashes($smtpHost, "'\\") . "'"; + $replacements['/\$smtpuser\s*=\s*\'[^\']*\'/'] = "\$smtpuser = '" . addcslashes($smtpUser, "'\\") . "'"; + $replacements['/\$smtppass\s*=\s*\'[^\']*\'/'] = "\$smtppass = '" . addcslashes($smtpPass, "'\\") . "'"; + + // Clear remaining sanitized placeholders (proxy, Redis, DB TLS) + $replacements['/\$proxy_user\s*=\s*\'[^\']*\'/'] = "\$proxy_user = ''"; + $replacements['/\$proxy_pass\s*=\s*\'[^\']*\'/'] = "\$proxy_pass = ''"; + $replacements['/\$redis_server_auth\s*=\s*\'[^\']*\'/'] = "\$redis_server_auth = ''"; + $replacements['/\$session_redis_server_auth\s*=\s*\'[^\']*\'/'] = "\$session_redis_server_auth = ''"; + $replacements['/\$dbsslkey\s*=\s*\'[^\']*\'/'] = "\$dbsslkey = ''"; + $replacements['/\$dbsslcert\s*=\s*\'[^\']*\'/'] = "\$dbsslcert = ''"; + $replacements['/\$dbsslca\s*=\s*\'[^\']*\'/'] = "\$dbsslca = ''"; foreach ($replacements as $pattern => $replacement) { $config = preg_replace($pattern, $replacement, $config); } - file_put_contents($configFile, $config); + file_put_contents($configPath, $config); - return ['success' => true, 'message' => 'configuration.php updated with new settings and fresh secret']; + // Remove .bak after successful rebuild + if (is_file($bakPath)) { + @unlink($bakPath); + } + + return ['success' => true, 'message' => 'Joomla configuration rebuilt with fresh credentials and secret']; } // Create new configuration.php from scratch diff --git a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php index 2470708..819285d 100644 --- a/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php +++ b/source/packages/com_mokosuitebackup/src/Engine/SteppedBackupEngine.php @@ -281,10 +281,10 @@ class SteppedBackupEngine continue; } - // Sanitize Joomla config — replace credentials with placeholders + // Store config as .bak with credentials stripped — restore rebuilds it if (basename($relativePath) === 'configuration.php' && dirname($relativePath) === '.') { $sanitized = BackupEngine::sanitizeConfiguration($fullPath); - $zip->addFromString($relativePath, $sanitized); + $zip->addFromString('configuration.php.bak', $sanitized); } else { $zip->addFile($fullPath, $relativePath); }