Skip to content

Commit

Permalink
[Mailer] Check email validity before opening an SMTP connection
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Sep 6, 2019
1 parent b7371ea commit 3a5fe8c
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public function testSend()
$email = new Email();
$email->from('foo@example.com')
->to('bar@example.com')
->bcc('baz@example.com');
->bcc('baz@example.com')
->text('content');

$response = $this->createMock(ResponseInterface::class);

Expand All @@ -74,14 +75,15 @@ public function testSend()
],
],
'from' => ['email' => 'foo@example.com'],
'content' => [],
'content' => [
['type' => 'text/plain', 'value' => 'content'],
],
],
'auth_bearer' => 'foo',
])
->willReturn($response);

$mailer = new SendgridApiTransport('foo', $httpClient);

$mailer->send($email);
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Component/Mailer/SentMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class SentMessage
*/
public function __construct(RawMessage $message, SmtpEnvelope $envelope)
{
$message->ensureValidity();

$this->raw = $message instanceof Message ? new RawMessage($message->toIterable()) : $message;
$this->original = $message;
$this->envelope = $envelope;
Expand Down
20 changes: 11 additions & 9 deletions src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,15 @@ public function getLocalDomain(): string

public function send(RawMessage $message, SmtpEnvelope $envelope = null): ?SentMessage
{
$this->ping();
if (!$this->started) {
$this->start();
}

try {
$message = parent::send($message, $envelope);
} catch (TransportExceptionInterface $e) {
try {
$this->executeCommand("RSET\r\n", [250]);
} catch (TransportExceptionInterface $_) {
// ignore this exception as it probably means that the server error was final
if ($this->started) {
try {
$this->executeCommand("RSET\r\n", [250]);
} catch (TransportExceptionInterface $_) {
// ignore this exception as it probably means that the server error was final
}
}

throw $e;
Expand Down Expand Up @@ -163,6 +160,11 @@ public function executeCommand(string $command, array $codes): string

protected function doSend(SentMessage $message): void
{
$this->ping();
if (!$this->started) {
$this->start();
}

try {
$envelope = $message->getEnvelope();
$this->doMailFromCommand($envelope->getSender()->getAddress());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class SocketStream extends AbstractStream
private $url;
private $host = 'localhost';
private $port = 465;
private $timeout = 15;
private $timeout = 5;
private $tls = true;
private $sourceIp;
private $streamContextOptions = [];
Expand Down
14 changes: 11 additions & 3 deletions src/Symfony/Component/Mime/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,15 @@ public function getBody(): AbstractPart
return $this->generateBody();
}

public function ensureValidity()
{
if (null === $this->text && null === $this->html && !$this->attachments) {
throw new LogicException('A message must have a text or an HTML part or attachments.');
}

parent::ensureValidity();
}

/**
* Generates an AbstractPart based on the raw body of a message.
*
Expand All @@ -421,10 +430,9 @@ public function getBody(): AbstractPart
*/
private function generateBody(): AbstractPart
{
$this->ensureValidity();

[$htmlPart, $attachmentParts, $inlineParts] = $this->prepareParts();
if (null === $this->text && null === $this->html && !$attachmentParts) {
throw new LogicException('A message must have a text or an HTML part or attachments.');
}

$part = null === $this->text ? null : new TextPart($this->text, $this->textCharset);
if (null !== $htmlPart) {
Expand Down
9 changes: 9 additions & 0 deletions src/Symfony/Component/Mime/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ public function toIterable(): iterable
yield from $body->toIterable();
}

public function ensureValidity()
{
if (!$this->headers->has('From')) {
throw new LogicException('An email must have a "From" header.');
}

parent::ensureValidity();
}

private function generateMessageId(string $email): string
{
return bin2hex(random_bytes(16)).strstr($email, '@');
Expand Down
9 changes: 9 additions & 0 deletions src/Symfony/Component/Mime/RawMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Mime;

use Symfony\Component\Mime\Exception\LogicException;

/**
* @author Fabien Potencier <fabien@symfony.com>
*/
Expand Down Expand Up @@ -51,6 +53,13 @@ public function toIterable(): iterable
$this->message = $message;
}

/**
* @throws LogicException if the message is not valid
*/
public function ensureValidity()
{
}

/**
* @internal
*/
Expand Down

0 comments on commit 3a5fe8c

Please sign in to comment.