diff options
Diffstat (limited to 'mailnews/extensions/smime/content')
19 files changed, 2542 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); + + |