From d1b13684e3fc18876d93990aa7778acec8d9e0b2 Mon Sep 17 00:00:00 2001 From: ip-qi <57226580+ip-qi@users.noreply.github.com> Date: Sun, 23 Feb 2025 18:33:02 +0100 Subject: [PATCH] Refactor SMTPAuthenticate() + added config option SMTPAuthMethod for either LOGIN or PLAIN SMTH AUTH mechanism --- system/Email/Email.php | 94 ++++++++++++++++------- user_guide_src/source/libraries/email.rst | 3 +- 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/system/Email/Email.php b/system/Email/Email.php index 24c3af4cd376..24da4e9ca905 100644 --- a/system/Email/Email.php +++ b/system/Email/Email.php @@ -279,6 +279,13 @@ class Email */ protected $SMTPAuth = false; + /** + * Which SMTP AUTH method to use ('LOGIN', 'PLAIN') + * + * @var string + */ + protected $SMTPAuthMethod = 'LOGIN'; + /** * Whether to send a Reply-To header * @@ -1920,9 +1927,9 @@ protected function SMTPConnect() $this->SMTPConnect, true, STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT - | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT - | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT - | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT + | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT + | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, ); if ($crypto !== true) { @@ -2015,54 +2022,87 @@ protected function sendCommand($cmd, $data = '') */ protected function SMTPAuthenticate() { - if (! $this->SMTPAuth) { + if (!$this->SMTPAuth) { return true; } - if ($this->SMTPUser === '' && $this->SMTPPass === '') { + // If no username or password is set + if ($this->SMTPUser === '' || $this->SMTPPass === '') { $this->setErrorMessage(lang('Email.noSMTPAuth')); - return false; } - $this->sendData('AUTH LOGIN'); - $reply = $this->getSMTPData(); + switch ($this->SMTPAuthMethod) { + case 'LOGIN': + $this->sendData('AUTH LOGIN'); // Start authentication + if (!$this->checkSMTPAuthResponse($this->getSMTPData())) { + return false; + } - if (str_starts_with($reply, '503')) { // Already authenticated - return true; - } + // Send encoded username + $this->sendData(base64_encode($this->SMTPUser)); + if (!$this->checkSMTPAuthResponse($this->getSMTPData())) { + return false; + } - if (! str_starts_with($reply, '334')) { - $this->setErrorMessage(lang('Email.failedSMTPLogin', [$reply])); + // Send encoded password + $this->sendData(base64_encode($this->SMTPPass)); + $reply = $this->getSMTPData(); - return false; - } + // Authentication success (235 expected) + if (!str_starts_with($reply, '235')) { + $this->setErrorMessage(lang('Email.SMTPAuthPassword', [$reply])); + return false; + } + break; + case 'PLAIN': + // Generate single command for PLAIN authentication + $authString = "\0" . $this->SMTPUser . "\0" . $this->SMTPPass; - $this->sendData(base64_encode($this->SMTPUser)); - $reply = $this->getSMTPData(); + $this->sendData('AUTH PLAIN ' . base64_encode($authString)); - if (! str_starts_with($reply, '334')) { - $this->setErrorMessage(lang('Email.SMTPAuthUsername', [$reply])); + if (!$this->checkSMTPAuthResponse($this->getSMTPData())) { + return false; + } - return false; + break; + default: + $this->setErrorMessage(lang('Email.noSMTPAuthMethod')); + return false; + break; } - $this->sendData(base64_encode($this->SMTPPass)); - $reply = $this->getSMTPData(); + if ($this->SMTPKeepAlive) { + $this->SMTPAuth = false; // Prevent re-authentication for keep-alive sessions + } - if (! str_starts_with($reply, '235')) { - $this->setErrorMessage(lang('Email.SMTPAuthPassword', [$reply])); + return true; + } + + private function checkSMTPAuthResponse($replyData) + { + // 503 already authenticated || 235 authentication success ('AUTH PLAIN') + if (str_starts_with($replyData, '503') || str_starts_with($replyData, '235')) { + return true; + } + + if (str_starts_with($replyData, '535')) { // Authentication failed (bad credentials) + $this->setErrorMessage(lang('Email.failedSMTPLogin', [$replyData])); return false; } - if ($this->SMTPKeepAlive) { - $this->SMTPAuth = false; + if (str_starts_with($replyData, '334')) { // Server requesting username or password + return true; } - return true; + // Unexpected response + $this->setErrorMessage(lang('Email.SMTPUnexpectedResponse', [$replyData])); + return false; } + + /** * @param string $data * diff --git a/user_guide_src/source/libraries/email.rst b/user_guide_src/source/libraries/email.rst index ec21b79ddcf2..440eeaaf0a0f 100644 --- a/user_guide_src/source/libraries/email.rst +++ b/user_guide_src/source/libraries/email.rst @@ -39,7 +39,7 @@ Here is a basic example demonstrating how you might send email: Setting Email Preferences ========================= -There are 21 different preferences available to tailor how your email +There are 22 different preferences available to tailor how your email messages are sent. You can either set them manually as described here, or automatically via preferences stored in your config file, described in `Email Preferences`_. @@ -120,6 +120,7 @@ Preference Default Value Options Description or ``smtp`` **mailPath** /usr/sbin/sendmail The server path to Sendmail. **SMTPHost** SMTP Server Hostname. +**SMTPAuthMethod** LOGIN ``LOGIN``, ``PLAIN`` SMTP Authentication Method. **SMTPUser** SMTP Username. **SMTPPass** SMTP Password. **SMTPPort** 25 SMTP Port. (If set to ``465``, TLS will be used for the connection