Skip to content

Commit 791c5d3

Browse files
committed
bug #53704 Fix client side connection timeout breaks mail authentication (bytestream)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- Fix client side connection timeout breaks mail authentication | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #53647 | License | MIT The authentication loop should only continue when an unexpected response has been received. Any other exception, for example, `throw new TransportException('Connection to "localhost" timed out.'));` should be treated as fatal and thrown. As demonstrated in #53647, when anything other than a server response is skipped it results in later commands not matching their expected response codes. Commits ------- bb2e7fdf7a Fix client side connection timeout breaks mail authentication
2 parents 2ff642e + c8d965a commit 791c5d3

File tree

5 files changed

+57
-2
lines changed

5 files changed

+57
-2
lines changed
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Exception;
13+
14+
class UnexpectedResponseException extends TransportException
15+
{
16+
}

Tests/Transport/Smtp/DummyStream.php

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public function write(string $bytes, $debug = true): void
6262
$this->nextResponse = '334 UGFzc3dvcmQ6';
6363
} elseif (str_starts_with($bytes, 'cDRzc3cwcmQ=')) {
6464
$this->nextResponse = '535 5.7.139 Authentication unsuccessful';
65+
} elseif (str_starts_with($bytes, 'dGltZWRvdXQ=')) {
66+
throw new TransportException('Connection to "localhost" timed out.');
6567
} elseif (str_starts_with($bytes, 'AUTH CRAM-MD5')) {
6668
$this->nextResponse = '334 PDAxMjM0NTY3ODkuMDEyMzQ1NjdAc3ltZm9ueT4=';
6769
} elseif (str_starts_with($bytes, 'dGVzdHVzZXIgNTdlYzg2ODM5OWZhZThjY2M5OWFhZGVjZjhiZTAwNmY=')) {

Tests/Transport/Smtp/EsmtpTransportTest.php

+35
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,41 @@ public function testConstructorWithEmptyAuthenticator()
214214
$this->assertEquals(504, $e->getCode());
215215
}
216216
}
217+
218+
public function testSocketTimeout()
219+
{
220+
$stream = new DummyStream();
221+
$transport = new EsmtpTransport(stream: $stream);
222+
$transport->setUsername('testuser');
223+
$transport->setPassword('timedout');
224+
$transport->setAuthenticators([new LoginAuthenticator()]);
225+
226+
$message = new Email();
227+
$message->from('[email protected]');
228+
$message->addTo('[email protected]');
229+
$message->text('.');
230+
231+
try {
232+
$transport->send($message);
233+
$this->fail('Symfony\Component\Mailer\Exception\TransportException to be thrown');
234+
} catch (TransportException $e) {
235+
$this->assertStringStartsWith('Connection to "localhost" timed out.', $e->getMessage());
236+
}
237+
238+
$this->assertEquals(
239+
[
240+
"EHLO [127.0.0.1]\r\n",
241+
// S: 250 localhost
242+
// S: 250-AUTH PLAIN LOGIN CRAM-MD5 XOAUTH2
243+
"AUTH LOGIN\r\n",
244+
// S: 334 VXNlcm5hbWU6
245+
"dGVzdHVzZXI=\r\n",
246+
// S: 334 UGFzc3dvcmQ6
247+
"dGltZWRvdXQ=\r\n",
248+
],
249+
$stream->getCommands()
250+
);
251+
}
217252
}
218253

219254
class CustomEsmtpTransport extends EsmtpTransport

Transport/Smtp/EsmtpTransport.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Psr\Log\LoggerInterface;
1616
use Symfony\Component\Mailer\Exception\TransportException;
1717
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
18+
use Symfony\Component\Mailer\Exception\UnexpectedResponseException;
1819
use Symfony\Component\Mailer\Transport\Smtp\Auth\AuthenticatorInterface;
1920
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
2021
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
@@ -199,7 +200,7 @@ private function handleAuth(array $modes): void
199200
$authenticator->authenticate($this);
200201

201202
return;
202-
} catch (TransportExceptionInterface $e) {
203+
} catch (UnexpectedResponseException $e) {
203204
$code = $e->getCode();
204205

205206
try {

Transport/Smtp/SmtpTransport.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Mailer\Exception\LogicException;
1818
use Symfony\Component\Mailer\Exception\TransportException;
1919
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
20+
use Symfony\Component\Mailer\Exception\UnexpectedResponseException;
2021
use Symfony\Component\Mailer\SentMessage;
2122
use Symfony\Component\Mailer\Transport\AbstractTransport;
2223
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
@@ -335,7 +336,7 @@ private function assertResponseCode(string $response, array $codes): void
335336
$codeStr = $code ? sprintf('code "%s"', $code) : 'empty code';
336337
$responseStr = $response ? sprintf(', with message "%s"', trim($response)) : '';
337338

338-
throw new TransportException(sprintf('Expected response code "%s" but got ', implode('/', $codes)).$codeStr.$responseStr.'.', $code ?: 0);
339+
throw new UnexpectedResponseException(sprintf('Expected response code "%s" but got ', implode('/', $codes)).$codeStr.$responseStr.'.', $code ?: 0);
339340
}
340341
}
341342

0 commit comments

Comments
 (0)