Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions src/PhpImap/Mailbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,14 @@ public function getMail(int $mailId, bool $markAsSeen = true): IncomingMail
return $mail;
}

protected function hasAttachmentDisposition(object $partStructure): bool
{
/** @var scalar|array|object|resource|null */
$disposition = $partStructure->disposition ?? null;

return \is_string($disposition) && 'attachment' === \mb_strtolower($disposition);
}

/**
* Download attachment.
*
Expand All @@ -1446,7 +1454,7 @@ public function getMail(int $mailId, bool $markAsSeen = true): IncomingMail
*/
public function downloadAttachment(DataPartInfo $dataInfo, array $params, object $partStructure, bool $emlOrigin = false): IncomingMailAttachment
{
if ('RFC822' == $partStructure->subtype && isset($partStructure->disposition) && 'attachment' == $partStructure->disposition) {
if ('RFC822' == $partStructure->subtype && $this->hasAttachmentDisposition($partStructure)) {
$fileName = \strtolower($partStructure->subtype).'.eml';
} elseif ('ALTERNATIVE' == $partStructure->subtype) {
$fileName = \strtolower($partStructure->subtype).'.eml';
Expand Down Expand Up @@ -1943,9 +1951,7 @@ protected function initMailPart(IncomingMail $mail, object $partStructure, $part

$isAttachment = isset($params['filename']) || isset($params['name']) || isset($partStructure->id);

$dispositionAttachment = (isset($partStructure->disposition) &&
\is_string($partStructure->disposition) &&
'attachment' === \mb_strtolower($partStructure->disposition));
$dispositionAttachment = $this->hasAttachmentDisposition($partStructure);

// ignore contentId on body when mail isn't multipart (https://github.com/barbushin/php-imap/issues/71)
if (
Expand All @@ -1961,7 +1967,7 @@ protected function initMailPart(IncomingMail $mail, object $partStructure, $part
}

// check if the part is a subpart of another attachment part (RFC822)
if ('RFC822' === $partStructure->subtype && isset($partStructure->disposition) && 'attachment' === $partStructure->disposition) {
if ('RFC822' === $partStructure->subtype && $dispositionAttachment) {
// Although we are downloading each part separately, we are going to download the EML to a single file
//incase someone wants to process or parse in another process
$attachment = self::downloadAttachment($dataInfo, $params, $partStructure, false);
Expand Down Expand Up @@ -1993,14 +1999,14 @@ protected function initMailPart(IncomingMail $mail, object $partStructure, $part

if (!empty($partStructure->parts)) {
foreach ($partStructure->parts as $subPartNum => $subPartStructure) {
$not_attachment = (!isset($partStructure->disposition) || 'attachment' !== $partStructure->disposition);
$not_attachment = !$dispositionAttachment;

if (TYPEMESSAGE === $partStructure->type && 'RFC822' === $partStructure->subtype && $not_attachment) {
$this->initMailPart($mail, $subPartStructure, $partNum, $markAsSeen);
} elseif (TYPEMULTIPART === $partStructure->type && 'ALTERNATIVE' === $partStructure->subtype && $not_attachment) {
// https://github.com/barbushin/php-imap/issues/198
$this->initMailPart($mail, $subPartStructure, $partNum, $markAsSeen);
} elseif ('RFC822' === $partStructure->subtype && isset($partStructure->disposition) && 'attachment' === $partStructure->disposition) {
} elseif ('RFC822' === $partStructure->subtype && $dispositionAttachment) {
//If it comes from am EML attachment, download each part separately as a file
$this->initMailPart($mail, $subPartStructure, $partNum.'.'.($subPartNum + 1), $markAsSeen, true);
} else {
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/Fixtures/Mailbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,9 @@ public function getImapOpenOptionsForTests(): int
{
return $this->getImapOpenOptions();
}

public function hasAttachmentDispositionForTests(object $partStructure): bool
{
return $this->hasAttachmentDisposition($partStructure);
}
}
33 changes: 33 additions & 0 deletions tests/unit/MailboxTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,39 @@ public function testSetAttachmentsIgnore(bool $paramValue): void
$this->assertEquals($mailbox->getAttachmentsIgnore(), $paramValue);
}

public function testHasAttachmentDispositionRecognizesMixedCaseAttachment(): void
{
$partStructure = (object) [
'disposition' => 'Attachment',
];

$this->assertTrue($this->getMailbox()->hasAttachmentDispositionForTests($partStructure));
}

public function testDownloadAttachmentTreatsMixedCaseRfc822DispositionAsEmlAttachment(): void
{
$mailbox = new Fixtures\Mailbox($this->imapPath, $this->login, $this->password, null, $this->serverEncoding);
$dataInfo = new Fixtures\DataPartInfo($mailbox, 1, '2', 0, 0);
$dataInfo->setData("From: sender@example.com\r\n\r\nAttachment body");

$partStructure = (object) [
'type' => Mailbox::PART_TYPE_TWO,
'subtype' => 'RFC822',
'disposition' => 'Attachment',
'bytes' => 42,
'encoding' => 0,
'ifid' => 0,
'ifsubtype' => 1,
'ifdescription' => 0,
];

$attachment = $mailbox->downloadAttachment($dataInfo, [], $partStructure);

$this->assertSame('rfc822.eml', $attachment->name);
$this->assertSame('Attachment', $attachment->disposition);
$this->assertFalse($attachment->emlOrigin);
}

/**
* Provides test data for testing encoding.
*
Expand Down
Loading