array( 'name' => 'Gmail', 'auth_url' => 'https://accounts.google.com/o/oauth2/auth', 'token_url' => 'https://oauth2.googleapis.com/token', 'scope' => 'https://mail.google.com/', 'client_id' => '', 'client_secret' => '' ), 'outlook' => array( 'name' => 'Outlook', 'auth_url' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', 'token_url' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token', 'scope' => 'https://outlook.office.com/SMTP.Send offline_access', 'client_id' => '', 'client_secret' => '' ), 'zoho' => array( 'name' => 'Zoho', 'auth_url' => 'https://accounts.zoho.com/oauth/v2/auth', 'token_url' => 'https://accounts.zoho.com/oauth/v2/token', 'scope' => 'ZohoMail.messages.CREATE', 'client_id' => '', 'client_secret' => '' ) ); /** * Get single instance */ public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ private function __construct() { $this->init_hooks(); } /** * Initialize hooks */ private function init_hooks() { add_action('wp_ajax_aha_mail_smtp_oauth_start', array($this, 'start_oauth_flow')); add_action('wp_ajax_aha_mail_smtp_oauth_callback', array($this, 'handle_oauth_callback')); add_action('wp_ajax_aha_mail_smtp_oauth_disconnect', array($this, 'disconnect_oauth')); add_action('wp_ajax_aha_mail_smtp_oauth_refresh', array($this, 'refresh_oauth_token')); // Handle OAuth callback from redirect add_action('admin_init', array($this, 'handle_oauth_redirect')); } /** * Get OAuth providers */ public static function get_oauth_providers() { return self::$oauth_providers; } /** * Check if provider supports OAuth */ public static function provider_supports_oauth($provider) { return isset(self::$oauth_providers[$provider]); } /** * Start OAuth flow */ public function start_oauth_flow() { // Verify nonce if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'aha_mail_smtp_admin')) { wp_send_json_error(__('Security check failed', 'aha-mail-smtp')); } // Check user capabilities if (!current_user_can('manage_options')) { wp_send_json_error(__('You do not have sufficient permissions', 'aha-mail-smtp')); } $provider = sanitize_text_field($_POST['provider']); if (!self::provider_supports_oauth($provider)) { wp_send_json_error(__('OAuth not supported for this provider', 'aha-mail-smtp')); } $oauth_config = self::$oauth_providers[$provider]; // Check if client credentials are configured $client_id = $this->get_client_id($provider); if (empty($client_id)) { wp_send_json_error(__('OAuth client credentials not configured', 'aha-mail-smtp')); } // Generate state parameter for security $state = wp_generate_password(32, false); set_transient('aha_mail_smtp_oauth_state_' . $state, $provider, 600); // 10 minutes // Build authorization URL $auth_params = array( 'client_id' => $client_id, 'response_type' => 'code', 'scope' => $oauth_config['scope'], 'redirect_uri' => $this->get_redirect_uri(), 'state' => $state, 'access_type' => 'offline', // For refresh tokens 'prompt' => 'consent' ); $auth_url = $oauth_config['auth_url'] . '?' . http_build_query($auth_params); wp_send_json_success(array( 'auth_url' => $auth_url, 'message' => __('Redirecting to OAuth provider...', 'aha-mail-smtp') )); } /** * Handle OAuth redirect callback */ public function handle_oauth_redirect() { if (!isset($_GET['page']) || $_GET['page'] !== 'aha-mail-smtp') { return; } if (!isset($_GET['code']) || !isset($_GET['state'])) { return; } // Check user capabilities before processing OAuth tokens if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have sufficient permissions', 'aha-mail-smtp')); } $code = sanitize_text_field(wp_unslash($_GET['code'])); $state = sanitize_text_field(wp_unslash($_GET['state'])); // Verify state parameter $provider = get_transient('aha_mail_smtp_oauth_state_' . $state); if (!$provider) { wp_die(__('Invalid OAuth state parameter', 'aha-mail-smtp')); } // Delete the state transient delete_transient('aha_mail_smtp_oauth_state_' . $state); // Exchange code for tokens $result = $this->exchange_code_for_tokens($provider, $code); if (is_wp_error($result)) { wp_die($result->get_error_message()); } // Store tokens AHA_Mail_SMTP_Settings::set('oauth_access_token', $result['access_token']); if (isset($result['refresh_token'])) { AHA_Mail_SMTP_Settings::set('oauth_refresh_token', $result['refresh_token']); } AHA_Mail_SMTP_Settings::set('oauth_provider', $provider); AHA_Mail_SMTP_Settings::set('oauth_expires_at', time() + $result['expires_in']); // Redirect back to settings page with success message wp_redirect(add_query_arg(array( 'page' => 'aha-mail-smtp', 'oauth_success' => '1' ), admin_url('admin.php'))); exit; } /** * Exchange authorization code for tokens */ private function exchange_code_for_tokens($provider, $code) { $oauth_config = self::$oauth_providers[$provider]; $token_params = array( 'client_id' => $this->get_client_id($provider), 'client_secret' => $this->get_client_secret($provider), 'code' => $code, 'grant_type' => 'authorization_code', 'redirect_uri' => $this->get_redirect_uri() ); $response = wp_remote_post($oauth_config['token_url'], array( 'body' => $token_params, 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ), 'timeout' => 30 )); if (is_wp_error($response)) { return $response; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['error'])) { return new WP_Error('oauth_error', $data['error_description'] ?? $data['error']); } return $data; } /** * Refresh OAuth token */ public function refresh_oauth_token() { $provider = AHA_Mail_SMTP_Settings::get('oauth_provider'); $refresh_token = AHA_Mail_SMTP_Settings::get('oauth_refresh_token'); if (empty($provider) || empty($refresh_token)) { return new WP_Error('no_refresh_token', __('No refresh token available', 'aha-mail-smtp')); } $oauth_config = self::$oauth_providers[$provider]; $token_params = array( 'client_id' => $this->get_client_id($provider), 'client_secret' => $this->get_client_secret($provider), 'refresh_token' => $refresh_token, 'grant_type' => 'refresh_token' ); $response = wp_remote_post($oauth_config['token_url'], array( 'body' => $token_params, 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ), 'timeout' => 30 )); if (is_wp_error($response)) { return $response; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['error'])) { return new WP_Error('oauth_error', $data['error_description'] ?? $data['error']); } // Update tokens AHA_Mail_SMTP_Settings::set('oauth_access_token', $data['access_token']); if (isset($data['refresh_token'])) { AHA_Mail_SMTP_Settings::set('oauth_refresh_token', $data['refresh_token']); } AHA_Mail_SMTP_Settings::set('oauth_expires_at', time() + $data['expires_in']); return $data; } /** * Disconnect OAuth */ public function disconnect_oauth() { // Verify nonce if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'aha_mail_smtp_admin')) { wp_send_json_error(__('Security check failed', 'aha-mail-smtp')); } // Check user capabilities if (!current_user_can('manage_options')) { wp_send_json_error(__('You do not have sufficient permissions', 'aha-mail-smtp')); } // Clear OAuth tokens AHA_Mail_SMTP_Settings::delete('oauth_access_token'); AHA_Mail_SMTP_Settings::delete('oauth_refresh_token'); AHA_Mail_SMTP_Settings::delete('oauth_provider'); AHA_Mail_SMTP_Settings::delete('oauth_expires_at'); wp_send_json_success(__('OAuth connection disconnected successfully!', 'aha-mail-smtp')); } /** * Get valid access token */ public static function get_access_token() { $access_token = AHA_Mail_SMTP_Settings::get('oauth_access_token'); $expires_at = AHA_Mail_SMTP_Settings::get('oauth_expires_at'); if (empty($access_token)) { return false; } // Check if token is expired if ($expires_at && time() >= $expires_at) { // Try to refresh token $oauth = self::get_instance(); $result = $oauth->refresh_oauth_token(); if (is_wp_error($result)) { return false; } $access_token = AHA_Mail_SMTP_Settings::get('oauth_access_token'); } return $access_token; } /** * Check if OAuth is connected */ public static function is_oauth_connected() { $access_token = AHA_Mail_SMTP_Settings::get('oauth_access_token'); return !empty($access_token); } /** * Get OAuth status */ public static function get_oauth_status() { if (!self::is_oauth_connected()) { return array( 'connected' => false, 'provider' => '', 'message' => __('OAuth not connected', 'aha-mail-smtp') ); } $provider = AHA_Mail_SMTP_Settings::get('oauth_provider'); $expires_at = AHA_Mail_SMTP_Settings::get('oauth_expires_at'); $status = array( 'connected' => true, 'provider' => $provider, 'provider_name' => self::$oauth_providers[$provider]['name'] ?? $provider ); if ($expires_at) { if (time() >= $expires_at) { $status['message'] = __('OAuth token expired', 'aha-mail-smtp'); $status['expired'] = true; } else { $status['message'] = sprintf( __('OAuth connected, expires %s', 'aha-mail-smtp'), date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $expires_at) ); $status['expired'] = false; } } else { $status['message'] = __('OAuth connected', 'aha-mail-smtp'); $status['expired'] = false; } return $status; } /** * Get redirect URI */ private function get_redirect_uri() { return admin_url('admin.php?page=aha-mail-smtp'); } /** * Get client ID for provider */ private function get_client_id($provider) { // In a real implementation, these would be stored as options or constants // For this demo, we'll use placeholder values switch ($provider) { case 'gmail': return defined('AHA_MAIL_SMTP_GMAIL_CLIENT_ID') ? AHA_MAIL_SMTP_GMAIL_CLIENT_ID : ''; case 'outlook': return defined('AHA_MAIL_SMTP_OUTLOOK_CLIENT_ID') ? AHA_MAIL_SMTP_OUTLOOK_CLIENT_ID : ''; case 'zoho': return defined('AHA_MAIL_SMTP_ZOHO_CLIENT_ID') ? AHA_MAIL_SMTP_ZOHO_CLIENT_ID : ''; default: return ''; } } /** * Get client secret for provider */ private function get_client_secret($provider) { // In a real implementation, these would be stored as options or constants // For this demo, we'll use placeholder values switch ($provider) { case 'gmail': return defined('AHA_MAIL_SMTP_GMAIL_CLIENT_SECRET') ? AHA_MAIL_SMTP_GMAIL_CLIENT_SECRET : ''; case 'outlook': return defined('AHA_MAIL_SMTP_OUTLOOK_CLIENT_SECRET') ? AHA_MAIL_SMTP_OUTLOOK_CLIENT_SECRET : ''; case 'zoho': return defined('AHA_MAIL_SMTP_ZOHO_CLIENT_SECRET') ? AHA_MAIL_SMTP_ZOHO_CLIENT_SECRET : ''; default: return ''; } } /** * Configure PHPMailer for OAuth */ public static function configure_phpmailer_oauth($phpmailer, $provider) { $access_token = self::get_access_token(); if (!$access_token) { return false; } switch ($provider) { case 'gmail': $phpmailer->isSMTP(); $phpmailer->Host = 'smtp.gmail.com'; $phpmailer->Port = 587; $phpmailer->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; $phpmailer->SMTPAuth = true; $phpmailer->AuthType = 'XOAUTH2'; // Set OAuth parameters $phpmailer->setOAuth( new PHPMailer\PHPMailer\OAuth(array( 'provider' => 'Google', 'clientId' => self::get_instance()->get_client_id('gmail'), 'clientSecret' => self::get_instance()->get_client_secret('gmail'), 'refreshToken' => AHA_Mail_SMTP_Settings::get('oauth_refresh_token'), 'userName' => AHA_Mail_SMTP_Settings::get('smtp_username') )) ); break; case 'outlook': $phpmailer->isSMTP(); $phpmailer->Host = 'smtp-mail.outlook.com'; $phpmailer->Port = 587; $phpmailer->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; $phpmailer->SMTPAuth = true; $phpmailer->AuthType = 'XOAUTH2'; // Set OAuth parameters for Outlook $phpmailer->setOAuth( new PHPMailer\PHPMailer\OAuth(array( 'provider' => 'Microsoft', 'clientId' => self::get_instance()->get_client_id('outlook'), 'clientSecret' => self::get_instance()->get_client_secret('outlook'), 'refreshToken' => AHA_Mail_SMTP_Settings::get('oauth_refresh_token'), 'userName' => AHA_Mail_SMTP_Settings::get('smtp_username') )) ); break; } return true; } }