From fa1c53786878da321926ab4a41ad1224af7e1d6e Mon Sep 17 00:00:00 2001 From: John Bieling Date: Mon, 10 Jun 2024 22:25:03 +0200 Subject: [PATCH] Bug 1852746 - Fix listAttachments() being empty for encrypted messages. r=mkmelin This is a regression from switching to the MimeTreeEmitter. The old implementation always tried to decrpypt the message for attachment extraction. The new implementation did not, which is fixed by this patch. This also adds an extended test to actually check listAttachments() on an encrypted message (including nested messages). Note: getRaw() and getFull() have an option to work on the original or on the decrypted message. This patch does not add the same option for listAttachments(). We currently do not handle encrypted parts as "attachments" (this is why listAttachments() did not return the raw encrypted part before this patch) and there has not been a request to access the raw enrcrypted part. We may come back to this at a later time. The changes in `utils.js` are fixing an incomplete type check. Only `boolean`, `number` and `string` variables where type checked, but not `date` for example. This patch also adds some more verbose output to make it easier to track errors. Differential Revision: https://phabricator.services.mozilla.com/D212990 --HG-- extra : amend_source : 4bb7d6715e9337f42d2da1837aa67b31b9339a0b --- .../extensions/ExtensionMessages.sys.mjs | 4 +- .../extensions/schemas/messages.json | 2 +- .../extensions/test/xpcshell/data/utils.js | 57 +- .../messages/encryptedNestedMessages.eml | 51 ++ .../test_ext_messages_encrypted_attachment.js | 598 ++++++++++++++++++ .../extensions/test/xpcshell/xpcshell.ini | 1 + 6 files changed, 700 insertions(+), 13 deletions(-) create mode 100644 mail/components/extensions/test/xpcshell/messages/encryptedNestedMessages.eml create mode 100644 mail/components/extensions/test/xpcshell/test_ext_messages_encrypted_attachment.js diff --git a/mail/components/extensions/ExtensionMessages.sys.mjs b/mail/components/extensions/ExtensionMessages.sys.mjs index 225add25e5..e811d77f77 100644 --- a/mail/components/extensions/ExtensionMessages.sys.mjs +++ b/mail/components/extensions/ExtensionMessages.sys.mjs @@ -597,7 +597,7 @@ export class MsgHdrProcessor { * @returns {Promise} */ async getAttachmentParts(options) { - const rawMessage = await this.getOriginalMessage(); + const rawMessage = await this.getDecryptedMessage(); const mimeTree = this.#parseMessage( rawMessage, { @@ -645,7 +645,7 @@ export class MsgHdrProcessor { * @returns {Promise} */ async getAttachmentPart(partName, options) { - const rawMessage = await this.getOriginalMessage(); + const rawMessage = await this.getDecryptedMessage(); const includeRaw = options?.includeRaw ?? false; const mimeTree = this.#parseMessage( rawMessage, diff --git a/mail/components/extensions/schemas/messages.json b/mail/components/extensions/schemas/messages.json index 3f903473c9..a12e2fe3ac 100644 --- a/mail/components/extensions/schemas/messages.json +++ b/mail/components/extensions/schemas/messages.json @@ -201,7 +201,7 @@ "size": { "type": "integer", "optional": true, - "description": "The size of this part. The size of message/* parts is not the actual message size (on disc), but the total size of its decoded body parts, excluding headers." + "description": "The size of this part. The size of parts with content type message/rfc822 is not the actual message size (on disc), but the total size of its decoded body parts, excluding headers." } } }, diff --git a/mail/components/extensions/test/xpcshell/data/utils.js b/mail/components/extensions/test/xpcshell/data/utils.js index e3a2ace8a7..4b5f8200cd 100644 --- a/mail/components/extensions/test/xpcshell/data/utils.js +++ b/mail/components/extensions/test/xpcshell/data/utils.js @@ -27,31 +27,46 @@ function assertDeepEqual( } } -function assertDeepEqualNested(expected, actual, strict) { +function assertDeepEqualNested(expected, actual, strict, description) { if (expected === null) { - browser.test.assertTrue(actual === null); + browser.test.assertTrue(actual === null, description); return actual === null; } if (expected === undefined) { - browser.test.assertTrue(actual === undefined); + browser.test.assertTrue(actual === undefined, description); return actual === undefined; } + browser.test.assertEq( + typeof expected, + typeof actual, + `${description} (type check)` + ); if (["boolean", "number", "string"].includes(typeof expected)) { - browser.test.assertEq(typeof expected, typeof actual); - browser.test.assertEq(expected, actual); + browser.test.assertEq(expected, actual, `${description} (value check)`); return typeof expected == typeof actual && expected == actual; } if (Array.isArray(expected)) { - browser.test.assertTrue(Array.isArray(actual)); - browser.test.assertEq(expected.length, actual.length); + browser.test.assertTrue(Array.isArray(actual), `${description} (exist)`); + browser.test.assertEq( + expected.length, + actual.length, + `${description} (length check)` + ); let ok = 0; let all = 0; for (let i = 0; i < expected.length; i++) { all++; - if (assertDeepEqualNested(expected[i], actual[i], strict)) { + if ( + assertDeepEqualNested( + expected[i], + actual[i], + strict, + `Array entry #${i} is correct` + ) + ) { ok++; } } @@ -66,14 +81,36 @@ function assertDeepEqualNested(expected, actual, strict) { const lengthOk = strict ? expectedKeys.length == actualKeys.length : expectedKeys.length <= actualKeys.length; - browser.test.assertTrue(lengthOk); + if (strict) { + browser.test.assertEq( + expectedKeys.length, + actualKeys.length, + `strict length check for ${description}, expected exactly: ${JSON.stringify( + expectedKeys + )}, actual: ${JSON.stringify(actualKeys)}` + ); + } else { + browser.test.assertTrue( + lengthOk, + `lazy length check for ${description}, expected at least: ${JSON.stringify( + expectedKeys + )}, actual: ${JSON.stringify(actualKeys)}` + ); + } let ok = 0; let all = 0; for (const key of expectedKeys) { all++; browser.test.assertTrue(actualKeys.includes(key), `Key ${key} exists`); - if (assertDeepEqualNested(expected[key], actual[key], strict)) { + if ( + assertDeepEqualNested( + expected[key], + actual[key], + strict, + `Key ${key} is correct` + ) + ) { ok++; } } diff --git a/mail/components/extensions/test/xpcshell/messages/encryptedNestedMessages.eml b/mail/components/extensions/test/xpcshell/messages/encryptedNestedMessages.eml new file mode 100644 index 0000000000..ee5af068ba --- /dev/null +++ b/mail/components/extensions/test/xpcshell/messages/encryptedNestedMessages.eml @@ -0,0 +1,51 @@ +Message-ID: +Date: Fri, 20 May 2000 00:29:55 -0400 +To: Alice Lovelace +From: Batman +Subject: Encrypted, attached message with attachments +Mime-Version: 1.0 +Content-Type: multipart/encrypted; + protocol="application/pgp-encrypted"; + boundary="------------M20GNGI1rLZhtbYD7WFekIXJ" + +This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) +--------------M20GNGI1rLZhtbYD7WFekIXJ +Content-Type: application/pgp-encrypted +Content-Description: PGP/MIME version identification + +Version: 1 + +--------------M20GNGI1rLZhtbYD7WFekIXJ +Content-Type: application/octet-stream; name="encrypted.asc" +Content-Description: OpenPGP encrypted message +Content-Disposition: inline; filename="encrypted.asc" + +-----BEGIN PGP MESSAGE----- + +hF4DR2b2udXyHrYSAQdAkHA/pErV3hMoJphrF/glWHFydVs+PeGud6VzdyG6rF4w +s0EzVZ9peVVd0+XewFpTTasMWEB9WcC1s/bVMTY2cz2TbHLufxp8XDUwyOwCoSvn +0ukBxBk5ZqNqYNGI/LZ/9zBltAWmrxTt00BKzNuigQyhWTMFZx1SMbTamro0+ej0 +9U61CaCfLT31b+DsMyOj+FQ+0DPq4rEZ5Oek9atkaRQ1N9kbvaLmgJB+Ya+kJTnQ +jPZEPrtHAVkSZSciPJxwkluacZMrRKWExq1HHoSoLI5NG0gIkT7+lqGX3e2HOFm8 +NuKvOyOk6Pza0v15vKf8NlXjwfgzR8ZuaNaMjO6q5ismCfm6qpLFG+YGRw7KydOQ +ZiQ2pAWhBQEaDJuDTGM9KT2onQjkFl4okIkMRU7dapjBAZpM01t3g1YDss6UHMil +8zRT1dlEdeoCIQiRLJykCHsSVAAujWLGLdMQt9oYekbiTC0U56t9tbQrO8P7ZB2F +JEHeNcKRAxGod7WSl8Nbxfor8mQ8zuxUMjEdhmrBzsKTcfteTYLeMbrcXslguxxD +LDrbM7MevUTjfPJGAF9B+OMOMnyw1g5El/tK+wEFE3zv03EBEtEHjl4RPDPIzfZ3 +ozKVPkzEmLkZPHMN2yKJzT2nsKI8RSqpoTxfk3QlKSrVHTaZhQDSTcorzFXLDL8Y +vdXQoozFyTfPVd3PHBp/L2tfnYZDpwqraicPK4eX0yQ+eoN1W5qFUXHFHcZFFyPo +InJsYtEBABGWtRWcZY58BX34/Fi8mHyrAWV6gRnAKVO3ecElfvgHgNdoufJzHLEY +b/B+I1UfRxKX37vrlPtszHpEJLk6+EYPHcPA5kjmDi4tMm3NkJJI6FBnybulzNRC +bgHnVKlJgJLoiRbKC7fbuOtM523+krkWlKPZhEXs+qY9PjIzaEpmAIUE64gPnCiT +OcyWQjG3hX2lo9E9CyBlI0SvyYkdHnbKAWIeZNd/k7STK99actjPg2Wn0XloYpx4 +/ldupx5oYAhcQW7lKHrczsEqXp4yi+f7KFEXVeerVxNuaSaBSpDIqTSQ82GVotB1 +3iGJFzqcpykeYVJZtYylMxRE5gKyU4wBkzPO7Thsxo8DVFcRkFAslnknEk2kqNf6 +fKiWh7k0cqYvldLTVhbNkn/Tk7XXWlFFIFlqpOB8cXqSgcUp09PxQHHQB8nlftJK +627u7FBhrH6J01YbuLpHP+aO8pykNrS+JgvOvYxa9aGDmWSz70lDmp27hPGPvpWF +UU74y4xTcBtPB1xM580zvDOZULDQHmTmNyGzcOlWvGXpCbcDzeYyJRKb2+arZ7CM +uQj5Kyari1l6fDttXtuVVvoaHpCk8h5+IqwVTWi2qqoK0C6VgMQnBYs2EnfjbU/I +ywL4O+JDUGkxFBjkSrwyQAUY3LoA5CqaxApOwCPbP/veHpLYOuMFIkw= +=fK7D +-----END PGP MESSAGE----- + +--------------M20GNGI1rLZhtbYD7WFekIXJ-- diff --git a/mail/components/extensions/test/xpcshell/test_ext_messages_encrypted_attachment.js b/mail/components/extensions/test/xpcshell/test_ext_messages_encrypted_attachment.js new file mode 100644 index 0000000000..48ba80d0a7 --- /dev/null +++ b/mail/components/extensions/test/xpcshell/test_ext_messages_encrypted_attachment.js @@ -0,0 +1,598 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var { FileUtils } = ChromeUtils.importESModule( + "resource://gre/modules/FileUtils.sys.mjs" +); +var { ExtensionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/ExtensionXPCShellUtils.sys.mjs" +); +var { OpenPGPTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/mail/OpenPGPTestUtils.sys.mjs" +); + +const OPENPGP_TEST_DIR = do_get_file("../../../../test/browser/openpgp"); +const OPENPGP_KEY_PATH = PathUtils.join( + OPENPGP_TEST_DIR.path, + "data", + "keys", + "alice@openpgp.example-0xf231550c4f47e38e-secret.asc" +); + +add_setup(async () => { + await OpenPGPTestUtils.initOpenPGP(); + + const _account = createAccount(); + const _identity = addIdentity(_account); + const _folder = await createSubfolder( + _account.incomingServer.rootFolder, + "test1" + ); + + const [id] = await OpenPGPTestUtils.importPrivateKey( + null, + new FileUtils.File(OPENPGP_KEY_PATH) + ); + _identity.setUnicharAttribute("openpgp_key_id", id); + + await createMessageFromFile( + _folder, + do_get_file("messages/encryptedNestedMessages.eml").path + ); +}); + +add_task(async function test_messages_encrypted_attachment() { + const extension = ExtensionTestUtils.loadExtension({ + files: { + "background.js": async () => { + const [folder] = await browser.folders.query({ name: "test1" }); + const { accountId } = folder; + const { type } = await browser.accounts.get(accountId); + + const { messages } = await browser.messages.list(folder.id); + browser.test.assertEq(1, messages.length); + browser.test.assertEq(1, messages[0].id); + + // Get the raw decrypted content of the message (id: 1) via getRaw(). + const raw1file = await browser.messages.getRaw(1, { decrypt: true }); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(raw1file instanceof File); + browser.test.assertEq("message-1.eml", raw1file.name); + browser.test.assertEq(type == "imap" ? 4024 : 3971, raw1file.size); + const raw1content = await raw1file.text(); + browser.test.assertTrue( + !raw1content.includes("-----BEGIN PGP MESSAGE-----"), + "getRaw() of the outer message should be decrypted" + ); + browser.test.assertTrue( + raw1content.includes(""), + "getRaw() of the outer message should include the message ID of the outer message" + ); + browser.test.assertTrue( + raw1content.includes(""), + "getRaw() of the outer message should include the message ID of the nested message" + ); + browser.test.assertTrue( + raw1content.includes(""), + "getRaw() of the outer message should include the message ID of the inner nested message" + ); + + // Get the full decrypted content of the message (id: 1) via getFull(). + const full1 = await browser.messages.getFull(1, { decrypt: true }); + window.assertDeepEqual( + { + contentType: "message/rfc822", + partName: "", + size: 2884, + decryptionStatus: "success", + headers: { + "message-id": [""], + date: ["Fri, 20 May 2000 00:29:55 -0400"], + to: ["Alice Lovelace "], + from: ["Batman "], + subject: ["Encrypted, attached message with attachments"], + "mime-version": ["1.0"], + "content-type": ["message/rfc822"], + }, + parts: [ + { + contentType: "multipart/mixed", + headers: { + "content-type": [ + 'multipart/mixed; boundary="------------M20GNGI1rLZhtbYD7WFekIXJ"', + ], + }, + size: 2884, + partName: "1", + parts: [ + { + contentType: "multipart/mixed", + headers: { + "content-type": [ + 'multipart/mixed; boundary="------------49CVLb1N6p6Spdka4qq7Naeg"', + ], + }, + size: 2884, + partName: "1.1", + parts: [ + { + contentType: "text/html", + headers: { + "content-type": ["text/html; charset=UTF-8"], + "content-transfer-encoding": ["7bit"], + }, + size: 248, + partName: "1.1.1", + body: '\n \n\n \n \n \n

This message has one normal attachment and one email attachment,\n which itself has 3 attachments.
\n

\n \n', + }, + { + contentType: "message/rfc822", + headers: { + "content-type": [ + 'message/rfc822; charset=UTF-8; name="message1.eml"', + ], + "content-disposition": [ + 'attachment; filename="message1.eml"', + ], + "content-transfer-encoding": ["7bit"], + }, + size: 2517, + partName: "1.1.2", + name: "message1.eml", + }, + { + contentType: "image/png", + headers: { + "content-type": ["image/png;"], + "content-transfer-encoding": ["base64"], + "content-disposition": [ + 'attachment; filename="yellowPixel.png"', + ], + }, + size: 119, + partName: "1.1.3", + name: "yellowPixel.png", + }, + ], + }, + ], + }, + ], + }, + full1, + "getFull() for the outer message should be correct" + ); + + // List the attachments from the decrypted outer message. + // Note: listAttachments() is always acting on the decrypted message. + const attachments1 = await browser.messages.listAttachments(1); + window.assertDeepEqual( + [ + { + contentType: "message/rfc822", + name: "message1.eml", + size: 442, + partName: "1.1.2", + message: { + id: 2, + date: new Date("2000-05-17T23:32:47.000Z"), + author: "Superman ", + recipients: ["Jimmy "], + ccList: [], + bccList: [], + subject: "Test message 1", + read: false, + new: false, + headersOnly: false, + flagged: false, + junk: false, + junkScore: 0, + headerMessageId: "sample-attached.eml@mime.sample", + size: 442, + tags: [], + external: true, + }, + }, + { + contentType: "image/png", + name: "yellowPixel.png", + size: 119, + partName: "1.1.3", + }, + ], + attachments1, + "Attachments of the outer message should be correct", + { + strict: true, + } + ); + + // Get the actual attachments of the outer message as File objects. + // Note: getAttachmentFile() is always acting on the decrypted message. + const file112 = await browser.messages.getAttachmentFile(1, "1.1.2"); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(file112 instanceof File); + browser.test.assertEq("message1.eml", file112.name); + browser.test.assertEq(2517, file112.size); + const file112content = await file112.text(); + browser.test.assertTrue( + !file112content.includes(""), + "file112content should not include the message ID of the outer message" + ); + browser.test.assertTrue( + file112content.includes(""), + "file112content should include the message ID of the nested message" + ); + browser.test.assertTrue( + file112content.includes(""), + "file112content should include the message ID of the inner nested message" + ); + + const file113 = await browser.messages.getAttachmentFile(1, "1.1.3"); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(file113 instanceof File); + browser.test.assertEq("yellowPixel.png", file113.name); + browser.test.assertEq(119, file113.size); + + // Get the raw content of the nested message (id: 2) via getRaw(). + const raw2file = await browser.messages.getRaw(2); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(raw2file instanceof File); + browser.test.assertEq("message-2.eml", raw2file.name); + browser.test.assertEq(2517, raw2file.size); + const raw2content = await raw2file.text(); + browser.test.assertEq( + raw2content, + file112content, + "getRaw() of the attached message and getAttachmentFile() of the attached message should be identical" + ); + + // Get the full content of the nested message via getFull(). + const full2 = await browser.messages.getFull(2); + window.assertDeepEqual( + { + contentType: "message/rfc822", + partName: "", + size: 1180, + decryptionStatus: "none", + headers: { + "message-id": [""], + from: ["Superman "], + to: ["Jimmy "], + subject: ["Test message 1"], + date: ["Wed, 17 May 2000 19:32:47 -0400"], + "mime-version": ["1.0"], + "content-type": ["message/rfc822"], + }, + parts: [ + { + contentType: "multipart/mixed", + headers: { + "content-type": [ + 'multipart/mixed;\tboundary="----=_NextPart_000_0002_01BFC036.AE309650"', + ], + }, + size: 1180, + partName: "1", + parts: [ + { + contentType: "text/plain", + headers: { + "content-type": ['text/plain;\tcharset="iso-8859-1"'], + "content-transfer-encoding": ["7bit"], + }, + size: 35, + partName: "1.1", + body: "Message with multiple attachments.\n", + }, + { + contentType: "image/png", + headers: { + "content-type": ['image/png;\tname="whitePixel.png"'], + "content-transfer-encoding": ["base64"], + "content-disposition": [ + 'attachment;\tfilename="whitePixel.png"', + ], + }, + size: 69, + partName: "1.2", + name: "whitePixel.png", + }, + { + contentType: "image/png", + headers: { + "content-type": ['image/png;\tname="greenPixel.png"'], + "content-transfer-encoding": ["base64"], + "content-disposition": ["attachment"], + }, + size: 119, + partName: "1.3", + name: "greenPixel.png", + }, + { + contentType: "image/png", + headers: { + "content-type": ["image/png"], + "content-transfer-encoding": ["base64"], + "content-disposition": [ + 'attachment;\tfilename="redPixel.png"', + ], + }, + size: 119, + partName: "1.4", + name: "redPixel.png", + }, + { + contentType: "message/rfc822", + headers: { + "content-type": [ + 'message/rfc822; charset=UTF-8; name="message2.eml"', + ], + "content-disposition": [ + 'attachment; filename="message2.eml"', + ], + "content-transfer-encoding": ["7bit"], + }, + size: 838, + partName: "1.5", + name: "message2.eml", + }, + ], + }, + ], + }, + full2, + "getFull() of the nested message should be correct" + ); + + // List the attachments from the nested message. + const attachments2 = await browser.messages.listAttachments(2); + window.assertDeepEqual( + [ + { + contentType: "image/png", + name: "whitePixel.png", + size: 69, + partName: "1.2", + }, + { + contentType: "image/png", + name: "greenPixel.png", + size: 119, + partName: "1.3", + }, + { + contentType: "image/png", + name: "redPixel.png", + size: 119, + partName: "1.4", + }, + { + contentType: "message/rfc822", + name: "message2.eml", + size: 100, + partName: "1.5", + message: { + id: 3, + date: new Date("2000-05-16T23:32:47.000Z"), + author: "Jimmy ", + recipients: ["Superman "], + ccList: [], + bccList: [], + subject: "Test message 2", + read: false, + new: false, + headersOnly: false, + flagged: false, + junk: false, + junkScore: 0, + headerMessageId: "sample-nested-attached.eml@mime.sample", + size: 100, + tags: [], + external: true, + }, + }, + ], + attachments2, + "Attachments of the nested message should be correct", + { + strict: true, + } + ); + + // Get the actual attachments of the nested message as File objects. + const files = new Map(); + const expectedAttachments2 = [ + { + name: "whitePixel.png", + size: 69, + partName: "1.2", + }, + { + name: "greenPixel.png", + size: 119, + partName: "1.3", + }, + { + name: "redPixel.png", + size: 119, + partName: "1.4", + }, + { + name: "message2.eml", + size: 838, + partName: "1.5", + }, + ]; + for (const expectedAttachment2 of expectedAttachments2) { + const f = await browser.messages.getAttachmentFile( + 2, + expectedAttachment2.partName + ); + files.set(expectedAttachment2.partName, f); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(f instanceof File); + browser.test.assertEq( + expectedAttachment2.name, + f.name, + `Name of part ${expectedAttachment2.partName} should be correct` + ); + browser.test.assertEq( + expectedAttachment2.size, + f.size, + `Size of part ${expectedAttachment2.partName} should be correct` + ); + } + + // Check content of the inner nested message (part 1.5, id: 3). + const file15content = await files.get("1.5").text(); + browser.test.assertTrue( + !file15content.includes(""), + "file15content should not include the message ID of the outer message" + ); + browser.test.assertTrue( + !file15content.includes(""), + "file15content should not include the message ID of the nested message" + ); + browser.test.assertTrue( + file15content.includes(""), + "file15content should include the message ID of the inner nested message" + ); + + // Get the raw content of the inner nested message (part 1.5, id: 3) via + // getRaw(). + const raw3file = await browser.messages.getRaw(3); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(raw3file instanceof File); + browser.test.assertEq("message-3.eml", raw3file.name); + browser.test.assertEq(838, raw3file.size); + const raw3content = await raw3file.text(); + browser.test.assertEq( + raw3content, + file15content, + "getRaw() of the inner attached message and getAttachmentFile() of the inner attached message should be identical" + ); + + // Get the full content of the inner nested message via getFull(). + const full3 = await browser.messages.getFull(3); + window.assertDeepEqual( + { + contentType: "message/rfc822", + partName: "", + size: 100, + decryptionStatus: "none", + headers: { + "message-id": [""], + from: ["Jimmy "], + to: ["Superman "], + subject: ["Test message 2"], + date: ["Wed, 16 May 2000 19:32:47 -0400"], + "mime-version": ["1.0"], + "content-type": ["message/rfc822"], + }, + parts: [ + { + contentType: "multipart/mixed", + headers: { + "content-type": [ + 'multipart/mixed; boundary="----=_NextPart_000_0003_01BFC036.AE309650"', + ], + }, + size: 100, + partName: "1", + parts: [ + { + contentType: "text/plain", + headers: { + "content-type": ['text/plain; charset="iso-8859-1"'], + "content-transfer-encoding": ["7bit"], + }, + size: 31, + partName: "1.1", + body: "This message has an attachment\n", + }, + { + contentType: "image/png", + headers: { + "content-type": ['image/png; name="whitePixel.png"'], + "content-transfer-encoding": ["base64"], + "content-disposition": [ + 'attachment; filename="whitePixel.png"', + ], + }, + size: 69, + partName: "1.2", + name: "whitePixel.png", + }, + ], + }, + ], + }, + full3, + "getFull() of the inner nested message should be correct" + ); + + // List the attachments from the inner nested message. + const attachments3 = await browser.messages.listAttachments(3); + window.assertDeepEqual( + [ + { + contentType: "image/png", + name: "whitePixel.png", + size: 69, + partName: "1.2", + }, + ], + attachments3, + "Attachments of the inner nested message should be correct", + { + strict: true, + } + ); + + // Get the actual attachments of the inner nested message as File objects. + const expectedAttachments3 = [ + { + name: "whitePixel.png", + size: 69, + partName: "1.2", + }, + ]; + for (const expectedAttachment3 of expectedAttachments3) { + const f = await browser.messages.getAttachmentFile( + 3, + expectedAttachment3.partName + ); + files.set(expectedAttachment3.partName, f); + // eslint-disable-next-line mozilla/use-isInstance + browser.test.assertTrue(f instanceof File); + browser.test.assertEq( + expectedAttachment3.name, + f.name, + `Name of part ${expectedAttachment3.partName} should be correct` + ); + browser.test.assertEq( + expectedAttachment3.size, + f.size, + `Size of part ${expectedAttachment3.partName} should be correct` + ); + } + + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }, + manifest: { + manifest_version: 3, + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["accountsRead", "messagesRead"], + }, + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); diff --git a/mail/components/extensions/test/xpcshell/xpcshell.ini b/mail/components/extensions/test/xpcshell/xpcshell.ini index 10c3032106..69dbc7cb11 100644 --- a/mail/components/extensions/test/xpcshell/xpcshell.ini +++ b/mail/components/extensions/test/xpcshell/xpcshell.ini @@ -9,6 +9,7 @@ tags = addrbook [test_ext_messages_attachments.js] # IMAP disabled (doesn't work with test server). support-files = messages/** [test_ext_messages_bug_1737532.js] +[test_ext_messages_encrypted_attachment.js] [test_ext_messages_get.js] # PGP test disabled for NNTP. support-files = messages/** [test_ext_messages_id.js] # Disabled for NNTP (message move not supported).