Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6bf4d4d

Browse filesBrowse files
committed
Merge branch '5.4' into 6.4
* 5.4: [Mailer] Fix sendmail transport failure handling and interactive mode [Security] reviewed Romanian translation of key 20
2 parents 7e23cea + f60c3cf commit 6bf4d4d
Copy full SHA for 6bf4d4d

File tree

Expand file treeCollapse file tree

5 files changed

+106
-22
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+106
-22
lines changed
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#!/usr/bin/env php
22
<?php
3+
$argsPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'sendmail_args';
4+
5+
file_put_contents($argsPath, implode(' ', $argv));
6+
37
print "Sending failed";
48
exit(42);

‎src/Symfony/Component/Mailer/Tests/Transport/SendmailTransportTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Tests/Transport/SendmailTransportTest.php
+89-20Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,21 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Mailer\DelayedEnvelope;
16+
use Symfony\Component\Mailer\Envelope;
1617
use Symfony\Component\Mailer\Exception\TransportException;
18+
use Symfony\Component\Mailer\SentMessage;
1719
use Symfony\Component\Mailer\Transport\SendmailTransport;
20+
use Symfony\Component\Mailer\Transport\Smtp\Stream\ProcessStream;
21+
use Symfony\Component\Mailer\Transport\TransportInterface;
1822
use Symfony\Component\Mime\Address;
1923
use Symfony\Component\Mime\Email;
24+
use Symfony\Component\Mime\RawMessage;
2025

2126
class SendmailTransportTest extends TestCase
2227
{
2328
private const FAKE_SENDMAIL = __DIR__.'/Fixtures/fake-sendmail.php -t';
2429
private const FAKE_FAILING_SENDMAIL = __DIR__.'/Fixtures/fake-failing-sendmail.php -t';
30+
private const FAKE_INTERACTIVE_SENDMAIL = __DIR__.'/Fixtures/fake-failing-sendmail.php -bs';
2531

2632
private string $argsPath;
2733

@@ -46,9 +52,7 @@ public function testToString()
4652

4753
public function testToIsUsedWhenRecipientsAreNotSet()
4854
{
49-
if ('\\' === \DIRECTORY_SEPARATOR) {
50-
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
51-
}
55+
$this->skipOnWindows();
5256

5357
$mail = new Email();
5458
$mail
@@ -68,20 +72,9 @@ public function testToIsUsedWhenRecipientsAreNotSet()
6872

6973
public function testRecipientsAreUsedWhenSet()
7074
{
71-
if ('\\' === \DIRECTORY_SEPARATOR) {
72-
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
73-
}
75+
$this->skipOnWindows();
7476

75-
$mail = new Email();
76-
$mail
77-
->from('from@mail.com')
78-
->to('to@mail.com')
79-
->subject('Subject')
80-
->text('Some text')
81-
;
82-
83-
$envelope = new DelayedEnvelope($mail);
84-
$envelope->setRecipients([new Address('recipient@mail.com')]);
77+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
8578

8679
$sendmailTransport = new SendmailTransport(self::FAKE_SENDMAIL);
8780
$sendmailTransport->send($mail, $envelope);
@@ -90,11 +83,90 @@ public function testRecipientsAreUsedWhenSet()
9083
}
9184

9285
public function testThrowsTransportExceptionOnFailure()
86+
{
87+
$this->skipOnWindows();
88+
89+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
90+
91+
$sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
92+
$this->expectException(TransportException::class);
93+
$this->expectExceptionMessage('Process failed with exit code 42: Sending failed');
94+
$sendmailTransport->send($mail, $envelope);
95+
96+
$streamProperty = new \ReflectionProperty(SendmailTransport::class, 'stream');
97+
$streamProperty->setAccessible(true);
98+
$stream = $streamProperty->getValue($sendmailTransport);
99+
$this->assertNull($stream->stream);
100+
}
101+
102+
public function testStreamIsClearedOnFailure()
103+
{
104+
$this->skipOnWindows();
105+
106+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
107+
108+
$sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
109+
try {
110+
$sendmailTransport->send($mail, $envelope);
111+
} catch (TransportException $e) {
112+
}
113+
114+
$streamProperty = new \ReflectionProperty(SendmailTransport::class, 'stream');
115+
$streamProperty->setAccessible(true);
116+
$stream = $streamProperty->getValue($sendmailTransport);
117+
$innerStreamProperty = new \ReflectionProperty(ProcessStream::class, 'stream');
118+
$innerStreamProperty->setAccessible(true);
119+
$this->assertNull($innerStreamProperty->getValue($stream));
120+
}
121+
122+
public function testDoesNotThrowWhenInteractive()
123+
{
124+
$this->skipOnWindows();
125+
126+
[$mail, $envelope] = $this->defaultMailAndEnvelope();
127+
128+
$sendmailTransport = new SendmailTransport(self::FAKE_INTERACTIVE_SENDMAIL);
129+
$transportProperty = new \ReflectionProperty(SendmailTransport::class, 'transport');
130+
$transportProperty->setAccessible(true);
131+
132+
// Replace the transport with an anonymous consumer that trigger the stream methods
133+
$transportProperty->setValue($sendmailTransport, new class($transportProperty->getValue($sendmailTransport)->getStream()) implements TransportInterface {
134+
private $stream;
135+
136+
public function __construct(ProcessStream $stream)
137+
{
138+
$this->stream = $stream;
139+
}
140+
141+
public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMessage
142+
{
143+
$this->stream->initialize();
144+
$this->stream->write('SMTP');
145+
$this->stream->terminate();
146+
147+
return new SentMessage($message, $envelope);
148+
}
149+
150+
public function __toString(): string
151+
{
152+
return 'Interactive mode test';
153+
}
154+
});
155+
156+
$sendmailTransport->send($mail, $envelope);
157+
158+
$this->assertStringEqualsFile($this->argsPath, __DIR__.'/Fixtures/fake-failing-sendmail.php -bs');
159+
}
160+
161+
private function skipOnWindows()
93162
{
94163
if ('\\' === \DIRECTORY_SEPARATOR) {
95164
$this->markTestSkipped('Windows does not support shebangs nor non-blocking standard streams');
96165
}
166+
}
97167

168+
private function defaultMailAndEnvelope(): array
169+
{
98170
$mail = new Email();
99171
$mail
100172
->from('from@mail.com')
@@ -106,9 +178,6 @@ public function testThrowsTransportExceptionOnFailure()
106178
$envelope = new DelayedEnvelope($mail);
107179
$envelope->setRecipients([new Address('recipient@mail.com')]);
108180

109-
$sendmailTransport = new SendmailTransport(self::FAKE_FAILING_SENDMAIL);
110-
$this->expectException(TransportException::class);
111-
$this->expectExceptionMessage('Process failed with exit code 42: Sending failed');
112-
$sendmailTransport->send($mail, $envelope);
181+
return [$mail, $envelope];
113182
}
114183
}

‎src/Symfony/Component/Mailer/Transport/SendmailTransport.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Transport/SendmailTransport.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public function __construct(?string $command = null, ?EventDispatcherInterface $
6464
$this->stream = new ProcessStream();
6565
if (str_contains($this->command, ' -bs')) {
6666
$this->stream->setCommand($this->command);
67+
$this->stream->setInteractive(true);
6768
$this->transport = new SmtpTransport($this->stream, $dispatcher, $logger);
6869
}
6970
}

‎src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
+11-1Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@
2424
final class ProcessStream extends AbstractStream
2525
{
2626
private string $command;
27+
private bool $interactive = false;
2728

2829
public function setCommand(string $command): void
2930
{
3031
$this->command = $command;
3132
}
3233

34+
public function setInteractive(bool $interactive)
35+
{
36+
$this->interactive = $interactive;
37+
}
38+
3339
public function initialize(): void
3440
{
3541
$descriptorSpec = [
@@ -57,11 +63,15 @@ public function terminate(): void
5763
$err = stream_get_contents($this->err);
5864
fclose($this->err);
5965
if (0 !== $exitCode = proc_close($this->stream)) {
60-
throw new TransportException('Process failed with exit code '.$exitCode.': '.$out.$err);
66+
$errorMessage = 'Process failed with exit code '.$exitCode.': '.$out.$err;
6167
}
6268
}
6369

6470
parent::terminate();
71+
72+
if (!$this->interactive && isset($errorMessage)) {
73+
throw new TransportException($errorMessage);
74+
}
6575
}
6676

6777
protected function getReadConnectionDescription(): string

‎src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Core/Resources/translations/security.ro.xlf
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
</trans-unit>
7777
<trans-unit id="20">
7878
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
79-
<target state="needs-review-translation">Prea multe încercări eșuate de autentificare, vă rugăm să încercați din nou peste %minutes% minut.|Prea multe încercări eșuate de autentificare, vă rugăm să încercați din nou peste %minutes% minute.</target>
79+
<target>Prea multe încercări eșuate de autentificare, vă rugăm să încercați din nou peste %minutes% minut.|Prea multe încercări eșuate de autentificare, vă rugăm să încercați din nou peste %minutes% minute.</target>
8080
</trans-unit>
8181
</body>
8282
</file>

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.