summaryrefslogtreecommitdiffstats
path: root/comm/mail/extensions/am-e2e
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/extensions/am-e2e')
-rw-r--r--comm/mail/extensions/am-e2e/AME2E.jsm24
-rw-r--r--comm/mail/extensions/am-e2e/am-e2e.inc.xhtml237
-rw-r--r--comm/mail/extensions/am-e2e/am-e2e.js1591
-rw-r--r--comm/mail/extensions/am-e2e/am-e2e.xhtml32
-rw-r--r--comm/mail/extensions/am-e2e/components.conf15
-rw-r--r--comm/mail/extensions/am-e2e/moz.build16
-rw-r--r--comm/mail/extensions/am-e2e/prefs/e2e-prefs.js285
7 files changed, 2200 insertions, 0 deletions
diff --git a/comm/mail/extensions/am-e2e/AME2E.jsm b/comm/mail/extensions/am-e2e/AME2E.jsm
new file mode 100644
index 0000000000..86237d3e0e
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/AME2E.jsm
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+var EXPORTED_SYMBOLS = ["E2EService"];
+
+function E2EService() {}
+
+E2EService.prototype = {
+ name: "e2e",
+ chromePackageName: "messenger",
+ showPanel(server) {
+ // don't show the panel for news, rss, or local accounts
+ return (
+ server.type != "nntp" &&
+ server.type != "rss" &&
+ server.type != "im" &&
+ server.type != "none"
+ );
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIMsgAccountManagerExtension"]),
+};
diff --git a/comm/mail/extensions/am-e2e/am-e2e.inc.xhtml b/comm/mail/extensions/am-e2e/am-e2e.inc.xhtml
new file mode 100644
index 0000000000..76841870aa
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/am-e2e.inc.xhtml
@@ -0,0 +1,237 @@
+# 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/.
+
+ <vbox id="e2eEditing">
+
+ <stringbundle id="bundle_e2e" src="chrome://messenger/locale/am-smime.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+
+ <linkset>
+ <html:link rel="localization" href="branding/brand.ftl"/>
+ <html:link rel="localization" href="messenger/openpgp/openpgp.ftl"/>
+ </linkset>
+
+ <vbox flex="1">
+ <html:p class="intro-paragraph">&e2eEnc.description;</html:p>
+ <html:p class="intro-paragraph"
+ data-l10n-id="e2e-intro-description"></html:p>
+ <html:span class="tail-with-learn-more"
+ data-l10n-id="e2e-intro-description-more"></html:span>
+ <label is="text-link" id="acceptLearnMoreE2E"
+ href="https://support.mozilla.org/kb/thunderbird-help-setup-account-e2ee"
+ value="&e2eLearnMore.label;"/>
+ </vbox>
+
+ <html:div>
+ <html:fieldset id="openpgpOptions"
+ aria-describedby="openPgpDescription">
+ <html:legend>&openpgpKeys.label;</html:legend>
+
+ <vbox data-subcategory="openpgp" class="openpgp-container">
+ <hbox align="center" class="opengpg-intro-section">
+ <html:img id="openPgpKey"
+ src="chrome://messenger/skin/icons/new/compact/key.svg"
+ alt="" />
+ <vbox flex="1">
+ <description class="description-with-side-element openpgp-description">
+ <html:p id="openPgpDescription"></html:p>
+ <html:img id="openPgpStatusImage" class="openpgp-status"
+ alt="" hidden="hidden"/>
+ <html:span id="openPgpSelectionStatus"
+ class="tail-with-learn-more"
+ hidden="hidden"></html:span>
+ <label is="text-link" id="openPgpLearnMore"
+ href="https://support.mozilla.org/kb/introduction-to-e2e-encryption"
+ data-l10n-id="e2e-learn-more"
+ class="learnMore"
+ hidden="true"/>
+ </description>
+ </vbox>
+ <vbox>
+ <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
+ <hbox>
+ <button id="addOpenPgpButton"
+ data-l10n-id="openpgp-add-key-button"
+ oncommand="openKeyWizard();"
+ class="accessory-button openpgp-add-key-button openpgp-image-btn"
+ flex="1"/>
+ </hbox>
+ </vbox>
+ </hbox>
+
+ <hbox id="openPgpNotification"
+ class="inline-notification-container success-container"
+ collapsed="true">
+ <hbox class="inline-notification-wrapper align-center">
+ <html:img class="notification-image"
+ src="chrome://global/skin/icons/check.svg"
+ alt="" />
+ <description id="openPgpNotificationDescription"/>
+ <button class="close-icon" oncommand="closeNotification()"/>
+ </hbox>
+ </hbox>
+
+ <vbox id="openPgpKeyList">
+ <radiogroup id="openPgpKeyListRadio">
+ <vbox id="openPgpOptionNone" class="content-blocking-category">
+ <hbox>
+ <radio id="openPgpNone"
+ value=""
+ data-l10n-id="openpgp-radio-none"
+ flex="1"/>
+ </hbox>
+ <vbox class="indent">
+ <description data-l10n-id="openpgp-radio-none-desc"/>
+ </vbox>
+ </vbox>
+ <!-- All available keys will be appended here. -->
+ </radiogroup>
+ </vbox>
+ </vbox>
+
+ <separator/>
+
+ <description flex="1" data-l10n-id="openpgp-manager-description"/>
+
+ <separator class="thin"/>
+
+ <hbox>
+ <button id="openOpenPGPKeyManagerButton"
+ data-l10n-id="openpgp-manager-button"
+ class="first-element"
+ oncommand="openKeyManager()"/>
+ </hbox>
+ </html:fieldset>
+ </html:div>
+
+ <separator/>
+
+ <html:div>
+ <html:fieldset id="smimeOptions">
+ <html:legend>&certificates2.label;</html:legend>
+
+ <label id="identity_signing_cert_nameLabel"
+ value="&signingCert2.message;" control="identity_signing_cert_name"/>
+
+ <hbox align="center" class="input-container">
+ <html:input id="identity_signing_cert_name" type="text"
+ class="input-inline"
+ readonly="readonly"
+ disabled="disabled"
+ aria-labelledby="identity_signing_cert_nameLabel"
+ wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.signing_cert_name"/>
+
+ <button id="signingCertSelectButton"
+ label="&digitalSign.certificate.button;"
+ accesskey="&digitalSign.certificate.accesskey;"
+ oncommand="smimeSelectCert('identity_signing_cert_name')"/>
+
+ <button id="signingCertClearButton"
+ label="&digitalSign.certificate_clear.button;"
+ accesskey="&digitalSign.certificate_clear.accesskey;"
+ oncommand="smimeClearCert('identity_signing_cert_name')"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <label value="&encryptionCert2.message;"
+ control="identity_encryption_cert_name"/>
+
+ <hbox align="center" class="input-container">
+ <html:input id="identity_encryption_cert_name" type="text"
+ class="input-inline"
+ readonly="readonly"
+ disabled="disabled"
+ wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.encryption_cert_name"/>
+
+ <button id="encryptionCertSelectButton"
+ label="&encryption.certificate.button;"
+ accesskey="&encryption.certificate.accesskey;"
+ oncommand="smimeSelectCert('identity_encryption_cert_name')"/>
+
+ <button id="encryptionCertClearButton"
+ label="&encryption.certificate_clear.button;"
+ accesskey="&encryption.certificate_clear.accesskey;"
+ oncommand="smimeClearCert('identity_encryption_cert_name')"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <hbox align="right">
+ <button id="openCertManagerButton" oncommand="openCertManager();"
+ label="&manageCerts3.label;" accesskey="&manageCerts3.accesskey;"/>
+ <button id="openDeviceManagerButton" oncommand="openDeviceManager();"
+ label="&manageDevices2.label;" accesskey="&manageDevices2.accesskey;"/>
+ </hbox>
+
+ </html:fieldset>
+ </html:div>
+
+ <separator/>
+
+ <html:legend>&sendingDefaults.label;</html:legend>
+
+ <html:div>
+ <html:fieldset>
+ <radiogroup id="encryptionChoices" class="indent" hidden="true">
+ <radio id="disable_encryption" wsm_persist="true" value="0"
+ data-l10n-id="e2e-disable-enc"/>
+ <radio id="enable_encryption" wsm_persist="true" value="2"
+ data-l10n-id="e2e-enable-enc"/>
+ <hbox>
+ <description flex="1" data-l10n-id="e2e-enable-description"
+ class="option-description tip-caption"/>
+ </hbox>
+ </radiogroup>
+
+ <separator/>
+
+ <description flex="1" data-l10n-id="e2e-signing-description"></description>
+ <checkbox id="identity_sign_mail" class="indent" wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.sign_mail"
+ data-l10n-id="e2e-sign-message"/>
+
+ </html:fieldset>
+ </html:div>
+
+ <separator/>
+
+ <html:legend data-l10n-id="e2e-advanced-section"/>
+
+ <html:div>
+ <html:fieldset>
+ <checkbox id="identity_attach_key" wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.attachPgpKey"
+ data-l10n-id="e2e-attach-key"/>
+
+ <checkbox id="identity_autocrypt_headers" wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.sendAutocryptHeaders"
+ data-l10n-id="e2e-autocrypt-headers"/>
+
+ <checkbox id="identity_encrypt_subject" wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.protectSubject"
+ data-l10n-id="e2e-encrypt-subject"/>
+
+ <checkbox id="identity_encrypt_drafts" wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.autoEncryptDrafts"
+ data-l10n-id="e2e-encrypt-drafts"/>
+
+ <separator/>
+
+ <description>&e2eTechPref.description;</description>
+ <radiogroup id="technologyChoices" class="indent">
+ <radio id="technology_automatic" wsm_persist="true" value="0"
+ label="&technologyAutomatic.label;"/>
+
+ <radio id="technology_prefer_openpgp" wsm_persist="true" value="2"
+ label="&technologyOpenPGP.label;"/>
+
+ <radio id="technology_prefer_smime" wsm_persist="true" value="1"
+ label="&technologySMIME.label;"/>
+ </radiogroup>
+ </html:fieldset>
+ </html:div>
+ </vbox>
diff --git a/comm/mail/extensions/am-e2e/am-e2e.js b/comm/mail/extensions/am-e2e/am-e2e.js
new file mode 100644
index 0000000000..1926d38d32
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/am-e2e.js
@@ -0,0 +1,1591 @@
+/* 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/. */
+
+/* import-globals-from ../../../../toolkit/content/preferencesBindings.js */
+/* import-globals-from ../../../mailnews/base/prefs/content/am-identity-edit.js */
+
+/* global GetEnigmailSvc, EnigRevokeKey */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { RNP } = ChromeUtils.import("chrome://openpgp/content/modules/RNP.jsm");
+var { EnigmailKey } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/key.jsm"
+);
+var { EnigmailDialog } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/dialog.jsm"
+);
+var { EnigmailKeyRing } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/keyRing.jsm"
+);
+var { EnigmailKeyserverURIs } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/keyserverUris.jsm"
+);
+var { EnigmailKeyServer } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/keyserver.jsm"
+);
+var { EnigmailCryptoAPI } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/cryptoAPI.jsm"
+);
+var { PgpSqliteDb2 } = ChromeUtils.import(
+ "chrome://openpgp/content/modules/sqliteDb.jsm"
+);
+
+var email_signing_cert_usage = 4; // SECCertUsage.certUsageEmailSigner
+var email_recipient_cert_usage = 5; // SECCertUsage.certUsageEmailRecipient
+
+var gIdentity;
+var gEncryptionCertName = null;
+var gEncryptionChoices = null;
+var gSignCertName = null;
+var gTechChoices = null;
+var gSignMessages = null;
+var gRequireEncrypt = null;
+var gDoNotEncrypt = null;
+var gAttachKey = null;
+var gSendAutocryptHeaders = null;
+var gEncryptSubject = null;
+var gEncryptDrafts = null;
+
+var gKeyId = null; // "" will denote selection 'None'.
+var gBundle = null;
+var gBrandBundle;
+var gSmimePrefbranch;
+var kEncryptionCertPref = "identity_encryption_cert_name";
+var kSigningCertPref = "identity_signing_cert_name";
+
+var gTechAuto = null;
+var gTechPrefOpenPGP = null;
+var gTechPrefSMIME = null;
+
+function onInit() {
+ initE2EEncryption(gIdentity);
+ Services.prefs.addObserver("mail.e2ee.auto_enable", autoEncryptPrefObserver);
+ Services.prefs.addObserver("mail.e2ee.auto_disable", autoEncryptPrefObserver);
+}
+
+window.addEventListener("unload", function () {
+ Services.prefs.removeObserver(
+ "mail.e2ee.auto_enable",
+ autoEncryptPrefObserver
+ );
+ Services.prefs.removeObserver(
+ "mail.e2ee.auto_disable",
+ autoEncryptPrefObserver
+ );
+});
+
+let gDisableEncryption;
+let gEnableEncryption;
+
+var autoEncryptPrefObserver = {
+ observe(subject, topic, prefName) {
+ if (topic == "nsPref:changed") {
+ if (
+ prefName == "mail.e2ee.auto_enable" ||
+ prefName == "mail.e2ee.auto_disable"
+ ) {
+ updateAutoEncryptRelated();
+ }
+ }
+ },
+};
+
+function updateAutoEncryptRelated() {
+ if (Services.prefs.getBoolPref("mail.e2ee.auto_enable")) {
+ document.getElementById("encryptionChoices").hidden = true;
+ } else {
+ document.getElementById("encryptionChoices").hidden = false;
+ }
+}
+
+async function initE2EEncryption(identity) {
+ // Initialize all of our elements based on the current identity values...
+ gEncryptionCertName = document.getElementById(kEncryptionCertPref);
+ gEncryptionChoices = document.getElementById("encryptionChoices");
+ gSignCertName = document.getElementById(kSigningCertPref);
+ gSignMessages = document.getElementById("identity_sign_mail");
+ gDisableEncryption = document.getElementById("disable_encryption");
+ gEnableEncryption = document.getElementById("enable_encryption");
+ gAttachKey = document.getElementById("identity_attach_key");
+ gSendAutocryptHeaders = document.getElementById("identity_autocrypt_headers");
+ gEncryptSubject = document.getElementById("identity_encrypt_subject");
+ gEncryptDrafts = document.getElementById("identity_encrypt_drafts");
+
+ gBundle = document.getElementById("bundle_e2e");
+ gBrandBundle = document.getElementById("bundle_brand");
+
+ gTechChoices = document.getElementById("technologyChoices");
+ gTechAuto = document.getElementById("technology_automatic");
+ gTechPrefOpenPGP = document.getElementById("technology_prefer_openpgp");
+ gTechPrefSMIME = document.getElementById("technology_prefer_smime");
+
+ if (!identity) {
+ // We're setting up a new identity. Set most prefs to default values.
+ // Only take selected values from gAccount.defaultIdentity
+ // as the new identity is going to have a different mail address.
+
+ gEncryptionCertName.value = "";
+ gEncryptionCertName.displayName = "";
+ gEncryptionCertName.dbKey = "";
+
+ gSignCertName.value = "";
+ gSignCertName.displayName = "";
+ gSignCertName.dbKey = "";
+
+ gDisableEncryption.disabled = true;
+ gEnableEncryption.disabled = true;
+ gEncryptSubject.disabled = true;
+ gEncryptDrafts.disabled = true;
+ gSignMessages.disabled = true;
+
+ gAttachKey.checked = gAccount.defaultIdentity.attachPgpKey;
+ gSendAutocryptHeaders.checked =
+ gAccount.defaultIdentity.sendAutocryptHeaders;
+ gEncryptSubject.checked = gAccount.defaultIdentity.protectSubject;
+ gEncryptDrafts.checked = gAccount.defaultIdentity.autoEncryptDrafts;
+ gSignMessages.checked = gAccount.defaultIdentity.signMail;
+ gEncryptionChoices.value = gAccount.defaultIdentity.encryptionPolicy;
+
+ gTechChoices.value = 0;
+ } else {
+ // We're editing an existing identity.
+
+ initSMIMESettings();
+ await initOpenPgpSettings();
+
+ let enableEnc = !!gEncryptionCertName.value;
+ enableEnc = enableEnc || !!gKeyId;
+ enableEncryptionControls(enableEnc);
+
+ gSignMessages.checked = identity.signMail;
+ gAttachKey.checked = identity.attachPgpKey;
+ gSendAutocryptHeaders.checked = identity.sendAutocryptHeaders;
+ gEncryptSubject.checked = identity.protectSubject;
+ gEncryptDrafts.checked = identity.autoEncryptDrafts;
+
+ let enableSig = gSignCertName.value;
+ enableSig = enableSig || !!gKeyId;
+ enableSigningControls(enableSig);
+ }
+
+ updateAutoEncryptRelated();
+
+ // Always start with enabling select buttons.
+ // This will keep the visibility of buttons in a sane state as user
+ // jumps from security panel of one account to another.
+ enableSelectButtons();
+ updateTechPref();
+}
+
+/**
+ * Initialize the S/MIME settings based on identity preferences.
+ */
+function initSMIMESettings() {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ gEncryptionCertName.value = gIdentity.getUnicharAttribute(
+ "encryption_cert_name"
+ );
+ gEncryptionCertName.dbKey = gIdentity.getCharAttribute(
+ "encryption_cert_dbkey"
+ );
+ // If we succeed in looking up the certificate by the dbkey pref, then
+ // append the serial number " [...]" to the display value, and remember the
+ // displayName in a separate property.
+ try {
+ let x509cert = null;
+ if (
+ gEncryptionCertName.dbKey &&
+ (x509cert = certdb.findCertByDBKey(gEncryptionCertName.dbKey))
+ ) {
+ gEncryptionCertName.value =
+ x509cert.displayName + " [" + x509cert.serialNumber + "]";
+ gEncryptionCertName.displayName = x509cert.displayName;
+ }
+ } catch (e) {}
+
+ gEncryptionChoices.value = gIdentity.encryptionPolicy;
+ gTechChoices.value = gIdentity.getIntAttribute("e2etechpref");
+
+ gSignCertName.value = gIdentity.getUnicharAttribute("signing_cert_name");
+ gSignCertName.dbKey = gIdentity.getCharAttribute("signing_cert_dbkey");
+
+ // same procedure as with gEncryptionCertName (see above)
+ try {
+ let x509cert = null;
+ if (
+ gSignCertName.dbKey &&
+ (x509cert = certdb.findCertByDBKey(gSignCertName.dbKey))
+ ) {
+ gSignCertName.value =
+ x509cert.displayName + " [" + x509cert.serialNumber + "]";
+ gSignCertName.displayName = x509cert.displayName;
+ }
+ } catch (e) {}
+}
+
+/**
+ * Initialize the OpenPGP settings, apply strings, and load the key radio UI.
+ */
+async function initOpenPgpSettings() {
+ let result = {};
+ await EnigmailKeyRing.getAllSecretKeysByEmail(gIdentity.email, result, true);
+
+ let externalKey = gIdentity.getUnicharAttribute(
+ "last_entered_external_gnupg_key_id"
+ );
+
+ let keyCount = result.all.length + (externalKey ? 1 : 0);
+ if (keyCount) {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpDescription"),
+ "openpgp-description-has-keys",
+ {
+ count: keyCount,
+ identity: gIdentity.email,
+ }
+ );
+ } else {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpDescription"),
+ "openpgp-description-no-key",
+ {
+ identity: gIdentity.email,
+ }
+ );
+ }
+
+ closeNotification();
+
+ let keyId = gIdentity.getUnicharAttribute("openpgp_key_id");
+ useOpenPGPKey(keyId);
+
+ // When key changes, update settings.
+ let openPgpKeyListRadio = document.getElementById("openPgpKeyListRadio");
+ openPgpKeyListRadio.addEventListener("command", event => {
+ closeNotification();
+ useOpenPGPKey(event.target.value);
+ });
+}
+
+function onPreInit(account, accountValues) {
+ gIdentity = account.defaultIdentity;
+}
+
+// NOTE: AccountManager.js checks and calls "onSave" in savePage.
+function onSave() {
+ saveE2EEncryptionSettings(gIdentity);
+}
+
+function saveE2EEncryptionSettings(identity) {
+ // Find out which radio for the encryption radio group is selected and set
+ // that on our hidden encryptionChoice pref.
+ let newValue = gEncryptionChoices.value;
+ identity.encryptionPolicy = newValue;
+
+ newValue = gTechChoices.value;
+ identity.setIntAttribute("e2etechpref", newValue);
+
+ identity.setUnicharAttribute(
+ "encryption_cert_name",
+ gEncryptionCertName.displayName || gEncryptionCertName.value
+ );
+ identity.setCharAttribute("encryption_cert_dbkey", gEncryptionCertName.dbKey);
+
+ identity.signMail = gSignMessages.checked;
+ identity.setUnicharAttribute(
+ "signing_cert_name",
+ gSignCertName.displayName || gSignCertName.value
+ );
+ identity.setCharAttribute("signing_cert_dbkey", gSignCertName.dbKey);
+
+ identity.attachPgpKey = gAttachKey.checked;
+ identity.sendAutocryptHeaders = gSendAutocryptHeaders.checked;
+ identity.protectSubject = gEncryptSubject.checked;
+ identity.autoEncryptDrafts = gEncryptDrafts.checked;
+}
+
+function alertUser(message) {
+ Services.prompt.alert(
+ window,
+ gBrandBundle.getString("brandShortName"),
+ message
+ );
+}
+
+function askUser(message) {
+ let button = Services.prompt.confirmEx(
+ window,
+ gBrandBundle.getString("brandShortName"),
+ message,
+ Services.prompt.STD_YES_NO_BUTTONS,
+ null,
+ null,
+ null,
+ null,
+ {}
+ );
+ // confirmEx returns button index:
+ return button == 0;
+}
+
+function checkOtherCert(
+ cert,
+ pref,
+ usage,
+ msgNeedCertWantSame,
+ msgWantSame,
+ msgNeedCertWantToSelect,
+ enabler
+) {
+ var otherCertInfo = document.getElementById(pref);
+ if (otherCertInfo.dbKey == cert.dbKey) {
+ // All is fine, same cert is now selected for both purposes.
+ return;
+ }
+
+ var secMsg = Cc["@mozilla.org/nsCMSSecureMessage;1"].getService(
+ Ci.nsICMSSecureMessage
+ );
+
+ var matchingOtherCert;
+ if (email_recipient_cert_usage == usage) {
+ if (secMsg.canBeUsedForEmailEncryption(cert)) {
+ matchingOtherCert = cert;
+ }
+ } else if (email_signing_cert_usage == usage) {
+ if (secMsg.canBeUsedForEmailSigning(cert)) {
+ matchingOtherCert = cert;
+ }
+ } else {
+ throw new Error("Unexpected SECCertUsage: " + usage);
+ }
+
+ var userWantsSameCert = false;
+ if (!otherCertInfo.value) {
+ if (matchingOtherCert) {
+ userWantsSameCert = askUser(gBundle.getString(msgNeedCertWantSame));
+ } else if (askUser(gBundle.getString(msgNeedCertWantToSelect))) {
+ smimeSelectCert(pref);
+ }
+ } else if (matchingOtherCert) {
+ userWantsSameCert = askUser(gBundle.getString(msgWantSame));
+ }
+
+ if (userWantsSameCert) {
+ otherCertInfo.value = cert.displayName + " [" + cert.serialNumber + "]";
+ otherCertInfo.displayName = cert.displayName;
+ otherCertInfo.dbKey = cert.dbKey;
+ enabler(true);
+ }
+}
+
+function smimeSelectCert(smime_cert) {
+ var certInfo = document.getElementById(smime_cert);
+ if (!certInfo) {
+ return;
+ }
+
+ var picker = Cc["@mozilla.org/user_cert_picker;1"].createInstance(
+ Ci.nsIUserCertPicker
+ );
+ var canceled = {};
+ var x509cert;
+ var certUsage;
+ var selectEncryptionCert;
+
+ if (smime_cert == kEncryptionCertPref) {
+ selectEncryptionCert = true;
+ certUsage = email_recipient_cert_usage;
+ } else if (smime_cert == kSigningCertPref) {
+ selectEncryptionCert = false;
+ certUsage = email_signing_cert_usage;
+ }
+
+ try {
+ x509cert = picker.pickByUsage(
+ window,
+ certInfo.value,
+ certUsage, // this is from enum SECCertUsage
+ false,
+ true,
+ gIdentity.email,
+ canceled
+ );
+ } catch (e) {
+ canceled.value = false;
+ x509cert = null;
+ }
+
+ if (!canceled.value) {
+ if (!x509cert) {
+ if (gIdentity.email) {
+ alertUser(
+ gBundle.getFormattedString(
+ selectEncryptionCert
+ ? "NoEncryptionCertForThisAddress"
+ : "NoSigningCertForThisAddress",
+ [gIdentity.email]
+ )
+ );
+ } else {
+ alertUser(
+ gBundle.getString(
+ selectEncryptionCert ? "NoEncryptionCert" : "NoSigningCert"
+ )
+ );
+ }
+ } else {
+ certInfo.disabled = false;
+ certInfo.value =
+ x509cert.displayName + " [" + x509cert.serialNumber + "]";
+ certInfo.displayName = x509cert.displayName;
+ certInfo.dbKey = x509cert.dbKey;
+
+ if (selectEncryptionCert) {
+ enableEncryptionControls(true);
+
+ checkOtherCert(
+ x509cert,
+ kSigningCertPref,
+ email_signing_cert_usage,
+ "signing_needCertWantSame",
+ "signing_wantSame",
+ "signing_needCertWantToSelect",
+ enableSigningControls
+ );
+ } else {
+ enableSigningControls(true);
+
+ checkOtherCert(
+ x509cert,
+ kEncryptionCertPref,
+ email_recipient_cert_usage,
+ "encryption_needCertWantSame",
+ "encryption_wantSame",
+ "encryption_needCertWantToSelect",
+ enableEncryptionControls
+ );
+ }
+ }
+ }
+
+ updateTechPref();
+ enableSelectButtons();
+ onSave();
+}
+
+function enableEncryptionControls(do_enable) {
+ gDisableEncryption.disabled = !do_enable;
+ gEnableEncryption.disabled = !do_enable;
+ if (!do_enable) {
+ gEncryptionChoices.value = 0;
+ }
+ // If we have a certificate or key configured that allows encryption,
+ // then we are able to encrypt drafts, too.
+ gEncryptDrafts.disabled = !do_enable;
+}
+
+function enableSigningControls(do_enable) {
+ gSignMessages.disabled = !do_enable;
+ if (!do_enable) {
+ gSignMessages.checked = false;
+ }
+}
+
+function enableSelectButtons() {
+ gSignCertName.disabled = !gSignCertName.value;
+ document.getElementById("signingCertClearButton").disabled =
+ !gSignCertName.value;
+
+ gEncryptionCertName.disabled = !gEncryptionCertName.value;
+ document.getElementById("encryptionCertClearButton").disabled =
+ !gEncryptionCertName.value;
+}
+
+function smimeClearCert(smime_cert) {
+ var certInfo = document.getElementById(smime_cert);
+ if (!certInfo) {
+ return;
+ }
+
+ certInfo.disabled = true;
+ certInfo.value = "";
+ certInfo.displayName = "";
+ certInfo.dbKey = "";
+
+ let stillHaveOther = false;
+ stillHaveOther = gKeyId != "";
+
+ if (!stillHaveOther) {
+ if (smime_cert == kEncryptionCertPref) {
+ enableEncryptionControls(false);
+ } else if (smime_cert == kSigningCertPref) {
+ enableSigningControls(false);
+ }
+ }
+
+ updateTechPref();
+ enableSelectButtons();
+ onSave();
+}
+
+function updateTechPref() {
+ let haveSigCert = gSignCertName && gSignCertName.value;
+ let haveEncCert = gEncryptionCertName && gEncryptionCertName.value;
+ let havePgpkey = !!gKeyId;
+
+ let enable = (haveSigCert || haveEncCert) && havePgpkey;
+
+ gTechAuto.disabled = !enable;
+ gTechPrefOpenPGP.disabled = !enable;
+ gTechPrefSMIME.disabled = !enable;
+
+ if (!enable) {
+ gTechChoices.value = 0;
+ }
+}
+
+function openCertManager() {
+ parent.gSubDialog.open("chrome://pippki/content/certManager.xhtml");
+}
+
+function openDeviceManager() {
+ parent.gSubDialog.open("chrome://pippki/content/device_manager.xhtml");
+}
+
+/**
+ * Open the OpenPGP Key Manager.
+ */
+function openKeyManager() {
+ window.browsingContext.topChromeWindow.openDialog(
+ "chrome://openpgp/content/ui/enigmailKeyManager.xhtml",
+ "enigmail:KeyManager",
+ "dialog,centerscreen,resizable",
+ {
+ cancelCallback: reloadOpenPgpUI,
+ okCallback: reloadOpenPgpUI,
+ }
+ );
+}
+
+/**
+ * Open the subdialog to create or import an OpenPGP key.
+ */
+function openKeyWizard() {
+ let args = {
+ identity: gIdentity,
+ gSubDialog: parent.gSubDialog,
+ cancelCallback: reloadOpenPgpUI,
+ okCallback: keyWizardSuccess,
+ okImportCallback: keyImportSuccess,
+ okExternalCallback: keyExternalSuccess,
+ keyDetailsDialog: enigmailKeyDetails,
+ };
+
+ parent.gSubDialog.open(
+ "chrome://openpgp/content/ui/keyWizard.xhtml",
+ undefined,
+ args
+ );
+}
+
+/**
+ * Show a successful notification after a new OpenPGP key was created, and
+ * trigger the reload of the key listing UI.
+ *
+ * @param {string} keyId - Id of key that the key wizard set up.
+ */
+async function keyWizardSuccess(keyId) {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpNotificationDescription"),
+ "openpgp-keygen-success"
+ );
+ document.getElementById("openPgpNotification").collapsed = false;
+
+ useOpenPGPKey(keyId);
+}
+
+/**
+ * Show a successful notification after an external key was saved, and trigger
+ * the reload of the key listing UI.
+ *
+ * @param {string} keyId - Id of key that the key wizard set up.
+ */
+async function keyExternalSuccess(keyId) {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpNotificationDescription"),
+ "openpgp-keygen-external-success"
+ );
+ document.getElementById("openPgpNotification").collapsed = false;
+
+ gIdentity.setUnicharAttribute("last_entered_external_gnupg_key_id", keyId);
+ useOpenPGPKey(keyId);
+}
+
+/**
+ * Adjust the key listing to account for newly created keys. Then set
+ * the current identity to start using this key and adjust the UI elements
+ * to be enabled now that there's a key to use.
+ *
+ * NOTE! Please always go through this to change gKeyId!
+ *
+ * @param {string} keyId - Id of key that the key wizard set up.
+ */
+function useOpenPGPKey(keyId) {
+ // Rebuild the UI so that any new keys are listed.
+ gKeyId = keyId.toUpperCase();
+
+ // Update the identity with the key obtained from the key wizard.
+ gIdentity.setUnicharAttribute("openpgp_key_id", keyId || "");
+
+ // Always update the GnuPG boolean pref to be sure the currently used key is
+ // internal or external.
+ gIdentity.setBoolAttribute(
+ "is_gnupg_key_id",
+ gKeyId ==
+ gIdentity.getUnicharAttribute("last_entered_external_gnupg_key_id")
+ );
+
+ reloadOpenPgpUI();
+}
+
+/**
+ * Show a successful notification after an import of keys, and trigger the
+ * reload of the key listing UI.
+ */
+async function keyImportSuccess() {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpNotificationDescription"),
+ "openpgp-keygen-import-success"
+ );
+ document.getElementById("openPgpNotification").collapsed = false;
+
+ reloadOpenPgpUI();
+}
+
+/**
+ * Collapse the inline notification.
+ */
+function closeNotification() {
+ document.getElementById("openPgpNotification").collapsed = true;
+}
+
+/**
+ * Refresh the UI on init or after a successful OpenPGP key generation.
+ */
+async function reloadOpenPgpUI() {
+ let result = {};
+ await EnigmailKeyRing.getAllSecretKeysByEmail(gIdentity.email, result, true);
+ let keyCount = result.all.length;
+
+ let externalKey = null;
+ if (Services.prefs.getBoolPref("mail.openpgp.allow_external_gnupg")) {
+ externalKey = gIdentity.getUnicharAttribute(
+ "last_entered_external_gnupg_key_id"
+ );
+ if (externalKey) {
+ keyCount++;
+ }
+ }
+
+ // Show the radiogroup container only if the current identity has keys.
+ // But still show it if a key (missing or unusable) is configured.
+ document.getElementById("openPgpKeyList").hidden = keyCount == 0 && !gKeyId;
+
+ // Update the OpenPGP intro description with the current key count.
+ if (keyCount) {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpDescription"),
+ "openpgp-description-has-keys",
+ {
+ count: keyCount,
+ identity: gIdentity.email,
+ }
+ );
+ } else {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpDescription"),
+ "openpgp-description-no-key",
+ {
+ identity: gIdentity.email,
+ }
+ );
+ }
+
+ let radiogroup = document.getElementById("openPgpKeyListRadio");
+
+ if (!gKeyId) {
+ radiogroup.selectedIndex = 0; // None
+ }
+
+ // Remove all the previously generated radio options, except the first.
+ while (radiogroup.lastChild.id != "openPgpOptionNone") {
+ radiogroup.removeChild(radiogroup.lastChild);
+ }
+
+ // Currently configured key is not in available, maybe deleted by the user?
+ if (gKeyId && !externalKey && !result.all.find(key => key.keyId == gKeyId)) {
+ let container = document.createXULElement("vbox");
+ container.id = `openPgpOption${gKeyId}`;
+ container.classList.add("content-blocking-category");
+
+ let box = document.createXULElement("hbox");
+ let radio = document.createXULElement("radio");
+ radio.setAttribute("flex", "1");
+ radio.disabled = true;
+ radio.id = `openPgp${gKeyId}`;
+ radio.value = gKeyId;
+ radio.label = `0x${gKeyId}`;
+ box.appendChild(radio);
+
+ let box2 = document.createXULElement("vbox");
+ box2.classList.add("indent");
+ let desc = document.createXULElement("description");
+ box2.appendChild(desc);
+
+ let key = EnigmailKeyRing.getKeyById(gKeyId);
+ if (key && !key.secretAvailable) {
+ document.l10n.setAttributes(desc, "openpgp-radio-key-not-usable");
+ } else if (key && !(await PgpSqliteDb2.isAcceptedAsPersonalKey(key.fpr))) {
+ document.l10n.setAttributes(desc, "openpgp-radio-key-not-accepted");
+ let btnContainer = document.createXULElement("hbox");
+ btnContainer.setAttribute("pack", "end");
+ btnContainer.style.width = "100%";
+ let info = document.createXULElement("button");
+ info.classList.add("openpgp-image-btn", "openpgp-props-btn");
+ document.l10n.setAttributes(info, "openpgp-key-man-key-props");
+ info.addEventListener("command", event => {
+ event.stopPropagation();
+ enigmailKeyDetails(key.keyId);
+ });
+ btnContainer.appendChild(info);
+ box2.appendChild(btnContainer);
+ } else {
+ document.l10n.setAttributes(desc, "openpgp-radio-key-not-found");
+ }
+
+ container.appendChild(box);
+ container.appendChild(box2);
+ radiogroup.appendChild(container);
+ }
+
+ // Sort keys by create date from newest to oldest.
+ result.all.sort((a, b) => {
+ return b.keyCreated - a.keyCreated;
+ });
+
+ // If the user has an external key saved, and the allow_external_gnupg
+ // pref is true, we show it on top of the list.
+ if (externalKey) {
+ let container = document.createXULElement("vbox");
+ container.id = `openPgpOption${externalKey}`;
+ container.classList.add("content-blocking-category");
+
+ let box = document.createXULElement("hbox");
+
+ let radio = document.createXULElement("radio");
+ radio.setAttribute("flex", "1");
+ radio.id = `openPgp${externalKey}`;
+ radio.value = externalKey;
+ radio.label = `0x${externalKey}`;
+
+ let remove = document.createXULElement("button");
+ document.l10n.setAttributes(remove, "openpgp-key-remove-external");
+ remove.addEventListener("command", removeExternalKey);
+ remove.classList.add("button-small");
+
+ box.appendChild(radio);
+ box.appendChild(remove);
+
+ let indent = document.createXULElement("vbox");
+ indent.classList.add("indent");
+
+ let dateContainer = document.createXULElement("hbox");
+ dateContainer.classList.add("expiration-date-container");
+ dateContainer.setAttribute("align", "center");
+
+ let external = document.createXULElement("description");
+ external.classList.add("external-pill");
+ document.l10n.setAttributes(external, "key-external-label");
+
+ dateContainer.appendChild(external);
+ indent.appendChild(dateContainer);
+
+ container.appendChild(box);
+ container.appendChild(indent);
+
+ radiogroup.appendChild(container);
+ }
+
+ // List all the available keys.
+ for (let key of result.all) {
+ let container = document.createXULElement("vbox");
+ container.id = `openPgpOption${key.keyId}`;
+ container.classList.add("content-blocking-category");
+
+ let box = document.createXULElement("hbox");
+
+ let radio = document.createXULElement("radio");
+ radio.setAttribute("flex", "1");
+ radio.id = `openPgp${key.keyId}`;
+ radio.value = key.keyId;
+ radio.label = `0x${key.keyId}`;
+
+ let toggle = document.createXULElement("button");
+ toggle.classList.add("arrowhead");
+ toggle.setAttribute("aria-expanded", "false");
+ document.l10n.setAttributes(toggle, "openpgp-key-expand-section");
+ toggle.addEventListener("command", toggleExpansion);
+
+ box.appendChild(radio);
+ box.appendChild(toggle);
+
+ let indent = document.createXULElement("vbox");
+ indent.classList.add("indent");
+
+ let dateContainer = document.createXULElement("hbox");
+ dateContainer.classList.add("expiration-date-container");
+ dateContainer.setAttribute("align", "center");
+
+ let dateIcon = document.createElement("img");
+ dateIcon.classList.add("expiration-date-icon");
+
+ let dateButton = document.createXULElement("button");
+ document.l10n.setAttributes(dateButton, "openpgp-key-man-change-expiry");
+ dateButton.addEventListener("command", event => {
+ event.stopPropagation();
+ enigmailEditKeyDate(key);
+ });
+ dateButton.setAttribute("hidden", "true");
+ dateButton.classList.add("button-small");
+
+ let description = document.createXULElement("description");
+
+ if (key.expiryTime) {
+ if (Math.round(Date.now() / 1000) > key.expiryTime) {
+ // Has expired.
+ dateContainer.classList.add("key-expired");
+ dateIcon.setAttribute(
+ "src",
+ "chrome://messenger/skin/icons/new/compact/warning.svg"
+ );
+ // Sets the title attribute.
+ // The alt attribute is not set because the accessible name is already
+ // set by the title.
+ document.l10n.setAttributes(dateIcon, "openpgp-key-has-expired-icon");
+
+ document.l10n.setAttributes(description, "openpgp-radio-key-expired", {
+ date: key.expiry,
+ });
+
+ dateButton.removeAttribute("hidden");
+ // This key is expired, so make it unselectable.
+ radio.setAttribute("disabled", "true");
+ } else {
+ // If the key expires in less than 6 months.
+ let sixMonths = new Date();
+ sixMonths.setMonth(sixMonths.getMonth() + 6);
+ if (Math.round(Date.parse(sixMonths) / 1000) > key.expiryTime) {
+ dateContainer.classList.add("key-is-expiring");
+ dateIcon.setAttribute(
+ "src",
+ "chrome://messenger/skin/icons/new/compact/info.svg"
+ );
+ // Sets the title attribute.
+ // The alt attribute is not set because the accessible name is already
+ // set by the title.
+ document.l10n.setAttributes(
+ dateIcon,
+ "openpgp-key-expires-within-6-months-icon"
+ );
+ dateButton.removeAttribute("hidden");
+ }
+
+ document.l10n.setAttributes(description, "openpgp-radio-key-expires", {
+ date: key.expiry,
+ });
+ }
+ } else {
+ document.l10n.setAttributes(description, "key-does-not-expire");
+ }
+
+ dateContainer.appendChild(dateIcon);
+ dateContainer.appendChild(description);
+ dateContainer.appendChild(dateButton);
+
+ let publishContainer = null;
+
+ // If this key is the currently selected key, suggest publishing.
+ if (key.keyId == gKeyId) {
+ publishContainer = document.createXULElement("hbox");
+ publishContainer.setAttribute("align", "center");
+
+ let publishButton = document.createElement("button");
+ document.l10n.setAttributes(publishButton, "openpgp-key-publish");
+ publishButton.addEventListener("click", () => {
+ amE2eUploadKey(key);
+ });
+ publishButton.classList.add("button-small");
+
+ let description = document.createXULElement("description");
+ document.l10n.setAttributes(
+ description,
+ "openpgp-suggest-publishing-key"
+ );
+
+ publishContainer.appendChild(description);
+ publishContainer.appendChild(publishButton);
+ }
+
+ let hiddenContainer = document.createXULElement("vbox");
+ hiddenContainer.classList.add(
+ "content-blocking-extra-information",
+ "indent"
+ );
+
+ // Start key info section.
+ let grid = document.createXULElement("hbox");
+ grid.classList.add("extra-information-label");
+
+ // Key fingerprint.
+ let fingerprintImage = document.createElement("img");
+ fingerprintImage.setAttribute(
+ "src",
+ "chrome://messenger/skin/icons/new/compact/fingerprint.svg"
+ );
+ fingerprintImage.setAttribute("alt", "");
+
+ let fingerprintLabel = document.createXULElement("label");
+ document.l10n.setAttributes(
+ fingerprintLabel,
+ "openpgp-key-details-fingerprint-label"
+ );
+ fingerprintLabel.classList.add("extra-information-label-type");
+
+ let fgrInputContainer = document.createXULElement("hbox");
+ fgrInputContainer.classList.add("input-container");
+ fgrInputContainer.setAttribute("flex", "1");
+
+ let fingerprintInput = document.createElement("input");
+ fingerprintInput.setAttribute("type", "text");
+ fingerprintInput.classList.add("plain");
+ fingerprintInput.setAttribute("readonly", "readonly");
+ fingerprintInput.value = EnigmailKey.formatFpr(key.fpr);
+
+ fgrInputContainer.appendChild(fingerprintInput);
+
+ grid.appendChild(fingerprintImage);
+ grid.appendChild(fingerprintLabel);
+ grid.appendChild(fgrInputContainer);
+
+ // Key creation date.
+ let createdImage = document.createElement("img");
+ createdImage.setAttribute(
+ "src",
+ "chrome://messenger/skin/icons/new/compact/calendar.svg"
+ );
+ createdImage.setAttribute("alt", "");
+
+ let createdLabel = document.createXULElement("label");
+ document.l10n.setAttributes(
+ createdLabel,
+ "openpgp-key-details-created-header"
+ );
+ createdLabel.classList.add("extra-information-label-type");
+
+ let createdValueContainer = document.createXULElement("hbox");
+ createdValueContainer.classList.add("input-container");
+ createdValueContainer.setAttribute("flex", "1");
+
+ let createdValue = document.createElement("input");
+ createdValue.setAttribute("type", "text");
+ createdValue.classList.add("plain");
+ createdValue.setAttribute("readonly", "readonly");
+ createdValue.value = key.created;
+
+ createdValueContainer.appendChild(createdValue);
+
+ grid.appendChild(createdImage);
+ grid.appendChild(createdLabel);
+ grid.appendChild(createdValueContainer);
+ // End key info section.
+
+ hiddenContainer.appendChild(grid);
+
+ // Action buttons.
+ let btnContainer = document.createXULElement("hbox");
+ btnContainer.setAttribute("pack", "end");
+
+ let info = document.createXULElement("button");
+ info.classList.add("openpgp-image-btn", "openpgp-props-btn");
+ document.l10n.setAttributes(info, "openpgp-key-man-key-props");
+ info.addEventListener("command", event => {
+ event.stopPropagation();
+ enigmailKeyDetails(key.keyId);
+ });
+
+ let more = document.createXULElement("button");
+ more.setAttribute("type", "menu");
+ more.classList.add("openpgp-more-btn", "last-element");
+ document.l10n.setAttributes(more, "openpgp-key-man-key-more");
+
+ let menupopup = document.createXULElement("menupopup");
+
+ let copyItem = document.createXULElement("menuitem");
+ document.l10n.setAttributes(copyItem, "openpgp-key-copy-key");
+ copyItem.addEventListener("command", event => {
+ event.stopPropagation();
+ openPgpCopyToClipboard(`0x${key.keyId}`);
+ });
+
+ let sendItem = document.createXULElement("menuitem");
+ document.l10n.setAttributes(sendItem, "openpgp-key-send-key");
+ sendItem.addEventListener("command", event => {
+ event.stopPropagation();
+ openPgpSendKeyEmail(`0x${key.keyId}`);
+ });
+
+ let exportItem = document.createXULElement("menuitem");
+ document.l10n.setAttributes(exportItem, "openpgp-key-export-key");
+ exportItem.addEventListener("command", event => {
+ event.stopPropagation();
+ openPgpExportPublicKey(`0x${key.keyId}`);
+ });
+
+ let backupItem = document.createXULElement("menuitem");
+ document.l10n.setAttributes(backupItem, "openpgp-key-backup-key");
+ backupItem.addEventListener("command", event => {
+ event.stopPropagation();
+ openPgpExportSecretKey(`0x${key.keyId}`, `${key.fpr}`);
+ });
+
+ let revokeItem = document.createXULElement("menuitem");
+ document.l10n.setAttributes(revokeItem, "openpgp-key-man-revoke-key");
+ revokeItem.addEventListener("command", event => {
+ event.stopPropagation();
+ openPgpRevokeKey(key);
+ });
+
+ let deleteItem = document.createXULElement("menuitem");
+ document.l10n.setAttributes(deleteItem, "openpgp-delete-key");
+ deleteItem.addEventListener("command", event => {
+ event.stopPropagation();
+ enigmailDeleteKey(key);
+ });
+
+ menupopup.appendChild(copyItem);
+ menupopup.appendChild(sendItem);
+ menupopup.appendChild(exportItem);
+ menupopup.appendChild(document.createXULElement("menuseparator"));
+ menupopup.appendChild(backupItem);
+ menupopup.appendChild(document.createXULElement("menuseparator"));
+ menupopup.appendChild(revokeItem);
+ menupopup.appendChild(deleteItem);
+
+ more.appendChild(menupopup);
+
+ btnContainer.appendChild(info);
+ btnContainer.appendChild(more);
+
+ hiddenContainer.appendChild(btnContainer);
+
+ indent.appendChild(dateContainer);
+ if (publishContainer) {
+ indent.appendChild(publishContainer);
+ }
+ indent.appendChild(hiddenContainer);
+
+ container.appendChild(box);
+ container.appendChild(indent);
+
+ radiogroup.appendChild(container);
+ }
+
+ // Reflect the selected key in the UI.
+ radiogroup.selectedItem = radiogroup.querySelector(
+ `radio[value="${gKeyId}"]`
+ );
+
+ // Update all the encryption options based on the selected OpenPGP key.
+ if (gKeyId) {
+ enableEncryptionControls(true);
+ enableSigningControls(true);
+ } else {
+ let stillHaveOtherEncryption =
+ gEncryptionCertName && gEncryptionCertName.value;
+ if (!stillHaveOtherEncryption) {
+ enableEncryptionControls(false);
+ }
+ let stillHaveOtherSigning = gSignCertName && gSignCertName.value;
+ if (!stillHaveOtherSigning) {
+ enableSigningControls(false);
+ }
+ }
+
+ updateTechPref();
+ enableSelectButtons();
+ updateUIForSelectedOpenPgpKey();
+
+ gAttachKey.disabled = !gKeyId;
+ gEncryptSubject.disabled = !gKeyId;
+ gSendAutocryptHeaders.disabled = !gKeyId;
+}
+
+/**
+ * Open the Key Properties subdialog.
+ *
+ * @param {string} keyId - The ID of the selected OpenPGP Key.
+ */
+function enigmailKeyDetails(keyId) {
+ keyId = keyId.replace(/^0x/, "");
+
+ parent.gSubDialog.open(
+ "chrome://openpgp/content/ui/keyDetailsDlg.xhtml",
+ undefined,
+ {
+ keyId,
+ modified: onDataModified,
+ }
+ );
+}
+
+/**
+ * Delete an OpenPGP Key.
+ *
+ * @param {object} key - The selected OpenPGP Key.
+ */
+async function enigmailDeleteKey(key) {
+ // Interrupt if the selected key is currently being used.
+ if (key.keyId == gIdentity.getUnicharAttribute("openpgp_key_id")) {
+ let [alertTitle, alertDescription] = await document.l10n.formatValues([
+ { id: "key-in-use-title" },
+ { id: "delete-key-in-use-description" },
+ ]);
+
+ Services.prompt.alert(null, alertTitle, alertDescription);
+ return;
+ }
+
+ let l10nKey = key.secretAvailable ? "delete-secret-key" : "delete-pub-key";
+ let [title, description] = await document.l10n.formatValues([
+ { id: "delete-key-title" },
+ { id: l10nKey, args: { userId: key.userId } },
+ ]);
+
+ // Ask for confirmation before proceeding.
+ if (!Services.prompt.confirm(null, title, description)) {
+ return;
+ }
+
+ let cApi = EnigmailCryptoAPI();
+ await cApi.deleteKey(key.fpr, key.secretAvailable);
+ await PgpSqliteDb2.deleteAcceptance(key.fpr);
+
+ EnigmailKeyRing.clearCache();
+ reloadOpenPgpUI();
+}
+
+/**
+ * Revoke the selected OpenPGP Key.
+ *
+ * @param {object} key - The selected OpenPGP Key.
+ */
+async function openPgpRevokeKey(key) {
+ // Interrupt if the selected key is currently being used.
+ if (key.keyId == gIdentity.getUnicharAttribute("openpgp_key_id")) {
+ let [alertTitle, alertDescription] = await document.l10n.formatValues([
+ { id: "key-in-use-title" },
+ { id: "revoke-key-in-use-description" },
+ ]);
+
+ Services.prompt.alert(null, alertTitle, alertDescription);
+ return;
+ }
+
+ EnigRevokeKey(key, function (success) {
+ if (success) {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpNotificationDescription"),
+ "openpgp-key-revoke-success"
+ );
+ document.getElementById("openPgpNotification").collapsed = false;
+
+ EnigmailKeyRing.clearCache();
+ reloadOpenPgpUI();
+ }
+ });
+}
+
+async function amE2eUploadKey(key) {
+ let ks = EnigmailKeyserverURIs.getUploadKeyServer();
+
+ let ok = await EnigmailKeyServer.upload(key.keyId, ks);
+ let msg = await document.l10n.formatValue(
+ ok ? "openpgp-key-publish-ok" : "openpgp-key-publish-fail",
+ {
+ keyserver: ks,
+ }
+ );
+
+ EnigmailDialog.alert(null, msg);
+}
+
+/**
+ * Open the subdialog to enable the user to edit the expiration date of the
+ * selected OpenPGP Key.
+ *
+ * @param {object} key - The selected OpenPGP Key.
+ */
+async function enigmailEditKeyDate(key) {
+ if (!key.iSimpleOneSubkeySameExpiry()) {
+ Services.prompt.alert(
+ null,
+ document.title,
+ await document.l10n.formatValue("openpgp-cannot-change-expiry")
+ );
+ return;
+ }
+
+ let args = {
+ keyId: key.keyId,
+ modified: onDataModified,
+ };
+
+ parent.gSubDialog.open(
+ "chrome://openpgp/content/ui/changeExpiryDlg.xhtml",
+ undefined,
+ args
+ );
+}
+
+function onDataModified() {
+ EnigmailKeyRing.clearCache();
+ reloadOpenPgpUI();
+}
+
+/**
+ * Toggle the visibility of the OpenPgp Key radio container.
+ *
+ * @param {Event} event - The DOM event.
+ */
+function toggleExpansion(event) {
+ let carat = event.target;
+ carat.classList.toggle("up");
+ carat.closest(".content-blocking-category").classList.toggle("expanded");
+ carat.setAttribute(
+ "aria-expanded",
+ carat.getAttribute("aria-expanded") === "false"
+ );
+ event.stopPropagation();
+}
+
+/**
+ * Apply a .selected class to the radio container of the currently selected
+ * OpenPGP Key.
+ * Also update UI strings describing the status of current selection.
+ */
+function updateUIForSelectedOpenPgpKey() {
+ // Remove a previously selected container, if any.
+ let current = document.querySelector(".content-blocking-category.selected");
+
+ if (current) {
+ current.classList.remove("selected");
+ }
+
+ // Highlight the parent container of the currently selected radio button.
+ // The condition needs to be sure the key is not null as a selection of "None"
+ // returns a value of "".
+ if (gKeyId !== null) {
+ let radio = document.querySelector(`radio[value="${gKeyId}"]`);
+
+ // If the currently used key was deleted, we might not have the
+ // corresponding radio element.
+ if (radio) {
+ radio.closest(".content-blocking-category").classList.add("selected");
+ }
+ }
+
+ // Reset the image in case of async reload of the list.
+ let statusLabel = document.getElementById("openPgpSelectionStatus");
+ let image = document.getElementById("openPgpStatusImage");
+ image.classList.remove("status-success", "status-error");
+
+ // Check if the currently selected key has expired.
+ if (gKeyId) {
+ let key = EnigmailKeyRing.getKeyById(gKeyId, true);
+ if (key?.expiryTime && Math.round(Date.now() / 1000) > key.expiryTime) {
+ image.setAttribute(
+ "src",
+ "chrome://messenger/skin/icons/new/compact/close.svg"
+ );
+ image.classList.add("status-error");
+ document.l10n.setAttributes(
+ statusLabel,
+ "openpgp-selection-status-error",
+ { key: `0x${gKeyId}` }
+ );
+ } else {
+ image.setAttribute(
+ "src",
+ "chrome://messenger/skin/icons/new/compact/check.svg"
+ );
+ image.classList.add("status-success");
+ document.l10n.setAttributes(
+ statusLabel,
+ "openpgp-selection-status-have-key",
+ { key: `0x${gKeyId}` }
+ );
+ }
+ }
+
+ let hide = !gKeyId;
+ statusLabel.hidden = hide;
+ document.getElementById("openPgpLearnMore").hidden = hide;
+ image.hidden = hide;
+}
+
+/**
+ * Generic method to copy a string in the user's clipboard.
+ *
+ * @param {string} val - The formatted string to be copied in the clipboard.
+ */
+async function openPgpCopyToClipboard(keyId) {
+ let exitCodeObj = {};
+
+ let keyData = await EnigmailKeyRing.extractPublicKeys(
+ [keyId], // full
+ null,
+ null,
+ null,
+ exitCodeObj,
+ {}
+ );
+
+ // Alert the user if the copy failed.
+ if (exitCodeObj.value !== 0) {
+ alertUser(await document.l10n.formatValue("copy-to-clipbrd-failed"));
+ return;
+ }
+
+ navigator.clipboard
+ .writeText(keyData)
+ .then(async () => {
+ alertUser(await document.l10n.formatValue("copy-to-clipbrd-ok"));
+ })
+ .catch(async () => {
+ alertUser(await document.l10n.formatValue("copy-to-clipbrd-failed"));
+ });
+}
+
+/**
+ * Create an attachment with the currently selected OpenPgp public Key and open
+ * a new message compose window.
+ *
+ * @param {string} keyId - The formatted OpenPgp Key ID.
+ */
+async function openPgpSendKeyEmail(keyId) {
+ let tmpFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ tmpFile.append("key.asc");
+ tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+ let exitCodeObj = {};
+ let errorMsgObj = {};
+ let keyIdArray = [keyId];
+
+ await EnigmailKeyRing.extractPublicKeys(
+ keyIdArray, // full
+ null,
+ null,
+ tmpFile,
+ exitCodeObj,
+ errorMsgObj
+ );
+
+ if (exitCodeObj.value !== 0) {
+ alertUser(errorMsgObj.value);
+ return;
+ }
+
+ // Create the key attachment.
+ let tmpFileURI = Services.io.newFileURI(tmpFile);
+ let keyAttachment = Cc[
+ "@mozilla.org/messengercompose/attachment;1"
+ ].createInstance(Ci.nsIMsgAttachment);
+
+ keyAttachment.url = tmpFileURI.spec;
+ keyAttachment.name = `${keyId}.asc`;
+ keyAttachment.temporary = true;
+ keyAttachment.contentType = "application/pgp-keys";
+
+ // Create the new message.
+ let msgCompFields = Cc[
+ "@mozilla.org/messengercompose/composefields;1"
+ ].createInstance(Ci.nsIMsgCompFields);
+ msgCompFields.addAttachment(keyAttachment);
+
+ let msgCompParam = Cc[
+ "@mozilla.org/messengercompose/composeparams;1"
+ ].createInstance(Ci.nsIMsgComposeParams);
+ msgCompParam.composeFields = msgCompFields;
+ msgCompParam.identity = gIdentity;
+ msgCompParam.type = Ci.nsIMsgCompType.New;
+ msgCompParam.format = Ci.nsIMsgCompFormat.Default;
+ msgCompParam.originalMsgURI = "";
+
+ MailServices.compose.OpenComposeWindowWithParams("", msgCompParam);
+}
+
+/**
+ * Export the selected OpenPGP public key to a file.
+ *
+ * @param {string} keyId - The ID of the selected OpenPGP Key.
+ */
+async function openPgpExportPublicKey(keyId) {
+ let outFile = EnigmailKeyRing.promptKeyExport2AsciiFilename(
+ window,
+ await document.l10n.formatValue("export-to-file"),
+ `${gIdentity.fullName}_${gIdentity.email}-${keyId}-pub.asc`
+ );
+
+ if (!outFile) {
+ return;
+ }
+
+ let exitCodeObj = {};
+ let errorMsgObj = {};
+ await EnigmailKeyRing.extractPublicKeys(
+ [keyId], // full
+ null,
+ null,
+ outFile,
+ exitCodeObj,
+ errorMsgObj
+ );
+
+ // Alert the user if the save process failed.
+ if (exitCodeObj.value !== 0) {
+ document.l10n.formatValue("openpgp-export-public-fail").then(value => {
+ alertUser(value);
+ });
+ return;
+ }
+
+ document.l10n.setAttributes(
+ document.getElementById("openPgpNotificationDescription"),
+ "openpgp-export-public-success"
+ );
+ document.getElementById("openPgpNotification").collapsed = false;
+}
+
+/**
+ * Ask the user to pick a file location and choose a password before proceeding
+ * with the backup of a secret key.
+ *
+ * @param {string} keyId - The ID of the selected OpenPGP Key.
+ * @param {string} keyFpr - The fingerprint of the selected OpenPGP Key.
+ */
+async function openPgpExportSecretKey(keyId, keyFpr) {
+ let outFile = EnigmailKeyRing.promptKeyExport2AsciiFilename(
+ window,
+ await document.l10n.formatValue("export-keypair-to-file"),
+ `${gIdentity.fullName}_${gIdentity.email}-${keyId}-secret.asc`
+ );
+
+ if (!outFile) {
+ return;
+ }
+
+ let args = {
+ okCallback: exportSecretKey,
+ file: outFile,
+ fprArray: [keyFpr],
+ };
+
+ window.browsingContext.topChromeWindow.openDialog(
+ "chrome://openpgp/content/ui/backupKeyPassword.xhtml",
+ "",
+ "dialog,modal,centerscreen,resizable",
+ args
+ );
+}
+
+/**
+ * Export the secret key after a successful password setup.
+ *
+ * @param {string} password - The declared password to protect the keys.
+ * @param {Array} fprArray - The array of fingerprint of the selected keys.
+ * @param {object} file - The file where the keys should be saved.
+ * @param {boolean} confirmed - If the password was properly typed in the prompt.
+ */
+async function exportSecretKey(password, fprArray, file, confirmed = false) {
+ // Interrupt in case this method has been called directly without confirming
+ // the input password through the password prompt.
+ if (!confirmed) {
+ return;
+ }
+
+ let backupKeyBlock = await RNP.backupSecretKeys(fprArray, password);
+ if (!backupKeyBlock) {
+ Services.prompt.alert(
+ null,
+ await document.l10n.formatValue("save-keys-failed")
+ );
+ return;
+ }
+
+ await IOUtils.writeUTF8(file.path, backupKeyBlock)
+ .then(() => {
+ document.l10n.setAttributes(
+ document.getElementById("openPgpNotificationDescription"),
+ "openpgp-export-secret-success"
+ );
+ document.getElementById("openPgpNotification").collapsed = false;
+ })
+ .catch(async err => {
+ alertUser(await document.l10n.formatValue("openpgp-export-secret-fail"));
+ });
+}
+
+/**
+ * Remove the saved external GnuPG Key.
+ */
+async function removeExternalKey() {
+ if (!GetEnigmailSvc()) {
+ return;
+ }
+
+ // Interrupt if the external key is currently being used.
+ if (
+ gIdentity.getUnicharAttribute("last_entered_external_gnupg_key_id") ==
+ gIdentity.getUnicharAttribute("openpgp_key_id")
+ ) {
+ let [alertTitle, alertDescription] = await document.l10n.formatValues([
+ { id: "key-in-use-title" },
+ { id: "delete-key-in-use-description" },
+ ]);
+
+ Services.prompt.alert(null, alertTitle, alertDescription);
+ return;
+ }
+
+ let [title, description] = await document.l10n.formatValues([
+ { id: "delete-external-key-title" },
+ { id: "delete-external-key-description" },
+ ]);
+
+ // Ask for confirmation before proceeding.
+ if (!Services.prompt.confirm(null, title, description)) {
+ return;
+ }
+
+ gIdentity.setBoolAttribute("is_gnupg_key_id", false);
+ gIdentity.setUnicharAttribute("last_entered_external_gnupg_key_id", "");
+
+ reloadOpenPgpUI();
+}
diff --git a/comm/mail/extensions/am-e2e/am-e2e.xhtml b/comm/mail/extensions/am-e2e/am-e2e.xhtml
new file mode 100644
index 0000000000..8c5620dd2f
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/am-e2e.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/openpgp/inlineNotification.css" type="text/css"?>
+
+<!DOCTYPE window SYSTEM "chrome://messenger/locale/am-smime.dtd">
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="parent.onPanelLoaded('am-e2e.xhtml');">
+
+ <script src="chrome://messenger/content/globalOverlay.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+ <script src="chrome://global/content/preferencesBindings.js"/>
+ <script src="chrome://messenger/content/AccountManager.js"/>
+ <script src="chrome://openpgp/content/ui/enigmailCommon.js"/>
+ <script src="chrome://messenger/content/am-e2e.js"/>
+
+ <vbox flex="1" style="overflow: auto;"><vbox id="containerBox" flex="1">
+ <hbox class="dialogheader">
+ <label class="dialogheader-title" value="&e2eTitle.label;"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+#include am-e2e.inc.xhtml
+ </vbox></vbox>
+
+</window>
diff --git a/comm/mail/extensions/am-e2e/components.conf b/comm/mail/extensions/am-e2e/components.conf
new file mode 100644
index 0000000000..f2399f06c2
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/components.conf
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{d7aad508-991c-401a-8b3f-7e4e055e1e2b}',
+ 'contract_ids': ['@mozilla.org/accountmanager/extension;1?name=e2e'],
+ 'jsm': 'resource:///modules/AME2E.jsm',
+ 'constructor': 'E2EService',
+ 'categories': {'mailnews-accountmanager-extensions': 'e2e-account-manager-extension'},
+ },
+]
diff --git a/comm/mail/extensions/am-e2e/moz.build b/comm/mail/extensions/am-e2e/moz.build
new file mode 100644
index 0000000000..5e89391d9a
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/moz.build
@@ -0,0 +1,16 @@
+# vim: set filetype=python:
+# 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/.
+
+EXTRA_JS_MODULES += [
+ "AME2E.jsm",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+JS_PREFERENCE_PP_FILES += [
+ "prefs/e2e-prefs.js",
+]
diff --git a/comm/mail/extensions/am-e2e/prefs/e2e-prefs.js b/comm/mail/extensions/am-e2e/prefs/e2e-prefs.js
new file mode 100644
index 0000000000..1a88003a75
--- /dev/null
+++ b/comm/mail/extensions/am-e2e/prefs/e2e-prefs.js
@@ -0,0 +1,285 @@
+#filter dumbComments emptyLines substitution
+
+// 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/.
+
+//
+// Prefs shared by OpenPGP and S/MIME
+//
+
+pref("mail.identity.default.encryptionpolicy", 0);
+pref("mail.identity.default.sign_mail", false);
+
+//
+// S/MIME prefs
+//
+
+pref("mail.identity.default.encryption_cert_name", "");
+pref("mail.identity.default.signing_cert_name", "");
+
+//
+// OpenPGP prefs
+//
+
+pref("openpgp.loglevel", "Warn");
+
+// If true, we allow the use of GnuPG for OpenPGP secret key operations
+pref("mail.openpgp.allow_external_gnupg", false);
+// If allow_external_gnupg is true: Optionally use a different gpg executable
+pref("mail.openpgp.alternative_gpg_path", "");
+// The hexadecimal OpenPGP key ID used for an identity.
+pref("mail.identity.default.openpgp_key_id", "");
+// If true, then openpgp_key_id is managed externally by GnuPG
+pref("mail.identity.default.is_gnupg_key_id", false);
+// The hexadecimal OpenPGP key ID externally configured by GnuPG used for an identity.
+pref("mail.identity.default.last_entered_external_gnupg_key_id", "");
+// When using external GnuPG, also load public keys from GnuPG keyring
+pref("mail.openpgp.fetch_pubkeys_from_gnupg", false);
+
+// When sending an OpenPGP message that is both signed and encrypted,
+// it's possible to use one combined MIME layer, or separate layers.
+pref("mail.openpgp.separate_mime_layers", false);
+
+// Load a JSON file that contains recipient key alias rules. See bug 1644085.
+// Suggested filename: openpgp-alias-rules.json
+// Simple filenames (without path) are loaded from the profile directory.
+// If you need to specify a path, use a file:// URL
+pref("mail.openpgp.alias_rules_file", "");
+
+pref("mail.openpgp.key_assistant.enable", true);
+
+// If set to true, enable user interface that allows the user to optionally set
+// and manage individual, user-defined passphrases for OpenPGP secret keys.
+// If set to false, the respective user interface will be hidden.
+// Even when set to true, the user may decide to use the original approach
+// for OpenPGP key protection (using the global primary password or none),
+// by selecting the respective choices in the user interface.
+// Note, if a user sets an user-defined passphrase while this this setting
+// is true, and then switches this setting to false, the keys will keep
+// the user-defined passphrase protection. The application will still prompt
+// to unlock the key using the user-defined passphrase whenever necessary.
+pref("mail.openpgp.passphrases.enabled", false);
+
+// Automatically enable encryption if S/MIME certificates or OpenPGP keys are
+// available for all recipients, and thus encryption is possible.
+// This pref is only about enabling, and doesn't control automatic disabling.
+pref("mail.e2ee.auto_enable", false);
+
+// If end-to-end encryption with S/MIME or OpenPGP is enabled,
+// and the user adds another recipient with unavailable certificate or key,
+// and this preference is true, then automatically disable encryption.
+// This pref is dangerous, and it is recommended to always keep it at false.
+// If you change the pref to true, the user might assume that encryption
+// is still enabled, and might not notice that encryption gets disabled.
+// There is an exception: If encryption was enabled, because the message
+// refers to an existing encrypted conversation (e.g. replying to an
+// encrypted message), this preference is ignored, encryption will
+// remain on. It isn't possible to override that behavior.
+// Note that encryption will never be disabled automatically on sending,
+// only when the list of recipients is changed.
+// If mail.e2ee.auto_enable is false, then mail.e2ee.auto_disable
+// will be ignored.
+pref("mail.e2ee.auto_disable", false);
+
+// If end-to-end encryption gets automatically disabled, inform the user
+// using a prompt.
+pref("mail.e2ee.notify_on_auto_disable", true);
+
+// If false, disable the reminder in composer, whether email could be
+// sent with OpenPGP encryption (without further user actions/decisions).
+pref("mail.openpgp.remind_encryption_possible", true);
+
+// If false, disable the reminder in composer, whether email could be
+// sent with S/MIME encryption (without further user actions/decisions).
+pref("mail.smime.remind_encryption_possible", true);
+
+pref("mail.smime.accept_insecure_sha1_message_signatures", false);
+
+// When sending, encrypt to this additional key. Not available in release channel builds.
+pref("mail.openpgp.debug.extra_encryption_key", "");
+
+// Hide prefs and menu entries from non-advanced users
+pref("temp.openpgp.advancedUser", false);
+
+// ** enigmail keySel preferences:
+// use rules to assign keys
+pref("temp.openpgp.assignKeysByRules", true);
+// use email addresses to assign keys
+pref("temp.openpgp.assignKeysByEmailAddr", true);
+// use manual dialog to assign missing keys
+pref("temp.openpgp.assignKeysManuallyIfMissing", true);
+// always srats manual dialog for keys
+pref("temp.openpgp.assignKeysManuallyAlways", false);
+
+// enable automatically decrypt/verify
+pref("temp.openpgp.autoDecrypt", true);
+
+// countdown for alerts when composing inline PGP HTML msgs
+pref("temp.openpgp.composeHtmlAlertCount", 3);
+
+// show warning message when clicking on sign icon
+pref("temp.openpgp.displaySignWarn", true);
+
+// try to match secondary uid to from address
+pref("temp.openpgp.displaySecondaryUid", true);
+
+// treat '-- ' as signature separator
+pref("temp.openpgp.doubleDashSeparator", true);
+
+// skip the attachments dialog
+pref("temp.openpgp.encryptAttachmentsSkipDlg", 0);
+
+// Encrypt to self
+pref("temp.openpgp.encryptToSelf", true);
+
+// enable 'Decrypt & open' for double click on attachment (if possible)
+pref("temp.openpgp.handleDoubleClick", true);
+
+// disable '<' and '>' around email addresses
+pref("temp.openpgp.hushMailSupport", false);
+
+// use -a for encrypting attachments for inline PGP
+pref("temp.openpgp.inlineAttachAsciiArmor", false);
+
+// extension to append for inline-encrypted attachments
+pref("temp.openpgp.inlineAttachExt", ".pgp");
+
+// extension to append for inline-signed attachments
+pref("temp.openpgp.inlineSigAttachExt", ".sig");
+
+// debug log directory (if set, also enabled debugging)
+pref("temp.openpgp.logDirectory", "");
+
+// List of key servers to use (comma separated list), ordered by priority.
+// Only the first supported keyserver will be used for uploading keys.
+pref("mail.openpgp.keyserver_list", "vks://keys.openpgp.org, hkps://keys.mailvelope.com");
+
+// keep passphrase for ... minutes
+pref("temp.openpgp.maxIdleMinutes", 5);
+
+// maximum number of parallel decrypt processes that Enigmaik will handle
+// (requests above the threshold are ignored)
+pref("temp.openpgp.maxNumProcesses", 3);
+
+// GnuPG hash algorithm
+// 0: automatic seletion (i.e. let GnuPG choose)
+// 1: SHA1, 2: RIPEMD160, 3: SHA256, 4: SHA384, 5: SHA512, 6: SHA224
+pref("temp.openpgp.mimeHashAlgorithm", 0);
+
+// no passphrase for GnuPG key needed
+pref("temp.openpgp.noPassphrase", false);
+
+// show quoted printable warning message (and remember selected state)
+pref("temp.openpgp.quotedPrintableWarn", 0);
+
+// use http proxy settings as set in Mozilla/Thunderbird
+pref("temp.openpgp.respectHttpProxy", true);
+
+// selection for which encryption model to prefer
+// 0: convenient encryption settings DEFAULT
+// 1: manual encryption settings
+pref("temp.openpgp.encryptionModel", 0);
+
+// enable encryption for replies to encrypted mails
+pref("temp.openpgp.keepSettingsForReply", true);
+
+// holds the last result of the dayily key expiry check
+pref("temp.openpgp.keyCheckResult", "");
+
+// selection for automatic send encrypted if all keys valid
+// 0: never
+// 1: if all keys found and accepted DEFAULT
+pref("temp.openpgp.autoSendEncrypted", 1);
+
+// enable automatic lookup of keys using Web Key Directory (WKD)
+// (see https://tools.ietf.org/html/draft-koch-openpgp-webkey-service)
+// 0: no
+// 1: yes DEFAULT
+pref("temp.openpgp.autoWkdLookup", 1);
+
+// ask to confirm before sending
+// 0: never DEFAULT
+// 1: always
+// 2: if send encrypted
+// 3: if send unencrypted
+// 4: if send (un)encrypted due to rules
+pref("temp.openpgp.confirmBeforeSending", 0);
+
+// show "Missing Trust in own keys" message (and remember selected state)
+pref("temp.openpgp.warnOnMissingOwnerTrust", true);
+
+// use GnuPG's default instead of Enigmail/Mozilla comment of for signed messages
+pref("temp.openpgp.useDefaultComment", true);
+
+// allow encryption to newsgroups
+pref("temp.openpgp.encryptToNews", false);
+pref("temp.openpgp.warnOnSendingNewsgroups", true);
+
+// holds the timestamp of the last check for GnuPG updates
+pref("temp.openpgp.gpgLastUpdate", "0");
+
+// set locale for GnuPG calls to en-US (Windows only)
+pref("temp.openpgp.gpgLocaleEn", true);
+
+// use PGP/MIME (0=never, 1=allow, 2=always)
+// pref("temp.openpgp.usePGPMimeOption",1); -- OBSOLETE, see mail.identity.default.pgpMimeMode
+
+// show "conflicting rules" message (and remember selected state)
+pref("temp.openpgp.warnOnRulesConflict", 0);
+
+// display a warning when the passphrase is cleared
+pref("temp.openpgp.warnClearPassphrase", true);
+
+// display a warning if the GnuPG version is deprecated
+pref("temp.openpgp.warnDeprecatedGnuPG", true);
+
+// warn if gpg-agent is found and "remember passphrase for X minutes is active"
+pref("temp.openpgp.warnGpgAgentAndIdleTime", true);
+
+// display a warning when the keys for all contacts are downloaded
+pref("temp.openpgp.warnDownloadContactKeys", true);
+
+// wrap HTML messages before sending inline PGP messages
+pref("temp.openpgp.wrapHtmlBeforeSend", true);
+
+// do reset the "references" and "in-reply-to" headers?
+pref("temp.openpgp.protectReferencesHdr", false);
+
+// tor configuration
+pref("temp.openpgp.torIpAddr", "127.0.0.1");
+pref("temp.openpgp.torServicePort", "9050");
+pref("temp.openpgp.torBrowserBundlePort", "9150");
+
+// gpg tor actions
+pref("temp.openpgp.downloadKeyWithTor", false);
+pref("temp.openpgp.downloadKeyRequireTor", false);
+pref("temp.openpgp.searchKeyWithTor", false);
+pref("temp.openpgp.searchKeyRequireTor", false);
+pref("temp.openpgp.uploadKeyWithTor", false);
+pref("temp.openpgp.uploadKeyRequireTor", false);
+
+// enable experimental features.
+// WARNING: such features may unfinished functions or tests that can break
+// existing functionality in Enigmail and Thunderbird!
+pref("temp.openpgp.enableExperiments", false);
+
+
+// Default pref values for the enigmail per-identity
+// settings
+
+pref("mail.identity.default.sendAutocryptHeaders", true);
+pref("mail.identity.default.attachPgpKey", true);
+pref("mail.identity.default.autoEncryptDrafts", true);
+pref("mail.identity.default.protectSubject", true);
+
+// 0 selected automatically, 1 prefer S/MIME, 2 prefer OpenPGP
+pref("mail.identity.default.e2etechpref", 0);
+
+//
+// Other settings (change Mozilla behaviour)
+//
+
+// disable flowed text by default
+// TODO: pref("mailnews.send_plaintext_flowed", false);
+