usage = new QuickConnectUsage(); } /** * Register hooks. * * @since 4.8.0 */ public function hooks() { add_action( 'wp_ajax_wp_mail_smtp_sendlayer_connect', [ $this, 'ajax_init_connect' ] ); add_action( 'init', [ $this, 'handle_ownership_verification_callback' ] ); add_action( 'admin_init', [ $this, 'handle_auth_complete' ] ); add_action( 'admin_init', [ $this, 'display_notice' ] ); add_action( 'admin_init', [ $this, 'handle_disconnect' ] ); add_action( 'wp_ajax_wp_mail_smtp_sendlayer_disconnect', [ $this, 'ajax_disconnect' ] ); add_action( 'wp_mail_smtp_admin_connection_settings_display_after_from_email_setting_row', [ $this, 'display_from_email_field' ], 10, 3 ); add_filter( 'wp_mail_smtp_options_get', [ $this, 'filter_option' ], 10, 3 ); add_action( 'wp_mail_smtp_admin_pages_before_content', [ $this, 'display_sendlayer_education_banner' ] ); add_filter( 'wp_mail_smtp_admin_connection_settings_process_data', [ $this, 'process_settings_data' ], 10, 2 ); $this->usage->hooks(); } /** * AJAX handler: Initiate the connect session with the marketing site. * * Called when the "Connect to SendLayer" button is clicked. * * @since 4.8.0 */ public function ajax_init_connect() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded // Verify nonce. check_ajax_referer( 'wp-mail-smtp-sendlayer-connect', 'nonce' ); // Check for permissions. if ( ! current_user_can( wp_mail_smtp()->get_capability_manage_global_options() ) ) { wp_send_json_error( [ 'message' => esc_html__( 'You do not have permission to perform this action.', 'wp-mail-smtp' ), 'error_code' => 'plugin.init_connect.permission_denied', ] ); } // Accept the return URL from the initiator page (settings, wizard, additional connection). // This is where the user ends up after the flow completes. if ( empty( $_POST['return_url'] ) ) { wp_send_json_error( [ 'message' => $this->get_generic_error_message(), 'error_code' => 'plugin.init_connect.missing_return_url', ] ); } // Validate it's a local URL to prevent open redirect. $return_url = wp_validate_redirect( esc_url_raw( wp_unslash( $_POST['return_url'] ) ), false ); if ( ! $return_url ) { wp_send_json_error( [ 'message' => $this->get_generic_error_message(), 'error_code' => 'plugin.init_connect.invalid_return_url', ] ); } $connection_id = ! empty( $_POST['connection_id'] ) ? sanitize_key( $_POST['connection_id'] ) : ''; // Optional mode flag. Currently only `backup_mailer` is supported — the // return handler will create a new additional connection and assign it // as the backup connection in one OAuth round-trip. $mode = ! empty( $_POST['connect_args']['mode'] ) ? sanitize_key( $_POST['connect_args']['mode'] ) : ''; $allowed_modes = [ 'backup_mailer' ]; $mode = in_array( $mode, $allowed_modes, true ) ? $mode : ''; // Build the redirect URL on the general settings page. // The auth handler always fires here; return_url is the final clean destination. $redirect_args = [ 'wp_mail_smtp_sendlayer_quick_connect_auth_complete' => 1, 'nonce' => wp_create_nonce( 'wp_mail_smtp_sendlayer_quick_connect' ), 'return_url' => rawurlencode( $return_url ), ]; if ( ! empty( $connection_id ) ) { $redirect_args['connection_id'] = $connection_id; } if ( ! empty( $mode ) ) { $redirect_args['mode'] = $mode; } $redirect_url = add_query_arg( $redirect_args, wp_mail_smtp()->get_admin()->get_admin_page_url() ); // Generate a challenge secret for HMAC-based ownership verification. $challenge_secret = bin2hex( random_bytes( 16 ) ); set_transient( 'wp_mail_smtp_sendlayer_quick_connect_challenge_secret', $challenge_secret, 300 ); // POST to the marketing site start-session endpoint. $response = wp_remote_post( $this->get_marketing_site_url() . '/wp-json/smtp-plugin-connect/v1/start-session', [ 'timeout' => 30, 'body' => [ 'site_url' => $this->get_site_url(), 'return_url' => $redirect_url, 'challenge_secret' => $challenge_secret, 'source' => 'wpms', ], ] ); if ( is_wp_error( $response ) ) { $error_code = 'plugin.init_connect.start_session_request_to_site.connection_failed'; DebugEvents::add( 'SendLayer Quick Connect: ' . $error_code . ' — ' . $response->get_error_message() ); wp_send_json_error( [ 'message' => $this->get_generic_error_message(), 'error_code' => $error_code, ] ); } $response_code = wp_remote_retrieve_response_code( $response ); $response_body = json_decode( wp_remote_retrieve_body( $response ), true ); // Non-200 response. if ( $response_code !== 200 ) { // Expected error — site returned a parseable error code. $error_code = ! empty( $response_body['code'] ) ? $this->sanitize_error_code( $response_body['code'] ) : 'plugin.init_connect.start_session_request_to_site.unexpected_error_response'; DebugEvents::add( 'SendLayer Quick Connect: ' . $error_code . ' — HTTP ' . $response_code ); wp_send_json_error( [ 'message' => $this->get_generic_error_message(), 'error_code' => $error_code, ] ); } // Unexpected success — 200 but missing expected data. if ( empty( $response_body['session_id'] ) ) { $error_code = 'plugin.init_connect.start_session_request_to_site.unexpected_success_response'; DebugEvents::add( 'SendLayer Quick Connect: ' . $error_code . ' — missing session_id' ); wp_send_json_error( [ 'message' => $this->get_generic_error_message(), 'error_code' => $error_code, ] ); } $session_id = sanitize_text_field( $response_body['session_id'] ); $redirect_url = add_query_arg( 'session', $session_id, $this->get_marketing_site_url() . '/smtp-plugin-connect' ); // Add UTM parameters to the redirect URL for tracking which button initiated the flow. $utm_content = ! empty( $_POST['connect_args']['utm_content'] ) ? sanitize_text_field( wp_unslash( $_POST['connect_args']['utm_content'] ) ) : 'Quick Connect'; // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound $redirect_url = wp_mail_smtp()->get_utm_url( $redirect_url, [ 'source' => 'wpmailsmtpplugin', 'medium' => 'WordPress', 'content' => $utm_content ] ); wp_send_json_success( [ 'redirect_url' => $redirect_url, ] ); } /** * Handle the ownership verification callback. * * The marketing site sends a server-to-server GET request with a `nonce` * query parameter during the start-session process. We compute an * HMAC-SHA256 signature using the stored challenge secret and return it * for verification. * * Listens for the `wp_mail_smtp_sendlayer_quick_connect_verify` GET parameter. * * @since 4.8.0 */ public function handle_ownership_verification_callback() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! isset( $_GET['wp_mail_smtp_sendlayer_quick_connect_verify'] ) ) { return; } // Read the nonce from the GET query parameter. // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( empty( $_GET['nonce'] ) ) { wp_send_json_error( [ 'message' => 'Missing nonce.', 'code' => 'missing_nonce', ], 400 ); } // Retrieve the stored challenge secret. $challenge_secret = get_transient( 'wp_mail_smtp_sendlayer_quick_connect_challenge_secret' ); if ( empty( $challenge_secret ) ) { wp_send_json_error( [ 'message' => 'No active verification session.', 'code' => 'no_active_session', ], 400 ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $nonce = sanitize_text_field( wp_unslash( $_GET['nonce'] ) ); $signature = hash_hmac( 'sha256', $nonce, $challenge_secret ); // Delete the transient after use (single-use). delete_transient( 'wp_mail_smtp_sendlayer_quick_connect_challenge_secret' ); wp_send_json( [ 'signature' => $signature ] ); } /** * Handle the return from the marketing site signup flow. * * Handles both success (`_auth_complete`) and error (`_auth_error`) redirects. * On success, exchanges the code for an API key, stores it, and redirects. * On error, forwards the error code to the notice display via redirect. * * @since 4.8.0 */ public function handle_auth_complete() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded, Generic.Metrics.CyclomaticComplexity.TooHigh // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( empty( $_GET['wp_mail_smtp_sendlayer_quick_connect_auth_complete'] ) || ! wp_mail_smtp()->get_admin()->is_admin_page() ) { return; } // Resolve the return URL early so every exit path redirects to it. // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $return_url = ! empty( $_GET['return_url'] ) ? wp_validate_redirect( esc_url_raw( wp_unslash( $_GET['return_url'] ) ), wp_mail_smtp()->get_admin()->get_admin_page_url() ) : wp_mail_smtp()->get_admin()->get_admin_page_url(); if ( ! current_user_can( wp_mail_smtp()->get_capability_manage_global_options() ) ) { $this->redirect_with_result( $return_url, 'plugin.return.permission_denied' ); } // Verify the nonce that was appended to the return URL during session init. // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'wp_mail_smtp_sendlayer_quick_connect' ) ) { $this->redirect_with_result( $return_url, 'plugin.return.invalid_nonce' ); } if ( ! empty( $_GET['exit'] ) ) { wp_safe_redirect( $return_url ); exit; } // If the marketing site redirected with an error, forward to the notice display. if ( ! empty( $_GET['error'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $error_code = $this->sanitize_error_code( wp_unslash( $_GET['error'] ) ); $this->redirect_with_result( $return_url, $error_code ); } $exchange_code = isset( $_GET['exchange_code'] ) ? sanitize_text_field( wp_unslash( $_GET['exchange_code'] ) ) : ''; if ( empty( $exchange_code ) ) { $this->redirect_with_result( $return_url, 'plugin.return.missing_exchange_code' ); } // POST to the marketing site REST API to retrieve connection details. $response = wp_remote_post( $this->get_marketing_site_url() . '/wp-json/smtp-plugin-connect/v1/connection', [ 'timeout' => 30, 'body' => [ 'exchange_code' => $exchange_code, 'source' => 'wpms', ], ] ); if ( is_wp_error( $response ) ) { $this->redirect_with_result( $return_url, 'plugin.return.exchange_request_to_site.connection_failed' ); } $response_code = wp_remote_retrieve_response_code( $response ); $response_body = json_decode( wp_remote_retrieve_body( $response ), true ); // Non-200 response. if ( $response_code !== 200 ) { // Expected error — site returned a parseable error code. $error_code = ! empty( $response_body['code'] ) ? $this->sanitize_error_code( $response_body['code'] ) : 'plugin.return.exchange_request_to_site.unexpected_error_response'; $this->redirect_with_result( $return_url, $error_code ); } // Unexpected success — 200 but missing expected data. if ( empty( $response_body['api_key'] ) ) { $this->redirect_with_result( $return_url, 'plugin.return.exchange_request_to_site.unexpected_success_response' ); } $api_key = sanitize_text_field( $response_body['api_key'] ); $sender_domain = ! empty( $response_body['sender_domain'] ) ? sanitize_text_field( $response_body['sender_domain'] ) : ''; $is_shared_domain = ! empty( $response_body['is_shared_domain'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $mode = ! empty( $_GET['mode'] ) ? sanitize_key( $_GET['mode'] ) : ''; $connection = wp_mail_smtp()->get_connections_manager()->get_primary_connection(); if ( $mode === 'backup_mailer' && wp_mail_smtp()->is_pro() ) { $new_connection_id = uniqid(); $connection_options = new ConnectionOptions( $new_connection_id ); $connection_options->set( [ 'connection' => [ 'name' => __( 'Backup', 'wp-mail-smtp' ), ], ] ); $connection = new AdditionalConnection( $new_connection_id ); } else { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $connection_id = ! empty( $_GET['connection_id'] ) ? sanitize_key( $_GET['connection_id'] ) : ''; if ( ! empty( $connection_id ) && wp_mail_smtp()->is_pro() ) { $connection = wp_mail_smtp()->get_connections_manager()->get_connection( $connection_id, false ); } } if ( $connection === false ) { $this->redirect_with_result( $return_url, 'plugin.return.invalid_connection' ); } // Store the API key using the connection's Options. $options = $connection->get_options(); $all_opt = $options->get_all_raw(); $all_opt['mail']['mailer'] = 'sendlayer'; $all_opt['sendlayer']['api_key'] = $api_key; $all_opt['sendlayer']['quick_connect'] = true; $all_opt['sendlayer']['is_shared_domain'] = $is_shared_domain; $all_opt['sendlayer']['free_upgrade_url'] = ! empty( $response_body['free_upgrade_url'] ) ? esc_url_raw( $response_body['free_upgrade_url'] ) : ''; if ( $mode === 'backup_mailer' && wp_mail_smtp()->is_pro() ) { $all_opt['mail']['from_name'] = (string) Options::init()->get( 'mail', 'from_name' ); $all_opt['mail']['from_email'] = (string) Options::init()->get( 'mail', 'from_email' ); } // Store the sender domain and configure From Email for shared domains. if ( ! empty( $sender_domain ) ) { $all_opt['sendlayer']['sender_domain'] = $sender_domain; $current_from = ! empty( $all_opt['mail']['from_email'] ) ? $all_opt['mail']['from_email'] : ''; $all_opt['mail']['from_email'] = $this->build_from_email( $current_from, $sender_domain ); $all_opt['mail']['from_email_force'] = true; } $options->set( $all_opt ); if ( $mode === 'backup_mailer' && wp_mail_smtp()->is_pro() ) { Options::init()->set( [ 'backup_connection' => [ 'connection_id' => $connection->get_id(), ], ], false, false ); $this->redirect_with_result( $return_url, 'backup_success' ); } $this->usage->reset(); $this->redirect_with_result( $return_url, 'success' ); } /** * Display admin notices for SendLayer Quick Connect and disconnect results. * * Reads result query params set by handle_auth_complete() and handle_disconnect() * and renders the appropriate success or error notice. * * @since 4.8.0 */ public function display_notice() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh if ( ! wp_mail_smtp()->get_admin()->is_admin_page() ) { return; } // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $disconnect_result = isset( $_GET['sendlayer_quick_connect_disconnect_result'] ) ? $this->sanitize_error_code( wp_unslash( $_GET['sendlayer_quick_connect_disconnect_result'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( $disconnect_result === 'success' ) { WP::add_admin_notice( esc_html__( 'SendLayer disconnected successfully.', 'wp-mail-smtp' ), WP::ADMIN_NOTICE_SUCCESS ); return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( empty( $_GET['sendlayer_quick_connect_result'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $result = $this->sanitize_error_code( wp_unslash( $_GET['sendlayer_quick_connect_result'] ) ); // Success — show admin notice banner. if ( $result === 'success' ) { WP::add_admin_notice( esc_html__( 'SendLayer connected successfully! You can now send emails through SendLayer.', 'wp-mail-smtp' ), WP::ADMIN_NOTICE_SUCCESS ); return; } // Backup-mailer mode success. if ( $result === 'backup_success' ) { WP::add_admin_notice( esc_html__( 'SendLayer is now set up as your Backup Connection. Emails that fail to send with your primary connection will be sent via SendLayer.', 'wp-mail-smtp' ), WP::ADMIN_NOTICE_SUCCESS ); return; } // Errors — show admin notice with error code box. $actionable_messages = [ 'plugin.return.permission_denied' => esc_html__( 'SendLayer connection failed. You do not have permission to perform this action.', 'wp-mail-smtp' ), 'plugin.return.invalid_nonce' => esc_html__( 'SendLayer connection failed. Your session has expired. Please try again.', 'wp-mail-smtp' ), 'site.exchange.rate_limit' => esc_html__( 'SendLayer connection failed. Too many attempts. Please wait a few minutes and try again.', 'wp-mail-smtp' ), 'site.exchange.code_expired' => esc_html__( 'SendLayer connection failed. Your connection code has expired. Please try again.', 'wp-mail-smtp' ), ]; $message = isset( $actionable_messages[ $result ] ) ? $actionable_messages[ $result ] : $this->get_generic_error_message(); WP::add_admin_notice( $message, WP::ADMIN_NOTICE_ERROR, true, '', $result ); } /** * Handle SendLayer disconnect action. * * Clears the API key, quick connect flag, and sender domain when the user * clicks "Disconnect" via a nonce-protected URL, then redirects back to * the settings page without the nonce query parameter. * * @since 4.8.0 */ public function handle_disconnect() { if ( ! isset( $_GET['sendlayer_quick_connect_disconnect_nonce'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended ! wp_mail_smtp()->get_admin()->is_admin_page() ) { return; } if ( ! current_user_can( wp_mail_smtp()->get_capability_manage_global_options() ) ) { WP::add_admin_notice( esc_html__( 'SendLayer disconnect failed. You do not have permission to perform this action.', 'wp-mail-smtp' ), WP::ADMIN_NOTICE_ERROR ); return; } if ( ! wp_verify_nonce( sanitize_key( $_GET['sendlayer_quick_connect_disconnect_nonce'] ), 'sendlayer_quick_connect_disconnect' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended WP::add_admin_notice( esc_html__( 'SendLayer disconnect failed. The nonce is invalid. Please try again.', 'wp-mail-smtp' ), WP::ADMIN_NOTICE_ERROR ); return; } $connection = wp_mail_smtp()->get_connections_manager()->get_primary_connection(); $options = $connection->get_options(); $all_opt = $options->get_all_raw(); $all_opt['sendlayer']['api_key'] = ''; unset( $all_opt['sendlayer']['quick_connect'] ); unset( $all_opt['sendlayer']['is_shared_domain'] ); unset( $all_opt['sendlayer']['sender_domain'] ); $options->set( $all_opt ); $this->usage->reset(); wp_safe_redirect( add_query_arg( 'sendlayer_quick_connect_disconnect_result', 'success', remove_query_arg( 'sendlayer_quick_connect_disconnect_nonce' ) ) ); exit; } /** * AJAX handler: Disconnect SendLayer quick connect. * * Clears the API key, quick connect flag, shared domain flag, and sender domain. * * @since 4.8.0 */ public function ajax_disconnect() { check_ajax_referer( 'wp-mail-smtp-sendlayer-connect', 'nonce' ); if ( ! current_user_can( wp_mail_smtp()->get_capability_manage_global_options() ) ) { wp_send_json_error( [ 'message' => esc_html__( 'You do not have permission to perform this action.', 'wp-mail-smtp' ), ] ); } $old_opt = Options::init()->get_all_raw(); $old_opt['sendlayer']['api_key'] = ''; unset( $old_opt['sendlayer']['quick_connect'] ); unset( $old_opt['sendlayer']['is_shared_domain'] ); unset( $old_opt['sendlayer']['sender_domain'] ); Options::init()->set( $old_opt ); $this->usage->reset(); wp_send_json_success(); } /** * Display the SendLayer education banner when the default PHP mailer is active. * * Shown above the page content (below tabs) on the Settings page only. * * @since 4.8.0 */ public function display_sendlayer_education_banner() { $mailer = wp_mail_smtp()->get_connections_manager()->get_primary_connection()->get_mailer_slug(); if ( $mailer !== 'mail' ) { return; } if ( ! wp_mail_smtp()->get_admin()->is_admin_page() ) { return; } $is_dismissed = (bool) get_user_meta( get_current_user_id(), 'wp_mail_smtp_notice_sendlayer_education_dismissed', true ); if ( $is_dismissed ) { return; } // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound $sendlayer_url = wp_mail_smtp()->get_utm_url( 'https://sendlayer.com/', [ 'source' => 'wpmailsmtpplugin', 'medium' => 'WordPress', 'content' => 'Default Mailer - SendLayer Education' ] ); ?>

SendLayer, our recommended mailer, and send your first 200 emails for free with just a few clicks!', 'wp-mail-smtp' ), [ 'a' => [ 'href' => [], 'rel' => [], 'target' => [], ], 'strong' => [], ] ), esc_url( $sendlayer_url ) ); ?>

get( 'sendlayer', 'quick_connect' ) || ! $connection_options->get( 'sendlayer', 'is_shared_domain' ) ) { return; } $sender_domain = $connection_options->get( 'sendlayer', 'sender_domain' ); if ( empty( $sender_domain ) ) { return; } $from_email = $connection_options->get( 'mail', 'from_email' ); // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled -- email local part, not a brand reference. $local_part = 'wordpress'; if ( ! empty( $from_email ) && strpos( $from_email, '@' ) !== false ) { $parts = explode( '@', $from_email ); $local_part = $parts[0]; } $is_active = $mailer === 'sendlayer'; ?>
/>

SendLayer dashboard. Check our documentation on how to add a custom domain.', 'wp-mail-smtp' ), [ 'a' => [ 'href' => [], 'rel' => [], 'target' => [], ], ] ), // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound esc_url( wp_mail_smtp()->get_utm_url( 'https://app.sendlayer.com/', [ 'source' => 'wpmailsmtpplugin', 'medium' => 'WordPress', 'content' => 'Plugin Settings - From Email Domain Link' ] ) ), // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound esc_url( wp_mail_smtp()->get_utm_url( 'https://sendlayer.com/docs/authorizing-your-domain/', [ 'source' => 'wpmailsmtpplugin', 'medium' => 'WordPress', 'content' => 'Plugin Settings - Custom Domain Documentation' ] ) ) ); ?>

usage->reset(); } return $data; } /** * Force quick_connect to false when the API key is defined via constant. * * Constant-based configuration takes precedence over quick connect state. * * @since 4.8.0 * * @param mixed $value The option value. * @param string $group The options group. * @param string $key The options key. * * @return mixed */ public function filter_option( $value, $group, $key ) { if ( $group === 'sendlayer' && in_array( $key, [ 'quick_connect', 'is_shared_domain' ], true ) && Options::init()->is_const_defined( 'sendlayer', 'api_key' ) ) { return false; } return $value; } /** * Get the marketing site URL. * * Allows overriding via the WP_MAIL_SMTP_SENDLAYER_SITE_URL constant. * * @since 4.8.0 * * @return string */ private function get_marketing_site_url() { if ( defined( 'WP_MAIL_SMTP_SENDLAYER_SITE_URL' ) && WP_MAIL_SMTP_SENDLAYER_SITE_URL ) { return rtrim( WP_MAIL_SMTP_SENDLAYER_SITE_URL, '/' ); } return self::SITE_URL; } /** * Build a from email address for the shared sender domain. * * Takes the user part (local-part) from the current from email and * combines it with the shared sender domain. Falls back to "WordPress" * when no current from email is configured. * * @since 4.8.0 * * @param string $current_from The current from email address. * @param string $sender_domain The shared sender domain. * * @return string */ private function build_from_email( $current_from, $sender_domain ) { // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled -- email local part, not a brand reference. $user_part = 'wordpress'; if ( ! empty( $current_from ) && strpos( $current_from, '@' ) !== false ) { $parts = explode( '@', $current_from ); $user_part = $parts[0]; } return $user_part . '@' . $sender_domain; } /** * Redirect to a URL with a quick connect result code. * * @since 4.8.0 * * @param string $url The URL to redirect to. * @param string $result The result code (e.g. 'success', 'plugin_invalid_session'). */ private function redirect_with_result( $url, $result ) { if ( $result !== 'success' ) { DebugEvents::add( 'SendLayer Quick Connect: ' . $result ); } wp_safe_redirect( add_query_arg( 'sendlayer_quick_connect_result', $result, $url ) ); exit; } /** * Build a generic error message for support reference. * * @since 4.8.0 * * @return string */ private function get_generic_error_message() { return esc_html__( 'Something went wrong while connecting to SendLayer. Please try again, or contact support and provide the error code.', 'wp-mail-smtp' ); } /** * Get the home URL bypassing filters. * * Reads from WP_HOME constant or directly from the database * to avoid any filter modifications to the URL. * * @since 4.8.0 * * @return string */ private function get_site_url() { global $wpdb; if ( defined( 'WP_HOME' ) && WP_HOME ) { return WP_HOME; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- intentional: bypass filters to get unmodified site URL. return $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s", 'home' ) ); } /** * Sanitize an error code slug, preserving dots for dot-notation format. * * WordPress's `sanitize_key()` strips dots. This method allows dots, * lowercase letters, digits, underscores, and hyphens. * * @since 4.8.0 * * @param string $code The error code to sanitize. * * @return string */ private function sanitize_error_code( $code ) { $code = strtolower( trim( $code ) ); return preg_replace( '/[^a-z0-9._-]/', '', $code ); } }