From d83933b20f65e88940a4bf84e3ccf16c97b85474 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 17 Nov 2023 12:56:46 +0000 Subject: [PATCH 1/2] multipart/* and other text/* parts no longer treated as attachments --- src/Service/AbstractMailService.php | 25 ++++++++++--- .../Service/AbstractMailServiceTest.php | 36 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/Service/AbstractMailService.php b/src/Service/AbstractMailService.php index f57f4a9..2e2a42b 100644 --- a/src/Service/AbstractMailService.php +++ b/src/Service/AbstractMailService.php @@ -45,8 +45,10 @@ use Laminas\Mail\Message; use Laminas\Mime\Message as MimeMessage; use Laminas\Mime\Mime; +use Laminas\Mime\Part; use function in_array; +use function strpos; /** * Class AbstractMailService @@ -90,7 +92,7 @@ protected function extractText(Message $message): ?string private function extractTextFromMimeMessage(MimeMessage $message): ?string { foreach ($message->getParts() as $part) { - if ($part->type === 'text/plain' && $part->disposition !== Mime::DISPOSITION_ATTACHMENT) { + if ($this->isType($part, Mime::TYPE_TEXT)) { return $part->getContent(); } } @@ -126,7 +128,7 @@ protected function extractHtml(Message $message): ?string private function extractHtmlFromMimeMessage(MimeMessage $message): ?string { foreach ($message->getParts() as $part) { - if ($part->type === 'text/html' && $part->disposition !== Mime::DISPOSITION_ATTACHMENT) { + if ($this->isType($part, Mime::TYPE_HTML)) { return $part->getContent(); } } @@ -141,6 +143,11 @@ private function extractHtmlFromMimeMessage(MimeMessage $message): ?string return null; } + private function isType(Part $part, string $mimeType): bool + { + return strpos($part->type, $mimeType) === 0 && $part->disposition !== Mime::DISPOSITION_ATTACHMENT; + } + /** * Extract all attachments from a message * @@ -160,10 +167,9 @@ protected function extractAttachments(Message $message): array return []; } - $filter = ['text/plain', 'text/html']; $attachments = []; foreach ($body->getParts() as $part) { - if (!in_array($part->type, $filter) || $part->disposition === Mime::DISPOSITION_ATTACHMENT) { + if ($this->isAttachment($part)) { $attachments[] = $part; } } @@ -171,6 +177,17 @@ protected function extractAttachments(Message $message): array return $attachments; } + private function isAttachment(Part $part): bool + { + if (in_array($part->type, self::MULTIPART_TYPES)) { + return false; + } + if ($part->disposition === Mime::DISPOSITION_ATTACHMENT) { + return true; + } + return strpos($part->type, 'text/') !== 0; + } + /** * Get HTTP client * diff --git a/tests/SlmMailTest/Service/AbstractMailServiceTest.php b/tests/SlmMailTest/Service/AbstractMailServiceTest.php index b511272..0a05405 100644 --- a/tests/SlmMailTest/Service/AbstractMailServiceTest.php +++ b/tests/SlmMailTest/Service/AbstractMailServiceTest.php @@ -11,6 +11,8 @@ use SlmMail\Service\AbstractMailService; use PHPUnit\Framework\TestCase; +use function current; + /** * @covers \SlmMail\Service\AbstractMailService */ @@ -176,4 +178,38 @@ public function testExtractHtmlFromMultipartMessageReturnsString(): void $this->service->send($message); self::assertSame($expected, trim($this->service->html)); } + + /** + * @dataProvider extractAttachmentProvider + */ + public function testExtractAttachment(string $mimeType, string $disposition, bool $expected): void + { + $message = new Message(); + $body = new MimeMessage(); + $attachment = (new Part('Foo')) + ->setType($mimeType) + ->setDisposition($disposition); + $body->addPart($attachment); + $message->setBody($body); + + $this->service->send($message); + if ($expected) { + $actual = current($this->service->attachments); + self::assertSame($attachment, $actual); + } else { + self::assertEmpty($this->service->attachments); + } + } + + public static function extractAttachmentProvider(): array + { + return [ + 'html' => [Mime::TYPE_HTML, '', false], + 'text' => [Mime::TYPE_TEXT, '', false], + 'xml' => [Mime::TYPE_XML, '', false], + 'multipart/alternative' => [Mime::MULTIPART_ALTERNATIVE, '', false], + 'xml attachment' => [Mime::TYPE_XML, Mime::DISPOSITION_ATTACHMENT, true], + 'pdf' => ['application/pdf', '', true], + ]; + } } From 4158126d091dd94bb652cc8a860be66a93e47bbc Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 17 Nov 2023 14:03:47 +0000 Subject: [PATCH 2/2] Fix issue extracting text and html from multipart messages with encoding set --- src/Service/AbstractMailService.php | 4 ++-- .../Service/AbstractMailServiceTest.php | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Service/AbstractMailService.php b/src/Service/AbstractMailService.php index 2e2a42b..471efa7 100644 --- a/src/Service/AbstractMailService.php +++ b/src/Service/AbstractMailService.php @@ -93,7 +93,7 @@ private function extractTextFromMimeMessage(MimeMessage $message): ?string { foreach ($message->getParts() as $part) { if ($this->isType($part, Mime::TYPE_TEXT)) { - return $part->getContent(); + return $part->getRawContent(); } } foreach ($message->getParts() as $part) { @@ -129,7 +129,7 @@ private function extractHtmlFromMimeMessage(MimeMessage $message): ?string { foreach ($message->getParts() as $part) { if ($this->isType($part, Mime::TYPE_HTML)) { - return $part->getContent(); + return $part->getRawContent(); } } foreach ($message->getParts() as $part) { diff --git a/tests/SlmMailTest/Service/AbstractMailServiceTest.php b/tests/SlmMailTest/Service/AbstractMailServiceTest.php index 0a05405..0ee77b1 100644 --- a/tests/SlmMailTest/Service/AbstractMailServiceTest.php +++ b/tests/SlmMailTest/Service/AbstractMailServiceTest.php @@ -12,6 +12,8 @@ use PHPUnit\Framework\TestCase; use function current; +use function str_repeat; +use function trim; /** * @covers \SlmMail\Service\AbstractMailService @@ -58,13 +60,14 @@ public function testExtractTextFromEmptyBodyReturnsNull(): void public function testExtractTextFromTwoPartMessageReturnsString(): void { - $expected = 'Foo'; + $expected = trim(str_repeat('Foo ', 100)); $message = new Message(); $body = new MimeMessage(); $body->addPart(new Part('')); $body->addPart( (new Part($expected)) ->setType(Mime::TYPE_TEXT) + ->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE) ); $message->setBody($body); @@ -89,7 +92,7 @@ public function testExtractTextFromTextAttachmentReturnsNull(): void public function testExtractTextFromMultipartMessageReturnsString(): void { - $expected = 'Foo'; + $expected = trim(str_repeat('Foo ', 100)); $message = new Message(); $body = new MimeMessage(); $contentPart = new MimeMessage(); @@ -97,6 +100,7 @@ public function testExtractTextFromMultipartMessageReturnsString(): void $contentPart->addPart( (new Part($expected)) ->setType(Mime::TYPE_TEXT) + ->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE) ); $body->addPart( (new Part($contentPart->generateMessage())) @@ -128,13 +132,14 @@ public function testExtractHtmlFromEmptyBodyReturnsNull(): void public function testExtractHtmlFromTwoPartMessageReturnsString(): void { - $expected = 'Foo'; + $expected = trim(str_repeat('Foo ', 100)); $message = new Message(); $body = new MimeMessage(); $body->addPart(new Part('')); $body->addPart( (new Part($expected)) ->setType(Mime::TYPE_HTML) + ->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE) ); $message->setBody($body); @@ -159,7 +164,7 @@ public function testExtractHtmlFromHtmlAttachmentReturnsNull(): void public function testExtractHtmlFromMultipartMessageReturnsString(): void { - $expected = 'Foo'; + $expected = trim(str_repeat('Foo ', 100)); $message = new Message(); $body = new MimeMessage(); $contentPart = new MimeMessage(); @@ -167,6 +172,7 @@ public function testExtractHtmlFromMultipartMessageReturnsString(): void $contentPart->addPart( (new Part($expected)) ->setType(Mime::TYPE_HTML) + ->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE) ); $body->addPart( (new Part($contentPart->generateMessage()))