--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -3152,25 +3152,23 @@ var gMessageNotificationBar = {
},
get brandBundle() {
delete this.brandBundle;
return (this.brandBundle = document.getElementById("bundle_brand"));
},
get msgNotificationBar() {
- delete this.msgNotificationBar;
-
- let newNotificationBox = new MozElements.NotificationBox(element => {
- element.setAttribute("flex", "1");
- element.setAttribute("notificationside", "top");
- document.getElementById("mail-notification-top").append(element);
- });
-
- return (this.msgNotificationBar = newNotificationBox);
+ if (!this._notificationBox) {
+ this._notificationBox = new MozElements.NotificationBox(element => {
+ element.setAttribute("notificationside", "top");
+ document.getElementById("mail-notification-top").append(element);
+ });
+ }
+ return this._notificationBox;
},
setJunkMsg(aMsgHdr) {
goUpdateCommand("button_junk");
let brandName = this.brandBundle.getString("brandShortName");
let junkBarMsg = this.stringBundle.getFormattedString("junkBarMessage", [
brandName,
--- a/mail/base/content/messageWindow.xhtml
+++ b/mail/base/content/messageWindow.xhtml
@@ -238,19 +238,19 @@
<hbox id="msgHeaderView" collapsed="true" class="main-header-area">
#include msgHdrView.inc.xhtml
</hbox>
<vbox id="singlemessage">
#include ../../../calendar/lightning/content/imip-bar-overlay.inc.xhtml
</vbox>
<!-- The msgNotificationBar appears on top of the message and displays
information like: junk, mdn, remote content and phishing warnings -->
- <hbox id="mail-notification-top">
+ <vbox id="mail-notification-top">
<!-- notificationbox will be added here lazily. -->
- </hbox>
+ </vbox>
<!-- message view -->
<browser id="messagepane"
context="mailContext"
tooltip="aHTMLTooltip"
style="height: 0px; min-height: 1px"
flex="1"
name="messagepane"
--- a/mail/base/content/messenger.xhtml
+++ b/mail/base/content/messenger.xhtml
@@ -742,19 +742,19 @@
<vbox id="singlemessage" flex="1">
<hbox id="msgHeaderView" collapsed="true" class="main-header-area">
#include msgHdrView.inc.xhtml
</hbox>
#include ../../../calendar/lightning/content/imip-bar-overlay.inc.xhtml
<!-- The msgNotificationBar appears on top of the message
and displays information like: junk, contains remote
images, or is a suspected phishing URL. -->
- <hbox id="mail-notification-top">
+ <vbox id="mail-notification-top">
<!-- notificationbox will be added here lazily. -->
- </hbox>
+ </vbox>
<vbox flex="1">
<!-- The messagepanewrapper hbox exists to allow
extensions to add sidebars to the message. -->
<hbox id="messagepanewrapper" flex="1">
<stack flex="1">
<browser id="messagepane"
context="mailContext"
datetimepicker="DateTimePickerPanel"
--- a/mail/base/content/msgSecurityPane.inc.xhtml
+++ b/mail/base/content/msgSecurityPane.inc.xhtml
@@ -32,49 +32,16 @@
class="inline-notification-container info-container">
<hbox class="inline-notification-wrapper align-center">
<image class="notification-image notification-image-info"/>
<description data-l10n-id="openpgp-missing-signature-key"/>
<button data-l10n-id="openpgp-search-signature-key"
oncommand="Enigmail.msg.searchSignatureKey();"/>
</hbox>
</hbox>
-
- <!-- Corrupted message by MS Exchange -->
- <hbox id="brokenExchangeBox" hidden="true"
- class="inline-notification-container info-container">
- <hbox class="inline-notification-wrapper">
- <image class="notification-image notification-image-info"/>
- <description data-l10n-id="openpgp-broken-exchange-info"/>
- <button id="brokenExchangeRepairButton"
- data-l10n-id="openpgp-broken-exchange-repair"
- oncommand="Enigmail.msg.fixBuggyExchangeMail();"/>
- <label id="brokenExchangeWait" data-l10n-id="openpgp-broken-exchange-wait"/>
- </hbox>
- </hbox>
-
- <!-- Unable to decrypt -->
- <hbox id="cannotDecryptBox" hidden="true"
- class="inline-notification-container error-container">
- <hbox class="inline-notification-wrapper align-center">
- <image class="notification-image notification-image-error"/>
- <description id="cannot-decrypt-explanation"/>
- </hbox>
- </hbox>
-
- <!-- Message partially encrypted -->
- <hbox id="partialOpenPGPBox" hidden="true"
- class="inline-notification-container info-container">
- <hbox class="inline-notification-wrapper align-center">
- <image class="notification-image notification-image-info"/>
- <description id="preventedPartialExplanation"/>
- <button id="openpgpProcessPartial"
- oncommand="Enigmail.msg.processOpenPGPSubset();"/>
- </hbox>
- </hbox>
</vbox>
<label id="signatureLabel" class="message-security-label"/>
<label id="signatureHeader" collapsed="true" />
<description id="signatureExplanation"/>
<vbox id="signatureCert" class="message-security-container"
collapsed="true">
--- a/mail/extensions/openpgp/content/ui/enigmailMessengerOverlay.js
+++ b/mail/extensions/openpgp/content/ui/enigmailMessengerOverlay.js
@@ -9,19 +9,24 @@
// TODO: check if this is safe
/* eslint-disable no-unsanitized/property */
/* Globals from Thunderbird: */
/* global ReloadMessage: false, gDBView: false, gSignatureStatus: false, gEncryptionStatus: false, showMessageReadSecurityInfo: false */
/* global gFolderDisplay: false, messenger: false, currentAttachments: false, msgWindow: false, PanelUI: false */
/* global currentHeaderData: false, gViewAllHeaders: false, gExpandedHeaderList: false, goDoCommand: false, HandleSelectedAttachments: false */
/* global statusFeedback: false, displayAttachmentsForExpandedView: false, gMessageListeners: false, gExpandedHeaderView */
-/* global MailServices: false, gMessageDisplay: false */
-
-/* import-globals-from ../BondOpenPGP.jsm */
+/* globals gMessageNotificationBar, gMessageDisplay */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { BondOpenPGP } = ChromeUtils.import(
+ "chrome://openpgp/content/BondOpenPGP.jsm"
+);
var EnigmailCompat = ChromeUtils.import(
"chrome://openpgp/content/modules/compat.jsm"
).EnigmailCompat;
var EnigmailCore = ChromeUtils.import(
"chrome://openpgp/content/modules/core.jsm"
).EnigmailCore;
var EnigmailFuncs = ChromeUtils.import(
@@ -167,16 +172,29 @@ Enigmail.msg = {
//"autocrypt-setup-message",
],
buggyExchangeEmailContent: null, // for HACK for MS-EXCHANGE-Server Problem
buggyMailType: null,
changedAttributes: [],
lastSMimeReloadURI: "",
allAttachmentsDone: false,
messageDecryptDone: false,
+ showPartialDecryptionReminder: false,
+
+ get notificationBox() {
+ return gMessageNotificationBar.msgNotificationBar;
+ },
+
+ removeNotification(value) {
+ let item = this.notificationBox.getNotificationWithValue(value);
+ // Remove the notification only if the user didn't previously close it.
+ if (item) {
+ this.notificationBox.removeNotification(item, true);
+ }
+ },
messengerStartup() {
if (!BondOpenPGP.isEnabled()) {
return;
}
let self = this;
@@ -370,46 +388,37 @@ Enigmail.msg = {
if (revealBox) {
// there are situations when evealBox is not yet present
revealBox.setAttribute("hidden", !attachmentList ? "true" : "false");
}
},
messageCleanup() {
EnigmailLog.DEBUG("enigmailMessengerOverlay.js: messageCleanup\n");
-
- let element = document.getElementById("brokenExchangeBox");
- if (element) {
- element.hidden = true;
+ for (let value of [
+ "decryptInlinePGReminder",
+ "decryptInlinePG",
+ "brokenExchangeProgress",
+ ]) {
+ this.removeNotification(value);
}
- element = document.getElementById("openpgpKeyBox");
+
+ let element = document.getElementById("openpgpKeyBox");
if (element) {
element.hidden = true;
}
element = document.getElementById("hasConflictingKeyOpenPGP");
if (element) {
element.setAttribute("hidden", true);
}
element = document.getElementById("signatureKeyBox");
if (element) {
element.hidden = true;
element.removeAttribute("keyid");
}
- element = document.getElementById("cannotDecryptBox");
- if (element) {
- element.hidden = true;
- }
- element = document.getElementById("partialOpenPGPBox");
- if (element) {
- element.hidden = true;
- }
- element = document.getElementById("openpgpProcessPartial");
- if (element) {
- element.removeAttribute("hidden");
- }
this.setAttachmentReveal(null);
if (Enigmail.msg.createdURIs.length) {
// Cleanup messages belonging to this window (just in case)
var enigmailSvc = Enigmail.getEnigmailSvc();
if (enigmailSvc) {
EnigmailLog.DEBUG(
@@ -653,19 +662,41 @@ Enigmail.msg = {
},
// callback function for automatic decryption
messageAutoDecrypt() {
EnigmailLog.DEBUG("enigmailMessengerOverlay.js: messageAutoDecrypt:\n");
Enigmail.msg.messageDecrypt(null, true);
},
- notifyMessageDecryptDone() {
+ async notifyMessageDecryptDone() {
Enigmail.msg.messageDecryptDone = true;
Enigmail.msg.processAfterAttachmentsAndDecrypt();
+
+ // Interrupt if the message wasn't properly decrypted.
+ if (
+ !Enigmail.msg.decryptedMessage ||
+ typeof Enigmail.msg.decryptedMessage == "undefined"
+ ) {
+ return;
+ }
+
+ // Show the partial inline encryption reminder only if the decryption action
+ // came from a partially inline encrypted message.
+ if (Enigmail.msg.showPartialDecryptionReminder) {
+ Enigmail.msg.showPartialDecryptionReminder = false;
+
+ this.notificationBox.appendNotification(
+ await document.l10n.formatValue("openpgp-reminder-partial-display"),
+ "decryptInlinePGReminder",
+ null,
+ this.notificationBox.PRIORITY_INFO_HIGH,
+ null
+ );
+ }
},
// analyse message header and decrypt/verify message
messageDecrypt(event, isAuto) {
EnigmailLog.DEBUG(
"enigmailMessengerOverlay.js: messageDecrypt: " + event + "\n"
);
@@ -1041,17 +1072,17 @@ Enigmail.msg = {
"",
"",
uri,
"",
"1"
);
},
- messageParse(
+ async messageParse(
interactive,
importOnly,
contentEncoding,
msgUriSpec,
isAuto,
pbMessageIndex = "0"
) {
EnigmailLog.DEBUG(
@@ -1220,27 +1251,39 @@ Enigmail.msg = {
if (ht & EnigmailConstants.UNCERTAIN_SIGNATURE) {
infoId = "openpgp-partially-signed";
buttonId = "openpgp-partial-verify-button";
} else {
infoId = "openpgp-partially-encrypted";
buttonId = "openpgp-partial-decrypt-button";
}
- document.getElementById("partialOpenPGPBox").removeAttribute("hidden");
- let descElement = document.getElementById(
- "preventedPartialExplanation"
+ let [description, buttonLabel] = await document.l10n.formatValues([
+ { id: infoId },
+ { id: buttonId },
+ ]);
+
+ let buttons = [
+ {
+ label: buttonLabel,
+ popup: null,
+ callback(aNotification, aButton) {
+ Enigmail.msg.processOpenPGPSubset();
+ return false; // Close notification.
+ },
+ },
+ ];
+
+ this.notificationBox.appendNotification(
+ description,
+ "decryptInlinePG",
+ null,
+ this.notificationBox.PRIORITY_INFO_HIGH,
+ buttons
);
- if (descElement) {
- document.l10n.setAttributes(descElement, infoId);
- }
- let buttonElement = document.getElementById("openpgpProcessPartial");
- if (buttonElement) {
- document.l10n.setAttributes(buttonElement, buttonId);
- }
return;
}
}
var mozPlainText = bodyElement.innerHTML.search(/class="moz-text-plain"/);
if (mozPlainText >= 0 && mozPlainText < 40) {
// workaround for too much expanded emoticons in plaintext msg
@@ -1317,25 +1360,18 @@ Enigmail.msg = {
if (hasHead || hasTail) {
return EnigmailConstants.PARTIALLY_PGP | crypto;
}
return 0;
},
- processOpenPGPSubset() {
- let descElement = document.getElementById("preventedPartialExplanation");
- document.l10n.setAttributes(
- descElement,
- "openpgp-reminder-partial-display"
- );
- document
- .getElementById("openpgpProcessPartial")
- .setAttribute("hidden", true);
+ async processOpenPGPSubset() {
+ Enigmail.msg.showPartialDecryptionReminder = true;
this.messageDecrypt(null, false);
},
getBodyElement(pbMessageIndex = "0") {
let bodyElement = null;
// Thunderbird
let msgFrame = document.getElementById("messagepane");
@@ -1886,58 +1922,56 @@ Enigmail.msg = {
},
/**
* Fix broken PGP/MIME messages from MS-Exchange by replacing the broken original
* message with a fixed copy.
*
* no return
*/
- fixBuggyExchangeMail() {
+ async fixBuggyExchangeMail() {
EnigmailLog.DEBUG("enigmailMessengerOverlay.js: fixBuggyExchangeMail:\n");
- document
- .getElementById("brokenExchangeRepairButton")
- .setAttribute("hidden", true);
- document.getElementById("brokenExchangeWait").removeAttribute("hidden");
-
- function hideBrokenExchangePane() {
- document.getElementById("brokenExchangeBox").hidden = true;
- }
+ this.notificationBox.appendNotification(
+ await document.l10n.formatValue("openpgp-broken-exchange-wait"),
+ "brokenExchangeProgress",
+ null,
+ this.notificationBox.PRIORITY_INFO_HIGH,
+ null
+ );
let msg = gFolderDisplay.messageDisplay.displayedMessage;
-
let p = EnigmailFixExchangeMsg.fixExchangeMessage(msg, this.buggyMailType);
+
p.then(function(msgKey) {
- // display message with given msgKey
-
+ // Display message with given msgKey.
EnigmailLog.DEBUG(
"enigmailMessengerOverlay.js: fixBuggyExchangeMail: _success: msgKey=" +
msgKey +
"\n"
);
if (msgKey) {
let index = gFolderDisplay.view.dbView.findIndexFromKey(msgKey, true);
EnigmailLog.DEBUG(" ** index = " + index + "\n");
EnigmailTimer.setTimeout(function() {
gFolderDisplay.view.dbView.selectMsgByKey(msgKey);
}, 750);
}
-
- hideBrokenExchangePane();
});
p.catch(async function() {
EnigmailDialog.alert(
window,
l10n.formatValueSync("fix-broken-exchange-msg-failed")
);
- hideBrokenExchangePane();
});
+
+ // Remove the brokenExchangeProgress notification at the end fo the process.
+ this.removeNotification("brokenExchangeProgress");
},
/**
* Hide attachments containing OpenPGP keys
*/
hidePgpKeys() {
let keys = [];
for (let i = 0; i < currentAttachments.length; i++) {
--- a/mail/extensions/openpgp/content/ui/enigmailMsgHdrViewOverlay.js
+++ b/mail/extensions/openpgp/content/ui/enigmailMsgHdrViewOverlay.js
@@ -104,16 +104,19 @@ Enigmail.hdrView = {
msgEncryptionAllKeyIds: null,
reset() {
this.msgSignatureState = EnigmailConstants.MSG_SIG_NONE;
this.msgEncryptionState = EnigmailConstants.MSG_ENC_NONE;
this.msgSignatureKeyId = "";
this.msgEncryptionKeyId = null;
this.msgEncryptionAllKeyIds = null;
+ for (let value of ["decryptionFailed", "brokenExchange"]) {
+ Enigmail.msg.removeNotification(value);
+ }
},
hdrViewLoad() {
EnigmailLog.DEBUG("enigmailMsgHdrViewOverlay.js: this.hdrViewLoad\n");
// THE FOLLOWING OVERRIDES CODE IN msgHdrViewOverlay.js
// which wouldn't work otherwise
@@ -490,23 +493,27 @@ Enigmail.hdrView = {
infoId = "openpgp-cannot-decrypt-because-missing-key";
} else {
this.msgEncryptionState = EnigmailConstants.MSG_ENC_FAILURE;
if (statusFlags & EnigmailConstants.MISSING_MDC) {
unhideBar = true;
infoId = "openpgp-cannot-decrypt-because-mdc";
}
}
+
if (unhideBar) {
- document.getElementById("cannotDecryptBox").removeAttribute("hidden");
- let descElement = document.getElementById("cannot-decrypt-explanation");
- if (descElement) {
- document.l10n.setAttributes(descElement, infoId);
- }
+ Enigmail.msg.notificationBox.appendNotification(
+ await document.l10n.formatValue(infoId),
+ "decryptionFailed",
+ "chrome://global/skin/icons/warning.svg",
+ Enigmail.msg.notificationBox.PRIORITY_CRITICAL_MEDIUM,
+ null
+ );
}
+
this.msgSignatureState = EnigmailConstants.MSG_SIG_NONE;
encryptedUINode.hidden = false;
} else if (statusFlags & EnigmailConstants.DECRYPTION_OKAY) {
EnigmailURIs.rememberEncryptedUri(this.lastEncryptedMsgKey);
encryptedUINode.setAttribute("encrypted", "ok");
this.msgEncryptionState = EnigmailConstants.MSG_ENC_OK;
if (secInfo.xtraStatus && secInfo.xtraStatus == "buggyMailFormat") {
console.log(
@@ -1092,17 +1099,17 @@ Enigmail.hdrView = {
if (hasSiblings(Enigmail.msg.mimeParts, mimePartNumber, parentNum)) {
return true;
}
}
return false;
},
- updateSecurityStatus(
+ async updateSecurityStatus(
unusedUriSpec,
exitCode,
statusFlags,
extStatusFlags,
keyId,
userId,
sigDetails,
errorMsg,
@@ -1167,22 +1174,34 @@ Enigmail.hdrView = {
blockSeparation,
encToDetails,
null,
mimePartNumber
);
}
if (uriSpec && uriSpec.search(/^enigmail:message\//) === 0) {
- // Display header for broken MS-Exchange message.
- document.getElementById("brokenExchangeBox").removeAttribute("hidden");
- document
- .getElementById("brokenExchangeRepairButton")
- .removeAttribute("hidden");
- document.getElementById("brokenExchangeWait").hidden = true;
+ let buttons = [
+ {
+ "l10n-id": "openpgp-broken-exchange-repair",
+ popup: null,
+ callback(aNotification, aButton) {
+ Enigmail.msg.fixBuggyExchangeMail();
+ return false; // Close notification.
+ },
+ },
+ ];
+
+ Enigmail.msg.notificationBox.appendNotification(
+ await document.l10n.formatValue("openpgp-broken-exchange-info"),
+ "brokenExchange",
+ null,
+ Enigmail.msg.notificationBox.PRIORITY_WARNING_MEDIUM,
+ buttons
+ );
}
},
processDecryptionResult(uri, actionType, processData, mimePartNumber) {
EnigmailLog.DEBUG(
"enigmailMsgHdrViewOverlay.js: EnigMimeHeaderSink.processDecryptionResult:\n"
);
EnigmailLog.DEBUG(
--- a/mail/test/browser/openpgp/browser_viewMessage.js
+++ b/mail/test/browser/openpgp/browser_viewMessage.js
@@ -11,16 +11,25 @@
const { open_message_from_file } = ChromeUtils.import(
"resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
const {
close_window,
plan_for_modal_dialog,
wait_for_modal_dialog,
} = ChromeUtils.import("resource://testing-common/mozmill/WindowHelpers.jsm");
+const {
+ assert_notification_displayed,
+ get_notification_button,
+ wait_for_notification_to_show,
+ wait_for_notification_to_stop,
+} = ChromeUtils.import(
+ "resource://testing-common/mozmill/NotificationBoxHelpers.jsm"
+);
+
const { OpenPGPTestUtils } = ChromeUtils.import(
"resource://testing-common/mozmill/OpenPGPTestUtils.jsm"
);
const { FileUtils } = ChromeUtils.import(
"resource://gre/modules/FileUtils.jsm"
);
const { MailServices } = ChromeUtils.import(
"resource:///modules/MailServices.jsm"
@@ -293,12 +302,90 @@ add_task(async function testUpdateMessag
// Verify the new acceptance level is correct.
Assert.ok(
OpenPGPTestUtils.hasSignedIconState(mc.window.document, "unverified"),
"signed unverified icon is displayed"
);
close_window(mc);
});
+/**
+ * Test the notification and decryption behavior for partially encrypted inline
+ * PGP messages.
+ */
+add_task(async function testPartialInlinePGPDecrypt() {
+ // Setup the message.
+ let mc = await open_message_from_file(
+ new FileUtils.File(
+ getTestFilePath("data/eml/alice-partially-encrypted.eml")
+ )
+ );
+
+ let notificationBox = "mail-notification-top";
+ let notificationValue = "decryptInlinePG";
+
+ // Assert the "partially encrypted notification" is visible.
+ assert_notification_displayed(mc, notificationBox, notificationValue, true);
+
+ // Click on the "decrypt" button.
+ let decryptButton = get_notification_button(
+ mc,
+ notificationBox,
+ notificationValue,
+ {
+ popup: null,
+ }
+ );
+ EventUtils.synthesizeMouseAtCenter(decryptButton, {}, mc.window);
+
+ // Assert that the message was decrypted and the partial decryption reminder
+ // notification is visible.
+ wait_for_notification_to_show(mc, notificationBox, "decryptInlinePGReminder");
+
+ Assert.ok(
+ OpenPGPTestUtils.hasEncryptedIconState(mc.window.document, "ok"),
+ "encrypted icon is displayed"
+ );
+
+ close_window(mc);
+});
+
+/**
+ * Test the notification and repairing of a message corrupted by MS-Exchange.
+ */
+add_task(async function testBrokenMSExchangeEncryption() {
+ // Setup the message.
+ let mc = await open_message_from_file(
+ new FileUtils.File(getTestFilePath("data/eml/alice-broken-exchange.eml"))
+ );
+ let notificationBox = "mail-notification-top";
+ let notificationValue = "brokenExchange";
+
+ // Assert the "corrupted by MS-Exchange" notification is visible.
+ assert_notification_displayed(mc, notificationBox, notificationValue, true);
+
+ // Click on the "repair" button.
+ let repairButton = get_notification_button(
+ mc,
+ notificationBox,
+ notificationValue,
+ {
+ popup: null,
+ }
+ );
+ EventUtils.synthesizeMouseAtCenter(repairButton, {}, mc.window);
+
+ // Wait for the "fixing in progress" notification to go away.
+ wait_for_notification_to_stop(mc, notificationBox, "brokenExchangeProgress");
+
+ // Assert that the message was repaired and decrypted.
+ Assert.ok(
+ OpenPGPTestUtils.hasEncryptedIconState(mc.window.document, "ok"),
+ "encrypted icon is displayed"
+ );
+
+ close_window(mc);
+}).skip(); // Bug 1674013
+
registerCleanupFunction(function tearDown() {
aliceIdentity.setUnicharAttribute("openpgp_key_id", initialKeyIdPref);
MailServices.accounts.removeIncomingServer(aliceAcct.incomingServer, true);
});
new file mode 100644
--- /dev/null
+++ b/mail/test/browser/openpgp/data/eml/alice-broken-exchange.eml
@@ -0,0 +1,64 @@
+From: "Alice Lovelace" <alice@openpgp.example>
+To: "Alice Lovelace" <alice@openpgp.example>
+Subject: broken exchange test message
+Date: Wed, 28 Oct 2020 01:23:45 +0000
+Content-Type: multipart/mixed;
+ boundary="_003_38fbdabc0a957344544c1642f2e764cd1234567890_"
+MIME-Version: 1.0
+
+--_003_38fbdabc0a957344544c1642f2e764cd1234567890_
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+
+--_003_38fbdabc0a957344544c1642f2e764cd1234567890_
+Content-Type: application/pgp-encrypted; name="PGPMIME version identification"
+Content-Description: PGP/MIME version identification
+Content-Disposition: attachment; filename="PGPMIME version identification";
+ size=12; creation-date="Wed, 28 Oct 2020 01:23:45 GMT";
+ modification-date="Wed, 28 Oct 2020 01:23:45 GMT"
+Content-ID: <ECC4CBEC65D5CD42A8BF99DC0A800A0F@namprd00.prod.outlook.com>
+Content-Transfer-Encoding: base64
+
+VmVyc2lvbjogMQ0K
+
+--_003_38fbdabc0a957344544c1642f2e764cd1234567890_
+Content-Type: application/octet-stream; name="encrypted.asc"
+Content-Description: OpenPGP encrypted message.asc
+Content-Disposition: attachment; filename="encrypted.asc";
+ creation-date="Wed, 20 May 2020 19:11:08 GMT";
+ modification-date="Wed, 20 May 2020 19:11:08 GMT"
+Content-ID: <3A449618AD0B6A43AE8979E0E971B0FF@namprd00.prod.outlook.com>
+Content-Transfer-Encoding: base64
+
+LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoRjREUjJiMnVkWHlIcllTQVFk
+QS9ZM0VaZlp4SVM1WlRPcmx3NkVnTkFMKzhENnV2cGVpTWdSRUxzSDFQemd3Ck1N
+d3ZxMnFUT01DaGJUd0RQSGY0WDVETGpZbXowWnN5TWlJY3BzNXo4ZU5XQ0Jyd01k
+ODBtWGlKem8vM0ZCaGkKMHVrQmVaSkFTOUlvTE5DbnRZNUpqWWljbTZZSnBHSGVH
+azA3VUN5dENYUTFEZmF5OWk1cHc5Nk54UTZicE5PSgpFamttRmlEYk1JaExnM25C
+TjVRN2ZzZ01WbFBxMTNTbmJaMmVVZXdRVHVESG10dlByUzFHRmFROExBclFRNVdU
+CjJyampkUmZ1Q040MmNXeUszSHNUNVNMZDRkOTVjZm1CeTIrRnkwQ09tZU5obmRq
+aGxPQWlEL2dOUWE4TVAzYW4KWlQ0OVlmbDdYdHp2Yjh0V0dSL1IzSDFBMU9wYnEz
+d3BrQm1hNlpXMzBvcG9rRlNkYkRqK3pTNlhmNWVmTkVLWApJZFpVQTg1U1hYRjAw
+RWFSZU9tdFQ1WTJLZWMyS1dlUXYybmlVMGRNN0FMNjFGQmw3VXVvaUJDc1IwaGRa
+OCtTClR1UElQa0daWnlSdk1nU3ZTMjduV0lNSUZvdThzeVlTdTg0MElzeHRWSThk
+SEFqM0RvbUNXa20rMnJXaDZ3cWkKM29xZDhOK1QvdGZ3ZFdoMUJnNVpRTm40SFF6
+WXhuYVlqZk1mNFVYMS9RbjFVUmtISnErMXkwYm1reDI4c1RXTwpXVS9mZndQZjBB
+WGVyNG9LNEdtMDNtanFQbkd2UFFYa2xSb2NDQ0prMG9sdmZ6M0dQRDZSNGNiNkc4
+SlFvTyt5CmNMRmhMdytCZFdqd0ZCWU1POTl5TFhJM3lEbDFGdWd5R2JvU3dNNFRT
+aHA3T1ZwYUlXSE1oa2VhVFJIQlVQUnMKNXNqUzR5QVI0OVppTDNMU1lHR2FJbmQr
+QUlzR2I0NTB0enZENWJPT1dUM2JBK0pRWXZ4dzRxa240WnI0OVdCYgptZE9nWmhD
+eUxET3BsVUo1VFJtY0ZkdFdrRFI2MUsyN1VPeno4RVcxcExYazZjQ1RFcU5ZanlT
+bDJCeE53R283ClpGV3IwTW1qbVJydnQvSmNvcm56cjl3WW9HdXJ3bERnc2cxNmpW
+aFZpY09qTUg2ellqcXN0amJRejREeDJqU2wKbGdabjdPUXY5dTR1SGZ0SXNaQk50
+RnFjUlJaSFZHVWpXMjNQK0xUWGNkQzhDSWFaZzMvNUN2blZyTU9jcGd0MwpWdmdB
+TDloeUJRNHV6UWVacVlpRCtHRmhDQmp6QUxtMGNRSzJQZnE3N1AwTjJBS1k2NUNP
+SWVnbjFubXJvNTF4CldIS09jKy9FdURmNU5VY0lieW5sQ0NycUJXenAzZWt4bE5D
+eFZNTkdJeThObEhyS1oxb0RQSlNMZi9vc0Jyb2EKTExCd0V1bllqRlY1czE1NVkv
+TFUybnQzUUY5NzBxTGdHMEVTWm1mV2ZTcEZzaEhuSVZBMXdXV3lMM3o3Ukd2YwpJ
+ZVFnYk9LSklDWlNSTDVlNDBpSHBMcEpERWlyUGpTeEQrYUpuazd1VVZGUDk4aTJu
+Z3FNVjNadUtlM0paV0VpCnc3ZDJvbmEwditscWVUUUp5OHcydUIvaGNpTFBOR0tN
+QUpBeEpSeUFFUDRTaWpXWmNDQjMKPUR2RkwKLS0tLS1FTkQgUEdQIE1FU1NBR0Ut
+LS0tLQo=
+
+--_003_38fbdabc0a957344544c1642f2e764cd1234567890_--
new file mode 100644
--- /dev/null
+++ b/mail/test/browser/openpgp/data/eml/alice-partially-encrypted.eml
@@ -0,0 +1,17 @@
+From: "Alice Lovelace" <alice@openpgp.example>
+To: "Alice Lovelace" <alice@openpgp.example>
+Subject: partially encrypted test message
+Date: Wed, 29 Oct 2020 02:34:56 +0000
+Content-Type: text/plain
+MIME-Version: 1.0
+
+-----BEGIN PGP MESSAGE-----
+
+hF4DR2b2udXyHrYSAQdACSB+uDWodBJ2WRuPm53gxq/RUAc9TSEa1pbow+HTWU8w
+5SomxZAC/JN0vnLqgLWL7JKgmQyYD9d7HI76iTThvJf2cLZXp1+HRyHN2c9gCHjE
+0lIB7FhFzhbBPoT+4Nhla6ISM/KNTkbkebJ4VtZL05/hFn98qKJq61hn9iBvLQ5/
+URrmx+0iVgA1jUTUDuZSt5PRFHYFJqGoOvdHD2mccmIhawet
+=lIZv
+-----END PGP MESSAGE-----
+
+Additional unexpected text