diff --git a/src/PhpImap/Mailbox.php b/src/PhpImap/Mailbox.php index 343e876a..011b497d 100644 --- a/src/PhpImap/Mailbox.php +++ b/src/PhpImap/Mailbox.php @@ -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. * @@ -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'; @@ -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 ( @@ -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); @@ -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 { diff --git a/tests/unit/Fixtures/Mailbox.php b/tests/unit/Fixtures/Mailbox.php index ee97980e..df5f3414 100644 --- a/tests/unit/Fixtures/Mailbox.php +++ b/tests/unit/Fixtures/Mailbox.php @@ -68,4 +68,9 @@ public function getImapOpenOptionsForTests(): int { return $this->getImapOpenOptions(); } + + public function hasAttachmentDispositionForTests(object $partStructure): bool + { + return $this->hasAttachmentDisposition($partStructure); + } } diff --git a/tests/unit/MailboxTest.php b/tests/unit/MailboxTest.php index 75bf7356..30f39b7f 100644 --- a/tests/unit/MailboxTest.php +++ b/tests/unit/MailboxTest.php @@ -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. *