diff --git a/REUSE.toml b/REUSE.toml index b39e489efc..8848fa3167 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -96,7 +96,7 @@ SPDX-FileCopyrightText = "2022 Nextcloud GmbH and Nextcloud contributors" SPDX-License-Identifier = "AGPL-3.0-or-later" [[annotations]] -path = ["src/tests/data/user@imap.localhost.chain.p12", "src/tests/data/user@imap.localhost.crt", "src/tests/data/user@imap.localhost.key", "src/tests/data/user@imap.localhost.p12", "tests/data/smime-certs/cn-only@imap.localhost.crt", "tests/data/smime-certs/cn-only@imap.localhost.key", "tests/data/smime-certs/imap.localhost.ca.crt", "tests/data/smime-certs/domain.tld.ca.crt", "tests/data/smime-certs/user@domain.tld.crt", "tests/data/smime-certs/user@domain.tld.key", "tests/data/smime-certs/user@imap.localhost.crt", "tests/data/smime-certs/user@imap.localhost.key", "tests/data/decrypted-message-body.txt", "tests/data/decrypted-signed-opaque-message-body.txt", "tests/data/encrypted-message.txt", "tests/data/encrypted-signed-opaque-message.txt", "tests/data/sieve-vacation-cleaned.sieve", "tests/data/sieve-vacation-off.sieve", "tests/data/sieve-vacation-on-no-end-date.sieve", "tests/data/sieve-vacation-on-no-tz.sieve", "tests/data/sieve-vacation-on-special-chars-message.sieve", "tests/data/sieve-vacation-on-special-chars-subject.sieve", "tests/data/sieve-vacation-on-subject-placeholder.sieve", "tests/data/sieve-vacation-on.sieve", "tests/data/signed-message.txt", "tests/data/signed-opaque-message.txt"] +path = ["src/tests/data/user@imap.localhost.chain.p12", "src/tests/data/user@imap.localhost.crt", "src/tests/data/user@imap.localhost.key", "src/tests/data/user@imap.localhost.p12", "tests/data/smime-certs/cn-only@imap.localhost.crt", "tests/data/smime-certs/cn-only@imap.localhost.key", "tests/data/smime-certs/imap.localhost.ca.crt", "tests/data/smime-certs/domain.tld.ca.crt", "tests/data/smime-certs/user@domain.tld.crt", "tests/data/smime-certs/user@domain.tld.key", "tests/data/smime-certs/user@imap.localhost.crt", "tests/data/smime-certs/user@imap.localhost.key", "tests/data/decrypted-message-body.txt", "tests/data/decrypted-signed-opaque-message-body.txt", "tests/data/encrypted-message.txt", "tests/data/encrypted-signed-opaque-message.txt", "tests/data/nobody-message.txt", "tests/data/sieve-vacation-cleaned.sieve", "tests/data/sieve-vacation-off.sieve", "tests/data/sieve-vacation-on-no-end-date.sieve", "tests/data/sieve-vacation-on-no-tz.sieve", "tests/data/sieve-vacation-on-special-chars-message.sieve", "tests/data/sieve-vacation-on-special-chars-subject.sieve", "tests/data/sieve-vacation-on-subject-placeholder.sieve", "tests/data/sieve-vacation-on.sieve", "tests/data/signed-message.txt", "tests/data/signed-opaque-message.txt"] precedence = "aggregate" SPDX-FileCopyrightText = "2023 Nextcloud GmbH and Nextcloud contributors" SPDX-License-Identifier = "AGPL-3.0-or-later" diff --git a/lib/IMAP/ImapMessageFetcher.php b/lib/IMAP/ImapMessageFetcher.php index 693465df79..31621aa72c 100644 --- a/lib/IMAP/ImapMessageFetcher.php +++ b/lib/IMAP/ImapMessageFetcher.php @@ -217,6 +217,8 @@ public function fetchMessage(?Horde_Imap_Client_Data_Fetch $fetch = null): IMAPM $bodyPartId = $structure->findBody(); if (!is_null($bodyPartId)) { $this->getPart($structure[$bodyPartId], $bodyPartId, $isEncrypted || $isSigned); + } else { + $this->getPart($structure, '1', $isEncrypted || $isSigned); } } } elseif (is_null($fetch)) { diff --git a/tests/Integration/IMAP/ImapMessageFetcherIntegrationTest.php b/tests/Integration/IMAP/ImapMessageFetcherIntegrationTest.php index 7c5d2a73fd..bc90afd1e5 100644 --- a/tests/Integration/IMAP/ImapMessageFetcherIntegrationTest.php +++ b/tests/Integration/IMAP/ImapMessageFetcherIntegrationTest.php @@ -231,4 +231,25 @@ public function testFetchMessageWithOpaqueSignedMessage(): void { $this->assertTrue($message->isSigned()); $this->assertTrue($message->isSignatureValid()); } + + public function testFetchMessageWithoutBody(): void { + $mimeMessage = file_get_contents(__DIR__ . '/../../data/nobody-message.txt'); + $uid = $this->saveMimeMessage('INBOX', $mimeMessage); + $fetcher = $this->fetcherFactory + ->build( + $uid, + 'INBOX', + $this->getTestClient(), + $this->account->getUserId() + ) + ->withBody(true); + + $message = $fetcher->fetchMessage(); + + $this->assertFalse($message->isEncrypted()); + $this->assertCount(1, $message->attachments); + $attachment = $message->attachments[0]; + $this->assertEquals('attach.zip', $attachment['fileName']); + $this->assertSame('application/zip', $attachment['mime']); + } } diff --git a/tests/data/nobody-message.txt b/tests/data/nobody-message.txt new file mode 100644 index 0000000000..7525fd6e28 --- /dev/null +++ b/tests/data/nobody-message.txt @@ -0,0 +1,24 @@ +Return-Path: +Delivered-To: user@imap.localhost +Received: from 0531f16fe281 + by 0531f16fe281 (Dovecot) with LMTP id doHuJnh71GVlNwEAcWVang + for ; Fri, 3 Jul 2026 00:35:01 +0000 +Received: from anton.vpn (unknown [172.17.0.1]) + by 0531f16fe281 (Postfix) with ESMTP id 636C81E56 + for ; Fri, 3 Jul 2026 00:35:01 +0000 (UTC) +From: debug@imap.localhost +To: user@imap.localhost +Cc: +Subject: Just a file +Message-ID: <20240220101506.Horde.UpbNuMMPVearQMy_n71Hu7U@anton.vpn> +User-Agent: Horde Application Framework 5 +Date: Fri, 3 Jul 2026 00:35:01 +0000 +Content-Type: application/zip; name=attach.zip +MIME-Version: 1.0 +Content-Disposition: attachment; filename=attach.zip +Content-Transfer-Encoding: base64 + +UEsDBAoAAAAAAC8E41wWNZYxBgAAAAYAAAAJABwAaGVsbG8udHh0VVQJAAM650ZqTedGanV4CwAB +BOgDAAAE6AMAAEhlbGxvClBLAQIeAwoAAAAAAC8E41wWNZYxBgAAAAYAAAAJABgAAAAAAAEAAAC0 +gQAAAABoZWxsby50eHRVVAUAAzrnRmp1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBPAAAASQAA +AAAA