init_backends(); } /** * Get security instance (lazy loading) * * @return TigerStyle_Life9_Security */ private function get_security() { if (!$this->security) { $this->security = tigerstyle_life9()->get_security(); } return $this->security; } /** * Initialize storage backends */ private function init_backends() { $this->backends = [ 'local' => [ 'name' => __('Local Storage', 'tigerstyle-life9'), 'description' => __('Store backups on the local server', 'tigerstyle-life9'), 'class' => 'TigerStyle_Life9_Storage_Local', 'enabled' => true, 'config_fields' => [] ], 's3' => [ 'name' => __('Amazon S3', 'tigerstyle-life9'), 'description' => __('Store backups on Amazon S3 or compatible services', 'tigerstyle-life9'), 'class' => 'TigerStyle_Life9_Storage_S3', 'enabled' => class_exists('Aws\S3\S3Client'), 'config_fields' => [ 'access_key' => __('Access Key ID', 'tigerstyle-life9'), 'secret_key' => __('Secret Access Key', 'tigerstyle-life9'), 'bucket' => __('Bucket Name', 'tigerstyle-life9'), 'region' => __('Region', 'tigerstyle-life9'), 'endpoint' => __('Custom Endpoint (optional)', 'tigerstyle-life9') ] ], 'google_drive' => [ 'name' => __('Google Drive', 'tigerstyle-life9'), 'description' => __('Store backups on Google Drive', 'tigerstyle-life9'), 'class' => 'TigerStyle_Life9_Storage_GoogleDrive', 'enabled' => false, // Would require Google API client 'config_fields' => [ 'client_id' => __('Client ID', 'tigerstyle-life9'), 'client_secret' => __('Client Secret', 'tigerstyle-life9'), 'folder_id' => __('Folder ID (optional)', 'tigerstyle-life9') ] ], 'ftp' => [ 'name' => __('FTP/SFTP', 'tigerstyle-life9'), 'description' => __('Store backups on FTP or SFTP server', 'tigerstyle-life9'), 'class' => 'TigerStyle_Life9_Storage_FTP', 'enabled' => extension_loaded('ftp'), 'config_fields' => [ 'host' => __('Host', 'tigerstyle-life9'), 'port' => __('Port', 'tigerstyle-life9'), 'username' => __('Username', 'tigerstyle-life9'), 'password' => __('Password', 'tigerstyle-life9'), 'path' => __('Remote Path', 'tigerstyle-life9'), 'passive' => __('Use Passive Mode', 'tigerstyle-life9'), 'ssl' => __('Use SSL/SFTP', 'tigerstyle-life9') ] ] ]; // Allow plugins to register additional backends $this->backends = apply_filters('tigerstyle_life9_storage_backends', $this->backends); } /** * Store backup file to configured storage locations * * @param string $file_path Local file path * @param array $storage_config Storage configuration * @return array Storage results */ public function store_backup($file_path, $storage_config = []) { if (!file_exists($file_path) || !is_readable($file_path)) { throw new Exception('Backup file not found or not readable'); } // Validate file path if (!$this->get_security()->validate_path($file_path)) { throw new Exception('Invalid file path'); } $results = []; $enabled_storages = $this->get_enabled_storage_locations($storage_config); foreach ($enabled_storages as $storage_type => $config) { try { $backend = $this->get_storage_backend($storage_type); if (!$backend) { $results[$storage_type] = [ 'success' => false, 'error' => 'Storage backend not available' ]; continue; } $this->log_info("Storing backup to {$storage_type}", [ 'file_path' => $file_path, 'file_size' => filesize($file_path) ]); $result = $backend->store($file_path, $config); $results[$storage_type] = [ 'success' => true, 'url' => $result['url'] ?? null, 'remote_path' => $result['remote_path'] ?? null, 'storage_id' => $result['storage_id'] ?? null, 'metadata' => $result['metadata'] ?? [] ]; $this->log_info("Backup stored successfully to {$storage_type}"); } catch (Exception $e) { $this->log_error("Failed to store backup to {$storage_type}", [ 'error' => $e->getMessage() ]); $results[$storage_type] = [ 'success' => false, 'error' => $e->getMessage() ]; } } return $results; } /** * Retrieve backup file from storage * * @param string $storage_type Storage type * @param string $remote_path Remote file path or ID * @param string $local_path Local destination path * @param array $config Storage configuration * @return bool Success status */ public function retrieve_backup($storage_type, $remote_path, $local_path, $config = []) { try { $backend = $this->get_storage_backend($storage_type); if (!$backend) { throw new Exception('Storage backend not available'); } // Validate local path if (!$this->get_security()->validate_path(dirname($local_path))) { throw new Exception('Invalid local destination path'); } $this->log_info("Retrieving backup from {$storage_type}", [ 'remote_path' => $remote_path, 'local_path' => $local_path ]); $success = $backend->retrieve($remote_path, $local_path, $config); if ($success) { $this->log_info("Backup retrieved successfully from {$storage_type}"); } else { throw new Exception('Retrieval failed'); } return $success; } catch (Exception $e) { $this->log_error("Failed to retrieve backup from {$storage_type}", [ 'error' => $e->getMessage() ]); return false; } } /** * Delete backup from storage * * @param string $storage_type Storage type * @param string $remote_path Remote file path or ID * @param array $config Storage configuration * @return bool Success status */ public function delete_backup($storage_type, $remote_path, $config = []) { try { $backend = $this->get_storage_backend($storage_type); if (!$backend) { throw new Exception('Storage backend not available'); } $this->log_info("Deleting backup from {$storage_type}", [ 'remote_path' => $remote_path ]); $success = $backend->delete($remote_path, $config); if ($success) { $this->log_info("Backup deleted successfully from {$storage_type}"); } return $success; } catch (Exception $e) { $this->log_error("Failed to delete backup from {$storage_type}", [ 'error' => $e->getMessage() ]); return false; } } /** * Test storage connection * * @param string $storage_type Storage type * @param array $config Storage configuration * @return array Test result */ public function test_connection($storage_type, $config = []) { try { $backend = $this->get_storage_backend($storage_type); if (!$backend) { return [ 'success' => false, 'error' => 'Storage backend not available' ]; } $result = $backend->test_connection($config); return [ 'success' => $result, 'message' => $result ? 'Connection successful' : 'Connection failed' ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Get storage backend instance * * @param string $storage_type Storage type * @return object|null Storage backend instance */ private function get_storage_backend($storage_type) { if (!isset($this->backends[$storage_type])) { return null; } $backend_info = $this->backends[$storage_type]; if (!$backend_info['enabled']) { return null; } $class_name = $backend_info['class']; if (!class_exists($class_name)) { // Try to load the storage backend class $file_name = 'class-storage-' . str_replace('_', '-', strtolower($storage_type)) . '.php'; $file_path = TIGERSTYLE_LIFE9_PLUGIN_DIR . 'includes/storage/' . $file_name; if (file_exists($file_path)) { require_once $file_path; } } if (!class_exists($class_name)) { return null; } return new $class_name(); } /** * Get enabled storage locations * * @param array $storage_config Storage configuration * @return array Enabled storage locations */ private function get_enabled_storage_locations($storage_config = []) { $default_config = get_option('tigerstyle_life9_storage_locations', ['local' => true]); if (!empty($storage_config)) { $enabled_storages = $storage_config; } else { $enabled_storages = $default_config; } $result = []; foreach ($enabled_storages as $storage_type => $config) { if ($config === true || (is_array($config) && !empty($config))) { $result[$storage_type] = is_array($config) ? $config : []; } } return $result; } /** * Get available storage backends * * @return array Available backends */ public function get_available_backends() { return $this->backends; } /** * Get storage configuration for backend * * @param string $storage_type Storage type * @return array Storage configuration */ public function get_storage_config($storage_type) { $config = get_option("tigerstyle_life9_storage_{$storage_type}", []); // Decrypt sensitive fields if (!empty($config)) { $sensitive_fields = ['password', 'secret_key', 'access_key', 'client_secret']; foreach ($sensitive_fields as $field) { if (isset($config[$field]) && !empty($config[$field])) { $decrypted = $this->get_security()->decrypt($config[$field]); if ($decrypted !== false) { $config[$field] = $decrypted; } } } } return $config; } /** * Save storage configuration * * @param string $storage_type Storage type * @param array $config Configuration data * @return bool Success status */ public function save_storage_config($storage_type, $config) { try { // Validate storage type if (!isset($this->backends[$storage_type])) { throw new Exception('Invalid storage type'); } // Sanitize configuration $sanitizer = new TigerStyle_Life9_Sanitizer(); $clean_config = $sanitizer->sanitize_array($config); // Encrypt sensitive fields $sensitive_fields = ['password', 'secret_key', 'access_key', 'client_secret']; foreach ($sensitive_fields as $field) { if (isset($clean_config[$field]) && !empty($clean_config[$field])) { $encrypted = $this->get_security()->encrypt($clean_config[$field]); if ($encrypted !== false) { $clean_config[$field] = $encrypted; } } } // Save configuration $result = update_option("tigerstyle_life9_storage_{$storage_type}", $clean_config); $this->get_security()->log_security_event('storage_config_updated', [ 'storage_type' => $storage_type ]); return $result; } catch (Exception $e) { $this->log_error('Failed to save storage configuration', [ 'storage_type' => $storage_type, 'error' => $e->getMessage() ]); return false; } } /** * Get storage usage information * * @param string $storage_type Storage type * @param array $config Storage configuration * @return array Usage information */ public function get_storage_usage($storage_type, $config = []) { try { $backend = $this->get_storage_backend($storage_type); if (!$backend || !method_exists($backend, 'get_usage')) { return [ 'success' => false, 'error' => 'Usage information not available' ]; } $usage = $backend->get_usage($config); return [ 'success' => true, 'data' => $usage ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Clean up old backups from storage * * @param string $storage_type Storage type * @param int $retention_days Number of days to retain backups * @param array $config Storage configuration * @return array Cleanup results */ public function cleanup_old_backups($storage_type, $retention_days, $config = []) { try { $backend = $this->get_storage_backend($storage_type); if (!$backend || !method_exists($backend, 'cleanup_old_files')) { return [ 'success' => false, 'error' => 'Cleanup not supported' ]; } $cutoff_date = date('Y-m-d', strtotime("-{$retention_days} days")); $this->log_info("Cleaning up old backups from {$storage_type}", [ 'retention_days' => $retention_days, 'cutoff_date' => $cutoff_date ]); $result = $backend->cleanup_old_files($cutoff_date, $config); return [ 'success' => true, 'data' => $result ]; } catch (Exception $e) { $this->log_error("Failed to cleanup old backups from {$storage_type}", [ 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Log info message * * @param string $message Log message * @param array $context Additional context */ private function log_info($message, $context = []) { error_log("TigerStyle Life9 Storage [INFO]: {$message}"); } /** * Log error message * * @param string $message Log message * @param array $context Additional context */ private function log_error($message, $context = []) { error_log("TigerStyle Life9 Storage [ERROR]: {$message}"); } } /** * Abstract storage backend class * * Base class for all storage backends */ abstract class TigerStyle_Life9_Storage_Backend { /** * Store file to storage * * @param string $file_path Local file path * @param array $config Storage configuration * @return array Storage result */ abstract public function store($file_path, $config = []); /** * Retrieve file from storage * * @param string $remote_path Remote file path or ID * @param string $local_path Local destination path * @param array $config Storage configuration * @return bool Success status */ abstract public function retrieve($remote_path, $local_path, $config = []); /** * Delete file from storage * * @param string $remote_path Remote file path or ID * @param array $config Storage configuration * @return bool Success status */ abstract public function delete($remote_path, $config = []); /** * Test storage connection * * @param array $config Storage configuration * @return bool Connection status */ abstract public function test_connection($config = []); /** * Generate remote file path * * @param string $file_path Local file path * @return string Remote file path */ protected function generate_remote_path($file_path) { $filename = basename($file_path); $date_path = date('Y/m/d'); return "tigerstyle-life9/{$date_path}/{$filename}"; } /** * Validate configuration * * @param array $config Configuration to validate * @param array $required_fields Required configuration fields * @return bool Validation status */ protected function validate_config($config, $required_fields) { foreach ($required_fields as $field) { if (empty($config[$field])) { throw new Exception("Missing required configuration: {$field}"); } } return true; } }