summaryrefslogtreecommitdiffstats
path: root/mailnews/extensions/smime
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/extensions/smime')
-rw-r--r--mailnews/extensions/smime/content/am-smime.js478
-rw-r--r--mailnews/extensions/smime/content/am-smime.xul26
-rw-r--r--mailnews/extensions/smime/content/am-smimeIdentityEditOverlay.xul39
-rw-r--r--mailnews/extensions/smime/content/am-smimeOverlay.xul102
-rw-r--r--mailnews/extensions/smime/content/certFetchingStatus.js265
-rw-r--r--mailnews/extensions/smime/content/certFetchingStatus.xul24
-rw-r--r--mailnews/extensions/smime/content/certpicker.js73
-rw-r--r--mailnews/extensions/smime/content/certpicker.xul38
-rw-r--r--mailnews/extensions/smime/content/msgCompSMIMEOverlay.js357
-rw-r--r--mailnews/extensions/smime/content/msgCompSMIMEOverlay.xul85
-rw-r--r--mailnews/extensions/smime/content/msgCompSecurityInfo.js244
-rw-r--r--mailnews/extensions/smime/content/msgCompSecurityInfo.xul68
-rw-r--r--mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.js264
-rw-r--r--mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.xul29
-rw-r--r--mailnews/extensions/smime/content/msgReadSMIMEOverlay.js102
-rw-r--r--mailnews/extensions/smime/content/msgReadSMIMEOverlay.xul34
-rw-r--r--mailnews/extensions/smime/content/msgReadSecurityInfo.js232
-rw-r--r--mailnews/extensions/smime/content/msgReadSecurityInfo.xul68
-rw-r--r--mailnews/extensions/smime/content/smime.js14
-rw-r--r--mailnews/extensions/smime/jar.mn30
-rw-r--r--mailnews/extensions/smime/moz.build15
-rw-r--r--mailnews/extensions/smime/public/moz.build15
-rw-r--r--mailnews/extensions/smime/public/nsICertPickDialogs.idl30
-rw-r--r--mailnews/extensions/smime/public/nsIEncryptedSMIMEURIsSrvc.idl24
-rw-r--r--mailnews/extensions/smime/public/nsIMsgSMIMECompFields.idl18
-rw-r--r--mailnews/extensions/smime/public/nsIMsgSMIMEHeaderSink.idl23
-rw-r--r--mailnews/extensions/smime/public/nsISMimeJSHelper.idl73
-rw-r--r--mailnews/extensions/smime/public/nsIUserCertPicker.idl28
-rw-r--r--mailnews/extensions/smime/src/moz.build23
-rw-r--r--mailnews/extensions/smime/src/nsCertPicker.cpp471
-rw-r--r--mailnews/extensions/smime/src/nsCertPicker.h36
-rw-r--r--mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp36
-rw-r--r--mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h25
-rw-r--r--mailnews/extensions/smime/src/nsMsgComposeSecure.cpp1203
-rw-r--r--mailnews/extensions/smime/src/nsMsgComposeSecure.h106
-rw-r--r--mailnews/extensions/smime/src/nsMsgSMIMECID.h42
-rw-r--r--mailnews/extensions/smime/src/nsSMimeJSHelper.cpp335
-rw-r--r--mailnews/extensions/smime/src/nsSMimeJSHelper.h26
-rw-r--r--mailnews/extensions/smime/src/smime-service.js24
-rw-r--r--mailnews/extensions/smime/src/smime-service.manifest3
40 files changed, 5128 insertions, 0 deletions
diff --git a/mailnews/extensions/smime/content/am-smime.js b/mailnews/extensions/smime/content/am-smime.js
new file mode 100644
index 000000000..4a90d0cd7
--- /dev/null
+++ b/mailnews/extensions/smime/content/am-smime.js
@@ -0,0 +1,478 @@
+/* -*- Mode: Java; 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/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var nsIX509CertDB = Components.interfaces.nsIX509CertDB;
+var nsX509CertDBContractID = "@mozilla.org/security/x509certdb;1";
+var nsIX509Cert = Components.interfaces.nsIX509Cert;
+
+var email_recipient_cert_usage = 5;
+var email_signing_cert_usage = 4;
+
+var gIdentity;
+var gPref = null;
+var gEncryptionCertName = null;
+var gHiddenEncryptionPolicy = null;
+var gEncryptionChoices = null;
+var gSignCertName = null;
+var gSignMessages = null;
+var gEncryptAlways = null;
+var gNeverEncrypt = null;
+var gBundle = null;
+var gBrandBundle;
+var gSmimePrefbranch;
+var gEncryptionChoicesLocked;
+var gSigningChoicesLocked;
+var kEncryptionCertPref = "identity.encryption_cert_name";
+var kSigningCertPref = "identity.signing_cert_name";
+
+function onInit()
+{
+ smimeInitializeFields();
+}
+
+function smimeInitializeFields()
+{
+ // initialize all of our elements based on the current identity values....
+ gEncryptionCertName = document.getElementById(kEncryptionCertPref);
+ gHiddenEncryptionPolicy = document.getElementById("identity.encryptionpolicy");
+ gEncryptionChoices = document.getElementById("encryptionChoices");
+ gSignCertName = document.getElementById(kSigningCertPref);
+ gSignMessages = document.getElementById("identity.sign_mail");
+ gEncryptAlways = document.getElementById("encrypt_mail_always");
+ gNeverEncrypt = document.getElementById("encrypt_mail_never");
+ gBundle = document.getElementById("bundle_smime");
+ gBrandBundle = document.getElementById("bundle_brand");
+
+ gEncryptionChoicesLocked = false;
+ gSigningChoicesLocked = false;
+
+ if (!gIdentity) {
+ // The user is going to create a new identity.
+ // Set everything to default values.
+ // Do not take over the values from gAccount.defaultIdentity
+ // as the new identity is going to have a different mail address.
+
+ gEncryptionCertName.value = "";
+ gEncryptionCertName.nickname = "";
+ gEncryptionCertName.dbKey = "";
+ gSignCertName.value = "";
+ gSignCertName.nickname = "";
+ gSignCertName.dbKey = "";
+
+ gEncryptAlways.setAttribute("disabled", true);
+ gNeverEncrypt.setAttribute("disabled", true);
+ gSignMessages.setAttribute("disabled", true);
+
+ gSignMessages.checked = false;
+ gEncryptionChoices.value = 0;
+ }
+ else {
+ var certdb = Components.classes[nsX509CertDBContractID].getService(nsIX509CertDB);
+ var x509cert = null;
+
+ 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
+ // nickname in a separate property.
+ try {
+ if (certdb && gEncryptionCertName.dbKey &&
+ (x509cert = certdb.findCertByDBKey(gEncryptionCertName.dbKey))) {
+ gEncryptionCertName.value = x509cert.nickname + " [" + x509cert.serialNumber + "]";
+ gEncryptionCertName.nickname = x509cert.nickname;
+ }
+ } catch(e) {}
+
+ gEncryptionChoices.value = gIdentity.getIntAttribute("encryptionpolicy");
+
+ if (!gEncryptionCertName.value) {
+ gEncryptAlways.setAttribute("disabled", true);
+ gNeverEncrypt.setAttribute("disabled", true);
+ }
+ else {
+ enableEncryptionControls(true);
+ }
+
+ gSignCertName.value = gIdentity.getUnicharAttribute("signing_cert_name");
+ gSignCertName.dbKey = gIdentity.getCharAttribute("signing_cert_dbkey");
+ x509cert = null;
+ // same procedure as with gEncryptionCertName (see above)
+ try {
+ if (certdb && gSignCertName.dbKey &&
+ (x509cert = certdb.findCertByDBKey(gSignCertName.dbKey))) {
+ gSignCertName.value = x509cert.nickname + " [" + x509cert.serialNumber + "]";
+ gSignCertName.nickname = x509cert.nickname;
+ }
+ } catch(e) {}
+
+ gSignMessages.checked = gIdentity.getBoolAttribute("sign_mail");
+ if (!gSignCertName.value)
+ {
+ gSignMessages.setAttribute("disabled", true);
+ }
+ else {
+ enableSigningControls(true);
+ }
+ }
+
+ // Always start with enabling signing and encryption cert select buttons.
+ // This will keep the visibility of buttons in a sane state as user
+ // jumps from security panel of one account to another.
+ enableCertSelectButtons();
+
+ // Disable all locked elements on the panel
+ if (gIdentity)
+ onLockPreference();
+}
+
+function onPreInit(account, accountValues)
+{
+ gIdentity = account.defaultIdentity;
+}
+
+function onSave()
+{
+ smimeSave();
+}
+
+function smimeSave()
+{
+ // find out which radio for the encryption radio group is selected and set that on our hidden encryptionChoice pref....
+ var newValue = gEncryptionChoices.value;
+ gHiddenEncryptionPolicy.setAttribute('value', newValue);
+ gIdentity.setIntAttribute("encryptionpolicy", newValue);
+ gIdentity.setUnicharAttribute("encryption_cert_name",
+ gEncryptionCertName.nickname || gEncryptionCertName.value);
+ gIdentity.setCharAttribute("encryption_cert_dbkey", gEncryptionCertName.dbKey);
+
+ gIdentity.setBoolAttribute("sign_mail", gSignMessages.checked);
+ gIdentity.setUnicharAttribute("signing_cert_name",
+ gSignCertName.nickname || gSignCertName.value);
+ gIdentity.setCharAttribute("signing_cert_dbkey", gSignCertName.dbKey);
+}
+
+function smimeOnAcceptEditor()
+{
+ try {
+ if (!onOk())
+ return false;
+ }
+ catch (ex) {}
+
+ smimeSave();
+
+ return true;
+}
+
+function onLockPreference()
+{
+ var initPrefString = "mail.identity";
+ var finalPrefString;
+
+ var allPrefElements = [
+ { prefstring:"signingCertSelectButton", id:"signingCertSelectButton"},
+ { prefstring:"encryptionCertSelectButton", id:"encryptionCertSelectButton"},
+ { prefstring:"sign_mail", id:"identity.sign_mail"},
+ { prefstring:"encryptionpolicy", id:"encryptionChoices"}
+ ];
+
+ finalPrefString = initPrefString + "." + gIdentity.key + ".";
+ gSmimePrefbranch = Services.prefs.getBranch(finalPrefString);
+
+ disableIfLocked( allPrefElements );
+}
+
+
+// Does the work of disabling an element given the array which contains xul id/prefstring pairs.
+// Also saves the id/locked state in an array so that other areas of the code can avoid
+// stomping on the disabled state indiscriminately.
+function disableIfLocked( prefstrArray )
+{
+ var i;
+ for (i=0; i<prefstrArray.length; i++) {
+ var id = prefstrArray[i].id;
+ var element = document.getElementById(id);
+ if (gSmimePrefbranch.prefIsLocked(prefstrArray[i].prefstring)) {
+ // If encryption choices radio group is locked, make sure the individual
+ // choices in the group are locked. Set a global (gEncryptionChoicesLocked)
+ // indicating the status so that locking can be maintained further.
+ if (id == "encryptionChoices") {
+ document.getElementById("encrypt_mail_never").setAttribute("disabled", "true");
+ document.getElementById("encrypt_mail_always").setAttribute("disabled", "true");
+ gEncryptionChoicesLocked = true;
+ }
+ // If option to sign mail is locked (with true/false set in config file), disable
+ // the corresponding checkbox and set a global (gSigningChoicesLocked) in order to
+ // honor the locking as user changes other elements on the panel.
+ if (id == "identity.sign_mail") {
+ document.getElementById("identity.sign_mail").setAttribute("disabled", "true");
+ gSigningChoicesLocked = true;
+ }
+ else {
+ element.setAttribute("disabled", "true");
+ if (id == "signingCertSelectButton") {
+ document.getElementById("signingCertClearButton").setAttribute("disabled", "true");
+ }
+ else if (id == "encryptionCertSelectButton") {
+ document.getElementById("encryptionCertClearButton").setAttribute("disabled", "true");
+ }
+ }
+ }
+ }
+}
+
+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)
+ return;
+
+ if (otherCertInfo.dbKey == cert.dbKey)
+ // all is fine, same cert is now selected for both purposes
+ return;
+
+ var certdb = Components.classes[nsX509CertDBContractID].getService(nsIX509CertDB);
+ if (!certdb)
+ return;
+
+ if (email_recipient_cert_usage == usage) {
+ matchingOtherCert = certdb.findEmailEncryptionCert(cert.nickname);
+ }
+ else if (email_signing_cert_usage == usage) {
+ matchingOtherCert = certdb.findEmailSigningCert(cert.nickname);
+ }
+ else
+ return;
+
+ var userWantsSameCert = false;
+
+ if (!otherCertInfo.value.length) {
+ if (matchingOtherCert && (matchingOtherCert.dbKey == cert.dbKey)) {
+ userWantsSameCert = askUser(gBundle.getString(msgNeedCertWantSame));
+ }
+ else {
+ if (askUser(gBundle.getString(msgNeedCertWantToSelect))) {
+ smimeSelectCert(pref);
+ }
+ }
+ }
+ else {
+ if (matchingOtherCert && (matchingOtherCert.dbKey == cert.dbKey)) {
+ userWantsSameCert = askUser(gBundle.getString(msgWantSame));
+ }
+ }
+
+ if (userWantsSameCert) {
+ otherCertInfo.value = cert.nickname + " [" + cert.serialNumber + "]";
+ otherCertInfo.nickname = cert.nickname;
+ otherCertInfo.dbKey = cert.dbKey;
+ enabler(true);
+ }
+}
+
+function smimeSelectCert(smime_cert)
+{
+ var certInfo = document.getElementById(smime_cert);
+ if (!certInfo)
+ return;
+
+ var picker = Components.classes["@mozilla.org/user_cert_picker;1"]
+ .createInstance(Components.interfaces.nsIUserCertPicker);
+ var canceled = new Object;
+ var x509cert = 0;
+ 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.removeAttribute("disabled");
+ certInfo.value = x509cert.nickname + " [" + x509cert.serialNumber + "]";
+ certInfo.nickname = x509cert.nickname;
+ 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);
+ }
+ }
+ }
+
+ enableCertSelectButtons();
+}
+
+function enableEncryptionControls(do_enable)
+{
+ if (gEncryptionChoicesLocked)
+ return;
+
+ if (do_enable) {
+ gEncryptAlways.removeAttribute("disabled");
+ gNeverEncrypt.removeAttribute("disabled");
+ gEncryptionCertName.removeAttribute("disabled");
+ }
+ else {
+ gEncryptAlways.setAttribute("disabled", "true");
+ gNeverEncrypt.setAttribute("disabled", "true");
+ gEncryptionCertName.setAttribute("disabled", "true");
+ gEncryptionChoices.value = 0;
+ }
+}
+
+function enableSigningControls(do_enable)
+{
+ if (gSigningChoicesLocked)
+ return;
+
+ if (do_enable) {
+ gSignMessages.removeAttribute("disabled");
+ gSignCertName.removeAttribute("disabled");
+ }
+ else {
+ gSignMessages.setAttribute("disabled", "true");
+ gSignCertName.setAttribute("disabled", "true");
+ gSignMessages.checked = false;
+ }
+}
+
+function enableCertSelectButtons()
+{
+ document.getElementById("signingCertSelectButton").removeAttribute("disabled");
+
+ if (document.getElementById('identity.signing_cert_name').value.length)
+ document.getElementById("signingCertClearButton").removeAttribute("disabled");
+ else
+ document.getElementById("signingCertClearButton").setAttribute("disabled", "true");
+
+ document.getElementById("encryptionCertSelectButton").removeAttribute("disabled");
+
+ if (document.getElementById('identity.encryption_cert_name').value.length)
+ document.getElementById("encryptionCertClearButton").removeAttribute("disabled");
+ else
+ document.getElementById("encryptionCertClearButton").setAttribute("disabled", "true");
+}
+
+function smimeClearCert(smime_cert)
+{
+ var certInfo = document.getElementById(smime_cert);
+ if (!certInfo)
+ return;
+
+ certInfo.setAttribute("disabled", "true");
+ certInfo.value = "";
+ certInfo.nickname = "";
+ certInfo.dbKey = "";
+
+ if (smime_cert == kEncryptionCertPref) {
+ enableEncryptionControls(false);
+ } else if (smime_cert == kSigningCertPref) {
+ enableSigningControls(false);
+ }
+
+ enableCertSelectButtons();
+}
+
+function openCertManager()
+{
+ // Check for an existing certManager window and focus it; it's not
+ // application modal.
+ let lastCertManager = Services.wm.getMostRecentWindow("mozilla:certmanager");
+ if (lastCertManager)
+ lastCertManager.focus();
+ else
+ window.openDialog("chrome://pippki/content/certManager.xul", "",
+ "centerscreen,resizable=yes,dialog=no");
+}
+
+function openDeviceManager()
+{
+ // Check for an existing deviceManager window and focus it; it's not
+ // application modal.
+ let lastCertManager = Services.wm.getMostRecentWindow("mozilla:devicemanager");
+ if (lastCertManager)
+ lastCertManager.focus();
+ else
+ window.openDialog("chrome://pippki/content/device_manager.xul", "",
+ "centerscreen,resizable=yes,dialog=no");
+}
+
+function smimeOnLoadEditor()
+{
+ smimeInitializeFields();
+
+ document.documentElement.setAttribute("ondialogaccept",
+ "return smimeOnAcceptEditor();");
+}
+
diff --git a/mailnews/extensions/smime/content/am-smime.xul b/mailnews/extensions/smime/content/am-smime.xul
new file mode 100644
index 000000000..bb46bb49d
--- /dev/null
+++ b/mailnews/extensions/smime/content/am-smime.xul
@@ -0,0 +1,26 @@
+<?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"?>
+
+<?xul-overlay href="chrome://messenger/content/am-smimeOverlay.xul"?>
+
+<!DOCTYPE page SYSTEM "chrome://messenger/locale/am-smime.dtd">
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ class="color-dialog"
+ onload="parent.onPanelLoaded('am-smime.xul');"
+ ondialogaccept="smimeOnAcceptEditor();">
+
+ <vbox flex="1" style="overflow: auto;">
+ <script type="application/javascript" src="chrome://messenger/content/AccountManager.js"/>
+ <script type="application/javascript" src="chrome://messenger/content/am-smime.js"/>
+
+ <dialogheader title="&securityTitle.label;"/>
+
+ <vbox flex="1" id="smimeEditing"/>
+ </vbox>
+
+</page>
diff --git a/mailnews/extensions/smime/content/am-smimeIdentityEditOverlay.xul b/mailnews/extensions/smime/content/am-smimeIdentityEditOverlay.xul
new file mode 100644
index 000000000..2ff5c2b7d
--- /dev/null
+++ b/mailnews/extensions/smime/content/am-smimeIdentityEditOverlay.xul
@@ -0,0 +1,39 @@
+<?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"?>
+
+<?xul-overlay href="chrome://messenger/content/am-smimeOverlay.xul"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/am-smime.dtd">
+
+<!--
+ This is the overlay that adds the SMIME configurator
+ to the identity editor of the account manager
+-->
+<overlay id="smimeAmIdEditOverlay"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://messenger/content/AccountManager.js"/>
+ <script type="application/javascript"
+ src="chrome://messenger/content/am-smime.js"/>
+
+ <tabs id="identitySettings">
+ <tab label="&securityTab.label;"/>
+ </tabs>
+
+ <tabpanels id="identityTabsPanels">
+ <vbox flex="1" name="smimeEditingContent" id="smimeEditing"/>
+ </tabpanels>
+
+ <script type="application/javascript">
+ <![CDATA[
+ window.addEventListener("load", smimeOnLoadEditor, false);
+ ]]>
+ </script>
+</overlay>
diff --git a/mailnews/extensions/smime/content/am-smimeOverlay.xul b/mailnews/extensions/smime/content/am-smimeOverlay.xul
new file mode 100644
index 000000000..eb76b4b2c
--- /dev/null
+++ b/mailnews/extensions/smime/content/am-smimeOverlay.xul
@@ -0,0 +1,102 @@
+<?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"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/am-smime.dtd">
+
+<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="smimeEditing">
+
+ <stringbundleset>
+ <stringbundle id="bundle_smime" src="chrome://messenger/locale/am-smime.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ </stringbundleset>
+
+ <label hidden="true" wsm_persist="true" id="identity.encryptionpolicy"/>
+
+ <description>&securityHeading.label;</description>
+
+ <groupbox id="signing.titlebox">
+ <caption label="&signingGroupTitle.label;"/>
+
+ <label value="&signingCert.message;" control="identity.signing_cert_name"
+ prefstring="mail.identity.%identitykey%.encryptionpolicy"/>
+
+ <hbox align="center">
+ <textbox id="identity.signing_cert_name" wsm_persist="true" flex="1"
+ prefstring="mail.identity.%identitykey%.signing_cert_name"
+ readonly="true" disabled="true"/>
+
+ <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"/>
+
+ <checkbox id="identity.sign_mail" wsm_persist="true"
+ prefstring="mail.identity.%identitykey%.sign_mail"
+ label="&signMessage.label;" accesskey="&signMessage.accesskey;"/>
+ </groupbox>
+
+ <groupbox id="encryption.titlebox">
+ <caption label="&encryptionGroupTitle.label;"/>
+
+ <label value="&encryptionCert.message;"
+ control="identity.encryption_cert_name"/>
+
+ <hbox align="center">
+ <textbox id="identity.encryption_cert_name" wsm_persist="true" flex="1"
+ prefstring="mail.identity.%identitykey%.encryption_cert_name"
+ readonly="true" disabled="true"/>
+
+ <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"/>
+
+ <label value="&encryptionChoiceLabel.label;" control="encryptionChoices"/>
+
+ <radiogroup id="encryptionChoices">
+ <radio id="encrypt_mail_never" wsm_persist="true" value="0"
+ label="&neverEncrypt.label;"
+ accesskey="&neverEncrypt.accesskey;"/>
+
+ <radio id="encrypt_mail_always" wsm_persist="true" value="2"
+ label="&alwaysEncryptMessage.label;"
+ accesskey="&alwaysEncryptMessage.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <!-- Certificate manager -->
+ <groupbox id="smimeCertificateManager" orient="horizontal">
+ <caption label="&certificates.label;"/>
+ <button id="openCertManagerButton" oncommand="openCertManager();"
+ label="&manageCerts2.label;" accesskey="&manageCerts2.accesskey;"
+ prefstring="security.disable_button.openCertManager"/>
+ <button id="openDeviceManagerButton" oncommand="openDeviceManager();"
+ label="&manageDevices.label;" accesskey="&manageDevices.accesskey;"
+ prefstring="security.disable_button.openDeviceManager"/>
+ </groupbox>
+ </vbox>
+</overlay>
diff --git a/mailnews/extensions/smime/content/certFetchingStatus.js b/mailnews/extensions/smime/content/certFetchingStatus.js
new file mode 100644
index 000000000..8848ff9b6
--- /dev/null
+++ b/mailnews/extensions/smime/content/certFetchingStatus.js
@@ -0,0 +1,265 @@
+/* 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/. */
+
+/* We expect the following arguments:
+ - pref name of LDAP directory to fetch from
+ - array with email addresses
+
+ Display modal dialog with message and stop button.
+ In onload, kick off binding to LDAP.
+ When bound, kick off the searches.
+ On finding certificates, import into permanent cert database.
+ When all searches are finished, close the dialog.
+*/
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var nsIX509CertDB = Components.interfaces.nsIX509CertDB;
+var nsX509CertDB = "@mozilla.org/security/x509certdb;1";
+var CertAttribute = "usercertificate;binary";
+
+var gEmailAddresses;
+var gDirectoryPref;
+var gLdapServerURL;
+var gLdapConnection;
+var gCertDB;
+var gLdapOperation;
+var gLogin;
+
+function onLoad()
+{
+ gDirectoryPref = window.arguments[0];
+ gEmailAddresses = window.arguments[1];
+
+ if (!gEmailAddresses.length)
+ {
+ window.close();
+ return;
+ }
+
+ setTimeout(search, 1);
+}
+
+function search()
+{
+ // get the login to authenticate as, if there is one
+ try {
+ gLogin = Services.prefs.getComplexValue(gDirectoryPref + ".auth.dn", Components.interfaces.nsISupportsString).data;
+ } catch (ex) {
+ // if we don't have this pref, no big deal
+ }
+
+ try {
+ let url = Services.prefs.getCharPref(gDirectoryPref + ".uri");
+
+ gLdapServerURL = Services.io
+ .newURI(url, null, null).QueryInterface(Components.interfaces.nsILDAPURL);
+
+ gLdapConnection = Components.classes["@mozilla.org/network/ldap-connection;1"]
+ .createInstance().QueryInterface(Components.interfaces.nsILDAPConnection);
+
+ gLdapConnection.init(gLdapServerURL, gLogin, new boundListener(),
+ null, Components.interfaces.nsILDAPConnection.VERSION3);
+
+ } catch (ex) {
+ dump(ex);
+ dump(" exception creating ldap connection\n");
+ window.close();
+ }
+}
+
+function stopFetching()
+{
+ if (gLdapOperation) {
+ try {
+ gLdapOperation.abandon();
+ }
+ catch (e) {
+ }
+ }
+ return true;
+}
+
+function importCert(ber_value)
+{
+ if (!gCertDB) {
+ gCertDB = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
+ }
+
+ var cert_length = new Object();
+ var cert_bytes = ber_value.get(cert_length);
+
+ if (cert_bytes) {
+ gCertDB.importEmailCertificate(cert_bytes, cert_length.value, null);
+ }
+}
+
+function getLDAPOperation()
+{
+ gLdapOperation = Components.classes["@mozilla.org/network/ldap-operation;1"]
+ .createInstance().QueryInterface(Components.interfaces.nsILDAPOperation);
+
+ gLdapOperation.init(gLdapConnection,
+ new ldapMessageListener(),
+ null);
+}
+
+function getPassword()
+{
+ // we only need a password if we are using credentials
+ if (gLogin)
+ {
+ let authPrompter = Services.ww.getNewAuthPrompter(window.QueryInterface(Components.interfaces.nsIDOMWindow));
+ let strBundle = document.getElementById('bundle_ldap');
+ let password = { value: "" };
+
+ // nsLDAPAutocompleteSession uses asciiHost instead of host for the prompt text, I think we should be
+ // consistent.
+ if (authPrompter.promptPassword(strBundle.getString("authPromptTitle"),
+ strBundle.getFormattedString("authPromptText", [gLdapServerURL.asciiHost]),
+ gLdapServerURL.spec,
+ authPrompter.SAVE_PASSWORD_PERMANENTLY,
+ password))
+ return password.value;
+ }
+
+ return null;
+}
+
+function kickOffBind()
+{
+ try {
+ getLDAPOperation();
+ gLdapOperation.simpleBind(getPassword());
+ }
+ catch (e) {
+ window.close();
+ }
+}
+
+function kickOffSearch()
+{
+ try {
+ var prefix1 = "";
+ var suffix1 = "";
+
+ var urlFilter = gLdapServerURL.filter;
+
+ if (urlFilter != null && urlFilter.length > 0 && urlFilter != "(objectclass=*)") {
+ if (urlFilter.startsWith('(')) {
+ prefix1 = "(&" + urlFilter;
+ }
+ else {
+ prefix1 = "(&(" + urlFilter + ")";
+ }
+ suffix1 = ")";
+ }
+
+ var prefix2 = "";
+ var suffix2 = "";
+
+ if (gEmailAddresses.length > 1) {
+ prefix2 = "(|";
+ suffix2 = ")";
+ }
+
+ var mailFilter = "";
+
+ for (var i = 0; i < gEmailAddresses.length; ++i) {
+ mailFilter += "(mail=" + gEmailAddresses[i] + ")";
+ }
+
+ var filter = prefix1 + prefix2 + mailFilter + suffix2 + suffix1;
+
+ var wanted_attributes = CertAttribute;
+
+ // Max search results =>
+ // Double number of email addresses, because each person might have
+ // multiple certificates listed. We expect at most two certificates,
+ // one for signing, one for encrypting.
+ // Maybe that number should be larger, to allow for deployments,
+ // where even more certs can be stored per user???
+
+ var maxEntriesWanted = gEmailAddresses.length * 2;
+
+ getLDAPOperation();
+ gLdapOperation.searchExt(gLdapServerURL.dn, gLdapServerURL.scope,
+ filter, wanted_attributes, 0, maxEntriesWanted);
+ }
+ catch (e) {
+ window.close();
+ }
+}
+
+
+function boundListener() {
+}
+
+boundListener.prototype.QueryInterface =
+ function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsILDAPMessageListener))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+
+boundListener.prototype.onLDAPMessage =
+ function(aMessage) {
+ }
+
+boundListener.prototype.onLDAPInit =
+ function(aConn, aStatus) {
+ kickOffBind();
+ }
+
+
+function ldapMessageListener() {
+}
+
+ldapMessageListener.prototype.QueryInterface =
+ function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsILDAPMessageListener))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+
+ldapMessageListener.prototype.onLDAPMessage =
+ function(aMessage) {
+ if (Components.interfaces.nsILDAPMessage.RES_SEARCH_RESULT == aMessage.type) {
+ window.close();
+ return;
+ }
+
+ if (Components.interfaces.nsILDAPMessage.RES_BIND == aMessage.type) {
+ if (Components.interfaces.nsILDAPErrors.SUCCESS != aMessage.errorCode) {
+ window.close();
+ }
+ else {
+ kickOffSearch();
+ }
+ return;
+ }
+
+ if (Components.interfaces.nsILDAPMessage.RES_SEARCH_ENTRY == aMessage.type) {
+ var outSize = new Object();
+ try {
+ var outBinValues = aMessage.getBinaryValues(CertAttribute, outSize);
+
+ var i;
+ for (i=0; i < outSize.value; ++i) {
+ importCert(outBinValues[i]);
+ }
+ }
+ catch (e) {
+ }
+ return;
+ }
+ }
+
+ldapMessageListener.prototype.onLDAPInit =
+ function(aConn, aStatus) {
+ }
diff --git a/mailnews/extensions/smime/content/certFetchingStatus.xul b/mailnews/extensions/smime/content/certFetchingStatus.xul
new file mode 100644
index 000000000..29b824fc9
--- /dev/null
+++ b/mailnews/extensions/smime/content/certFetchingStatus.xul
@@ -0,0 +1,24 @@
+<?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://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/smime/certFetchingStatus.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger-smime/locale/certFetchingStatus.dtd">
+
+<dialog id="certFetchingStatus" title="&title.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 50em;"
+ buttons="cancel"
+ buttonlabelcancel="&stop.label;"
+ ondialogcancel="return stopFetching();"
+ onload="onLoad();">
+
+ <stringbundle id="bundle_ldap" src="chrome://mozldap/locale/ldap.properties"/>
+<script type="application/javascript" src="chrome://messenger-smime/content/certFetchingStatus.js"/>
+
+ <description>&info.message;</description>
+
+</dialog>
diff --git a/mailnews/extensions/smime/content/certpicker.js b/mailnews/extensions/smime/content/certpicker.js
new file mode 100644
index 000000000..19554066f
--- /dev/null
+++ b/mailnews/extensions/smime/content/certpicker.js
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+
+var dialogParams;
+var itemCount = 0;
+
+function onLoad()
+{
+ dialogParams = window.arguments[0].QueryInterface(nsIDialogParamBlock);
+
+ var selectElement = document.getElementById("nicknames");
+ itemCount = dialogParams.GetInt(0);
+
+ var selIndex = dialogParams.GetInt(1);
+ if (selIndex < 0) {
+ selIndex = 0;
+ }
+
+ for (let i = 0; i < itemCount; i++) {
+ let menuItemNode = document.createElement("menuitem");
+ let nick = dialogParams.GetString(i);
+ menuItemNode.setAttribute("value", i);
+ menuItemNode.setAttribute("label", nick); // This is displayed.
+ selectElement.firstChild.appendChild(menuItemNode);
+
+ if (selIndex == i) {
+ selectElement.selectedItem = menuItemNode;
+ }
+ }
+
+ dialogParams.SetInt(0, 0); // Set cancel return value.
+ setDetails();
+}
+
+function setDetails()
+{
+ let selItem = document.getElementById("nicknames").value;
+ if (selItem.length == 0) {
+ return;
+ }
+
+ let index = parseInt(selItem);
+ let details = dialogParams.GetString(index + itemCount);
+ document.getElementById("details").value = details;
+}
+
+function onCertSelected()
+{
+ setDetails();
+}
+
+function doOK()
+{
+ // Signal that the user accepted.
+ dialogParams.SetInt(0, 1);
+
+ // Signal the index of the selected cert in the list of cert nicknames
+ // provided.
+ let index = parseInt(document.getElementById("nicknames").value);
+ dialogParams.SetInt(1, index);
+ return true;
+}
+
+function doCancel()
+{
+ dialogParams.SetInt(0, 0); // Signal that the user cancelled.
+ return true;
+}
diff --git a/mailnews/extensions/smime/content/certpicker.xul b/mailnews/extensions/smime/content/certpicker.xul
new file mode 100644
index 000000000..2c4cd3b22
--- /dev/null
+++ b/mailnews/extensions/smime/content/certpicker.xul
@@ -0,0 +1,38 @@
+<?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://global/skin/" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % amSMIMEDTD SYSTEM "chrome://messenger/locale/am-smime.dtd" >
+%amSMIMEDTD;
+]>
+
+<dialog id="certPicker" title="&certPicker.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 50em;"
+ buttons="accept,cancel"
+ ondialogaccept="return doOK();"
+ ondialogcancel="return doCancel();"
+ onload="onLoad();">
+
+<script type="application/javascript"
+ src="chrome://messenger/content/certpicker.js"/>
+
+ <hbox align="center">
+ <broadcaster id="certSelected" oncommand="onCertSelected();"/>
+ <label id="pickerInfo" value="&certPicker.info;"/>
+ <!-- The items in this menulist must never be sorted,
+ but remain in the order filled by the application
+ -->
+ <menulist id="nicknames" observes="certSelected">
+ <menupopup/>
+ </menulist>
+ </hbox>
+ <separator class="thin"/>
+ <label value="&certPicker.detailsLabel;"/>
+ <textbox readonly="true" id="details" multiline="true"
+ style="height: 12em;" flex="1"/>
+</dialog>
diff --git a/mailnews/extensions/smime/content/msgCompSMIMEOverlay.js b/mailnews/extensions/smime/content/msgCompSMIMEOverlay.js
new file mode 100644
index 000000000..582a073ea
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgCompSMIMEOverlay.js
@@ -0,0 +1,357 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Account encryption policy values:
+// const kEncryptionPolicy_Never = 0;
+// 'IfPossible' was used by ns4.
+// const kEncryptionPolicy_IfPossible = 1;
+var kEncryptionPolicy_Always = 2;
+
+var gEncryptedURIService =
+ Components.classes["@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"]
+ .getService(Components.interfaces.nsIEncryptedSMIMEURIsService);
+
+var gNextSecurityButtonCommand = "";
+var gSMFields = null;
+var gEncryptOptionChanged;
+var gSignOptionChanged;
+
+function onComposerLoad()
+{
+ // Are we already set up ? Or are the required fields missing ?
+ if (gSMFields || !gMsgCompose || !gMsgCompose.compFields)
+ return;
+
+ gMsgCompose.compFields.securityInfo = null;
+
+ gSMFields = Components.classes["@mozilla.org/messenger-smime/composefields;1"]
+ .createInstance(Components.interfaces.nsIMsgSMIMECompFields);
+ if (!gSMFields)
+ return;
+
+ gMsgCompose.compFields.securityInfo = gSMFields;
+
+ // Set up the intial security state.
+ gSMFields.requireEncryptMessage =
+ gCurrentIdentity.getIntAttribute("encryptionpolicy") == kEncryptionPolicy_Always;
+ if (!gSMFields.requireEncryptMessage &&
+ gEncryptedURIService &&
+ gEncryptedURIService.isEncrypted(gMsgCompose.originalMsgURI))
+ {
+ // Override encryption setting if original is known as encrypted.
+ gSMFields.requireEncryptMessage = true;
+ }
+ if (gSMFields.requireEncryptMessage)
+ setEncryptionUI();
+ else
+ setNoEncryptionUI();
+
+ gSMFields.signMessage = gCurrentIdentity.getBoolAttribute("sign_mail");
+ if (gSMFields.signMessage)
+ setSignatureUI();
+ else
+ setNoSignatureUI();
+}
+
+addEventListener("load", smimeComposeOnLoad, {capture: false, once: true});
+
+// this function gets called multiple times
+function smimeComposeOnLoad()
+{
+ onComposerLoad();
+
+ top.controllers.appendController(SecurityController);
+
+ addEventListener("compose-from-changed", onComposerFromChanged, true);
+ addEventListener("compose-send-message", onComposerSendMessage, true);
+
+ addEventListener("unload", smimeComposeOnUnload, {capture: false, once: true});
+}
+
+function smimeComposeOnUnload()
+{
+ removeEventListener("compose-from-changed", onComposerFromChanged, true);
+ removeEventListener("compose-send-message", onComposerSendMessage, true);
+
+ top.controllers.removeController(SecurityController);
+}
+
+function showNeedSetupInfo()
+{
+ let compSmimeBundle = document.getElementById("bundle_comp_smime");
+ let brandBundle = document.getElementById("bundle_brand");
+ if (!compSmimeBundle || !brandBundle)
+ return;
+
+ let buttonPressed = Services.prompt.confirmEx(window,
+ brandBundle.getString("brandShortName"),
+ compSmimeBundle.getString("NeedSetup"),
+ Services.prompt.STD_YES_NO_BUTTONS, 0, 0, 0, null, {});
+ if (buttonPressed == 0)
+ openHelp("sign-encrypt", "chrome://communicator/locale/help/suitehelp.rdf");
+}
+
+function toggleEncryptMessage()
+{
+ if (!gSMFields)
+ return;
+
+ gSMFields.requireEncryptMessage = !gSMFields.requireEncryptMessage;
+
+ if (gSMFields.requireEncryptMessage)
+ {
+ // Make sure we have a cert.
+ if (!gCurrentIdentity.getUnicharAttribute("encryption_cert_name"))
+ {
+ gSMFields.requireEncryptMessage = false;
+ showNeedSetupInfo();
+ return;
+ }
+
+ setEncryptionUI();
+ }
+ else
+ {
+ setNoEncryptionUI();
+ }
+
+ gEncryptOptionChanged = true;
+}
+
+function toggleSignMessage()
+{
+ if (!gSMFields)
+ return;
+
+ gSMFields.signMessage = !gSMFields.signMessage;
+
+ if (gSMFields.signMessage) // make sure we have a cert name...
+ {
+ if (!gCurrentIdentity.getUnicharAttribute("signing_cert_name"))
+ {
+ gSMFields.signMessage = false;
+ showNeedSetupInfo();
+ return;
+ }
+
+ setSignatureUI();
+ }
+ else
+ {
+ setNoSignatureUI();
+ }
+
+ gSignOptionChanged = true;
+}
+
+function setSecuritySettings(menu_id)
+{
+ if (!gSMFields)
+ return;
+
+ document.getElementById("menu_securityEncryptRequire" + menu_id)
+ .setAttribute("checked", gSMFields.requireEncryptMessage);
+ document.getElementById("menu_securitySign" + menu_id)
+ .setAttribute("checked", gSMFields.signMessage);
+}
+
+function setNextCommand(what)
+{
+ gNextSecurityButtonCommand = what;
+}
+
+function doSecurityButton()
+{
+ var what = gNextSecurityButtonCommand;
+ gNextSecurityButtonCommand = "";
+
+ switch (what)
+ {
+ case "encryptMessage":
+ toggleEncryptMessage();
+ break;
+
+ case "signMessage":
+ toggleSignMessage();
+ break;
+
+ case "show":
+ default:
+ showMessageComposeSecurityStatus();
+ }
+}
+
+function setNoSignatureUI()
+{
+ top.document.getElementById("securityStatus").removeAttribute("signing");
+ top.document.getElementById("signing-status").collapsed = true;
+}
+
+function setSignatureUI()
+{
+ top.document.getElementById("securityStatus").setAttribute("signing", "ok");
+ top.document.getElementById("signing-status").collapsed = false;
+}
+
+function setNoEncryptionUI()
+{
+ top.document.getElementById("securityStatus").removeAttribute("crypto");
+ top.document.getElementById("encryption-status").collapsed = true;
+}
+
+function setEncryptionUI()
+{
+ top.document.getElementById("securityStatus").setAttribute("crypto", "ok");
+ top.document.getElementById("encryption-status").collapsed = false;
+}
+
+function showMessageComposeSecurityStatus()
+{
+ Recipients2CompFields(gMsgCompose.compFields);
+
+ window.openDialog(
+ "chrome://messenger-smime/content/msgCompSecurityInfo.xul",
+ "",
+ "chrome,modal,resizable,centerscreen",
+ {
+ compFields : gMsgCompose.compFields,
+ subject : GetMsgSubjectElement().value,
+ smFields : gSMFields,
+ isSigningCertAvailable :
+ gCurrentIdentity.getUnicharAttribute("signing_cert_name") != "",
+ isEncryptionCertAvailable :
+ gCurrentIdentity.getUnicharAttribute("encryption_cert_name") != "",
+ currentIdentity : gCurrentIdentity
+ }
+ );
+}
+
+var SecurityController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_viewSecurityStatus":
+ return true;
+
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_viewSecurityStatus":
+ return true;
+
+ default:
+ return false;
+ }
+ }
+};
+
+function onComposerSendMessage()
+{
+ let missingCount = new Object();
+ let emailAddresses = new Object();
+
+ try
+ {
+ if (!gMsgCompose.compFields.securityInfo.requireEncryptMessage)
+ return;
+
+ Components.classes["@mozilla.org/messenger-smime/smimejshelper;1"]
+ .createInstance(Components.interfaces.nsISMimeJSHelper)
+ .getNoCertAddresses(gMsgCompose.compFields,
+ missingCount,
+ emailAddresses);
+ }
+ catch (e)
+ {
+ return;
+ }
+
+ if (missingCount.value > 0)
+ {
+ // The rules here: If the current identity has a directoryServer set, then
+ // use that, otherwise, try the global preference instead.
+
+ let autocompleteDirectory;
+
+ // Does the current identity override the global preference?
+ if (gCurrentIdentity.overrideGlobalPref)
+ {
+ autocompleteDirectory = gCurrentIdentity.directoryServer;
+ }
+ else
+ {
+ // Try the global one
+ if (Services.prefs.getBoolPref("ldap_2.autoComplete.useDirectory"))
+ autocompleteDirectory =
+ Services.prefs.getCharPref("ldap_2.autoComplete.directoryServer");
+ }
+
+ if (autocompleteDirectory)
+ window.openDialog("chrome://messenger-smime/content/certFetchingStatus.xul",
+ "",
+ "chrome,modal,resizable,centerscreen",
+ autocompleteDirectory,
+ emailAddresses.value);
+ }
+}
+
+function onComposerFromChanged()
+{
+ if (!gSMFields)
+ return;
+
+ var encryptionPolicy = gCurrentIdentity.getIntAttribute("encryptionpolicy");
+ var useEncryption = false;
+
+ if (!gEncryptOptionChanged)
+ {
+ // Encryption wasn't manually checked.
+ // Set up the encryption policy from the setting of the new identity.
+
+ // 0 == never, 1 == if possible (ns4), 2 == always encrypt.
+ useEncryption = (encryptionPolicy == kEncryptionPolicy_Always);
+ }
+ else
+ {
+ useEncryption = !!gCurrentIdentity.getUnicharAttribute("encryption_cert_name");
+ }
+
+ gSMFields.requireEncryptMessage = useEncryption;
+ if (useEncryption)
+ setEncryptionUI();
+ else
+ setNoEncryptionUI();
+
+ // - If signing is disabled, we will not turn it on automatically.
+ // - If signing is enabled, but the new account defaults to not sign, we will turn signing off.
+ var signMessage = gCurrentIdentity.getBoolAttribute("sign_mail");
+ var useSigning = false;
+
+ if (!gSignOptionChanged)
+ {
+ // Signing wasn't manually checked.
+ // Set up the signing policy from the setting of the new identity.
+ useSigning = signMessage;
+ }
+ else
+ {
+ useSigning = !!gCurrentIdentity.getUnicharAttribute("signing_cert_name");
+ }
+ gSMFields.signMessage = useSigning;
+ if (useSigning)
+ setSignatureUI();
+ else
+ setNoSignatureUI();
+}
diff --git a/mailnews/extensions/smime/content/msgCompSMIMEOverlay.xul b/mailnews/extensions/smime/content/msgCompSMIMEOverlay.xul
new file mode 100644
index 000000000..ec6495e20
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgCompSMIMEOverlay.xul
@@ -0,0 +1,85 @@
+<?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/smime/msgCompSMIMEOverlay.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger-smime/locale/msgCompSMIMEOverlay.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://messenger-smime/content/msgCompSMIMEOverlay.js"/>
+
+ <window id="msgcomposeWindow">
+ <broadcaster id="securityStatus" crypto="" signing=""/>
+ <observes element="securityStatus" attribute="crypto" />
+ <observes element="securityStatus" attribute="signing" />
+ <stringbundle id="bundle_comp_smime" src="chrome://messenger-smime/locale/msgCompSMIMEOverlay.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ </window>
+
+ <menupopup id="optionsMenuPopup"
+ onpopupshowing="setSecuritySettings(1);">
+ <menuseparator id="smimeOptionsSeparator"/>
+
+ <menuitem id="menu_securityEncryptRequire1"
+ type="checkbox"
+ label="&menu_securityEncryptRequire.label;"
+ accesskey="&menu_securityEncryptRequire.accesskey;"
+ oncommand="toggleEncryptMessage();"/>
+ <menuitem id="menu_securitySign1"
+ type="checkbox"
+ label="&menu_securitySign.label;"
+ accesskey="&menu_securitySign.accesskey;"
+ oncommand="toggleSignMessage();"/>
+ </menupopup>
+
+ <toolbarpalette id="MsgComposeToolbarPalette">
+ <toolbarbutton id="button-security"
+ type="menu-button"
+ class="toolbarbutton-1"
+ label="&securityButton.label;"
+ tooltiptext="&securityButton.tooltip;"
+ oncommand="doSecurityButton();">
+ <menupopup onpopupshowing="setSecuritySettings(2);">
+ <menuitem id="menu_securityEncryptRequire2"
+ type="checkbox"
+ label="&menu_securityEncryptRequire.label;"
+ accesskey="&menu_securityEncryptRequire.accesskey;"
+ oncommand="setNextCommand('encryptMessage');"/>
+ <menuitem id="menu_securitySign2"
+ type="checkbox"
+ label="&menu_securitySign.label;"
+ accesskey="&menu_securitySign.accesskey;"
+ oncommand="setNextCommand('signMessage');"/>
+ <menuseparator id="smimeToolbarButtonSeparator"/>
+ <menuitem id="menu_securityStatus2"
+ label="&menu_securityStatus.label;"
+ accesskey="&menu_securityStatus.accesskey;"
+ oncommand="setNextCommand('show');"/>
+ </menupopup>
+ </toolbarbutton>
+ </toolbarpalette>
+
+ <statusbar id="status-bar">
+ <statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic" collapsed="true"
+ id="signing-status" oncommand="showMessageComposeSecurityStatus();"/>
+ <statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic" collapsed="true"
+ id="encryption-status" oncommand="showMessageComposeSecurityStatus();"/>
+ </statusbar>
+
+ <commandset id="composeCommands">
+ <command id="cmd_viewSecurityStatus" oncommand="showMessageComposeSecurityStatus();"/>
+ </commandset>
+
+ <menupopup id="menu_View_Popup">
+ <menuseparator id="viewMenuBeforeSecurityStatusSeparator"/>
+ <menuitem id="menu_viewSecurityStatus"
+ label="&menu_viewSecurityStatus.label;"
+ accesskey="&menu_viewSecurityStatus.accesskey;"
+ command="cmd_viewSecurityStatus"/>
+ </menupopup>
+
+</overlay>
diff --git a/mailnews/extensions/smime/content/msgCompSecurityInfo.js b/mailnews/extensions/smime/content/msgCompSecurityInfo.js
new file mode 100644
index 000000000..5a2a7432f
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgCompSecurityInfo.js
@@ -0,0 +1,244 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gListBox;
+var gViewButton;
+var gBundle;
+
+var gEmailAddresses;
+var gCertStatusSummaries;
+var gCertIssuedInfos;
+var gCertExpiresInfos;
+var gCerts;
+var gCount;
+
+var gSMimeContractID = "@mozilla.org/messenger-smime/smimejshelper;1";
+var gISMimeJSHelper = Components.interfaces.nsISMimeJSHelper;
+var gIX509Cert = Components.interfaces.nsIX509Cert;
+var nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
+var nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1"
+
+function getStatusExplanation(value)
+{
+ switch (value)
+ {
+ case gIX509Cert.VERIFIED_OK:
+ return gBundle.getString("StatusValid");
+
+ case gIX509Cert.NOT_VERIFIED_UNKNOWN:
+ case gIX509Cert.INVALID_CA:
+ case gIX509Cert.USAGE_NOT_ALLOWED:
+ return gBundle.getString("StatusInvalid");
+
+ case gIX509Cert.CERT_REVOKED:
+ return gBundle.getString("StatusRevoked");
+
+ case gIX509Cert.CERT_EXPIRED:
+ return gBundle.getString("StatusExpired");
+
+ case gIX509Cert.CERT_NOT_TRUSTED:
+ case gIX509Cert.ISSUER_NOT_TRUSTED:
+ case gIX509Cert.ISSUER_UNKNOWN:
+ return gBundle.getString("StatusUntrusted");
+ }
+
+ return "";
+}
+
+function onLoad()
+{
+ var params = window.arguments[0];
+ if (!params)
+ return;
+
+ var helper = Components.classes[gSMimeContractID].createInstance(gISMimeJSHelper);
+
+ if (!helper)
+ return;
+
+ gListBox = document.getElementById("infolist");
+ gViewButton = document.getElementById("viewCertButton");
+ gBundle = document.getElementById("bundle_smime_comp_info");
+
+ gEmailAddresses = new Object();
+ gCertStatusSummaries = new Object();
+ gCertIssuedInfos = new Object();
+ gCertExpiresInfos = new Object();
+ gCerts = new Object();
+ gCount = new Object();
+ var canEncrypt = new Object();
+
+ var allow_ldap_cert_fetching = false;
+
+ try {
+ if (params.compFields.securityInfo.requireEncryptMessage) {
+ allow_ldap_cert_fetching = true;
+ }
+ }
+ catch (e)
+ {
+ }
+
+ while (true)
+ {
+ try
+ {
+ helper.getRecipientCertsInfo(
+ params.compFields,
+ gCount,
+ gEmailAddresses,
+ gCertStatusSummaries,
+ gCertIssuedInfos,
+ gCertExpiresInfos,
+ gCerts,
+ canEncrypt);
+ }
+ catch (e)
+ {
+ dump(e);
+ return;
+ }
+
+ if (!allow_ldap_cert_fetching)
+ break;
+
+ allow_ldap_cert_fetching = false;
+
+ var missing = new Array();
+
+ for (var j = gCount.value - 1; j >= 0; --j)
+ {
+ if (!gCerts.value[j])
+ {
+ missing[missing.length] = gEmailAddresses.value[j];
+ }
+ }
+
+ if (missing.length > 0)
+ {
+ var autocompleteLdap = Services.prefs
+ .getBoolPref("ldap_2.autoComplete.useDirectory");
+
+ if (autocompleteLdap)
+ {
+ var autocompleteDirectory = null;
+ if (params.currentIdentity.overrideGlobalPref) {
+ autocompleteDirectory = params.currentIdentity.directoryServer;
+ } else {
+ autocompleteDirectory = Services.prefs
+ .getCharPref("ldap_2.autoComplete.directoryServer");
+ }
+
+ if (autocompleteDirectory)
+ {
+ window.openDialog('chrome://messenger-smime/content/certFetchingStatus.xul',
+ '',
+ 'chrome,resizable=1,modal=1,dialog=1',
+ autocompleteDirectory,
+ missing
+ );
+ }
+ }
+ }
+ }
+
+ if (gBundle)
+ {
+ var yes_string = gBundle.getString("StatusYes");
+ var no_string = gBundle.getString("StatusNo");
+ var not_possible_string = gBundle.getString("StatusNotPossible");
+
+ var signed_element = document.getElementById("signed");
+ var encrypted_element = document.getElementById("encrypted");
+
+ if (params.smFields.requireEncryptMessage)
+ {
+ if (params.isEncryptionCertAvailable && canEncrypt.value)
+ {
+ encrypted_element.value = yes_string;
+ }
+ else
+ {
+ encrypted_element.value = not_possible_string;
+ }
+ }
+ else
+ {
+ encrypted_element.value = no_string;
+ }
+
+ if (params.smFields.signMessage)
+ {
+ if (params.isSigningCertAvailable)
+ {
+ signed_element.value = yes_string;
+ }
+ else
+ {
+ signed_element.value = not_possible_string;
+ }
+ }
+ else
+ {
+ signed_element.value = no_string;
+ }
+ }
+
+ var imax = gCount.value;
+
+ for (var i = 0; i < imax; ++i)
+ {
+ var listitem = document.createElement("listitem");
+
+ listitem.appendChild(createCell(gEmailAddresses.value[i]));
+
+ if (!gCerts.value[i])
+ {
+ listitem.appendChild(createCell(gBundle.getString("StatusNotFound")));
+ }
+ else
+ {
+ listitem.appendChild(createCell(getStatusExplanation(gCertStatusSummaries.value[i])));
+ listitem.appendChild(createCell(gCertIssuedInfos.value[i]));
+ listitem.appendChild(createCell(gCertExpiresInfos.value[i]));
+ }
+
+ gListBox.appendChild(listitem);
+ }
+}
+
+function onSelectionChange(event)
+{
+ gViewButton.disabled = !(gListBox.selectedItems.length == 1 &&
+ certForRow(gListBox.selectedIndex));
+}
+
+function viewCertHelper(parent, cert) {
+ var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs);
+ cd.viewCert(parent, cert);
+}
+
+function certForRow(aRowIndex) {
+ return gCerts.value[aRowIndex];
+}
+
+function viewSelectedCert()
+{
+ if (!gViewButton.disabled)
+ viewCertHelper(window, certForRow(gListBox.selectedIndex));
+}
+
+function doHelpButton()
+{
+ openHelp('compose_security', 'chrome://communicator/locale/help/suitehelp.rdf');
+}
+
+function createCell(label)
+{
+ var cell = document.createElement("listcell");
+ cell.setAttribute("label", label)
+ return cell;
+}
diff --git a/mailnews/extensions/smime/content/msgCompSecurityInfo.xul b/mailnews/extensions/smime/content/msgCompSecurityInfo.xul
new file mode 100644
index 000000000..c8769d621
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgCompSecurityInfo.xul
@@ -0,0 +1,68 @@
+<?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://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/smime/msgCompSecurityInfo.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger-smime/locale/msgCompSecurityInfo.dtd">
+
+<dialog id="msgCompSecurityInfo" title="&title.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 50em;"
+ persist="width height"
+ buttons="accept"
+ onload="onLoad();">
+
+ <script type="application/javascript" src="chrome://messenger-smime/content/msgCompSecurityInfo.js"/>
+
+ <stringbundle id="bundle_smime_comp_info" src="chrome://messenger-smime/locale/msgCompSecurityInfo.properties"/>
+
+ <description>&subject.plaintextWarning;</description>
+ <separator class="thin"/>
+ <description>&status.heading;</description>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <label value="&status.signed;"/>
+ <label id="signed"/>
+ </row>
+ <row>
+ <label value="&status.encrypted;"/>
+ <label id="encrypted"/>
+ </row>
+ </rows>
+ </grid>
+
+ <separator class="thin"/>
+ <label value="&status.certificates;" control="infolist"/>
+
+ <listbox id="infolist" flex="1"
+ onselect="onSelectionChange(event);">
+ <listcols>
+ <listcol flex="3" width="0"/>
+ <splitter class="tree-splitter"/>
+ <listcol flex="1" width="0"/>
+ <splitter class="tree-splitter"/>
+ <listcol flex="2" width="0"/>
+ <splitter class="tree-splitter"/>
+ <listcol flex="2" width="0"/>
+ </listcols>
+ <listhead>
+ <listheader label="&tree.recipient;"/>
+ <listheader label="&tree.status;"/>
+ <listheader label="&tree.issuedDate;"/>
+ <listheader label="&tree.expiresDate;"/>
+ </listhead>
+ </listbox>
+ <hbox pack="start">
+ <button id="viewCertButton" disabled="true"
+ label="&view.label;" accesskey="&view.accesskey;"
+ oncommand="viewSelectedCert();"/>
+ </hbox>
+</dialog>
diff --git a/mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.js b/mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.js
new file mode 100644
index 000000000..2d9469d6c
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.js
@@ -0,0 +1,264 @@
+/* -*- Mode: Java; 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 gSignedUINode = null;
+var gEncryptedUINode = null;
+var gSMIMEContainer = null;
+var gStatusBar = null;
+var gSignedStatusPanel = null;
+var gEncryptedStatusPanel = null;
+
+var gEncryptedURIService = null;
+var gMyLastEncryptedURI = null;
+
+var gSMIMEBundle = null;
+// var gBrandBundle; -- defined in mailWindow.js
+
+// manipulates some globals from msgReadSMIMEOverlay.js
+
+var nsICMSMessageErrors = Components.interfaces.nsICMSMessageErrors;
+
+/// Get the necko URL for the message URI.
+function neckoURLForMessageURI(aMessageURI)
+{
+ let msgSvc = Components.classes["@mozilla.org/messenger;1"]
+ .createInstance(Components.interfaces.nsIMessenger)
+ .messageServiceFromURI(aMessageURI);
+ let neckoURI = {};
+ msgSvc.GetUrlForUri(aMessageURI, neckoURI, null);
+ return neckoURI.value.spec;
+}
+
+var smimeHeaderSink =
+{
+ maxWantedNesting: function()
+ {
+ return 1;
+ },
+
+ signedStatus: function(aNestingLevel, aSignatureStatus, aSignerCert)
+ {
+ if (aNestingLevel > 1) {
+ // we are not interested
+ return;
+ }
+
+ gSignatureStatus = aSignatureStatus;
+ gSignerCert = aSignerCert;
+
+ gSMIMEContainer.collapsed = false;
+ gSignedUINode.collapsed = false;
+ gSignedStatusPanel.collapsed = false;
+
+ switch (aSignatureStatus) {
+ case nsICMSMessageErrors.SUCCESS:
+ gSignedUINode.setAttribute("signed", "ok");
+ gStatusBar.setAttribute("signed", "ok");
+ break;
+
+ case nsICMSMessageErrors.VERIFY_NOT_YET_ATTEMPTED:
+ gSignedUINode.setAttribute("signed", "unknown");
+ gStatusBar.setAttribute("signed", "unknown");
+ break;
+
+ case nsICMSMessageErrors.VERIFY_CERT_WITHOUT_ADDRESS:
+ case nsICMSMessageErrors.VERIFY_HEADER_MISMATCH:
+ gSignedUINode.setAttribute("signed", "mismatch");
+ gStatusBar.setAttribute("signed", "mismatch");
+ break;
+
+ default:
+ gSignedUINode.setAttribute("signed", "notok");
+ gStatusBar.setAttribute("signed", "notok");
+ break;
+ }
+ },
+
+ encryptionStatus: function(aNestingLevel, aEncryptionStatus, aRecipientCert)
+ {
+ if (aNestingLevel > 1) {
+ // we are not interested
+ return;
+ }
+
+ gEncryptionStatus = aEncryptionStatus;
+ gEncryptionCert = aRecipientCert;
+
+ gSMIMEContainer.collapsed = false;
+ gEncryptedUINode.collapsed = false;
+ gEncryptedStatusPanel.collapsed = false;
+
+ if (nsICMSMessageErrors.SUCCESS == aEncryptionStatus)
+ {
+ gEncryptedUINode.setAttribute("encrypted", "ok");
+ gStatusBar.setAttribute("encrypted", "ok");
+ }
+ else
+ {
+ gEncryptedUINode.setAttribute("encrypted", "notok");
+ gStatusBar.setAttribute("encrypted", "notok");
+ }
+
+ if (gEncryptedURIService)
+ {
+ // Remember the message URI and the corresponding necko URI.
+ gMyLastEncryptedURI = GetLoadedMessage();
+ gEncryptedURIService.rememberEncrypted(gMyLastEncryptedURI);
+ gEncryptedURIService.rememberEncrypted(
+ neckoURLForMessageURI(gMyLastEncryptedURI));
+ }
+
+ switch (aEncryptionStatus)
+ {
+ case nsICMSMessageErrors.SUCCESS:
+ case nsICMSMessageErrors.ENCRYPT_INCOMPLETE:
+ break;
+ default:
+ var brand = gBrandBundle.getString("brandShortName");
+ var title = gSMIMEBundle.getString("CantDecryptTitle").replace(/%brand%/g, brand);
+ var body = gSMIMEBundle.getString("CantDecryptBody").replace(/%brand%/g, brand);
+
+ // insert our message
+ msgWindow.displayHTMLInMessagePane(title,
+ "<html>\n" +
+ "<body bgcolor=\"#fafaee\">\n" +
+ "<center><br><br><br>\n" +
+ "<table>\n" +
+ "<tr><td>\n" +
+ "<center><strong><font size=\"+3\">\n" +
+ title+"</font></center><br>\n" +
+ body+"\n" +
+ "</td></tr></table></center></body></html>", false);
+ }
+ },
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Components.interfaces.nsIMsgSMIMEHeaderSink) || iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_NOINTERFACE;
+ }
+};
+
+function forgetEncryptedURI()
+{
+ if (gMyLastEncryptedURI && gEncryptedURIService)
+ {
+ gEncryptedURIService.forgetEncrypted(gMyLastEncryptedURI);
+ gEncryptedURIService.forgetEncrypted(
+ neckoURLForMessageURI(gMyLastEncryptedURI));
+ gMyLastEncryptedURI = null;
+ }
+}
+
+function onSMIMEStartHeaders()
+{
+ gEncryptionStatus = -1;
+ gSignatureStatus = -1;
+
+ gSignerCert = null;
+ gEncryptionCert = null;
+
+ gSMIMEContainer.collapsed = true;
+
+ gSignedUINode.collapsed = true;
+ gSignedUINode.removeAttribute("signed");
+ gSignedStatusPanel.collapsed = true;
+ gStatusBar.removeAttribute("signed");
+
+ gEncryptedUINode.collapsed = true;
+ gEncryptedUINode.removeAttribute("encrypted");
+ gEncryptedStatusPanel.collapsed = true;
+ gStatusBar.removeAttribute("encrypted");
+
+ forgetEncryptedURI();
+}
+
+function onSMIMEEndHeaders()
+{}
+
+function onSmartCardChange()
+{
+ // only reload encrypted windows
+ if (gMyLastEncryptedURI && gEncryptionStatus != -1)
+ ReloadMessage();
+}
+
+function msgHdrViewSMIMEOnLoad(event)
+{
+ window.crypto.enableSmartCardEvents = true;
+ document.addEventListener("smartcard-insert", onSmartCardChange, false);
+ document.addEventListener("smartcard-remove", onSmartCardChange, false);
+ if (!gSMIMEBundle)
+ gSMIMEBundle = document.getElementById("bundle_read_smime");
+
+ // we want to register our security header sink as an opaque nsISupports
+ // on the msgHdrSink used by mail.....
+ msgWindow.msgHeaderSink.securityInfo = smimeHeaderSink;
+
+ gSignedUINode = document.getElementById('signedHdrIcon');
+ gEncryptedUINode = document.getElementById('encryptedHdrIcon');
+ gSMIMEContainer = document.getElementById('smimeBox');
+ gStatusBar = document.getElementById('status-bar');
+ gSignedStatusPanel = document.getElementById('signed-status');
+ gEncryptedStatusPanel = document.getElementById('encrypted-status');
+
+ // add ourself to the list of message display listeners so we get notified when we are about to display a
+ // message.
+ var listener = {};
+ listener.onStartHeaders = onSMIMEStartHeaders;
+ listener.onEndHeaders = onSMIMEEndHeaders;
+ gMessageListeners.push(listener);
+
+ gEncryptedURIService =
+ Components.classes["@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"]
+ .getService(Components.interfaces.nsIEncryptedSMIMEURIsService);
+}
+
+function msgHdrViewSMIMEOnUnload(event)
+{
+ window.crypto.enableSmartCardEvents = false;
+ document.removeEventListener("smartcard-insert", onSmartCardChange, false);
+ document.removeEventListener("smartcard-remove", onSmartCardChange, false);
+ forgetEncryptedURI();
+ removeEventListener("messagepane-loaded", msgHdrViewSMIMEOnLoad, true);
+ removeEventListener("messagepane-unloaded", msgHdrViewSMIMEOnUnload, true);
+ removeEventListener("messagepane-hide", msgHdrViewSMIMEOnMessagePaneHide, true);
+ removeEventListener("messagepane-unhide", msgHdrViewSMIMEOnMessagePaneUnhide, true);
+}
+
+function msgHdrViewSMIMEOnMessagePaneHide()
+{
+ gSMIMEContainer.collapsed = true;
+ gSignedUINode.collapsed = true;
+ gSignedStatusPanel.collapsed = true;
+ gEncryptedUINode.collapsed = true;
+ gEncryptedStatusPanel.collapsed = true;
+}
+
+function msgHdrViewSMIMEOnMessagePaneUnhide()
+{
+ if (gEncryptionStatus != -1 || gSignatureStatus != -1)
+ {
+ gSMIMEContainer.collapsed = false;
+
+ if (gSignatureStatus != -1)
+ {
+ gSignedUINode.collapsed = false;
+ gSignedStatusPanel.collapsed = false;
+ }
+
+ if (gEncryptionStatus != -1)
+ {
+ gEncryptedUINode.collapsed = false;
+ gEncryptedStatusPanel.collapsed = false;
+ }
+ }
+}
+
+addEventListener('messagepane-loaded', msgHdrViewSMIMEOnLoad, true);
+addEventListener('messagepane-unloaded', msgHdrViewSMIMEOnUnload, true);
+addEventListener('messagepane-hide', msgHdrViewSMIMEOnMessagePaneHide, true);
+addEventListener('messagepane-unhide', msgHdrViewSMIMEOnMessagePaneUnhide, true);
diff --git a/mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.xul b/mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.xul
new file mode 100644
index 000000000..957d2a15b
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgHdrViewSMIMEOverlay.xul
@@ -0,0 +1,29 @@
+<?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/smime/msgHdrViewSMIMEOverlay.css" type="text/css"?>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://messenger-smime/content/msgHdrViewSMIMEOverlay.js"/>
+<!-- These stringbundles are already defined in msgReadSMIMEOverlay.xul!
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_read_smime" src="chrome://messenger-smime/locale/msgReadSMIMEOverlay.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ </stringbundleset>
+-->
+
+ <hbox id="expandedHeaderView">
+ <vbox id="smimeBox" insertafter="expandedHeaders" collapsed="true">
+ <spacer flex="1"/>
+ <image id="signedHdrIcon"
+ onclick="showMessageReadSecurityInfo();" collapsed="true"/>
+ <image id="encryptedHdrIcon"
+ onclick="showMessageReadSecurityInfo();" collapsed="true"/>
+ <spacer flex="1"/>
+ </vbox>
+ </hbox>
+</overlay>
+
diff --git a/mailnews/extensions/smime/content/msgReadSMIMEOverlay.js b/mailnews/extensions/smime/content/msgReadSMIMEOverlay.js
new file mode 100644
index 000000000..ab362d418
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgReadSMIMEOverlay.js
@@ -0,0 +1,102 @@
+/* -*- Mode: Java; 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/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gEncryptionStatus = -1;
+var gSignatureStatus = -1;
+var gSignerCert = null;
+var gEncryptionCert = null;
+
+addEventListener("load", smimeReadOnLoad, {capture: false, once: true});
+
+function smimeReadOnLoad()
+{
+ top.controllers.appendController(SecurityController);
+
+ addEventListener("unload", smimeReadOnUnload, {capture: false, once: true});
+}
+
+function smimeReadOnUnload()
+{
+ top.controllers.removeController(SecurityController);
+}
+
+function showImapSignatureUnknown()
+{
+ let readSmimeBundle = document.getElementById("bundle_read_smime");
+ let brandBundle = document.getElementById("bundle_brand");
+ if (!readSmimeBundle || !brandBundle)
+ return;
+
+ if (Services.prompt.confirm(window, brandBundle.getString("brandShortName"),
+ readSmimeBundle.getString("ImapOnDemand")))
+ {
+ gDBView.reloadMessageWithAllParts();
+ }
+}
+
+function showMessageReadSecurityInfo()
+{
+ let gSignedUINode = document.getElementById("signedHdrIcon");
+ if (gSignedUINode && gSignedUINode.getAttribute("signed") == "unknown")
+ {
+ showImapSignatureUnknown();
+ return;
+ }
+
+ let params = Components.classes["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Components.interfaces.nsIDialogParamBlock);
+ params.objects = Components.classes["@mozilla.org/array;1"]
+ .createInstance(Components.interfaces.nsIMutableArray);
+ // Append even if null... the receiver must handle that.
+ params.objects.appendElement(gSignerCert, false);
+ params.objects.appendElement(gEncryptionCert, false);
+
+ // int array starts with index 0, but that is used for window exit status
+ params.SetInt(1, gSignatureStatus);
+ params.SetInt(2, gEncryptionStatus);
+
+ window.openDialog("chrome://messenger-smime/content/msgReadSecurityInfo.xul",
+ "", "chrome,resizable,modal,dialog,centerscreen", params);
+}
+
+var SecurityController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_viewSecurityStatus":
+ return true;
+
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_viewSecurityStatus":
+ if (document.documentElement.getAttribute('windowtype') == "mail:messageWindow")
+ return GetNumSelectedMessages() > 0;
+
+ if (GetNumSelectedMessages() > 0 && gDBView)
+ {
+ let enabled = {value: false};
+ let checkStatus = {};
+ gDBView.getCommandStatus(nsMsgViewCommandType.cmdRequiringMsgBody,
+ enabled, checkStatus);
+ return enabled.value;
+ }
+ // else: fall through.
+
+ default:
+ return false;
+ }
+ }
+};
diff --git a/mailnews/extensions/smime/content/msgReadSMIMEOverlay.xul b/mailnews/extensions/smime/content/msgReadSMIMEOverlay.xul
new file mode 100644
index 000000000..a55828c0f
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgReadSMIMEOverlay.xul
@@ -0,0 +1,34 @@
+<?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/smime/msgReadSMIMEOverlay.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger-smime/locale/msgReadSMIMEOverlay.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript" src="chrome://messenger-smime/content/msgReadSMIMEOverlay.js"/>
+
+ <commandset id="mailViewMenuItems">
+ <command id="cmd_viewSecurityStatus" oncommand="showMessageReadSecurityInfo();" disabled="true"/>
+ </commandset>
+
+ <menupopup id="menu_View_Popup">
+ <menuitem insertafter="pageSourceMenuItem" label="&menu_securityStatus.label;"
+ accesskey="&menu_securityStatus.accesskey;" command="cmd_viewSecurityStatus"/>
+ </menupopup>
+
+ <statusbar id="status-bar">
+ <statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic"
+ id="signed-status" collapsed="true" oncommand="showMessageReadSecurityInfo();"/>
+ <statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic"
+ id="encrypted-status" collapsed="true" oncommand="showMessageReadSecurityInfo();"/>
+ <stringbundle id="bundle_read_smime" src="chrome://messenger-smime/locale/msgReadSMIMEOverlay.properties"/>
+<!-- This stringbundle is already defined on top window level!
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+-->
+ </statusbar>
+
+</overlay>
diff --git a/mailnews/extensions/smime/content/msgReadSecurityInfo.js b/mailnews/extensions/smime/content/msgReadSecurityInfo.js
new file mode 100644
index 000000000..310cfc18a
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgReadSecurityInfo.js
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+var nsIX509Cert = Components.interfaces.nsIX509Cert;
+var nsICMSMessageErrors = Components.interfaces.nsICMSMessageErrors;
+var nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
+var nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1"
+
+var gSignerCert = null;
+var gEncryptionCert = null;
+
+var gSignatureStatus = -1;
+var gEncryptionStatus = -1;
+
+function setText(id, value) {
+ var element = document.getElementById(id);
+ if (!element)
+ return;
+ if (element.hasChildNodes())
+ element.firstChild.remove();
+ var textNode = document.createTextNode(value);
+ element.appendChild(textNode);
+}
+
+function onLoad()
+{
+ var paramBlock = window.arguments[0].QueryInterface(nsIDialogParamBlock);
+ paramBlock.objects.QueryInterface(Components.interfaces.nsIMutableArray);
+ try {
+ gSignerCert = paramBlock.objects.queryElementAt(0, nsIX509Cert);
+ } catch(e) { } // maybe null
+ try {
+ gEncryptionCert = paramBlock.objects.queryElementAt(1, nsIX509Cert);
+ } catch(e) { } // maybe null
+
+ gSignatureStatus = paramBlock.GetInt(1);
+ gEncryptionStatus = paramBlock.GetInt(2);
+
+ var bundle = document.getElementById("bundle_smime_read_info");
+
+ if (bundle) {
+ var sigInfoLabel = null;
+ var sigInfoHeader = null;
+ var sigInfo = null;
+ var sigInfo_clueless = false;
+
+ switch (gSignatureStatus) {
+ case -1:
+ case nsICMSMessageErrors.VERIFY_NOT_SIGNED:
+ sigInfoLabel = "SINoneLabel";
+ sigInfo = "SINone";
+ break;
+
+ case nsICMSMessageErrors.SUCCESS:
+ sigInfoLabel = "SIValidLabel";
+ sigInfo = "SIValid";
+ break;
+
+
+ case nsICMSMessageErrors.VERIFY_BAD_SIGNATURE:
+ case nsICMSMessageErrors.VERIFY_DIGEST_MISMATCH:
+ sigInfoLabel = "SIInvalidLabel";
+ sigInfoHeader = "SIInvalidHeader";
+ sigInfo = "SIContentAltered";
+ break;
+
+ case nsICMSMessageErrors.VERIFY_UNKNOWN_ALGO:
+ case nsICMSMessageErrors.VERIFY_UNSUPPORTED_ALGO:
+ sigInfoLabel = "SIInvalidLabel";
+ sigInfoHeader = "SIInvalidHeader";
+ sigInfo = "SIInvalidCipher";
+ break;
+
+ case nsICMSMessageErrors.VERIFY_HEADER_MISMATCH:
+ sigInfoLabel = "SIPartiallyValidLabel";
+ sigInfoHeader = "SIPartiallyValidHeader";
+ sigInfo = "SIHeaderMismatch";
+ break;
+
+ case nsICMSMessageErrors.VERIFY_CERT_WITHOUT_ADDRESS:
+ sigInfoLabel = "SIPartiallyValidLabel";
+ sigInfoHeader = "SIPartiallyValidHeader";
+ sigInfo = "SICertWithoutAddress";
+ break;
+
+ case nsICMSMessageErrors.VERIFY_UNTRUSTED:
+ sigInfoLabel = "SIInvalidLabel";
+ sigInfoHeader = "SIInvalidHeader";
+ sigInfo = "SIUntrustedCA";
+ // XXX Need to extend to communicate better errors
+ // might also be:
+ // SIExpired SIRevoked SINotYetValid SIUnknownCA SIExpiredCA SIRevokedCA SINotYetValidCA
+ break;
+
+ case nsICMSMessageErrors.VERIFY_NOT_YET_ATTEMPTED:
+ case nsICMSMessageErrors.GENERAL_ERROR:
+ case nsICMSMessageErrors.VERIFY_NO_CONTENT_INFO:
+ case nsICMSMessageErrors.VERIFY_BAD_DIGEST:
+ case nsICMSMessageErrors.VERIFY_NOCERT:
+ case nsICMSMessageErrors.VERIFY_ERROR_UNVERIFIED:
+ case nsICMSMessageErrors.VERIFY_ERROR_PROCESSING:
+ case nsICMSMessageErrors.VERIFY_MALFORMED_SIGNATURE:
+ sigInfoLabel = "SIInvalidLabel";
+ sigInfoHeader = "SIInvalidHeader";
+ sigInfo_clueless = true;
+ break;
+ default:
+ Components.utils.reportError("Unexpected gSignatureStatus: " +
+ gSignatureStatus);
+ }
+
+ document.getElementById("signatureLabel").value =
+ bundle.getString(sigInfoLabel);
+
+ var label;
+ if (sigInfoHeader) {
+ label = document.getElementById("signatureHeader");
+ label.collapsed = false;
+ label.value = bundle.getString(sigInfoHeader);
+ }
+
+ var str;
+ if (sigInfo) {
+ str = bundle.getString(sigInfo);
+ }
+ else if (sigInfo_clueless) {
+ str = bundle.getString("SIClueless") + " (" + gSignatureStatus + ")";
+ }
+ setText("signatureExplanation", str);
+
+ var encInfoLabel = null;
+ var encInfoHeader = null;
+ var encInfo = null;
+ var encInfo_clueless = false;
+
+ switch (gEncryptionStatus) {
+ case -1:
+ encInfoLabel = "EINoneLabel2";
+ encInfo = "EINone";
+ break;
+
+ case nsICMSMessageErrors.SUCCESS:
+ encInfoLabel = "EIValidLabel";
+ encInfo = "EIValid";
+ break;
+
+ case nsICMSMessageErrors.ENCRYPT_INCOMPLETE:
+ encInfoLabel = "EIInvalidLabel";
+ encInfo = "EIContentAltered";
+ break;
+
+ case nsICMSMessageErrors.GENERAL_ERROR:
+ encInfoLabel = "EIInvalidLabel";
+ encInfoHeader = "EIInvalidHeader";
+ encInfo_clueless = 1;
+ break;
+ default:
+ Components.utils.reportError("Unexpected gEncryptionStatus: " +
+ gEncryptionStatus);
+ }
+
+ document.getElementById("encryptionLabel").value =
+ bundle.getString(encInfoLabel);
+
+ if (encInfoHeader) {
+ label = document.getElementById("encryptionHeader");
+ label.collapsed = false;
+ label.value = bundle.getString(encInfoHeader);
+ }
+
+ if (encInfo) {
+ str = bundle.getString(encInfo);
+ }
+ else if (encInfo_clueless) {
+ str = bundle.getString("EIClueless");
+ }
+ setText("encryptionExplanation", str);
+ }
+
+ if (gSignerCert) {
+ document.getElementById("signatureCert").collapsed = false;
+ if (gSignerCert.subjectName) {
+ document.getElementById("signedBy").value = gSignerCert.commonName;
+ }
+ if (gSignerCert.emailAddress) {
+ document.getElementById("signerEmail").value = gSignerCert.emailAddress;
+ }
+ if (gSignerCert.issuerName) {
+ document.getElementById("sigCertIssuedBy").value = gSignerCert.issuerCommonName;
+ }
+ }
+
+ if (gEncryptionCert) {
+ document.getElementById("encryptionCert").collapsed = false;
+ if (gEncryptionCert.subjectName) {
+ document.getElementById("encryptedFor").value = gEncryptionCert.commonName;
+ }
+ if (gEncryptionCert.emailAddress) {
+ document.getElementById("recipientEmail").value = gEncryptionCert.emailAddress;
+ }
+ if (gEncryptionCert.issuerName) {
+ document.getElementById("encCertIssuedBy").value = gEncryptionCert.issuerCommonName;
+ }
+ }
+}
+
+function viewCertHelper(parent, cert) {
+ var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs);
+ cd.viewCert(parent, cert);
+}
+
+function viewSignatureCert()
+{
+ if (gSignerCert) {
+ viewCertHelper(window, gSignerCert);
+ }
+}
+
+function viewEncryptionCert()
+{
+ if (gEncryptionCert) {
+ viewCertHelper(window, gEncryptionCert);
+ }
+}
+
+function doHelpButton()
+{
+ openHelp('received_security');
+}
diff --git a/mailnews/extensions/smime/content/msgReadSecurityInfo.xul b/mailnews/extensions/smime/content/msgReadSecurityInfo.xul
new file mode 100644
index 000000000..8e0a1f5f5
--- /dev/null
+++ b/mailnews/extensions/smime/content/msgReadSecurityInfo.xul
@@ -0,0 +1,68 @@
+<?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://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/smime/msgReadSecurityInfo.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger-smime/locale/msgReadSecurityInfo.dtd">
+
+<dialog id="msgReadSecurityInfo" title="&status.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 40em;"
+ buttons="accept"
+ onload="onLoad();">
+
+ <script type="application/javascript" src="chrome://messenger-smime/content/msgReadSecurityInfo.js"/>
+
+ <stringbundle id="bundle_smime_read_info" src="chrome://messenger-smime/locale/msgSecurityInfo.properties"/>
+
+ <vbox flex="1">
+ <label id="signatureLabel"/>
+ <label id="signatureHeader" collapsed="true"/>
+ <description id="signatureExplanation"/>
+ <vbox id="signatureCert" collapsed="true">
+ <hbox>
+ <label id="signedByLabel">&signer.name;</label>
+ <description id="signedBy"/>
+ </hbox>
+ <hbox>
+ <label id="signerEmailLabel">&email.address;</label>
+ <description id="signerEmail"/>
+ </hbox>
+ <hbox>
+ <label id="sigCertIssuedByLabel">&issuer.name;</label>
+ <description id="sigCertIssuedBy"/>
+ </hbox>
+ <hbox>
+ <button id="signatureCertView" label="&signatureCert.label;"
+ oncommand="viewSignatureCert()"/>
+ </hbox>
+ </vbox>
+
+ <separator/>
+
+ <label id="encryptionLabel"/>
+ <label id="encryptionHeader" collapsed="true"/>
+ <description id="encryptionExplanation"/>
+ <vbox id="encryptionCert" collapsed="true">
+ <hbox>
+ <label id="encryptedForLabel">&recipient.name;</label>
+ <description id="encryptedFor"/>
+ </hbox>
+ <hbox>
+ <label id="recipientEmailLabel">&email.address;</label>
+ <description id="recipientEmail"/>
+ </hbox>
+ <hbox>
+ <label id="encCertIssuedByLabel">&issuer.name;</label>
+ <description id="encCertIssuedBy"/>
+ </hbox>
+ <hbox>
+ <button id="encryptionCertView" label="&encryptionCert.label;"
+ oncommand="viewEncryptionCert()"/>
+ </hbox>
+ </vbox>
+ </vbox>
+</dialog>
diff --git a/mailnews/extensions/smime/content/smime.js b/mailnews/extensions/smime/content/smime.js
new file mode 100644
index 000000000..8259ead1a
--- /dev/null
+++ b/mailnews/extensions/smime/content/smime.js
@@ -0,0 +1,14 @@
+/* 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/. */
+
+/*
+ Add any default pref values we want for smime
+*/
+
+pref("mail.identity.default.encryption_cert_name","");
+pref("mail.identity.default.encryptionpolicy", 0);
+pref("mail.identity.default.signing_cert_name", "");
+pref("mail.identity.default.sign_mail", false);
+
+
diff --git a/mailnews/extensions/smime/jar.mn b/mailnews/extensions/smime/jar.mn
new file mode 100644
index 000000000..548fef63c
--- /dev/null
+++ b/mailnews/extensions/smime/jar.mn
@@ -0,0 +1,30 @@
+# 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/.
+
+#ifdef MOZ_SUITE
+messenger.jar:
+% content messenger-smime %content/messenger-smime/
+% overlay chrome://messenger/content/messengercompose/messengercompose.xul chrome://messenger-smime/content/msgCompSMIMEOverlay.xul
+% overlay chrome://messenger/content/msgHdrViewOverlay.xul chrome://messenger-smime/content/msgHdrViewSMIMEOverlay.xul
+% overlay chrome://messenger/content/mailWindowOverlay.xul chrome://messenger-smime/content/msgReadSMIMEOverlay.xul
+% overlay chrome://messenger/content/am-identity-edit.xul chrome://messenger/content/am-smimeIdentityEditOverlay.xul
+ content/messenger/am-smime.xul (content/am-smime.xul)
+ content/messenger/am-smime.js (content/am-smime.js)
+ content/messenger/am-smimeIdentityEditOverlay.xul (content/am-smimeIdentityEditOverlay.xul)
+ content/messenger/am-smimeOverlay.xul (content/am-smimeOverlay.xul)
+ content/messenger/certpicker.js (content/certpicker.js)
+ content/messenger/certpicker.xul (content/certpicker.xul)
+ content/messenger-smime/msgCompSMIMEOverlay.js (content/msgCompSMIMEOverlay.js)
+ content/messenger-smime/msgCompSMIMEOverlay.xul (content/msgCompSMIMEOverlay.xul)
+ content/messenger-smime/msgReadSMIMEOverlay.js (content/msgReadSMIMEOverlay.js)
+ content/messenger-smime/msgReadSMIMEOverlay.xul (content/msgReadSMIMEOverlay.xul)
+ content/messenger-smime/msgHdrViewSMIMEOverlay.xul (content/msgHdrViewSMIMEOverlay.xul)
+ content/messenger-smime/msgHdrViewSMIMEOverlay.js (content/msgHdrViewSMIMEOverlay.js)
+ content/messenger-smime/msgCompSecurityInfo.xul (content/msgCompSecurityInfo.xul)
+ content/messenger-smime/msgCompSecurityInfo.js (content/msgCompSecurityInfo.js)
+ content/messenger-smime/msgReadSecurityInfo.xul (content/msgReadSecurityInfo.xul)
+ content/messenger-smime/msgReadSecurityInfo.js (content/msgReadSecurityInfo.js)
+ content/messenger-smime/certFetchingStatus.xul (content/certFetchingStatus.xul)
+ content/messenger-smime/certFetchingStatus.js (content/certFetchingStatus.js)
+#endif
diff --git a/mailnews/extensions/smime/moz.build b/mailnews/extensions/smime/moz.build
new file mode 100644
index 000000000..3adf6a50e
--- /dev/null
+++ b/mailnews/extensions/smime/moz.build
@@ -0,0 +1,15 @@
+# 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/.
+
+DIRS += [
+ 'public',
+ 'src',
+]
+
+JAR_MANIFESTS += ['jar.mn']
+
+JS_PREFERENCE_FILES += [
+ 'content/smime.js',
+]
diff --git a/mailnews/extensions/smime/public/moz.build b/mailnews/extensions/smime/public/moz.build
new file mode 100644
index 000000000..b7acbb0b2
--- /dev/null
+++ b/mailnews/extensions/smime/public/moz.build
@@ -0,0 +1,15 @@
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsICertPickDialogs.idl',
+ 'nsIEncryptedSMIMEURIsSrvc.idl',
+ 'nsIMsgSMIMECompFields.idl',
+ 'nsIMsgSMIMEHeaderSink.idl',
+ 'nsISMimeJSHelper.idl',
+ 'nsIUserCertPicker.idl',
+]
+
+XPIDL_MODULE = 'msgsmime'
diff --git a/mailnews/extensions/smime/public/nsICertPickDialogs.idl b/mailnews/extensions/smime/public/nsICertPickDialogs.idl
new file mode 100644
index 000000000..01a7f3712
--- /dev/null
+++ b/mailnews/extensions/smime/public/nsICertPickDialogs.idl
@@ -0,0 +1,30 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIInterfaceRequestor;
+
+/**
+ * nsICertPickDialogs
+ * Provides generic UI for choosing a certificate
+ */
+[scriptable, uuid(51d59b08-1dd2-11b2-ad4a-a51b92f8a184)]
+interface nsICertPickDialogs : nsISupports
+{
+ /**
+ * PickCertificate
+ * General purpose certificate prompter
+ */
+ void PickCertificate(in nsIInterfaceRequestor ctx,
+ [array, size_is(count)] in wstring certNickList,
+ [array, size_is(count)] in wstring certDetailsList,
+ in unsigned long count,
+ inout long selectedIndex,
+ out boolean canceled);
+};
+
+%{C++
+#define NS_CERTPICKDIALOGS_CONTRACTID "@mozilla.org/nsCertPickDialogs;1"
+%}
diff --git a/mailnews/extensions/smime/public/nsIEncryptedSMIMEURIsSrvc.idl b/mailnews/extensions/smime/public/nsIEncryptedSMIMEURIsSrvc.idl
new file mode 100644
index 000000000..4b2b7c25c
--- /dev/null
+++ b/mailnews/extensions/smime/public/nsIEncryptedSMIMEURIsSrvc.idl
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* This is a private interface used exclusively by SMIME.
+ It provides functionality to the JS UI code,
+ that is only accessible from C++.
+*/
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(f86e55c9-530b-483f-91a7-10fb5b852488)]
+interface nsIEncryptedSMIMEURIsService : nsISupports
+{
+ /// Remember that this URI is encrypted.
+ void rememberEncrypted(in AUTF8String uri);
+
+ /// Forget that this URI is encrypted.
+ void forgetEncrypted(in AUTF8String uri);
+
+ /// Check if this URI is encrypted.
+ boolean isEncrypted(in AUTF8String uri);
+};
diff --git a/mailnews/extensions/smime/public/nsIMsgSMIMECompFields.idl b/mailnews/extensions/smime/public/nsIMsgSMIMECompFields.idl
new file mode 100644
index 000000000..0688afd76
--- /dev/null
+++ b/mailnews/extensions/smime/public/nsIMsgSMIMECompFields.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+
+/* This is a private interface used exclusively by SMIME. NO ONE outside of extensions/smime
+ should have any knowledge nor should be referring to this interface.
+*/
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(338E91F9-5970-4f81-B771-0822A32B1161)]
+interface nsIMsgSMIMECompFields : nsISupports
+{
+ attribute boolean signMessage;
+ attribute boolean requireEncryptMessage;
+};
diff --git a/mailnews/extensions/smime/public/nsIMsgSMIMEHeaderSink.idl b/mailnews/extensions/smime/public/nsIMsgSMIMEHeaderSink.idl
new file mode 100644
index 000000000..9bfa41a04
--- /dev/null
+++ b/mailnews/extensions/smime/public/nsIMsgSMIMEHeaderSink.idl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+
+/* This is a private interface used exclusively by SMIME. NO ONE outside of extensions/smime
+ or the hard coded smime decryption files in mime/src should have any knowledge nor should
+ be referring to this interface.
+*/
+
+#include "nsISupports.idl"
+
+interface nsIX509Cert;
+
+[scriptable, uuid(25380FA1-E70C-4e82-B0BC-F31C2F41C470)]
+interface nsIMsgSMIMEHeaderSink : nsISupports
+{
+ void signedStatus(in long aNestingLevel, in long aSignatureStatus, in nsIX509Cert aSignerCert);
+ void encryptionStatus(in long aNestingLevel, in long aEncryptionStatus, in nsIX509Cert aReceipientCert);
+
+ long maxWantedNesting(); // 1 == only info on outermost nesting level wanted
+};
diff --git a/mailnews/extensions/smime/public/nsISMimeJSHelper.idl b/mailnews/extensions/smime/public/nsISMimeJSHelper.idl
new file mode 100644
index 000000000..c29a77939
--- /dev/null
+++ b/mailnews/extensions/smime/public/nsISMimeJSHelper.idl
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/* This is a private interface used exclusively by SMIME.
+ It provides functionality to the JS UI code,
+ that is only accessible from C++.
+*/
+
+#include "nsISupports.idl"
+
+interface nsIMsgCompFields;
+interface nsIX509Cert;
+
+[scriptable, uuid(a54e3c8f-a000-4901-898f-fafb297b1546)]
+interface nsISMimeJSHelper : nsISupports
+{
+ /**
+ * Obtains detailed information about the certificate availability
+ * status of email recipients.
+ *
+ * @param compFields - Attributes of the composed message
+ *
+ * @param count - The number of entries in returned arrays
+ *
+ * @param emailAddresses - The list of all recipient email addresses
+ *
+ * @param certVerification - The verification/validity status of recipient certs
+ *
+ * @param certIssuedInfos - If a recipient cert was found, when has it been issued?
+ *
+ * @param certExpiredInfos - If a recipient cert was found, when will it expire?
+ *
+ * @param certs - The recipient certificates, which can contain null for not found
+ *
+ * @param canEncrypt - whether valid certificates have been found for all recipients
+ *
+ * @exception NS_ERROR_FAILURE - unexptected failure
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY - could not create the out list
+ *
+ * @exception NS_ERROR_INVALID_ARG
+ */
+ void getRecipientCertsInfo(in nsIMsgCompFields compFields,
+ out unsigned long count,
+ [array, size_is(count)] out wstring emailAddresses,
+ [array, size_is(count)] out long certVerification,
+ [array, size_is(count)] out wstring certIssuedInfos,
+ [array, size_is(count)] out wstring certExpiresInfos,
+ [array, size_is(count)] out nsIX509Cert certs,
+ out boolean canEncrypt);
+
+ /**
+ * Obtains a list of email addresses where valid email recipient certificates
+ * are not yet available.
+ *
+ * @param compFields - Attributes of the composed message
+ *
+ * @param count - The number of returned email addresses
+ *
+ * @param emailAddresses - The list of email addresses without valid certs
+ *
+ * @exception NS_ERROR_FAILURE - unexptected failure
+ *
+ * @exception NS_ERROR_OUT_OF_MEMORY - could not create the out list
+ *
+ * @exception NS_ERROR_INVALID_ARG
+ */
+ void getNoCertAddresses(in nsIMsgCompFields compFields,
+ out unsigned long count,
+ [array, size_is(count)] out wstring emailAddresses);
+};
diff --git a/mailnews/extensions/smime/public/nsIUserCertPicker.idl b/mailnews/extensions/smime/public/nsIUserCertPicker.idl
new file mode 100644
index 000000000..666941c29
--- /dev/null
+++ b/mailnews/extensions/smime/public/nsIUserCertPicker.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIX509Cert;
+interface nsIInterfaceRequestor;
+
+[scriptable, uuid(92396323-23f2-49e0-bf98-a25a725231ab)]
+interface nsIUserCertPicker : nsISupports {
+ nsIX509Cert pickByUsage(in nsIInterfaceRequestor ctx,
+ in wstring selectedNickname,
+ in long certUsage, // as defined by NSS enum SECCertUsage
+ in boolean allowInvalid,
+ in boolean allowDuplicateNicknames,
+ in AString emailAddress, // optional - if non-empty,
+ // skip certificates which
+ // have at least one e-mail
+ // address but do not
+ // include this specific one
+ out boolean canceled);
+};
+
+%{C++
+#define NS_CERT_PICKER_CONTRACTID "@mozilla.org/user_cert_picker;1"
+%}
diff --git a/mailnews/extensions/smime/src/moz.build b/mailnews/extensions/smime/src/moz.build
new file mode 100644
index 000000000..f3e888dd4
--- /dev/null
+++ b/mailnews/extensions/smime/src/moz.build
@@ -0,0 +1,23 @@
+# 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/.
+
+SOURCES += [
+ 'nsCertPicker.cpp',
+ 'nsEncryptedSMIMEURIsService.cpp',
+ 'nsMsgComposeSecure.cpp',
+ 'nsSMimeJSHelper.cpp',
+]
+
+EXTRA_COMPONENTS += [
+ 'smime-service.js',
+ 'smime-service.manifest',
+]
+
+FINAL_LIBRARY = 'mail'
+
+LOCAL_INCLUDES += [
+ '/mozilla/security/manager/pki',
+ '/mozilla/security/pkix/include'
+]
diff --git a/mailnews/extensions/smime/src/nsCertPicker.cpp b/mailnews/extensions/smime/src/nsCertPicker.cpp
new file mode 100644
index 000000000..183f9605c
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsCertPicker.cpp
@@ -0,0 +1,471 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsCertPicker.h"
+
+#include "MainThreadUtils.h"
+#include "ScopedNSSTypes.h"
+#include "cert.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsICertPickDialogs.h"
+#include "nsIDOMWindow.h"
+#include "nsIDialogParamBlock.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIServiceManager.h"
+#include "nsIX509CertValidity.h"
+#include "nsMemory.h"
+#include "nsMsgComposeSecure.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h"
+#include "nsNSSDialogHelper.h"
+#include "nsNSSHelper.h"
+#include "nsNSSShutDown.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "pkix/pkixtypes.h"
+
+using namespace mozilla;
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertNicknames,
+ CERTCertNicknames,
+ CERT_FreeNicknames)
+
+CERTCertNicknames*
+getNSSCertNicknamesFromCertList(const UniqueCERTCertList& certList)
+{
+ static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
+ nsresult rv;
+
+ nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsAutoString expiredString, notYetValidString;
+ nsAutoString expiredStringLeadingSpace, notYetValidStringLeadingSpace;
+
+ nssComponent->GetPIPNSSBundleString("NicknameExpired", expiredString);
+ nssComponent->GetPIPNSSBundleString("NicknameNotYetValid", notYetValidString);
+
+ expiredStringLeadingSpace.Append(' ');
+ expiredStringLeadingSpace.Append(expiredString);
+
+ notYetValidStringLeadingSpace.Append(' ');
+ notYetValidStringLeadingSpace.Append(notYetValidString);
+
+ NS_ConvertUTF16toUTF8 aUtf8ExpiredString(expiredStringLeadingSpace);
+ NS_ConvertUTF16toUTF8 aUtf8NotYetValidString(notYetValidStringLeadingSpace);
+
+ return CERT_NicknameStringsFromCertList(certList.get(),
+ const_cast<char*>(aUtf8ExpiredString.get()),
+ const_cast<char*>(aUtf8NotYetValidString.get()));
+}
+
+nsresult
+FormatUIStrings(nsIX509Cert* cert, const nsAutoString& nickname,
+ nsAutoString& nickWithSerial, nsAutoString& details)
+{
+ if (!NS_IsMainThread()) {
+ NS_ERROR("nsNSSCertificate::FormatUIStrings called off the main thread");
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+
+ RefPtr<nsMsgComposeSecure> mcs = new nsMsgComposeSecure;
+ if (!mcs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString info;
+ nsAutoString temp1;
+
+ nickWithSerial.Append(nickname);
+
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoIssuedFor", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+ if (NS_SUCCEEDED(cert->GetSubjectName(temp1)) && !temp1.IsEmpty()) {
+ details.Append(temp1);
+ }
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(cert->GetSerialNumber(temp1)) && !temp1.IsEmpty()) {
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertDumpSerialNo", info))) {
+ details.Append(info);
+ details.AppendLiteral(": ");
+ }
+ details.Append(temp1);
+
+ nickWithSerial.AppendLiteral(" [");
+ nickWithSerial.Append(temp1);
+ nickWithSerial.Append(char16_t(']'));
+
+ details.Append(char16_t('\n'));
+ }
+
+ nsCOMPtr<nsIX509CertValidity> validity;
+ nsresult rv = cert->GetValidity(getter_AddRefs(validity));
+ if (NS_SUCCEEDED(rv) && validity) {
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoValid", info))) {
+ details.Append(info);
+ }
+
+ if (NS_SUCCEEDED(validity->GetNotBeforeLocalTime(temp1)) && !temp1.IsEmpty()) {
+ details.Append(char16_t(' '));
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoFrom", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+ }
+ details.Append(temp1);
+ }
+
+ if (NS_SUCCEEDED(validity->GetNotAfterLocalTime(temp1)) && !temp1.IsEmpty()) {
+ details.Append(char16_t(' '));
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoTo", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+ }
+ details.Append(temp1);
+ }
+
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(cert->GetKeyUsages(temp1)) && !temp1.IsEmpty()) {
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertDumpKeyUsage", info))) {
+ details.Append(info);
+ details.AppendLiteral(": ");
+ }
+ details.Append(temp1);
+ details.Append(char16_t('\n'));
+ }
+
+ UniqueCERTCertificate nssCert(cert->GetCert());
+ if (!nssCert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString firstEmail;
+ const char* aWalkAddr;
+ for (aWalkAddr = CERT_GetFirstEmailAddress(nssCert.get())
+ ;
+ aWalkAddr
+ ;
+ aWalkAddr = CERT_GetNextEmailAddress(nssCert.get(), aWalkAddr))
+ {
+ NS_ConvertUTF8toUTF16 email(aWalkAddr);
+ if (email.IsEmpty())
+ continue;
+
+ if (firstEmail.IsEmpty()) {
+ // If the first email address from the subject DN is also present
+ // in the subjectAltName extension, GetEmailAddresses() will return
+ // it twice (as received from NSS). Remember the first address so that
+ // we can filter out duplicates later on.
+ firstEmail = email;
+
+ details.AppendLiteral(" ");
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoEmail", info))) {
+ details.Append(info);
+ details.AppendLiteral(": ");
+ }
+ details.Append(email);
+ }
+ else {
+ // Append current address if it's different from the first one.
+ if (!firstEmail.Equals(email)) {
+ details.AppendLiteral(", ");
+ details.Append(email);
+ }
+ }
+ }
+
+ if (!firstEmail.IsEmpty()) {
+ // We got at least one email address, so we want a newline
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoIssuedBy", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+
+ if (NS_SUCCEEDED(cert->GetIssuerName(temp1)) && !temp1.IsEmpty()) {
+ details.Append(temp1);
+ }
+
+ details.Append(char16_t('\n'));
+ }
+
+ if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoStoredIn", info))) {
+ details.Append(info);
+ details.Append(char16_t(' '));
+
+ if (NS_SUCCEEDED(cert->GetTokenName(temp1)) && !temp1.IsEmpty()) {
+ details.Append(temp1);
+ }
+ }
+
+ // the above produces the following output:
+ //
+ // Issued to: $subjectName
+ // Serial number: $serialNumber
+ // Valid from: $starting_date to $expiration_date
+ // Certificate Key usage: $usages
+ // Email: $address(es)
+ // Issued by: $issuerName
+ // Stored in: $token
+
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsCertPicker, nsICertPickDialogs, nsIUserCertPicker)
+
+nsCertPicker::nsCertPicker()
+{
+}
+
+nsCertPicker::~nsCertPicker()
+{
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
+
+ shutdown(ShutdownCalledFrom::Object);
+}
+
+nsresult
+nsCertPicker::Init()
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> psm = do_GetService("@mozilla.org/psm;1", &rv);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsCertPicker::PickCertificate(nsIInterfaceRequestor *ctx,
+ const char16_t **certNickList,
+ const char16_t **certDetailsList,
+ uint32_t count,
+ int32_t *selectedIndex,
+ bool *canceled)
+{
+ nsresult rv;
+ uint32_t i;
+
+ *canceled = false;
+
+ nsCOMPtr<nsIDialogParamBlock> block =
+ do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
+ if (!block) return NS_ERROR_FAILURE;
+
+ block->SetNumberStrings(1+count*2);
+
+ for (i = 0; i < count; i++) {
+ rv = block->SetString(i, certNickList[i]);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ for (i = 0; i < count; i++) {
+ rv = block->SetString(i+count, certDetailsList[i]);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ rv = block->SetInt(0, count);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = block->SetInt(1, *selectedIndex);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = nsNSSDialogHelper::openDialog(nullptr,
+ "chrome://messenger/content/certpicker.xul",
+ block);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t status;
+
+ rv = block->GetInt(0, &status);
+ if (NS_FAILED(rv)) return rv;
+
+ *canceled = (status == 0)?true:false;
+ if (!*canceled) {
+ rv = block->GetInt(1, selectedIndex);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx,
+ const char16_t *selectedNickname,
+ int32_t certUsage,
+ bool allowInvalid,
+ bool allowDuplicateNicknames,
+ const nsAString &emailAddress,
+ bool *canceled,
+ nsIX509Cert **_retval)
+{
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ int32_t selectedIndex = -1;
+ bool selectionFound = false;
+ char16_t **certNicknameList = nullptr;
+ char16_t **certDetailsList = nullptr;
+ CERTCertListNode* node = nullptr;
+ nsresult rv = NS_OK;
+
+ {
+ // Iterate over all certs. This assures that user is logged in to all hardware tokens.
+ nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
+ UniqueCERTCertList allcerts(PK11_ListCerts(PK11CertListUnique, ctx));
+ }
+
+ /* find all user certs that are valid for the specified usage */
+ /* note that we are allowing expired certs in this list */
+ UniqueCERTCertList certList(
+ CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
+ (SECCertUsage)certUsage,
+ !allowDuplicateNicknames,
+ !allowInvalid,
+ ctx));
+ if (!certList) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ /* if a (non-empty) emailAddress argument is supplied to PickByUsage, */
+ /* remove non-matching certificates from the candidate list */
+
+ if (!emailAddress.IsEmpty()) {
+ node = CERT_LIST_HEAD(certList);
+ while (!CERT_LIST_END(node, certList)) {
+ /* if the cert has at least one e-mail address, check if suitable */
+ if (CERT_GetFirstEmailAddress(node->cert)) {
+ RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
+ bool match = false;
+ rv = tempCert->ContainsEmailAddress(emailAddress, &match);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (!match) {
+ /* doesn't contain the specified address, so remove from the list */
+ CERTCertListNode* freenode = node;
+ node = CERT_LIST_NEXT(node);
+ CERT_RemoveCertListNode(freenode);
+ continue;
+ }
+ }
+ node = CERT_LIST_NEXT(node);
+ }
+ }
+
+ UniqueCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList));
+ if (!nicknames) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ certNicknameList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
+ certDetailsList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
+
+ if (!certNicknameList || !certDetailsList) {
+ free(certNicknameList);
+ free(certDetailsList);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int32_t CertsToUse;
+
+ for (CertsToUse = 0, node = CERT_LIST_HEAD(certList.get());
+ !CERT_LIST_END(node, certList.get()) &&
+ CertsToUse < nicknames->numnicknames;
+ node = CERT_LIST_NEXT(node)
+ )
+ {
+ RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
+
+ if (tempCert) {
+
+ nsAutoString i_nickname(NS_ConvertUTF8toUTF16(nicknames->nicknames[CertsToUse]));
+ nsAutoString nickWithSerial;
+ nsAutoString details;
+
+ if (!selectionFound) {
+ /* for the case when selectedNickname refers to a bare nickname */
+ if (i_nickname == nsDependentString(selectedNickname)) {
+ selectedIndex = CertsToUse;
+ selectionFound = true;
+ }
+ }
+
+ if (NS_SUCCEEDED(FormatUIStrings(tempCert, i_nickname, nickWithSerial,
+ details))) {
+ certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
+ certDetailsList[CertsToUse] = ToNewUnicode(details);
+ if (!selectionFound) {
+ /* for the case when selectedNickname refers to nickname + serial */
+ if (nickWithSerial == nsDependentString(selectedNickname)) {
+ selectedIndex = CertsToUse;
+ selectionFound = true;
+ }
+ }
+ }
+ else {
+ certNicknameList[CertsToUse] = nullptr;
+ certDetailsList[CertsToUse] = nullptr;
+ }
+
+ ++CertsToUse;
+ }
+ }
+
+ if (CertsToUse) {
+ nsCOMPtr<nsICertPickDialogs> dialogs;
+ rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsICertPickDialogs),
+ NS_CERTPICKDIALOGS_CONTRACTID);
+
+ if (NS_SUCCEEDED(rv)) {
+ // Show the cert picker dialog and get the index of the selected cert.
+ rv = dialogs->PickCertificate(ctx, (const char16_t**)certNicknameList,
+ (const char16_t**)certDetailsList,
+ CertsToUse, &selectedIndex, canceled);
+ }
+ }
+
+ int32_t i;
+ for (i = 0; i < CertsToUse; ++i) {
+ free(certNicknameList[i]);
+ free(certDetailsList[i]);
+ }
+ free(certNicknameList);
+ free(certDetailsList);
+
+ if (!CertsToUse) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (NS_SUCCEEDED(rv) && !*canceled) {
+ for (i = 0, node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList);
+ ++i, node = CERT_LIST_NEXT(node)) {
+
+ if (i == selectedIndex) {
+ RefPtr<nsNSSCertificate> cert = nsNSSCertificate::Create(node->cert);
+ if (!cert) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+
+ cert.forget(_retval);
+ break;
+ }
+ }
+ }
+
+ return rv;
+}
diff --git a/mailnews/extensions/smime/src/nsCertPicker.h b/mailnews/extensions/smime/src/nsCertPicker.h
new file mode 100644
index 000000000..e5881f14f
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsCertPicker.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsCertPicker_h
+#define nsCertPicker_h
+
+#include "nsICertPickDialogs.h"
+#include "nsIUserCertPicker.h"
+#include "nsNSSShutDown.h"
+
+#define NS_CERT_PICKER_CID \
+ { 0x735959a1, 0xaf01, 0x447e, { 0xb0, 0x2d, 0x56, 0xe9, 0x68, 0xfa, 0x52, 0xb4 } }
+
+class nsCertPicker : public nsICertPickDialogs
+ , public nsIUserCertPicker
+ , public nsNSSShutDownObject
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICERTPICKDIALOGS
+ NS_DECL_NSIUSERCERTPICKER
+
+ nsCertPicker();
+
+ // Nothing to actually release.
+ virtual void virtualDestroyNSSReference() override {}
+
+ nsresult Init();
+
+protected:
+ virtual ~nsCertPicker();
+};
+
+#endif // nsCertPicker_h
diff --git a/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp
new file mode 100644
index 000000000..9276a07a6
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.cpp
@@ -0,0 +1,36 @@
+/* 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/. */
+
+#include "nsEncryptedSMIMEURIsService.h"
+
+NS_IMPL_ISUPPORTS(nsEncryptedSMIMEURIsService, nsIEncryptedSMIMEURIsService)
+
+nsEncryptedSMIMEURIsService::nsEncryptedSMIMEURIsService()
+{
+}
+
+nsEncryptedSMIMEURIsService::~nsEncryptedSMIMEURIsService()
+{
+}
+
+NS_IMETHODIMP nsEncryptedSMIMEURIsService::RememberEncrypted(const nsACString & uri)
+{
+ // Assuming duplicates are allowed.
+ mEncryptedURIs.AppendElement(uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsEncryptedSMIMEURIsService::ForgetEncrypted(const nsACString & uri)
+{
+ // Assuming, this will only remove one copy of the string, if the array
+ // contains multiple copies of the same string.
+ mEncryptedURIs.RemoveElement(uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsEncryptedSMIMEURIsService::IsEncrypted(const nsACString & uri, bool *_retval)
+{
+ *_retval = mEncryptedURIs.Contains(uri);
+ return NS_OK;
+}
diff --git a/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h
new file mode 100644
index 000000000..429acbf0a
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsEncryptedSMIMEURIsService.h
@@ -0,0 +1,25 @@
+/* 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/. */
+
+#ifndef _nsEncryptedSMIMEURIsService_H_
+#define _nsEncryptedSMIMEURIsService_H_
+
+#include "nsIEncryptedSMIMEURIsSrvc.h"
+#include "nsTArray.h"
+#include "nsStringGlue.h"
+
+class nsEncryptedSMIMEURIsService : public nsIEncryptedSMIMEURIsService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIENCRYPTEDSMIMEURISSERVICE
+
+ nsEncryptedSMIMEURIsService();
+
+protected:
+ virtual ~nsEncryptedSMIMEURIsService();
+ nsTArray<nsCString> mEncryptedURIs;
+};
+
+#endif
diff --git a/mailnews/extensions/smime/src/nsMsgComposeSecure.cpp b/mailnews/extensions/smime/src/nsMsgComposeSecure.cpp
new file mode 100644
index 000000000..55383c828
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsMsgComposeSecure.cpp
@@ -0,0 +1,1203 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+#include "nsMsgComposeSecure.h"
+
+#include <algorithm>
+
+#include "ScopedNSSTypes.h"
+#include "cert.h"
+#include "keyhi.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Services.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "msgCore.h"
+#include "nsAlgorithm.h"
+#include "nsComponentManagerUtils.h"
+#include "nsICryptoHash.h"
+#include "nsIMimeConverter.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgIdentity.h"
+#include "nsIX509CertDB.h"
+#include "nsMemory.h"
+#include "nsMimeTypes.h"
+#include "nsMsgMimeCID.h"
+#include "nsNSSComponent.h"
+#include "nsServiceManagerUtils.h"
+#include "nspr.h"
+#include "pkix/Result.h"
+
+using namespace mozilla::mailnews;
+using namespace mozilla;
+using namespace mozilla::psm;
+
+#define MK_MIME_ERROR_WRITING_FILE -1
+
+#define SMIME_STRBUNDLE_URL "chrome://messenger/locale/am-smime.properties"
+
+// It doesn't make sense to encode the message because the message will be
+// displayed only if the MUA doesn't support MIME.
+// We need to consider what to do in case the server doesn't support 8BITMIME.
+// In short, we can't use non-ASCII characters here.
+static const char crypto_multipart_blurb[] = "This is a cryptographically signed message in MIME format.";
+
+static void mime_crypto_write_base64 (void *closure, const char *buf,
+ unsigned long size);
+static nsresult mime_encoder_output_fn(const char *buf, int32_t size,
+ void *closure);
+static nsresult mime_nested_encoder_output_fn(const char *buf, int32_t size,
+ void *closure);
+static nsresult make_multipart_signed_header_string(bool outer_p,
+ char **header_return,
+ char **boundary_return,
+ int16_t hash_type);
+static char *mime_make_separator(const char *prefix);
+
+
+static void
+GenerateGlobalRandomBytes(unsigned char *buf, int32_t len)
+{
+ static bool firstTime = true;
+
+ if (firstTime)
+ {
+ // Seed the random-number generator with current time so that
+ // the numbers will be different every time we run.
+ srand( (unsigned)PR_Now() );
+ firstTime = false;
+ }
+
+ for( int32_t i = 0; i < len; i++ )
+ buf[i] = rand() % 10;
+}
+
+char
+*mime_make_separator(const char *prefix)
+{
+ unsigned char rand_buf[13];
+ GenerateGlobalRandomBytes(rand_buf, 12);
+
+ return PR_smprintf("------------%s"
+ "%02X%02X%02X%02X"
+ "%02X%02X%02X%02X"
+ "%02X%02X%02X%02X",
+ prefix,
+ rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3],
+ rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7],
+ rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]);
+}
+
+// end of copied code which needs fixed....
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Implementation of nsMsgSMIMEComposeFields
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsMsgSMIMEComposeFields, nsIMsgSMIMECompFields)
+
+nsMsgSMIMEComposeFields::nsMsgSMIMEComposeFields()
+:mSignMessage(false), mAlwaysEncryptMessage(false)
+{
+}
+
+nsMsgSMIMEComposeFields::~nsMsgSMIMEComposeFields()
+{
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::SetSignMessage(bool value)
+{
+ mSignMessage = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::GetSignMessage(bool *_retval)
+{
+ *_retval = mSignMessage;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::SetRequireEncryptMessage(bool value)
+{
+ mAlwaysEncryptMessage = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgSMIMEComposeFields::GetRequireEncryptMessage(bool *_retval)
+{
+ *_retval = mAlwaysEncryptMessage;
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Implementation of nsMsgComposeSecure
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsMsgComposeSecure, nsIMsgComposeSecure)
+
+nsMsgComposeSecure::nsMsgComposeSecure()
+{
+ /* member initializers and constructor code */
+ mMultipartSignedBoundary = 0;
+ mBuffer = 0;
+ mBufferedBytes = 0;
+ mHashType = 0;
+}
+
+nsMsgComposeSecure::~nsMsgComposeSecure()
+{
+ /* destructor code */
+ if (mEncryptionContext) {
+ if (mBufferedBytes) {
+ mEncryptionContext->Update(mBuffer, mBufferedBytes);
+ mBufferedBytes = 0;
+ }
+ mEncryptionContext->Finish();
+ }
+
+ delete [] mBuffer;
+
+ PR_FREEIF(mMultipartSignedBoundary);
+}
+
+NS_IMETHODIMP nsMsgComposeSecure::RequiresCryptoEncapsulation(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aCompFields, bool * aRequiresEncryptionWork)
+{
+ NS_ENSURE_ARG_POINTER(aRequiresEncryptionWork);
+
+ *aRequiresEncryptionWork = false;
+
+ bool alwaysEncryptMessages = false;
+ bool signMessage = false;
+ nsresult rv = ExtractEncryptionState(aIdentity, aCompFields, &signMessage, &alwaysEncryptMessages);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (alwaysEncryptMessages || signMessage)
+ *aRequiresEncryptionWork = true;
+
+ return NS_OK;
+}
+
+
+nsresult nsMsgComposeSecure::GetSMIMEBundleString(const char16_t *name,
+ nsString &outString)
+{
+ outString.Truncate();
+
+ NS_ENSURE_ARG_POINTER(name);
+
+ NS_ENSURE_TRUE(InitializeSMIMEBundle(), NS_ERROR_FAILURE);
+
+ return mSMIMEBundle->GetStringFromName(name, getter_Copies(outString));
+}
+
+nsresult
+nsMsgComposeSecure::
+SMIMEBundleFormatStringFromName(const char16_t *name,
+ const char16_t **params,
+ uint32_t numParams,
+ char16_t **outString)
+{
+ NS_ENSURE_ARG_POINTER(name);
+
+ if (!InitializeSMIMEBundle())
+ return NS_ERROR_FAILURE;
+
+ return mSMIMEBundle->FormatStringFromName(name, params,
+ numParams, outString);
+}
+
+bool nsMsgComposeSecure::InitializeSMIMEBundle()
+{
+ if (mSMIMEBundle)
+ return true;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ nsresult rv = bundleService->CreateBundle(SMIME_STRBUNDLE_URL,
+ getter_AddRefs(mSMIMEBundle));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return true;
+}
+
+void nsMsgComposeSecure::SetError(nsIMsgSendReport *sendReport, const char16_t *bundle_string)
+{
+ if (!sendReport || !bundle_string)
+ return;
+
+ if (mErrorAlreadyReported)
+ return;
+
+ mErrorAlreadyReported = true;
+
+ nsString errorString;
+ nsresult res = GetSMIMEBundleString(bundle_string, errorString);
+ if (NS_SUCCEEDED(res) && !errorString.IsEmpty())
+ {
+ sendReport->SetMessage(nsIMsgSendReport::process_Current,
+ errorString.get(),
+ true);
+ }
+}
+
+void nsMsgComposeSecure::SetErrorWithParam(nsIMsgSendReport *sendReport, const char16_t *bundle_string, const char *param)
+{
+ if (!sendReport || !bundle_string || !param)
+ return;
+
+ if (mErrorAlreadyReported)
+ return;
+
+ mErrorAlreadyReported = true;
+
+ nsString errorString;
+ nsresult res;
+ const char16_t *params[1];
+
+ NS_ConvertASCIItoUTF16 ucs2(param);
+ params[0]= ucs2.get();
+
+ res = SMIMEBundleFormatStringFromName(bundle_string,
+ params,
+ 1,
+ getter_Copies(errorString));
+
+ if (NS_SUCCEEDED(res) && !errorString.IsEmpty())
+ {
+ sendReport->SetMessage(nsIMsgSendReport::process_Current,
+ errorString.get(),
+ true);
+ }
+}
+
+nsresult nsMsgComposeSecure::ExtractEncryptionState(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aComposeFields, bool * aSignMessage, bool * aEncrypt)
+{
+ if (!aComposeFields && !aIdentity)
+ return NS_ERROR_FAILURE; // kick out...invalid args....
+
+ NS_ENSURE_ARG_POINTER(aSignMessage);
+ NS_ENSURE_ARG_POINTER(aEncrypt);
+
+ nsCOMPtr<nsISupports> securityInfo;
+ if (aComposeFields)
+ aComposeFields->GetSecurityInfo(getter_AddRefs(securityInfo));
+
+ if (securityInfo) // if we were given security comp fields, use them.....
+ {
+ nsCOMPtr<nsIMsgSMIMECompFields> smimeCompFields = do_QueryInterface(securityInfo);
+ if (smimeCompFields)
+ {
+ smimeCompFields->GetSignMessage(aSignMessage);
+ smimeCompFields->GetRequireEncryptMessage(aEncrypt);
+ return NS_OK;
+ }
+ }
+
+ // get the default info from the identity....
+ int32_t ep = 0;
+ nsresult testrv = aIdentity->GetIntAttribute("encryptionpolicy", &ep);
+ if (NS_FAILED(testrv)) {
+ *aEncrypt = false;
+ }
+ else {
+ *aEncrypt = (ep > 0);
+ }
+
+ testrv = aIdentity->GetBoolAttribute("sign_mail", aSignMessage);
+ if (NS_FAILED(testrv))
+ {
+ *aSignMessage = false;
+ }
+ return NS_OK;
+}
+
+// Select a hash algorithm to sign message
+// based on subject public key type and size.
+static nsresult
+GetSigningHashFunction(nsIX509Cert *aSigningCert, int16_t *hashType)
+{
+ // Get the signing certificate
+ CERTCertificate *scert = nullptr;
+ if (aSigningCert) {
+ scert = aSigningCert->GetCert();
+ }
+ if (!scert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ UniqueSECKEYPublicKey scertPublicKey(CERT_ExtractPublicKey(scert));
+ if (!scertPublicKey) {
+ return mozilla::MapSECStatus(SECFailure);
+ }
+ KeyType subjectPublicKeyType = SECKEY_GetPublicKeyType(scertPublicKey.get());
+
+ // Get the length of the signature in bits.
+ unsigned siglen = SECKEY_SignatureLen(scertPublicKey.get()) * 8;
+ if (!siglen) {
+ return mozilla::MapSECStatus(SECFailure);
+ }
+
+ // Select a hash function for signature generation whose security strength
+ // meets or exceeds the security strength of the public key, using NIST
+ // Special Publication 800-57, Recommendation for Key Management - Part 1:
+ // General (Revision 3), where Table 2 specifies the security strength of
+ // the public key and Table 3 lists acceptable hash functions. (The security
+ // strength of the hash (for digital signatures) is half the length of the
+ // output.)
+ // [SP 800-57 is available at http://csrc.nist.gov/publications/PubsSPs.html.]
+ if (subjectPublicKeyType == rsaKey) {
+ // For RSA, siglen is the same as the length of the modulus.
+
+ // SHA-1 provides equivalent security strength for up to 1024 bits
+ // SHA-256 provides equivalent security strength for up to 3072 bits
+
+ if (siglen > 3072) {
+ *hashType = nsICryptoHash::SHA512;
+ } else if (siglen > 1024) {
+ *hashType = nsICryptoHash::SHA256;
+ } else {
+ *hashType = nsICryptoHash::SHA1;
+ }
+ } else if (subjectPublicKeyType == dsaKey) {
+ // For DSA, siglen is twice the length of the q parameter of the key.
+ // The security strength of the key is half the length (in bits) of
+ // the q parameter of the key.
+
+ // NSS only supports SHA-1, SHA-224, and SHA-256 for DSA signatures.
+ // The S/MIME code does not support SHA-224.
+
+ if (siglen >= 512) { // 512-bit signature = 256-bit q parameter
+ *hashType = nsICryptoHash::SHA256;
+ } else {
+ *hashType = nsICryptoHash::SHA1;
+ }
+ } else if (subjectPublicKeyType == ecKey) {
+ // For ECDSA, siglen is twice the length of the field size. The security
+ // strength of the key is half the length (in bits) of the field size.
+
+ if (siglen >= 1024) { // 1024-bit signature = 512-bit field size
+ *hashType = nsICryptoHash::SHA512;
+ } else if (siglen >= 768) { // 768-bit signature = 384-bit field size
+ *hashType = nsICryptoHash::SHA384;
+ } else if (siglen >= 512) { // 512-bit signature = 256-bit field size
+ *hashType = nsICryptoHash::SHA256;
+ } else {
+ *hashType = nsICryptoHash::SHA1;
+ }
+ } else {
+ // Unknown key type
+ *hashType = nsICryptoHash::SHA256;
+ NS_WARNING("GetSigningHashFunction: Subject public key type unknown.");
+ }
+ return NS_OK;
+}
+
+/* void beginCryptoEncapsulation (in nsOutputFileStream aStream, in boolean aEncrypt, in boolean aSign, in string aRecipeints, in boolean aIsDraft); */
+NS_IMETHODIMP nsMsgComposeSecure::BeginCryptoEncapsulation(nsIOutputStream * aStream,
+ const char * aRecipients,
+ nsIMsgCompFields * aCompFields,
+ nsIMsgIdentity * aIdentity,
+ nsIMsgSendReport *sendReport,
+ bool aIsDraft)
+{
+ mErrorAlreadyReported = false;
+ nsresult rv = NS_OK;
+
+ bool encryptMessages = false;
+ bool signMessage = false;
+ ExtractEncryptionState(aIdentity, aCompFields, &signMessage, &encryptMessages);
+
+ if (!signMessage && !encryptMessages) return NS_ERROR_FAILURE;
+
+ mStream = aStream;
+ mIsDraft = aIsDraft;
+
+ if (encryptMessages && signMessage)
+ mCryptoState = mime_crypto_signed_encrypted;
+ else if (encryptMessages)
+ mCryptoState = mime_crypto_encrypted;
+ else if (signMessage)
+ mCryptoState = mime_crypto_clear_signed;
+ else
+ PR_ASSERT(0);
+
+ aIdentity->GetUnicharAttribute("signing_cert_name", mSigningCertName);
+ aIdentity->GetCharAttribute("signing_cert_dbkey", mSigningCertDBKey);
+ aIdentity->GetUnicharAttribute("encryption_cert_name", mEncryptionCertName);
+ aIdentity->GetCharAttribute("encryption_cert_dbkey", mEncryptionCertDBKey);
+
+ rv = MimeCryptoHackCerts(aRecipients, sendReport, encryptMessages, signMessage, aIdentity);
+ if (NS_FAILED(rv)) {
+ goto FAIL;
+ }
+
+ if (signMessage && mSelfSigningCert) {
+ rv = GetSigningHashFunction(mSelfSigningCert, &mHashType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ switch (mCryptoState)
+ {
+ case mime_crypto_clear_signed:
+ rv = MimeInitMultipartSigned(true, sendReport);
+ break;
+ case mime_crypto_opaque_signed:
+ PR_ASSERT(0); /* #### no api for this yet */
+ rv = NS_ERROR_NOT_IMPLEMENTED;
+ break;
+ case mime_crypto_signed_encrypted:
+ rv = MimeInitEncryption(true, sendReport);
+ break;
+ case mime_crypto_encrypted:
+ rv = MimeInitEncryption(false, sendReport);
+ break;
+ case mime_crypto_none:
+ /* This can happen if mime_crypto_hack_certs() decided to turn off
+ encryption (by asking the user.) */
+ // XXX 1 is not a valid nsresult
+ rv = static_cast<nsresult>(1);
+ break;
+ default:
+ PR_ASSERT(0);
+ break;
+ }
+
+FAIL:
+ return rv;
+}
+
+/* void finishCryptoEncapsulation (in boolean aAbort); */
+NS_IMETHODIMP nsMsgComposeSecure::FinishCryptoEncapsulation(bool aAbort, nsIMsgSendReport *sendReport)
+{
+ nsresult rv = NS_OK;
+
+ if (!aAbort) {
+ switch (mCryptoState) {
+ case mime_crypto_clear_signed:
+ rv = MimeFinishMultipartSigned (true, sendReport);
+ break;
+ case mime_crypto_opaque_signed:
+ PR_ASSERT(0); /* #### no api for this yet */
+ rv = NS_ERROR_FAILURE;
+ break;
+ case mime_crypto_signed_encrypted:
+ rv = MimeFinishEncryption (true, sendReport);
+ break;
+ case mime_crypto_encrypted:
+ rv = MimeFinishEncryption (false, sendReport);
+ break;
+ default:
+ PR_ASSERT(0);
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgComposeSecure::MimeInitMultipartSigned(bool aOuter, nsIMsgSendReport *sendReport)
+{
+ /* First, construct and write out the multipart/signed MIME header data.
+ */
+ nsresult rv = NS_OK;
+ char *header = 0;
+ uint32_t L;
+
+ rv = make_multipart_signed_header_string(aOuter, &header,
+ &mMultipartSignedBoundary, mHashType);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ L = strlen(header);
+
+ if (aOuter){
+ /* If this is the outer block, write it to the file. */
+ uint32_t n;
+ rv = mStream->Write(header, L, &n);
+ if (NS_FAILED(rv) || n < L) {
+ // XXX This is -1, not an nsresult
+ rv = static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ }
+ } else {
+ /* If this is an inner block, feed it through the crypto stream. */
+ rv = MimeCryptoWriteBlock (header, L);
+ }
+
+ PR_Free(header);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /* Now initialize the crypto library, so that we can compute a hash
+ on the object which we are signing.
+ */
+
+ PR_SetError(0,0);
+ mDataHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDataHash->Init(mHashType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PR_SetError(0,0);
+ return rv;
+}
+
+nsresult nsMsgComposeSecure::MimeInitEncryption(bool aSign, nsIMsgSendReport *sendReport)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> sMIMEBundle;
+ nsString mime_smime_enc_content_desc;
+
+ bundleSvc->CreateBundle(SMIME_STRBUNDLE_URL, getter_AddRefs(sMIMEBundle));
+
+ if (!sMIMEBundle)
+ return NS_ERROR_FAILURE;
+
+ sMIMEBundle->GetStringFromName(u"mime_smimeEncryptedContentDesc",
+ getter_Copies(mime_smime_enc_content_desc));
+ NS_ConvertUTF16toUTF8 enc_content_desc_utf8(mime_smime_enc_content_desc);
+
+ nsCOMPtr<nsIMimeConverter> mimeConverter =
+ do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString encodedContentDescription;
+ mimeConverter->EncodeMimePartIIStr_UTF8(enc_content_desc_utf8, false, "UTF-8",
+ sizeof("Content-Description: "),
+ nsIMimeConverter::MIME_ENCODED_WORD_SIZE,
+ encodedContentDescription);
+
+ /* First, construct and write out the opaque-crypto-blob MIME header data.
+ */
+
+ char *s =
+ PR_smprintf("Content-Type: " APPLICATION_PKCS7_MIME
+ "; name=\"smime.p7m\"; smime-type=enveloped-data" CRLF
+ "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF
+ "Content-Disposition: attachment"
+ "; filename=\"smime.p7m\"" CRLF
+ "Content-Description: %s" CRLF
+ CRLF,
+ encodedContentDescription.get());
+
+ uint32_t L;
+ if (!s) return NS_ERROR_OUT_OF_MEMORY;
+ L = strlen(s);
+ uint32_t n;
+ rv = mStream->Write(s, L, &n);
+ if (NS_FAILED(rv) || n < L) {
+ return NS_ERROR_FAILURE;
+ }
+ PR_Free(s);
+ s = 0;
+
+ /* Now initialize the crypto library, so that we can filter the object
+ to be encrypted through it.
+ */
+
+ if (!mIsDraft) {
+ uint32_t numCerts;
+ mCerts->GetLength(&numCerts);
+ PR_ASSERT(numCerts > 0);
+ if (numCerts == 0) return NS_ERROR_FAILURE;
+ }
+
+ // Initialize the base64 encoder
+ MOZ_ASSERT(!mCryptoEncoder, "Shouldn't have an encoder already");
+ mCryptoEncoder = MimeEncoder::GetBase64Encoder(mime_encoder_output_fn,
+ this);
+
+ /* Initialize the encrypter (and add the sender's cert.) */
+ PR_ASSERT(mSelfEncryptionCert);
+ PR_SetError(0,0);
+ mEncryptionCinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ rv = mEncryptionCinfo->CreateEncrypted(mCerts);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorEncryptMail");
+ goto FAIL;
+ }
+
+ mEncryptionContext = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ if (!mBuffer) {
+ mBuffer = new char[eBufferSize];
+ if (!mBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mBufferedBytes = 0;
+
+ rv = mEncryptionContext->Start(mEncryptionCinfo, mime_crypto_write_base64, mCryptoEncoder);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorEncryptMail");
+ goto FAIL;
+ }
+
+ /* If we're signing, tack a multipart/signed header onto the front of
+ the data to be encrypted, and initialize the sign-hashing code too.
+ */
+ if (aSign) {
+ rv = MimeInitMultipartSigned(false, sendReport);
+ if (NS_FAILED(rv)) goto FAIL;
+ }
+
+ FAIL:
+ return rv;
+}
+
+nsresult nsMsgComposeSecure::MimeFinishMultipartSigned (bool aOuter, nsIMsgSendReport *sendReport)
+{
+ int status;
+ nsresult rv;
+ nsCOMPtr<nsICMSMessage> cinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsICMSEncoder> encoder = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char * header = nullptr;
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> sMIMEBundle;
+ nsString mime_smime_sig_content_desc;
+
+ bundleSvc->CreateBundle(SMIME_STRBUNDLE_URL, getter_AddRefs(sMIMEBundle));
+
+ if (!sMIMEBundle)
+ return NS_ERROR_FAILURE;
+
+ sMIMEBundle->GetStringFromName(u"mime_smimeSignatureContentDesc",
+ getter_Copies(mime_smime_sig_content_desc));
+
+ NS_ConvertUTF16toUTF8 sig_content_desc_utf8(mime_smime_sig_content_desc);
+
+ /* Compute the hash...
+ */
+
+ nsAutoCString hashString;
+ mDataHash->Finish(false, hashString);
+
+ mDataHash = nullptr;
+
+ status = PR_GetError();
+ if (status < 0) goto FAIL;
+
+ /* Write out the headers for the signature.
+ */
+ uint32_t L;
+ header =
+ PR_smprintf(CRLF
+ "--%s" CRLF
+ "Content-Type: " APPLICATION_PKCS7_SIGNATURE
+ "; name=\"smime.p7s\"" CRLF
+ "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF
+ "Content-Disposition: attachment; "
+ "filename=\"smime.p7s\"" CRLF
+ "Content-Description: %s" CRLF
+ CRLF,
+ mMultipartSignedBoundary,
+ sig_content_desc_utf8.get());
+
+ if (!header) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ L = strlen(header);
+ if (aOuter) {
+ /* If this is the outer block, write it to the file. */
+ uint32_t n;
+ rv = mStream->Write(header, L, &n);
+ if (NS_FAILED(rv) || n < L) {
+ // XXX This is -1, not an nsresult
+ rv = static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ }
+ } else {
+ /* If this is an inner block, feed it through the crypto stream. */
+ rv = MimeCryptoWriteBlock (header, L);
+ }
+
+ PR_Free(header);
+
+ /* Create the signature...
+ */
+
+ NS_ASSERTION(mHashType, "Hash function for signature has not been set.");
+
+ PR_ASSERT (mSelfSigningCert);
+ PR_SetError(0,0);
+
+ rv = cinfo->CreateSigned(mSelfSigningCert, mSelfEncryptionCert,
+ (unsigned char*)hashString.get(), hashString.Length(), mHashType);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorCanNotSignMail");
+ goto FAIL;
+ }
+
+ // Initialize the base64 encoder for the signature data.
+ MOZ_ASSERT(!mSigEncoder, "Shouldn't already have a mSigEncoder");
+ mSigEncoder = MimeEncoder::GetBase64Encoder(
+ (aOuter ? mime_encoder_output_fn : mime_nested_encoder_output_fn), this);
+
+ /* Write out the signature.
+ */
+ PR_SetError(0,0);
+ rv = encoder->Start(cinfo, mime_crypto_write_base64, mSigEncoder);
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorCanNotSignMail");
+ goto FAIL;
+ }
+
+ // We're not passing in any data, so no update needed.
+ rv = encoder->Finish();
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorCanNotSignMail");
+ goto FAIL;
+ }
+
+ // Shut down the sig's base64 encoder.
+ rv = mSigEncoder->Flush();
+ mSigEncoder = nullptr;
+ if (NS_FAILED(rv)) {
+ goto FAIL;
+ }
+
+ /* Now write out the terminating boundary.
+ */
+ {
+ uint32_t L;
+ char *header = PR_smprintf(CRLF "--%s--" CRLF,
+ mMultipartSignedBoundary);
+ PR_Free(mMultipartSignedBoundary);
+ mMultipartSignedBoundary = 0;
+
+ if (!header) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+ L = strlen(header);
+ if (aOuter) {
+ /* If this is the outer block, write it to the file. */
+ uint32_t n;
+ rv = mStream->Write(header, L, &n);
+ if (NS_FAILED(rv) || n < L)
+ // XXX This is -1, not an nsresult
+ rv = static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ } else {
+ /* If this is an inner block, feed it through the crypto stream. */
+ rv = MimeCryptoWriteBlock (header, L);
+ }
+ }
+
+FAIL:
+ return rv;
+}
+
+
+/* Helper function for mime_finish_crypto_encapsulation() to close off
+ an opaque crypto object (for encrypted or signed-and-encrypted messages.)
+ */
+nsresult nsMsgComposeSecure::MimeFinishEncryption (bool aSign, nsIMsgSendReport *sendReport)
+{
+ nsresult rv;
+
+ /* If this object is both encrypted and signed, close off the
+ signature first (since it's inside.) */
+ if (aSign) {
+ rv = MimeFinishMultipartSigned (false, sendReport);
+ if (NS_FAILED(rv)) {
+ goto FAIL;
+ }
+ }
+
+ /* Close off the opaque encrypted blob.
+ */
+ PR_ASSERT(mEncryptionContext);
+
+ if (mBufferedBytes) {
+ rv = mEncryptionContext->Update(mBuffer, mBufferedBytes);
+ mBufferedBytes = 0;
+ if (NS_FAILED(rv)) {
+ PR_ASSERT(PR_GetError() < 0);
+ goto FAIL;
+ }
+ }
+
+ rv = mEncryptionContext->Finish();
+ if (NS_FAILED(rv)) {
+ SetError(sendReport, u"ErrorEncryptMail");
+ goto FAIL;
+ }
+
+ mEncryptionContext = nullptr;
+
+ PR_ASSERT(mEncryptionCinfo);
+ if (!mEncryptionCinfo) {
+ rv = NS_ERROR_FAILURE;
+ }
+ if (mEncryptionCinfo) {
+ mEncryptionCinfo = nullptr;
+ }
+
+ // Shut down the base64 encoder.
+ mCryptoEncoder->Flush();
+ mCryptoEncoder = nullptr;
+
+ uint32_t n;
+ rv = mStream->Write(CRLF, 2, &n);
+ if (NS_FAILED(rv) || n < 2)
+ rv = NS_ERROR_FAILURE;
+
+ FAIL:
+ return rv;
+}
+
+/* Used to figure out what certs should be used when encrypting this message.
+ */
+nsresult nsMsgComposeSecure::MimeCryptoHackCerts(const char *aRecipients,
+ nsIMsgSendReport *sendReport,
+ bool aEncrypt,
+ bool aSign,
+ nsIMsgIdentity *aIdentity)
+{
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+ nsresult res;
+
+ mCerts = do_CreateInstance(NS_ARRAY_CONTRACTID, &res);
+ if (NS_FAILED(res)) {
+ return res;
+ }
+
+ PR_ASSERT(aEncrypt || aSign);
+
+ /*
+ Signing and encryption certs use the following (per-identity) preferences:
+ - "signing_cert_name"/"encryption_cert_name": a string specifying the
+ nickname of the certificate
+ - "signing_cert_dbkey"/"encryption_cert_dbkey": a Base64 encoded blob
+ specifying an nsIX509Cert dbKey (represents serial number
+ and issuer DN, which is considered to be unique for X.509 certificates)
+
+ When retrieving the prefs, we try (in this order):
+ 1) *_cert_dbkey, if available
+ 2) *_cert_name (for maintaining backwards compatibility with preference
+ attributes written by earlier versions)
+ */
+
+ RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
+ NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
+
+ UniqueCERTCertList builtChain;
+ if (!mEncryptionCertDBKey.IsEmpty()) {
+ certdb->FindCertByDBKey(mEncryptionCertDBKey.get(),
+ getter_AddRefs(mSelfEncryptionCert));
+ if (mSelfEncryptionCert &&
+ (certVerifier->VerifyCert(mSelfEncryptionCert->GetCert(),
+ certificateUsageEmailRecipient,
+ mozilla::pkix::Now(),
+ nullptr, nullptr,
+ builtChain) != mozilla::pkix::Success)) {
+ // not suitable for encryption, so unset cert and clear pref
+ mSelfEncryptionCert = nullptr;
+ mEncryptionCertDBKey.Truncate();
+ aIdentity->SetCharAttribute("encryption_cert_dbkey",
+ mEncryptionCertDBKey);
+ }
+ }
+ if (!mSelfEncryptionCert) {
+ certdb->FindEmailEncryptionCert(mEncryptionCertName,
+ getter_AddRefs(mSelfEncryptionCert));
+ }
+
+ // same procedure for the signing cert
+ if (!mSigningCertDBKey.IsEmpty()) {
+ certdb->FindCertByDBKey(mSigningCertDBKey.get(),
+ getter_AddRefs(mSelfSigningCert));
+ if (mSelfSigningCert &&
+ (certVerifier->VerifyCert(mSelfSigningCert->GetCert(),
+ certificateUsageEmailSigner,
+ mozilla::pkix::Now(),
+ nullptr, nullptr,
+ builtChain) != mozilla::pkix::Success)) {
+ // not suitable for signing, so unset cert and clear pref
+ mSelfSigningCert = nullptr;
+ mSigningCertDBKey.Truncate();
+ aIdentity->SetCharAttribute("signing_cert_dbkey", mSigningCertDBKey);
+ }
+ }
+ if (!mSelfSigningCert) {
+ certdb->FindEmailSigningCert(mSigningCertName,
+ getter_AddRefs(mSelfSigningCert));
+ }
+
+ // must have both the signing and encryption certs to sign
+ if (!mSelfSigningCert && aSign) {
+ SetError(sendReport, u"NoSenderSigningCert");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mSelfEncryptionCert && aEncrypt) {
+ SetError(sendReport, u"NoSenderEncryptionCert");
+ return NS_ERROR_FAILURE;
+ }
+
+
+ if (aEncrypt && mSelfEncryptionCert) {
+ // Make sure self's configured cert is prepared for being used
+ // as an email recipient cert.
+ UniqueCERTCertificate nsscert(mSelfEncryptionCert->GetCert());
+ if (!nsscert) {
+ return NS_ERROR_FAILURE;
+ }
+ // XXX: This does not respect the nsNSSShutDownObject protocol.
+ if (CERT_SaveSMimeProfile(nsscert.get(), nullptr, nullptr) != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ /* If the message is to be encrypted, then get the recipient certs */
+ if (aEncrypt) {
+ nsTArray<nsCString> mailboxes;
+ ExtractEmails(EncodedHeader(nsDependentCString(aRecipients)),
+ UTF16ArrayAdapter<>(mailboxes));
+ uint32_t count = mailboxes.Length();
+
+ bool already_added_self_cert = false;
+
+ for (uint32_t i = 0; i < count; i++) {
+ nsCString mailbox_lowercase;
+ ToLowerCase(mailboxes[i], mailbox_lowercase);
+ nsCOMPtr<nsIX509Cert> cert;
+ res = certdb->FindCertByEmailAddress(mailbox_lowercase.get(),
+ getter_AddRefs(cert));
+ if (NS_FAILED(res)) {
+ // Failure to find a valid encryption cert is fatal.
+ // Here I assume that mailbox is ascii rather than utf8.
+ SetErrorWithParam(sendReport,
+ u"MissingRecipientEncryptionCert",
+ mailboxes[i].get());
+
+ return res;
+ }
+
+ /* #### see if recipient requests `signedData'.
+ if (...) no_clearsigning_p = true;
+ (This is the only reason we even bother looking up the certs
+ of the recipients if we're sending a signed-but-not-encrypted
+ message.)
+ */
+
+ bool isSame;
+ if (NS_SUCCEEDED(cert->Equals(mSelfEncryptionCert, &isSame))
+ && isSame) {
+ already_added_self_cert = true;
+ }
+
+ mCerts->AppendElement(cert, false);
+ }
+
+ if (!already_added_self_cert) {
+ mCerts->AppendElement(mSelfEncryptionCert, false);
+ }
+ }
+ return res;
+}
+
+NS_IMETHODIMP nsMsgComposeSecure::MimeCryptoWriteBlock (const char *buf, int32_t size)
+{
+ int status = 0;
+ nsresult rv;
+
+ /* If this is a From line, mangle it before signing it. You just know
+ that something somewhere is going to mangle it later, and that's
+ going to cause the signature check to fail.
+
+ (This assumes that, in the cases where From-mangling must happen,
+ this function is called a line at a time. That happens to be the
+ case.)
+ */
+ if (size >= 5 && buf[0] == 'F' && !strncmp(buf, "From ", 5)) {
+ char mangle[] = ">";
+ nsresult res = MimeCryptoWriteBlock (mangle, 1);
+ if (NS_FAILED(res))
+ return res;
+ // This value will actually be cast back to an nsresult before use, so this
+ // cast is reasonable under the circumstances.
+ status = static_cast<int>(res);
+ }
+
+ /* If we're signing, or signing-and-encrypting, feed this data into
+ the computation of the hash. */
+ if (mDataHash) {
+ PR_SetError(0,0);
+ mDataHash->Update((const uint8_t*) buf, size);
+ status = PR_GetError();
+ if (status < 0) goto FAIL;
+ }
+
+ PR_SetError(0,0);
+ if (mEncryptionContext) {
+ /* If we're encrypting, or signing-and-encrypting, write this data
+ by filtering it through the crypto library. */
+
+ /* We want to create equally sized encryption strings */
+ const char *inputBytesIterator = buf;
+ uint32_t inputBytesLeft = size;
+
+ while (inputBytesLeft) {
+ const uint32_t spaceLeftInBuffer = eBufferSize - mBufferedBytes;
+ const uint32_t bytesToAppend = std::min(inputBytesLeft, spaceLeftInBuffer);
+
+ memcpy(mBuffer+mBufferedBytes, inputBytesIterator, bytesToAppend);
+ mBufferedBytes += bytesToAppend;
+
+ inputBytesIterator += bytesToAppend;
+ inputBytesLeft -= bytesToAppend;
+
+ if (eBufferSize == mBufferedBytes) {
+ rv = mEncryptionContext->Update(mBuffer, mBufferedBytes);
+ mBufferedBytes = 0;
+ if (NS_FAILED(rv)) {
+ status = PR_GetError();
+ PR_ASSERT(status < 0);
+ if (status >= 0) status = -1;
+ goto FAIL;
+ }
+ }
+ }
+ } else {
+ /* If we're not encrypting (presumably just signing) then write this
+ data directly to the file. */
+
+ uint32_t n;
+ rv = mStream->Write(buf, size, &n);
+ if (NS_FAILED(rv) || n < (uint32_t)size) {
+ // XXX MK_MIME_ERROR_WRITING_FILE is -1, which is not a valid nsresult
+ return static_cast<nsresult>(MK_MIME_ERROR_WRITING_FILE);
+ }
+ }
+ FAIL:
+ // XXX status sometimes has invalid nsresults like -1 or PR_GetError()
+ // assigned to it
+ return static_cast<nsresult>(status);
+}
+
+/* Returns a string consisting of a Content-Type header, and a boundary
+ string, suitable for moving from the header block, down into the body
+ of a multipart object. The boundary itself is also returned (so that
+ the caller knows what to write to close it off.)
+ */
+static nsresult
+make_multipart_signed_header_string(bool outer_p,
+ char **header_return,
+ char **boundary_return,
+ int16_t hash_type)
+{
+ const char *hashStr;
+ *header_return = 0;
+ *boundary_return = mime_make_separator("ms");
+
+ if (!*boundary_return)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ switch (hash_type) {
+ case nsICryptoHash::SHA1:
+ hashStr = PARAM_MICALG_SHA1;
+ break;
+ case nsICryptoHash::SHA256:
+ hashStr = PARAM_MICALG_SHA256;
+ break;
+ case nsICryptoHash::SHA384:
+ hashStr = PARAM_MICALG_SHA384;
+ break;
+ case nsICryptoHash::SHA512:
+ hashStr = PARAM_MICALG_SHA512;
+ break;
+ default:
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *header_return = PR_smprintf(
+ "Content-Type: " MULTIPART_SIGNED "; "
+ "protocol=\"" APPLICATION_PKCS7_SIGNATURE "\"; "
+ "micalg=%s; "
+ "boundary=\"%s\"" CRLF
+ CRLF
+ "%s%s"
+ "--%s" CRLF,
+ hashStr,
+ *boundary_return,
+ (outer_p ? crypto_multipart_blurb : ""),
+ (outer_p ? CRLF CRLF : ""),
+ *boundary_return);
+
+ if (!*header_return) {
+ PR_Free(*boundary_return);
+ *boundary_return = 0;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+/* Used as the output function of a SEC_PKCS7EncoderContext -- we feed
+ plaintext into the crypto engine, and it calls this function with encrypted
+ data; then this function writes a base64-encoded representation of that
+ data to the file (by filtering it through the given MimeEncoder object.)
+
+ Also used as the output function of SEC_PKCS7Encode() -- but in that case,
+ it's used to write the encoded representation of the signature. The only
+ difference is which MimeEncoder object is used.
+ */
+static void
+mime_crypto_write_base64 (void *closure, const char *buf, unsigned long size)
+{
+ MimeEncoder *encoder = (MimeEncoder *) closure;
+ nsresult rv = encoder->Write(buf, size);
+ PR_SetError(NS_FAILED(rv) ? static_cast<uint32_t>(rv) : 0, 0);
+}
+
+
+/* Used as the output function of MimeEncoder -- when we have generated
+ the signature for a multipart/signed object, this is used to write the
+ base64-encoded representation of the signature to the file.
+ */
+// TODO: size should probably be converted to uint32_t
+nsresult mime_encoder_output_fn(const char *buf, int32_t size, void *closure)
+{
+ nsMsgComposeSecure *state = (nsMsgComposeSecure *) closure;
+ nsCOMPtr<nsIOutputStream> stream;
+ state->GetOutputStream(getter_AddRefs(stream));
+ uint32_t n;
+ nsresult rv = stream->Write((char *) buf, size, &n);
+ if (NS_FAILED(rv) || n < (uint32_t)size)
+ return NS_ERROR_FAILURE;
+ else
+ return NS_OK;
+}
+
+/* Like mime_encoder_output_fn, except this is used for the case where we
+ are both signing and encrypting -- the base64-encoded output of the
+ signature should be fed into the crypto engine, rather than being written
+ directly to the file.
+ */
+static nsresult
+mime_nested_encoder_output_fn (const char *buf, int32_t size, void *closure)
+{
+ nsMsgComposeSecure *state = (nsMsgComposeSecure *) closure;
+
+ // Copy to new null-terminated string so JS glue doesn't crash when
+ // MimeCryptoWriteBlock() is implemented in JS.
+ nsCString bufWithNull;
+ bufWithNull.Assign(buf, size);
+ return state->MimeCryptoWriteBlock(bufWithNull.get(), size);
+}
diff --git a/mailnews/extensions/smime/src/nsMsgComposeSecure.h b/mailnews/extensions/smime/src/nsMsgComposeSecure.h
new file mode 100644
index 000000000..0f3b9ac60
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsMsgComposeSecure.h
@@ -0,0 +1,106 @@
+/* -*- Mode: idl; 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/. */
+#ifndef _nsMsgComposeSecure_H_
+#define _nsMsgComposeSecure_H_
+
+#include "nsIMsgComposeSecure.h"
+#include "nsIMsgSMIMECompFields.h"
+#include "nsCOMPtr.h"
+#include "nsICMSEncoder.h"
+#include "nsIX509Cert.h"
+#include "nsIStringBundle.h"
+#include "nsICryptoHash.h"
+#include "nsICMSMessage.h"
+#include "nsIMutableArray.h"
+#include "nsStringGlue.h"
+#include "nsIOutputStream.h"
+#include "nsAutoPtr.h"
+
+class nsIMsgCompFields;
+namespace mozilla {
+namespace mailnews {
+class MimeEncoder;
+}
+}
+
+class nsMsgSMIMEComposeFields : public nsIMsgSMIMECompFields
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGSMIMECOMPFIELDS
+
+ nsMsgSMIMEComposeFields();
+
+private:
+ virtual ~nsMsgSMIMEComposeFields();
+ bool mSignMessage;
+ bool mAlwaysEncryptMessage;
+};
+
+typedef enum {
+ mime_crypto_none, /* normal unencapsulated MIME message */
+ mime_crypto_clear_signed, /* multipart/signed encapsulation */
+ mime_crypto_opaque_signed, /* application/x-pkcs7-mime (signedData) */
+ mime_crypto_encrypted, /* application/x-pkcs7-mime */
+ mime_crypto_signed_encrypted /* application/x-pkcs7-mime */
+} mimeDeliveryCryptoState;
+
+class nsMsgComposeSecure : public nsIMsgComposeSecure
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGCOMPOSESECURE
+
+ nsMsgComposeSecure();
+
+ void GetOutputStream(nsIOutputStream **stream) { NS_IF_ADDREF(*stream = mStream);}
+ nsresult GetSMIMEBundleString(const char16_t *name, nsString &outString);
+
+private:
+ virtual ~nsMsgComposeSecure();
+ typedef mozilla::mailnews::MimeEncoder MimeEncoder;
+ nsresult MimeInitMultipartSigned(bool aOuter, nsIMsgSendReport *sendReport);
+ nsresult MimeInitEncryption(bool aSign, nsIMsgSendReport *sendReport);
+ nsresult MimeFinishMultipartSigned (bool aOuter, nsIMsgSendReport *sendReport);
+ nsresult MimeFinishEncryption (bool aSign, nsIMsgSendReport *sendReport);
+ nsresult MimeCryptoHackCerts(const char *aRecipients, nsIMsgSendReport *sendReport, bool aEncrypt, bool aSign, nsIMsgIdentity *aIdentity);
+ bool InitializeSMIMEBundle();
+ nsresult SMIMEBundleFormatStringFromName(const char16_t *name,
+ const char16_t **params,
+ uint32_t numParams,
+ char16_t **outString);
+ nsresult ExtractEncryptionState(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aComposeFields, bool * aSignMessage, bool * aEncrypt);
+
+ mimeDeliveryCryptoState mCryptoState;
+ nsCOMPtr<nsIOutputStream> mStream;
+ int16_t mHashType;
+ nsCOMPtr<nsICryptoHash> mDataHash;
+ nsAutoPtr<MimeEncoder> mSigEncoder;
+ char *mMultipartSignedBoundary;
+ nsString mSigningCertName;
+ nsAutoCString mSigningCertDBKey;
+ nsCOMPtr<nsIX509Cert> mSelfSigningCert;
+ nsString mEncryptionCertName;
+ nsAutoCString mEncryptionCertDBKey;
+ nsCOMPtr<nsIX509Cert> mSelfEncryptionCert;
+ nsCOMPtr<nsIMutableArray> mCerts;
+ nsCOMPtr<nsICMSMessage> mEncryptionCinfo;
+ nsCOMPtr<nsICMSEncoder> mEncryptionContext;
+ nsCOMPtr<nsIStringBundle> mSMIMEBundle;
+
+ nsAutoPtr<MimeEncoder> mCryptoEncoder;
+ bool mIsDraft;
+
+ enum {eBufferSize = 8192};
+ char *mBuffer;
+ uint32_t mBufferedBytes;
+
+ bool mErrorAlreadyReported;
+ void SetError(nsIMsgSendReport *sendReport, const char16_t *bundle_string);
+ void SetErrorWithParam(nsIMsgSendReport *sendReport, const char16_t *bundle_string, const char *param);
+};
+
+#endif
diff --git a/mailnews/extensions/smime/src/nsMsgSMIMECID.h b/mailnews/extensions/smime/src/nsMsgSMIMECID.h
new file mode 100644
index 000000000..0fbf2d1bf
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsMsgSMIMECID.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef nsMsgSMIMECID_h__
+#define nsMsgSMIMECID_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+#define NS_MSGSMIMECOMPFIELDS_CONTRACTID \
+ "@mozilla.org/messenger-smime/composefields;1"
+
+#define NS_MSGSMIMECOMPFIELDS_CID \
+{ /* 122C919C-96B7-49a0-BBC8-0ABC67EEFFE0 */ \
+ 0x122c919c, 0x96b7, 0x49a0, \
+ { 0xbb, 0xc8, 0xa, 0xbc, 0x67, 0xee, 0xff, 0xe0 }}
+
+#define NS_MSGCOMPOSESECURE_CID \
+{ /* dd753201-9a23-4e08-957f-b3616bf7e012 */ \
+ 0xdd753201, 0x9a23, 0x4e08, \
+ {0x95, 0x7f, 0xb3, 0x61, 0x6b, 0xf7, 0xe0, 0x12 }}
+
+#define NS_SMIMEJSHELPER_CONTRACTID \
+ "@mozilla.org/messenger-smime/smimejshelper;1"
+
+#define NS_SMIMEJSJELPER_CID \
+{ /* d57d928c-60e4-4f81-999d-5c762e611205 */ \
+ 0xd57d928c, 0x60e4, 0x4f81, \
+ {0x99, 0x9d, 0x5c, 0x76, 0x2e, 0x61, 0x12, 0x05 }}
+
+#define NS_SMIMEENCRYPTURISERVICE_CONTRACTID \
+ "@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"
+
+#define NS_SMIMEENCRYPTURISERVICE_CID \
+{ /* a0134d58-018f-4d40-a099-fa079e5024a6 */ \
+ 0xa0134d58, 0x018f, 0x4d40, \
+ {0xa0, 0x99, 0xfa, 0x07, 0x9e, 0x50, 0x24, 0xa6 }}
+
+#endif // nsMsgSMIMECID_h__
diff --git a/mailnews/extensions/smime/src/nsSMimeJSHelper.cpp b/mailnews/extensions/smime/src/nsSMimeJSHelper.cpp
new file mode 100644
index 000000000..c392980b6
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsSMimeJSHelper.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "nspr.h"
+#include "nsSMimeJSHelper.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsStringGlue.h"
+#include "nsIX509CertDB.h"
+#include "nsIX509CertValidity.h"
+#include "nsIServiceManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCRTGlue.h"
+
+using namespace mozilla::mailnews;
+
+NS_IMPL_ISUPPORTS(nsSMimeJSHelper, nsISMimeJSHelper)
+
+nsSMimeJSHelper::nsSMimeJSHelper()
+{
+}
+
+nsSMimeJSHelper::~nsSMimeJSHelper()
+{
+}
+
+NS_IMETHODIMP nsSMimeJSHelper::GetRecipientCertsInfo(
+ nsIMsgCompFields *compFields,
+ uint32_t *count,
+ char16_t ***emailAddresses,
+ int32_t **certVerification,
+ char16_t ***certIssuedInfos,
+ char16_t ***certExpiresInfos,
+ nsIX509Cert ***certs,
+ bool *canEncrypt)
+{
+ NS_ENSURE_ARG_POINTER(count);
+ *count = 0;
+
+ NS_ENSURE_ARG_POINTER(emailAddresses);
+ NS_ENSURE_ARG_POINTER(certVerification);
+ NS_ENSURE_ARG_POINTER(certIssuedInfos);
+ NS_ENSURE_ARG_POINTER(certExpiresInfos);
+ NS_ENSURE_ARG_POINTER(certs);
+ NS_ENSURE_ARG_POINTER(canEncrypt);
+
+ NS_ENSURE_ARG_POINTER(compFields);
+
+ nsTArray<nsCString> mailboxes;
+ nsresult rv = getMailboxList(compFields, mailboxes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t mailbox_count = mailboxes.Length();
+
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+
+ *count = mailbox_count;
+ *canEncrypt = false;
+ rv = NS_OK;
+
+ if (mailbox_count)
+ {
+ char16_t **outEA = static_cast<char16_t **>(moz_xmalloc(mailbox_count * sizeof(char16_t *)));
+ int32_t *outCV = static_cast<int32_t *>(moz_xmalloc(mailbox_count * sizeof(int32_t)));
+ char16_t **outCII = static_cast<char16_t **>(moz_xmalloc(mailbox_count * sizeof(char16_t *)));
+ char16_t **outCEI = static_cast<char16_t **>(moz_xmalloc(mailbox_count * sizeof(char16_t *)));
+ nsIX509Cert **outCerts = static_cast<nsIX509Cert **>(moz_xmalloc(mailbox_count * sizeof(nsIX509Cert *)));
+
+ if (!outEA || !outCV || !outCII || !outCEI || !outCerts)
+ {
+ free(outEA);
+ free(outCV);
+ free(outCII);
+ free(outCEI);
+ free(outCerts);
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else
+ {
+ char16_t **iEA = outEA;
+ int32_t *iCV = outCV;
+ char16_t **iCII = outCII;
+ char16_t **iCEI = outCEI;
+ nsIX509Cert **iCert = outCerts;
+
+ bool found_blocker = false;
+ bool memory_failure = false;
+
+ for (uint32_t i = 0;
+ i < mailbox_count;
+ ++i, ++iEA, ++iCV, ++iCII, ++iCEI, ++iCert)
+ {
+ *iCert = nullptr;
+ *iCV = 0;
+ *iCII = nullptr;
+ *iCEI = nullptr;
+
+ if (memory_failure) {
+ *iEA = nullptr;
+ continue;
+ }
+
+ nsCString &email = mailboxes[i];
+ *iEA = ToNewUnicode(NS_ConvertUTF8toUTF16(email));
+ if (!*iEA) {
+ memory_failure = true;
+ continue;
+ }
+
+ nsCString email_lowercase;
+ ToLowerCase(email, email_lowercase);
+
+ nsCOMPtr<nsIX509Cert> cert;
+ if (NS_SUCCEEDED(certdb->FindCertByEmailAddress(
+ email_lowercase.get(), getter_AddRefs(cert))))
+ {
+ *iCert = cert;
+ NS_ADDREF(*iCert);
+
+ nsCOMPtr<nsIX509CertValidity> validity;
+ rv = cert->GetValidity(getter_AddRefs(validity));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsString id, ed;
+
+ if (NS_SUCCEEDED(validity->GetNotBeforeLocalDay(id)))
+ {
+ *iCII = ToNewUnicode(id);
+ if (!*iCII) {
+ memory_failure = true;
+ continue;
+ }
+ }
+
+ if (NS_SUCCEEDED(validity->GetNotAfterLocalDay(ed)))
+ {
+ *iCEI = ToNewUnicode(ed);
+ if (!*iCEI) {
+ memory_failure = true;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ found_blocker = true;
+ }
+ }
+
+ if (memory_failure) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mailbox_count, outEA);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mailbox_count, outCII);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mailbox_count, outCEI);
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(mailbox_count, outCerts);
+ free(outCV);
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else {
+ if (mailbox_count > 0 && !found_blocker)
+ {
+ *canEncrypt = true;
+ }
+
+ *emailAddresses = outEA;
+ *certVerification = outCV;
+ *certIssuedInfos = outCII;
+ *certExpiresInfos = outCEI;
+ *certs = outCerts;
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsSMimeJSHelper::GetNoCertAddresses(
+ nsIMsgCompFields *compFields,
+ uint32_t *count,
+ char16_t ***emailAddresses)
+{
+ NS_ENSURE_ARG_POINTER(count);
+ *count = 0;
+
+ NS_ENSURE_ARG_POINTER(emailAddresses);
+
+ NS_ENSURE_ARG_POINTER(compFields);
+
+ nsTArray<nsCString> mailboxes;
+ nsresult rv = getMailboxList(compFields, mailboxes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t mailbox_count = mailboxes.Length();
+
+ if (!mailbox_count)
+ {
+ *count = 0;
+ *emailAddresses = nullptr;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+
+ uint32_t missing_count = 0;
+ bool *haveCert = new bool[mailbox_count];
+ if (!haveCert)
+ {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = NS_OK;
+
+ if (mailbox_count)
+ {
+ for (uint32_t i = 0; i < mailbox_count; ++i)
+ {
+ haveCert[i] = false;
+
+ nsCString email_lowercase;
+ ToLowerCase(mailboxes[i], email_lowercase);
+
+ nsCOMPtr<nsIX509Cert> cert;
+ if (NS_SUCCEEDED(certdb->FindCertByEmailAddress(
+ email_lowercase.get(), getter_AddRefs(cert))))
+ haveCert[i] = true;
+
+ if (!haveCert[i])
+ ++missing_count;
+ }
+ }
+
+ *count = missing_count;
+
+ if (missing_count)
+ {
+ char16_t **outEA = static_cast<char16_t **>(moz_xmalloc(missing_count * sizeof(char16_t *)));
+ if (!outEA )
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else
+ {
+ char16_t **iEA = outEA;
+
+ bool memory_failure = false;
+
+ for (uint32_t i = 0; i < mailbox_count; ++i)
+ {
+ if (!haveCert[i])
+ {
+ if (memory_failure) {
+ *iEA = nullptr;
+ }
+ else {
+ *iEA = ToNewUnicode(NS_ConvertUTF8toUTF16(mailboxes[i]));
+ if (!*iEA) {
+ memory_failure = true;
+ }
+ }
+ ++iEA;
+ }
+ }
+
+ if (memory_failure) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(missing_count, outEA);
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ else {
+ *emailAddresses = outEA;
+ }
+ }
+ }
+ else
+ {
+ *emailAddresses = nullptr;
+ }
+
+ delete [] haveCert;
+ return rv;
+}
+
+nsresult nsSMimeJSHelper::getMailboxList(nsIMsgCompFields *compFields,
+ nsTArray<nsCString> &mailboxes)
+{
+ if (!compFields)
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult res;
+ nsString to, cc, bcc, ng;
+
+ res = compFields->GetTo(to);
+ if (NS_FAILED(res))
+ return res;
+
+ res = compFields->GetCc(cc);
+ if (NS_FAILED(res))
+ return res;
+
+ res = compFields->GetBcc(bcc);
+ if (NS_FAILED(res))
+ return res;
+
+ res = compFields->GetNewsgroups(ng);
+ if (NS_FAILED(res))
+ return res;
+
+ {
+ nsCString all_recipients;
+
+ if (!to.IsEmpty()) {
+ all_recipients.Append(NS_ConvertUTF16toUTF8(to));
+ all_recipients.Append(',');
+ }
+
+ if (!cc.IsEmpty()) {
+ all_recipients.Append(NS_ConvertUTF16toUTF8(cc));
+ all_recipients.Append(',');
+ }
+
+ if (!bcc.IsEmpty()) {
+ all_recipients.Append(NS_ConvertUTF16toUTF8(bcc));
+ all_recipients.Append(',');
+ }
+
+ if (!ng.IsEmpty())
+ all_recipients.Append(NS_ConvertUTF16toUTF8(ng));
+
+ ExtractEmails(EncodedHeader(all_recipients),
+ UTF16ArrayAdapter<>(mailboxes));
+ }
+
+ return NS_OK;
+}
diff --git a/mailnews/extensions/smime/src/nsSMimeJSHelper.h b/mailnews/extensions/smime/src/nsSMimeJSHelper.h
new file mode 100644
index 000000000..403ab2098
--- /dev/null
+++ b/mailnews/extensions/smime/src/nsSMimeJSHelper.h
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#ifndef _nsSMimeJSHelper_H_
+#define _nsSMimeJSHelper_H_
+
+#include "nsISMimeJSHelper.h"
+#include "nsIX509Cert.h"
+#include "nsIMsgCompFields.h"
+
+class nsSMimeJSHelper : public nsISMimeJSHelper
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISMIMEJSHELPER
+
+ nsSMimeJSHelper();
+
+private:
+ virtual ~nsSMimeJSHelper();
+ nsresult getMailboxList(nsIMsgCompFields *compFields,
+ nsTArray<nsCString> &mailboxes);
+};
+
+#endif
diff --git a/mailnews/extensions/smime/src/smime-service.js b/mailnews/extensions/smime/src/smime-service.js
new file mode 100644
index 000000000..9fa618fd6
--- /dev/null
+++ b/mailnews/extensions/smime/src/smime-service.js
@@ -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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function SMIMEService() {}
+
+SMIMEService.prototype = {
+ name: "smime",
+ chromePackageName: "messenger",
+ showPanel: function(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: XPCOMUtils.generateQI([Components.interfaces.nsIMsgAccountManagerExtension]),
+ classID: Components.ID("{f2809796-1dd1-11b2-8c1b-8f15f007c699}"),
+};
+
+var components = [SMIMEService];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/mailnews/extensions/smime/src/smime-service.manifest b/mailnews/extensions/smime/src/smime-service.manifest
new file mode 100644
index 000000000..1b799ed7b
--- /dev/null
+++ b/mailnews/extensions/smime/src/smime-service.manifest
@@ -0,0 +1,3 @@
+component {f2809796-1dd1-11b2-8c1b-8f15f007c699} smime-service.js
+contract @mozilla.org/accountmanager/extension;1?name=smime {f2809796-1dd1-11b2-8c1b-8f15f007c699}
+category mailnews-accountmanager-extensions smime-account-manager-extension @mozilla.org/accountmanager/extension;1?name=smime