Skip to content

Commit

Permalink
Merge pull request #158 from kynx/extract-from-multipart-messages
Browse files Browse the repository at this point in the history
Fix extracting attachments and encoding in multipart messages
  • Loading branch information
roelvanduijnhoven authored Nov 20, 2023
2 parents b40a982 + 4158126 commit 4ff677e
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 10 deletions.
29 changes: 23 additions & 6 deletions src/Service/AbstractMailService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -90,8 +92,8 @@ 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) {
return $part->getContent();
if ($this->isType($part, Mime::TYPE_TEXT)) {
return $part->getRawContent();
}
}
foreach ($message->getParts() as $part) {
Expand Down Expand Up @@ -126,8 +128,8 @@ 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) {
return $part->getContent();
if ($this->isType($part, Mime::TYPE_HTML)) {
return $part->getRawContent();
}
}
foreach ($message->getParts() as $part) {
Expand All @@ -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
*
Expand All @@ -160,17 +167,27 @@ 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;
}
}

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
*
Expand Down
50 changes: 46 additions & 4 deletions tests/SlmMailTest/Service/AbstractMailServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
use SlmMail\Service\AbstractMailService;
use PHPUnit\Framework\TestCase;

use function current;
use function str_repeat;
use function trim;

/**
* @covers \SlmMail\Service\AbstractMailService
*/
Expand Down Expand Up @@ -56,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);

Expand All @@ -87,14 +92,15 @@ 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();
$contentPart->addPart(new Part());
$contentPart->addPart(
(new Part($expected))
->setType(Mime::TYPE_TEXT)
->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE)
);
$body->addPart(
(new Part($contentPart->generateMessage()))
Expand Down Expand Up @@ -126,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);

Expand All @@ -157,14 +164,15 @@ 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();
$contentPart->addPart(new Part());
$contentPart->addPart(
(new Part($expected))
->setType(Mime::TYPE_HTML)
->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE)
);
$body->addPart(
(new Part($contentPart->generateMessage()))
Expand All @@ -176,4 +184,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],
];
}
}

0 comments on commit 4ff677e

Please sign in to comment.