Skip to content

Commit d1b1368

Browse files
committed
Refactor SMTPAuthenticate() + added config option SMTPAuthMethod for either LOGIN or PLAIN SMTH AUTH mechanism
1 parent 0ee6ade commit d1b1368

File tree

2 files changed

+69
-28
lines changed

2 files changed

+69
-28
lines changed

system/Email/Email.php

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ class Email
279279
*/
280280
protected $SMTPAuth = false;
281281

282+
/**
283+
* Which SMTP AUTH method to use ('LOGIN', 'PLAIN')
284+
*
285+
* @var string
286+
*/
287+
protected $SMTPAuthMethod = 'LOGIN';
288+
282289
/**
283290
* Whether to send a Reply-To header
284291
*
@@ -1920,9 +1927,9 @@ protected function SMTPConnect()
19201927
$this->SMTPConnect,
19211928
true,
19221929
STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT
1923-
| STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
1924-
| STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
1925-
| STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
1930+
| STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT
1931+
| STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
1932+
| STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
19261933
);
19271934

19281935
if ($crypto !== true) {
@@ -2015,54 +2022,87 @@ protected function sendCommand($cmd, $data = '')
20152022
*/
20162023
protected function SMTPAuthenticate()
20172024
{
2018-
if (! $this->SMTPAuth) {
2025+
if (!$this->SMTPAuth) {
20192026
return true;
20202027
}
20212028

2022-
if ($this->SMTPUser === '' && $this->SMTPPass === '') {
2029+
// If no username or password is set
2030+
if ($this->SMTPUser === '' || $this->SMTPPass === '') {
20232031
$this->setErrorMessage(lang('Email.noSMTPAuth'));
2024-
20252032
return false;
20262033
}
20272034

2028-
$this->sendData('AUTH LOGIN');
2029-
$reply = $this->getSMTPData();
2035+
switch ($this->SMTPAuthMethod) {
2036+
case 'LOGIN':
2037+
$this->sendData('AUTH LOGIN'); // Start authentication
2038+
if (!$this->checkSMTPAuthResponse($this->getSMTPData())) {
2039+
return false;
2040+
}
20302041

2031-
if (str_starts_with($reply, '503')) { // Already authenticated
2032-
return true;
2033-
}
2042+
// Send encoded username
2043+
$this->sendData(base64_encode($this->SMTPUser));
2044+
if (!$this->checkSMTPAuthResponse($this->getSMTPData())) {
2045+
return false;
2046+
}
20342047

2035-
if (! str_starts_with($reply, '334')) {
2036-
$this->setErrorMessage(lang('Email.failedSMTPLogin', [$reply]));
2048+
// Send encoded password
2049+
$this->sendData(base64_encode($this->SMTPPass));
2050+
$reply = $this->getSMTPData();
20372051

2038-
return false;
2039-
}
2052+
// Authentication success (235 expected)
2053+
if (!str_starts_with($reply, '235')) {
2054+
$this->setErrorMessage(lang('Email.SMTPAuthPassword', [$reply]));
2055+
return false;
2056+
}
2057+
break;
2058+
case 'PLAIN':
2059+
// Generate single command for PLAIN authentication
2060+
$authString = "\0" . $this->SMTPUser . "\0" . $this->SMTPPass;
20402061

2041-
$this->sendData(base64_encode($this->SMTPUser));
2042-
$reply = $this->getSMTPData();
2062+
$this->sendData('AUTH PLAIN ' . base64_encode($authString));
20432063

2044-
if (! str_starts_with($reply, '334')) {
2045-
$this->setErrorMessage(lang('Email.SMTPAuthUsername', [$reply]));
2064+
if (!$this->checkSMTPAuthResponse($this->getSMTPData())) {
2065+
return false;
2066+
}
20462067

2047-
return false;
2068+
break;
2069+
default:
2070+
$this->setErrorMessage(lang('Email.noSMTPAuthMethod'));
2071+
return false;
2072+
break;
20482073
}
20492074

2050-
$this->sendData(base64_encode($this->SMTPPass));
2051-
$reply = $this->getSMTPData();
2075+
if ($this->SMTPKeepAlive) {
2076+
$this->SMTPAuth = false; // Prevent re-authentication for keep-alive sessions
2077+
}
20522078

2053-
if (! str_starts_with($reply, '235')) {
2054-
$this->setErrorMessage(lang('Email.SMTPAuthPassword', [$reply]));
2079+
return true;
2080+
}
20552081

2082+
2083+
private function checkSMTPAuthResponse($replyData)
2084+
{
2085+
// 503 already authenticated || 235 authentication success ('AUTH PLAIN')
2086+
if (str_starts_with($replyData, '503') || str_starts_with($replyData, '235')) {
2087+
return true;
2088+
}
2089+
2090+
if (str_starts_with($replyData, '535')) { // Authentication failed (bad credentials)
2091+
$this->setErrorMessage(lang('Email.failedSMTPLogin', [$replyData]));
20562092
return false;
20572093
}
20582094

2059-
if ($this->SMTPKeepAlive) {
2060-
$this->SMTPAuth = false;
2095+
if (str_starts_with($replyData, '334')) { // Server requesting username or password
2096+
return true;
20612097
}
20622098

2063-
return true;
2099+
// Unexpected response
2100+
$this->setErrorMessage(lang('Email.SMTPUnexpectedResponse', [$replyData]));
2101+
return false;
20642102
}
20652103

2104+
2105+
20662106
/**
20672107
* @param string $data
20682108
*

user_guide_src/source/libraries/email.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Here is a basic example demonstrating how you might send email:
3939
Setting Email Preferences
4040
=========================
4141

42-
There are 21 different preferences available to tailor how your email
42+
There are 22 different preferences available to tailor how your email
4343
messages are sent. You can either set them manually as described here,
4444
or automatically via preferences stored in your config file, described
4545
in `Email Preferences`_.
@@ -120,6 +120,7 @@ Preference Default Value Options Description
120120
or ``smtp``
121121
**mailPath** /usr/sbin/sendmail The server path to Sendmail.
122122
**SMTPHost** SMTP Server Hostname.
123+
**SMTPAuthMethod** LOGIN ``LOGIN``, ``PLAIN`` SMTP Authentication Method.
123124
**SMTPUser** SMTP Username.
124125
**SMTPPass** SMTP Password.
125126
**SMTPPort** 25 SMTP Port. (If set to ``465``, TLS will be used for the connection

0 commit comments

Comments
 (0)