From 3acc1d04423d3925a9592b5bae5158b9f9d58905 Mon Sep 17 00:00:00 2001 From: Roj Vroemen Date: Thu, 18 Jan 2024 17:43:59 +0100 Subject: [PATCH] [10.x] Fix decoding issue in MailLogTransport (#49727) * Handle multipart messages and only decode content * formatting --------- Co-authored-by: Taylor Otwell --- .../Mail/Transport/LogTransport.php | 40 +++++++++++++++++-- tests/Mail/MailLogTransportTest.php | 27 +++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Mail/Transport/LogTransport.php b/src/Illuminate/Mail/Transport/LogTransport.php index 291251200a48..682169e3eaa7 100644 --- a/src/Illuminate/Mail/Transport/LogTransport.php +++ b/src/Illuminate/Mail/Transport/LogTransport.php @@ -2,6 +2,7 @@ namespace Illuminate\Mail\Transport; +use Illuminate\Support\Str; use Psr\Log\LoggerInterface; use Symfony\Component\Mailer\Envelope; use Symfony\Component\Mailer\SentMessage; @@ -33,17 +34,48 @@ public function __construct(LoggerInterface $logger) */ public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage { - $string = $message->toString(); + $string = Str::of($message->toString()); - if (str_contains($string, 'Content-Transfer-Encoding: quoted-printable')) { - $string = quoted_printable_decode($string); + if ($string->contains('Content-Type: multipart/')) { + $boundary = $string + ->after('boundary=') + ->before("\r\n") + ->prepend('--') + ->append("\r\n"); + + $string = $string + ->explode($boundary) + ->map($this->decodeQuotedPrintableContent(...)) + ->implode($boundary); + } elseif ($string->contains('Content-Transfer-Encoding: quoted-printable')) { + $string = $this->decodeQuotedPrintableContent($string); } - $this->logger->debug($string); + $this->logger->debug((string) $string); return new SentMessage($message, $envelope ?? Envelope::create($message)); } + /** + * Decode the given quoted printable content. + * + * @param string $part + * @return string + */ + protected function decodeQuotedPrintableContent(string $part) + { + if (! str_contains($part, 'Content-Transfer-Encoding: quoted-printable')) { + return $part; + } + + [$headers, $content] = explode("\r\n\r\n", $part, 2); + + return implode("\r\n\r\n", [ + $headers, + quoted_printable_decode($content), + ]); + } + /** * Get the logger for the LogTransport instance. * diff --git a/tests/Mail/MailLogTransportTest.php b/tests/Mail/MailLogTransportTest.php index 3bb0c69a44df..33367d52610c 100644 --- a/tests/Mail/MailLogTransportTest.php +++ b/tests/Mail/MailLogTransportTest.php @@ -2,6 +2,7 @@ namespace Illuminate\Tests\Mail; +use Illuminate\Mail\Attachment; use Illuminate\Mail\Message; use Illuminate\Mail\Transport\LogTransport; use Monolog\Handler\StreamHandler; @@ -59,6 +60,32 @@ public function testItDecodesTheMessageBeforeLogging() $this->assertStringContainsString('https://example.com/reset-password=5e113c71a4c210aff04b3fa66f1b1299', $actualLoggedValue); } + public function testItOnlyDecodesQuotedPrintablePartsOfTheMessageBeforeLogging() + { + $message = (new Message(new Email)) + ->from('noreply@example.com', 'no-reply') + ->to('taylor@example.com', 'Taylor') + ->html(<<<'BODY' + Hi, + + Click here to reset your password. + + All the best, + + Burt & Irving + BODY) + ->text('A text part') + ->attach(Attachment::fromData(fn () => 'My attachment', 'attachment.txt')); + + $actualLoggedValue = $this->getLoggedEmailMessage($message); + + $this->assertStringContainsString('href=', $actualLoggedValue); + $this->assertStringContainsString('Burt & Irving', $actualLoggedValue); + $this->assertStringContainsString('https://example.com/reset-password=5e113c71a4c210aff04b3fa66f1b1299', $actualLoggedValue); + $this->assertStringContainsString('name=attachment.txt', $actualLoggedValue); + $this->assertStringContainsString('filename=attachment.txt', $actualLoggedValue); + } + public function testGetLogTransportWithPsrLogger() { $this->app['config']->set('mail.driver', 'log');