Logs_List::MENU_SLUG, 'reset' => 'true', ), \admin_url( 'admin.php' ) ) ); exit; } /** * Handle the export-settings request. * * @return void * * @since 3.4.0 */ public static function handle_export() { if ( ! isset( $_REQUEST['export-settings'] ) || ! \check_admin_referer( 'export-plugin-settings', 'export_nonce' ) ) { return; } if ( ! \current_user_can( 'manage_options' ) ) { \wp_die( \esc_html__( 'Insufficient permissions.', '0-day-analytics' ) ); } global $wpdb; $stored_options = $wpdb->get_results( $wpdb->prepare( 'SELECT option_name, option_value FROM ' . $wpdb->options . ' WHERE option_name = %s', \ADVAN_SETTINGS_NAME ), ARRAY_A ); header( 'Cache-Control: public, must-revalidate' ); header( 'Pragma: hack' ); header( 'Content-Type: text/plain' ); header( 'Content-Disposition: attachment; filename="' . ADVAN_TEXTDOMAIN . '-options-' . gmdate( 'dMy' ) . '.dat"' ); if ( ! empty( $stored_options ) && isset( $stored_options[0]['option_value'] ) ) { $data = json_decode( $stored_options[0]['option_value'], true ); if ( json_last_error() !== JSON_ERROR_NONE ) { $data = unserialize( $stored_options[0]['option_value'], array( 'allowed_classes' => false ) ); } if ( is_array( $data ) ) { self::mask_sensitive_fields( $data ); } if ( is_array( $data ) ) { $data['plugin_version'] = \ADVAN_VERSION; } echo \wp_json_encode( $data ); } else { echo \wp_json_encode( array() ); } die(); } /** * Handle the import (file upload) request. * * @return void * * @since 3.4.0 */ public static function handle_import() { if ( ! isset( $_FILES[ Settings::SETTINGS_FILE_FIELD ] ) || ! \check_admin_referer( 'aadvana-plugin-data', 'aadvana-security' ) ) { return; } $options = array(); if ( isset( $_FILES ) && isset( $_FILES[ Settings::SETTINGS_FILE_FIELD ] ) && isset( $_FILES[ Settings::SETTINGS_FILE_FIELD ]['error'] ) && ! $_FILES[ Settings::SETTINGS_FILE_FIELD ]['error'] > 0 && isset( $_FILES[ Settings::SETTINGS_FILE_FIELD ]['tmp_name'] ) ) { \add_filter( 'upload_mimes', function( $mimes ) { $mimes['dat'] = 'application/json'; return $mimes; } ); // Basic size limit (50KB) to avoid large payload abuse. if ( isset( $_FILES[ Settings::SETTINGS_FILE_FIELD ]['size'] ) && (int) $_FILES[ Settings::SETTINGS_FILE_FIELD ]['size'] > 51200 ) { $_FILES[ Settings::SETTINGS_FILE_FIELD ] = array(); } else { $ft = \wp_check_filetype_and_ext( $_FILES[ Settings::SETTINGS_FILE_FIELD ]['tmp_name'], $_FILES[ Settings::SETTINGS_FILE_FIELD ]['name'], array( 'json' => 'application/json', 'txt' => 'text/plain', 'dat' => 'application/json', ) ); if ( empty( $ft['ext'] ) || ! in_array( $ft['ext'], array( 'json', 'txt', 'dat' ), true ) ) { $_FILES[ Settings::SETTINGS_FILE_FIELD ] = array(); } else { $path = \sanitize_text_field( \wp_unslash( $_FILES[ Settings::SETTINGS_FILE_FIELD ]['tmp_name'] ) ); $content = ''; // Try WP_Filesystem first; fall back to native PHP on failure. try { global $wp_filesystem; if ( ! isset( $wp_filesystem ) || ! is_object( $wp_filesystem ) ) { \WP_Filesystem(); } if ( isset( $wp_filesystem ) && is_object( $wp_filesystem ) && $wp_filesystem->exists( $path ) ) { $tmp = $wp_filesystem->get_contents( $path ); if ( false !== $tmp && '' !== $tmp ) { $content = (string) $tmp; } } } catch ( \Throwable $e ) { // Ignore and fall back below. } if ( '' === $content && \is_readable( $path ) ) { $content = @file_get_contents( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents } $options = array(); if ( is_string( $content ) && '' !== $content ) { $options = json_decode( $content, true ); } if ( ! is_array( $options ) ) { $options = array(); } if ( ! empty( $options ) ) { // Handle version migration from imported payload. if ( isset( $options['plugin_version'] ) && is_string( $options['plugin_version'] ) && '' !== trim( $options['plugin_version'] ) ) { try { $import_version_norm = Abstract_Migration::normalize_version( (string) $options['plugin_version'] ); $current_version_norm = Abstract_Migration::normalize_version( (string) \ADVAN_VERSION ); if ( (int) $import_version_norm < (int) $current_version_norm ) { \update_option( Settings::SETTINGS_VERSION, \sanitize_text_field( $options['plugin_version'] ) ); } } catch ( \Throwable $e ) { // Ignore malformed version strings. } unset( $options['plugin_version'] ); } \remove_filter( 'sanitize_option_' . ADVAN_SETTINGS_NAME, array( Settings::class, 'collect_and_sanitize_options' ) ); \update_option( ADVAN_SETTINGS_NAME, Settings_Sanitizer::collect_and_sanitize( $options, true ) ); } } } } \wp_safe_redirect( \add_query_arg( array( 'page' => Logs_List::MENU_SLUG, 'import' => 'true', ), \admin_url( 'admin.php' ) ) ); exit; } /** * Process settings page requests (reset, export, import). * Called from add_options_page when on a plugin settings page. * * @return void * * @since 3.4.0 */ public static function process_requests() { if ( isset( $_REQUEST['reset-settings'] ) ) { self::handle_reset(); } elseif ( isset( $_REQUEST['export-settings'] ) ) { self::handle_export(); } elseif ( isset( $_FILES[ Settings::SETTINGS_FILE_FIELD ] ) ) { self::handle_import(); } } /** * Mask sensitive fields in an associative array using dot-notated paths. * * @param array $data Data to process (modified in place). * * @return void * * @since 3.3.2 */ public static function mask_sensitive_fields( array &$data ): void { if ( empty( $data ) || ! is_array( $data ) ) { return; } foreach ( self::SENSITIVE_EXPORT_PATHS as $path ) { if ( ! is_string( $path ) || '' === $path ) { continue; } $segments = explode( '.', $path ); $ref =& $data; $found = true; foreach ( $segments as $seg ) { if ( is_array( $ref ) && array_key_exists( $seg, $ref ) ) { $ref =& $ref[ $seg ]; } else { $found = false; break; } } if ( $found ) { $ref = '***'; } } } } }