$options Complete option value. * * @return bool */ public static function update_raw( array $options ): bool { self::$raw_write = true; try { return update_option( SMTP_Email::OPTION_NAME, $options ); } finally { self::$raw_write = false; } } /** * Option keys that only Pro's routing/failover/logging settings UI * ever writes. * * @return string[] */ protected function get_pro_only_option_keys(): array { return [ 'additional_profiles', 'routing', 'rules', 'delivery_fallback', 'enable_delivery_log', 'delivery_log_retention_days', ]; } /** * Constructor * * @param array $options Array of options. */ public function __construct( array $options ) { $this->options = $options; add_action( 'admin_init', [ $this, 'init' ] ); add_action( 'wp_ajax_adminoptim_smtp_email_test', [ $this, 'send_email_test' ] ); add_action( 'wp_ajax_adminoptim_smtp_send_email', [ $this, 'send_email' ] ); add_action( 'admin_notices', [ $this, 'maybe_show_auth_change_notice' ] ); add_action( 'admin_bar_menu', [ $this, 'add_admin_bar_quick_mail_link' ], 100 ); add_action( 'wp_dashboard_setup', [ $this, 'add_dashboard_email_widget' ] ); } /** * Start the engine running * * @return void */ public function init() { $this->settings_options = [ [ 'option_group' => SMTP_Email::OPTION_NAME, 'option_name' => SMTP_Email::OPTION_NAME, 'args' => [ 'sanitize_callback' => [ $this, 'sanitize_settings' ] ], ], ]; // The settings screen is rendered by hand (see render_settings_page()), so only the option itself is registered; no WP Settings API sections or fields. $this->setup(); } /** * Render Settings page * * @return void */ public function render_settings_page() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized via sanitize_key() inside sanitize_active_tab(). $active_tab = $this->sanitize_active_tab( $_GET['tab'] ?? 'settings' ); $send_prefill = 'send' === $active_tab ? $this->get_send_email_prefill() : []; // sanitize_settings() always writes smtp_provider, so its presence means the form has been saved. $smtp_saved = isset( $this->options['smtp_provider'] ); ?>
>
options ); ?> render_profiles_table( $this->options ); ?>

', '' ); echo wp_kses( $upgrade_message, 'a' ); ?>

>

render_editor_rows( $this->options ); ?> 'adminoptim-smtp-editor-submit' ] ); ?>
> options ); ?>

', '' ); echo wp_kses( $upgrade_message, 'a' ); ?>

render_routing_fields(); ?>

render_rule_fields(); ?>

>

render_send_email_fields( $send_prefill ); ?> render_send_email_shortcut_settings( $this->options ); ?>
>

', '' ); echo wp_kses( $upgrade_message, 'a' ); ?>

$options SMTP options. * * @return void */ private function render_profiles_table( array $options ) { $provider = $options['smtp_provider'] ?? 'generic'; $name = ! empty( $options['primary_profile_name'] ) ? $options['primary_profile_name'] : ( $this->get_provider_choices()[ $provider ] ?? __( 'Generic (Custom SMTP)', 'admin-optimizer' ) ); if ( $this->resets_on_delete_primary() ) { $delete_message = __( 'Delete this SMTP configuration and start over? The saved credentials will be removed.', 'admin-optimizer' ); } else { $next = reset( $options['additional_profiles'] ); /* translators: %s: name of the SMTP profile that becomes the primary. */ $delete_message = sprintf( __( 'Delete the primary SMTP configuration? “%s” becomes the primary profile.', 'admin-optimizer' ), ! empty( $next['name'] ) ? $next['name'] : __( 'The next profile', 'admin-optimizer' ) ); } /** * Filters the "Last Test" cell text for a profile row. * * Free keeps no test history; Pro answers this from its Delivery Log * (test emails are logged with the `smtp_test` source). * * @param string $label Plain text, escaped on output. Default ''. * @param string $profile_id Profile ID (`primary` for this row). */ $last_test = (string) apply_filters( 'adminoptim_smtp_profile_last_test', '', 'primary' ); ?>
get_provider_choices()[ $provider ] ?? __( 'Generic (Custom SMTP)', 'admin-optimizer' ) ); ?>
$options SMTP options. * * @return void */ private function render_editor_rows( array $options ) { // First run: start from the same defaults as a new additional profile (TLS, port 587, auth on). if ( ! isset( $options['smtp_provider'] ) ) { $options = array_merge( [ 'smtp_auth' => 1, 'smtp_secure' => 'tls', 'smtp_port' => 587 ], $options ); } $name = static function ( $key ) { return SMTP_Email::OPTION_NAME . '[editor][' . $key . ']'; }; /** * Fires before the first editor row. * * Free renders the "Profile Name" row itself just after this hook; * Pro can use it to add rows above that one. * * @param array $options Free's own SMTP options. */ do_action( 'adminoptim_smtp_primary_settings_rows_before', $options ); ?>

render_ses_region_select( $name( 'ses_region' ), 'smtp-ses-region', $options['smtp_ses_region'] ?? 'us-east-1' ); ?> render_encryption_select( $name( 'encryption' ), 'smtp-secure', $options['smtp_secure'] ?? 'none' ); ?> [ 'SMTP Server', 'text', 'smtp-server', 'smtp_server' ], 'port' => [ 'SMTP Port', 'number', 'smtp-port', 'smtp_port' ], 'username' => [ 'SMTP Username', 'text', 'smtp-username', 'smtp_username' ], 'password' => [ 'SMTP Password', 'password', 'smtp-password', 'smtp_password' ], 'from_name' => [ 'From Name', 'text', 'send-from', 'send_from', 'identity' ], 'from_email' => [ 'From Email', 'email', 'email-from', 'email_from', 'identity' ], 'reply_to' => [ 'Reply-To', 'email', 'reply-to', 'reply_to', 'identity' ], ]; ?> $field ) : ?> > > */ private function get_provider_choices() { return [ 'default_wp_mail' => __( 'Default WordPress Mail', 'admin-optimizer' ), 'generic' => __( 'Generic (Custom SMTP)', 'admin-optimizer' ), 'gmail' => __( 'Gmail', 'admin-optimizer' ), 'mailgun' => __( 'Mailgun', 'admin-optimizer' ), 'ses' => __( 'Amazon SES', 'admin-optimizer' ), 'sendgrid' => __( 'SendGrid', 'admin-optimizer' ), 'brevo' => __( 'Brevo (Sendinblue)', 'admin-optimizer' ), 'postmark' => __( 'Postmark', 'admin-optimizer' ), ]; } /** * Render SES region select. * * @param string $name Field name. * @param string $id Field ID. * @param string $selected Selected region. * * @return void */ private function render_ses_region_select( $name, $id, $selected ) { ?> */ private function get_ses_regions() { return [ 'us-east-1' => 'US East (N. Virginia)', 'us-east-2' => 'US East (Ohio)', 'us-west-1' => 'US West (N. California)', 'us-west-2' => 'US West (Oregon)', 'eu-west-1' => 'EU (Ireland)', 'eu-west-2' => 'EU (London)', 'eu-west-3' => 'EU (Paris)', 'eu-central-1' => 'EU (Frankfurt)', 'eu-north-1' => 'EU (Stockholm)', 'ap-southeast-1' => 'Asia Pacific (Singapore)', 'ap-southeast-2' => 'Asia Pacific (Sydney)', 'ap-northeast-1' => 'Asia Pacific (Tokyo)', 'ap-south-1' => 'Asia Pacific (Mumbai)', 'ca-central-1' => 'Canada (Central)', 'sa-east-1' => 'South America (Sao Paulo)', ]; } /** * Build an SMTP settings tab URL. * * @param string $tab Tab key. * * @return string */ private function get_tab_url( $tab ) { return SMTP_Email::get_module_admin_url( [ 'tab' => $this->sanitize_active_tab( $tab ) ] ); } /** * Sanitize the active SMTP settings tab. * * @param mixed $tab Active tab value. * * @return string */ private function sanitize_active_tab( $tab ) { $tab = sanitize_key( (string) $tab ); // The old "profiles" tab was merged into "settings"; it falls through to the default. return in_array( $tab, [ 'settings', 'routing', 'send', 'delivery_log' ], true ) ? $tab : 'settings'; } /** * Read sanitized composer prefill values from the current request. * * @return array */ private function get_send_email_prefill() { if ( ! current_user_can( 'manage_options' ) ) { return []; } $query_map = [ 'ao_email_to' => 'to', 'ao_email_cc' => 'cc', 'ao_email_bcc' => 'bcc', 'ao_email_subject' => 'subject', 'ao_email_message' => 'message', 'ao_email_context_type' => 'context_type', 'ao_email_context_id' => 'context_id', 'ao_email_return_url' => 'return_url', 'ao_email_profile' => 'profile_id', ]; $args = []; $has_input = false; foreach ( $query_map as $query_key => $arg_key ) { if ( isset( $_GET[ $query_key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $args[ $arg_key ] = wp_unslash( $_GET[ $query_key ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $has_input = true; } } // esc_url() (run on the composer URL wherever it's output) strips // encoded line breaks and HTML delimiters from href values, so a // multi-line or HTML body travels as base64 instead of plain text // — see adminoptim_email_compose_url() in functions.php. if ( isset( $_GET['ao_email_message_b64'] ) && is_scalar( $_GET['ao_email_message_b64'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $encoded_message = wp_unslash( (string) $_GET['ao_email_message_b64'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $encoded_message = strtr( $encoded_message, '-_', '+/' ); $padding_length = ( 4 - strlen( $encoded_message ) % 4 ) % 4; $decoded_message = base64_decode( $encoded_message . str_repeat( '=', $padding_length ), true ); if ( false !== $decoded_message ) { $args['message'] = $decoded_message; $has_input = true; } } if ( isset( $_GET['ao_email_html'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $args['html'] = ! empty( $_GET['ao_email_html'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $has_input = true; } if ( ! $has_input ) { return []; } return adminoptim_email_sanitize_compose_args( $args ); } /** * Build a compact context note for the composer. * * @param array $prefill Prefill data. * * @return string */ private function get_send_email_context_note( array $prefill ) { $context_type = isset( $prefill['context_type'] ) && is_scalar( $prefill['context_type'] ) ? sanitize_key( (string) $prefill['context_type'] ) : ''; $context_id = isset( $prefill['context_id'] ) && is_scalar( $prefill['context_id'] ) ? sanitize_text_field( (string) $prefill['context_id'] ) : ''; if ( '' === $context_type ) { return ''; } $labels = [ 'form_entry' => __( 'Form Entry', 'admin-optimizer' ), 'woocommerce_order' => __( 'WooCommerce Order', 'admin-optimizer' ), 'user' => __( 'User', 'admin-optimizer' ), ]; $label = $labels[ $context_type ] ?? ucwords( str_replace( '_', ' ', $context_type ) ); $suffix = '' !== $context_id ? ' #' . $context_id : ''; return sprintf( /* translators: %1$s: context label, %2$s: context ID. */ __( 'Composing email for %1$s%2$s.', 'admin-optimizer' ), $label, $suffix ); } /** * Render routing selects. * * @return void */ private function render_routing_fields() { $fields = [ 'default_profile' => __( 'Default / Unknown Emails', 'admin-optimizer' ), 'transactional_profile' => __( 'Transactional Emails', 'admin-optimizer' ), 'marketing_profile' => __( 'Marketing Emails', 'admin-optimizer' ), 'admin_profile' => __( 'Admin Notifications', 'admin-optimizer' ), 'password_reset_profile' => __( 'Password Reset Emails', 'admin-optimizer' ), 'new_user_profile' => __( 'New User Emails', 'admin-optimizer' ), 'form_notification_profile' => __( 'Form Builder Notifications', 'admin-optimizer' ), 'form_autoresponder_profile' => __( 'Form Builder Autoresponders', 'admin-optimizer' ), ]; if ( class_exists( 'WooCommerce' ) ) { $fields['woocommerce_profile'] = __( 'WooCommerce Emails', 'admin-optimizer' ); } ?> $label ) : ?>
$prefill Prefilled composer values. * * @return void */ private function render_send_email_fields( array $prefill = [] ) { $context_note = $this->get_send_email_context_note( $prefill ); $return_url = isset( $prefill['return_url'] ) ? (string) $prefill['return_url'] : ''; ?>

$options SMTP options. * * @return void */ private function render_send_email_shortcut_settings( array $options ) { ?>

options['add_quick_mail_admin_bar'] ) || ! current_user_can( 'manage_options' ) || ! is_admin_bar_showing() ) { return; } if ( ! is_object( $wp_admin_bar ) || ! method_exists( $wp_admin_bar, 'add_node' ) ) { return; } $wp_admin_bar->add_node( [ 'id' => 'adminoptim-smtp-quick-mail', 'title' => __( 'Quick Email', 'admin-optimizer' ), 'href' => $this->get_tab_url( 'send' ), ] ); } /** * Add dashboard email widget. * * @return void */ public function add_dashboard_email_widget() { if ( empty( $this->options['add_email_dashboard_widget'] ) || ! current_user_can( 'manage_options' ) ) { return; } wp_add_dashboard_widget( 'adminoptim_smtp_email_widget', __( 'Send Email', 'admin-optimizer' ), [ $this, 'render_dashboard_email_widget' ] ); } /** * Render dashboard email widget. * * @return void */ public function render_dashboard_email_widget() { ?>

$options Raw submitted options. * * @return array */ private function resolve_editor_input( array $options ) { $map = [ 'name' => 'primary_profile_name', 'provider' => 'smtp_provider', 'ses_region' => 'smtp_ses_region', 'host' => 'smtp_server', 'auth' => 'smtp_auth', 'encryption' => 'smtp_secure', 'port' => 'smtp_port', 'username' => 'smtp_username', 'password' => 'smtp_password', 'from_name' => 'send_from', 'from_email' => 'email_from', 'reply_to' => 'reply_to', 'auth_change_check' => 'auth_change_check', 'force_from' => 'force_from', ]; $editor = isset( $options['editor'] ) && is_array( $options['editor'] ) ? $options['editor'] : []; if ( isset( $editor['target'] ) && 'primary' === $editor['target'] ) { foreach ( $map as $from => $to ) { unset( $options[ $to ] ); if ( isset( $editor[ $from ] ) ) { $options[ $to ] = $editor[ $from ]; } } return $options; } $stored = get_option( SMTP_Email::OPTION_NAME, [] ); $stored = is_array( $stored ) ? $stored : []; foreach ( $map as $to ) { if ( ! isset( $options[ $to ] ) && isset( $stored[ $to ] ) ) { $options[ $to ] = $stored[ $to ]; } } return $options; } /** * Whether deleting the primary resets it to the first-run form. When * Pro is active and additional profiles exist, Pro promotes the next * profile instead (see SMTP_Email_Settings_Pro::sanitize_pro_fields()). * * @return bool */ private function resets_on_delete_primary() { return ! Module_Registry::is_pro_active() || empty( $this->options['additional_profiles'] ); } /** * Saved options with the primary connection, sender identity and * profile name removed, so the SMTP tab shows the first-run form again. * Everything else (shortcut toggles, Pro routing/logging) is kept. * * @return array */ private function reset_primary_settings() { $options = is_array( $this->options ) ? $this->options : []; foreach ( [ 'smtp_provider', 'smtp_ses_region', 'smtp_server', 'smtp_auth', 'smtp_secure', 'smtp_port', 'smtp_username', 'smtp_password', 'send_from', 'email_from', 'reply_to', 'primary_profile_name', 'force_from', 'auth_change_check', 'auth_fingerprint' ] as $key ) { unset( $options[ $key ] ); } return $options; } /** * Callback function to sanitize user options * * @param array $options User options. * * @return array */ public function sanitize_settings( $options ) { // An internal write of already-validated settings (see update_raw()). if ( self::$raw_write ) { return $options; } $sanitized_options = []; $protocol = [ 'none', 'tls', 'ssl' ]; $allowed_providers = array_keys( $this->get_provider_choices() ); $allowed_regions = [ 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-central-1', 'eu-north-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'ap-south-1', 'ca-central-1', 'sa-east-1' ]; if ( is_array( $options ) && Module_Registry::is_pro_active() ) { /** * Lets Pro save a part of the settings on its own (the Delivery * Log tab's form) without rebuilding the whole option from a * partial submission. Return the complete option array to use it * as the saved value, or null to continue normally. * * @param array|null $result Null by default. * @param array $options Raw submitted options. */ $short_circuit = apply_filters( 'adminoptim_smtp_sanitize_settings_short_circuit', null, $options ); if ( is_array( $short_circuit ) ) { return $short_circuit; } } if ( is_array( $options ) && ! empty( $options['delete_primary'] ) && $this->resets_on_delete_primary() ) { return $this->reset_primary_settings(); } if ( is_array( $options ) ) { $options = $this->resolve_editor_input( $options ); // Only when submitted: a save that doesn't touch the primary (e.g. the Delivery Log form on a fresh install) must not mark it as configured. if ( isset( $options['smtp_provider'] ) ) { $sanitized_options['smtp_provider'] = in_array( $options['smtp_provider'], $allowed_providers, true ) ? $options['smtp_provider'] : 'generic'; } if ( isset( $options['smtp_ses_region'] ) ) { $sanitized_options['smtp_ses_region'] = in_array( $options['smtp_ses_region'], $allowed_regions, true ) ? $options['smtp_ses_region'] : 'us-east-1'; } if ( isset( $options['primary_profile_name'] ) ) { $sanitized_options['primary_profile_name'] = sanitize_text_field( $options['primary_profile_name'] ); } if ( isset( $options['smtp_server'] ) ) { $sanitized_options['smtp_server'] = sanitize_text_field( $options['smtp_server'] ); } if ( isset( $options['smtp_auth'] ) ) { $sanitized_options['smtp_auth'] = 1; } if ( isset( $options['smtp_secure'] ) ) { $sanitized_options['smtp_secure'] = in_array( $options['smtp_secure'], $protocol, true ) ? sanitize_text_field( $options['smtp_secure'] ) : 'none'; } if ( isset( $options['smtp_port'] ) ) { $sanitized_options['smtp_port'] = absint( $options['smtp_port'] ); } if ( isset( $options['smtp_username'] ) ) { $sanitized_options['smtp_username'] = sanitize_text_field( $options['smtp_username'] ); } $password_reentered = false; if ( isset( $options['smtp_password'] ) ) { $raw_password = SMTP_Email::sanitize_password( $options['smtp_password'] ); $existing_options = get_option( SMTP_Email::OPTION_NAME, [] ); if ( '' === $raw_password ) { if ( ! empty( $existing_options['smtp_password'] ) ) { $sanitized_options['smtp_password'] = $existing_options['smtp_password']; } } elseif ( SMTP_Email::is_stored_password( $raw_password ) ) { // An already-stored value (an "enc:" value or a reference) is carried forward, not typed in. $sanitized_options['smtp_password'] = $raw_password; } else { $sanitized_options['smtp_password'] = SMTP_Email::store_password( $raw_password, 'primary', $existing_options['smtp_password'] ?? '' ); $password_reentered = true; } } $sanitized_options['auth_change_check'] = ! empty( $options['auth_change_check'] ) ? 1 : 0; if ( isset( $options['send_from'] ) ) { $sanitized_options['send_from'] = sanitize_text_field( $options['send_from'] ); } if ( isset( $options['email_from'] ) ) { $sanitized_options['email_from'] = sanitize_email( $options['email_from'] ); } if ( isset( $options['reply_to'] ) ) { $sanitized_options['reply_to'] = sanitize_email( $options['reply_to'] ); } $sanitized_options['force_from'] = ! empty( $options['force_from'] ) ? 1 : 0; $sanitized_options['add_quick_mail_admin_bar'] = ! empty( $options['add_quick_mail_admin_bar'] ) ? 1 : 0; $sanitized_options['add_email_dashboard_widget'] = ! empty( $options['add_email_dashboard_widget'] ) ? 1 : 0; // Only re-stamp the fingerprint when the password was typed in again (or none is stamped yet); otherwise an unrelated save would silence the AUTH-key-change warning while the saved password is still unreadable. $stored_options = get_option( SMTP_Email::OPTION_NAME, [] ); $stored_fingerprint = is_array( $stored_options ) ? (string) ( $stored_options['auth_fingerprint'] ?? '' ) : ''; $sanitized_options['auth_fingerprint'] = ( $password_reentered || '' === $stored_fingerprint ) ? SMTP_Email::auth_fingerprint() : $stored_fingerprint; if ( Module_Registry::is_pro_active() ) { /** * Filters in sanitized values for the Pro-owned SMTP * settings (additional profiles, routing, advanced rules, * delivery fallback) submitted from this same form. * * Free has no notion of these fields' shape; re-checking * is_pro_active() here (rather than trusting that the * submitted array simply contains them) is what keeps a * crafted POST from writing Pro-owned data while Pro is * inactive. Pro's callback receives the raw submitted * $options and Free's own already-sanitized * $sanitized_options, and must return only the keys it * owns (see get_pro_only_option_keys()). * * @param array $pro_sanitized Empty by default. * @param array $options Raw submitted options. * @param array $sanitized_options Free's own sanitized values so far. */ $sanitized_options = array_merge( $sanitized_options, apply_filters( 'adminoptim_smtp_sanitize_pro_fields', [], $options, $sanitized_options ) ); } } return $this->carry_forward_inactive_pro_keys( $sanitized_options ); } /** * Show a warning if the auth keys/salts changed. * * @return void */ public function maybe_show_auth_change_notice() { if ( ! current_user_can( 'manage_options' ) ) { return; } $this->render_auth_change_notice(); } /** * Render the auth change notice if needed. * * @return void */ private function render_auth_change_notice() { $options = get_option( SMTP_Email::OPTION_NAME, [] ); if ( ! SMTP_Email::has_auth_changed( $options ) ) { return; } $settings_url = SMTP_Email::get_module_admin_url(); ?>

'error', ] ) ) ); } check_ajax_referer( 'test_smtp_email', 'nonce' ); $response = []; $success = false; $profile_id = ''; $email_to = isset( $_POST['email_to'] ) && is_email( wp_unslash( $_POST['email_to'] ) ) ? sanitize_email( wp_unslash( $_POST['email_to'] ) ) : ''; if ( ! empty( $email_to ) ) { $subject = get_bloginfo( 'name' ) . ' Email Test'; $body = "Congrats, test email was sent successfully. \n\nYour Faithful servant"; /** * Optional SMTP profile ID to send this test through, submitted * only by Pro's per-profile "Send test email" control. Free * has no notion of profiles; it only relays the value through * the two narrow actions below, which are no-ops unless Pro * hooks them. */ $profile_id = isset( $_POST['profile_id'] ) && is_scalar( $_POST['profile_id'] ) ? sanitize_text_field( wp_unslash( $_POST['profile_id'] ) ) : ''; if ( '' !== $profile_id ) { do_action( 'adminoptim_smtp_push_profile', $profile_id ); } try { // Tagged so Pro's Delivery Log can show it as the profile's last test (a no-op without Pro). $sent = adminoptim_smtp_with_source( 'smtp_test', static function () use ( $email_to, $subject, $body ) { return wp_mail( $email_to, $subject, $body ); } ); } finally { if ( '' !== $profile_id ) { do_action( 'adminoptim_smtp_pop_profile' ); } } if ( $sent ) { $success = true; } else { $response['message'] = 'Sending is not successful.'; } } else { $response['message'] = 'Email address is empty.'; } if ( $success ) { wp_send_json_success( $response ); } else { wp_send_json_error( $response ); } } /** * Send a custom admin email. * * @return void */ public function send_email() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => __( 'Unauthorized action.', 'admin-optimizer' ) ] ); } check_ajax_referer( 'adminoptim_smtp_send_email', 'nonce' ); $return_url = ''; if ( isset( $_POST['return_url'] ) ) { $raw_return_url = wp_unslash( $_POST['return_url'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $raw_return_url = is_scalar( $raw_return_url ) ? esc_url_raw( (string) $raw_return_url ) : ''; $return_url = '' !== $raw_return_url ? wp_validate_redirect( $raw_return_url, '' ) : ''; } // adminoptim_email_send() applies this itself (via // adminoptim_smtp_with_profile(), a no-op unless Pro hooks it) once // it sanitizes the 'profile_id' key below. $profile_id = isset( $_POST['profile_id'] ) && is_scalar( $_POST['profile_id'] ) ? wp_unslash( $_POST['profile_id'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $result = adminoptim_email_send( [ 'to' => isset( $_POST['to'] ) ? wp_unslash( $_POST['to'] ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 'cc' => isset( $_POST['cc'] ) ? wp_unslash( $_POST['cc'] ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 'bcc' => isset( $_POST['bcc'] ) ? wp_unslash( $_POST['bcc'] ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 'subject' => isset( $_POST['subject'] ) ? wp_unslash( $_POST['subject'] ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 'message' => isset( $_POST['message'] ) ? wp_unslash( $_POST['message'] ) : '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 'html' => ! empty( $_POST['send_as_html'] ), 'profile_id' => $profile_id, ] ); if ( true === $result ) { wp_send_json_success( [ 'message' => __( 'Delivery successful.', 'admin-optimizer' ), 'return_url' => $return_url, ] ); } $error_message = is_wp_error( $result ) ? $result->get_error_message() : __( 'wp_mail returned false.', 'admin-optimizer' ); wp_send_json_error( [ 'message' => __( 'Email delivery failed.', 'admin-optimizer' ), 'error' => $error_message, ] ); } }