summaryrefslogtreecommitdiffstats
path: root/security/manager/pki/resources
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /security/manager/pki/resources
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'security/manager/pki/resources')
-rw-r--r--security/manager/pki/resources/content/CAOverlay.xul55
-rw-r--r--security/manager/pki/resources/content/MineOverlay.xul64
-rw-r--r--security/manager/pki/resources/content/OrphanOverlay.xul47
-rw-r--r--security/manager/pki/resources/content/OthersOverlay.xul54
-rw-r--r--security/manager/pki/resources/content/WebSitesOverlay.xul57
-rw-r--r--security/manager/pki/resources/content/certDump.xul44
-rw-r--r--security/manager/pki/resources/content/certManager.js542
-rw-r--r--security/manager/pki/resources/content/certManager.xul51
-rw-r--r--security/manager/pki/resources/content/certViewer.js354
-rw-r--r--security/manager/pki/resources/content/certViewer.xul40
-rw-r--r--security/manager/pki/resources/content/changepassword.js270
-rw-r--r--security/manager/pki/resources/content/changepassword.xul78
-rw-r--r--security/manager/pki/resources/content/choosetoken.js44
-rw-r--r--security/manager/pki/resources/content/choosetoken.xul34
-rw-r--r--security/manager/pki/resources/content/clientauthask.js158
-rw-r--r--security/manager/pki/resources/content/clientauthask.xul49
-rw-r--r--security/manager/pki/resources/content/createCertInfo.js39
-rw-r--r--security/manager/pki/resources/content/createCertInfo.xul30
-rw-r--r--security/manager/pki/resources/content/deletecert.js134
-rw-r--r--security/manager/pki/resources/content/deletecert.xul28
-rw-r--r--security/manager/pki/resources/content/device_manager.js509
-rw-r--r--security/manager/pki/resources/content/device_manager.xul90
-rw-r--r--security/manager/pki/resources/content/downloadcert.js92
-rw-r--r--security/manager/pki/resources/content/downloadcert.xul69
-rw-r--r--security/manager/pki/resources/content/editcacert.js58
-rw-r--r--security/manager/pki/resources/content/editcacert.xul36
-rw-r--r--security/manager/pki/resources/content/exceptionDialog.js368
-rw-r--r--security/manager/pki/resources/content/exceptionDialog.xul89
-rw-r--r--security/manager/pki/resources/content/load_device.xul42
-rw-r--r--security/manager/pki/resources/content/pippki.js191
-rw-r--r--security/manager/pki/resources/content/protectedAuth.js42
-rw-r--r--security/manager/pki/resources/content/protectedAuth.xul33
-rw-r--r--security/manager/pki/resources/content/resetpassword.js47
-rw-r--r--security/manager/pki/resources/content/resetpassword.xul35
-rw-r--r--security/manager/pki/resources/content/setp12password.js128
-rw-r--r--security/manager/pki/resources/content/setp12password.xul51
-rw-r--r--security/manager/pki/resources/content/viewCertDetails.xul104
-rw-r--r--security/manager/pki/resources/jar.mn43
-rw-r--r--security/manager/pki/resources/moz.build7
39 files changed, 4206 insertions, 0 deletions
diff --git a/security/manager/pki/resources/content/CAOverlay.xul b/security/manager/pki/resources/content/CAOverlay.xul
new file mode 100644
index 000000000..ba4f4abf8
--- /dev/null
+++ b/security/manager/pki/resources/content/CAOverlay.xul
@@ -0,0 +1,55 @@
+<?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 overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="CAOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="CACerts">
+ <description>&certmgr.cas;</description>
+ <separator class="thin"/>
+ <tree id="ca-tree" flex="1" enableColumnDrag="true"
+ onselect="ca_enableButtons()">
+ <treecols>
+ <treecol id="certcol" label="&certmgr.certname;" primary="true"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="tokencol" label="&certmgr.tokenname;"
+ persist="hidden width ordinal" flex="1"/>
+ </treecols>
+ <treechildren ondblclick="viewCerts();"/>
+ </tree>
+
+ <separator class="thin"/>
+
+ <hbox>
+ <button id="ca_viewButton"
+ label="&certmgr.view2.label;"
+ accesskey="&certmgr.view2.accesskey;"
+ disabled="true" oncommand="viewCerts();"/>
+ <button id="ca_editButton"
+ label="&certmgr.edit3.label;"
+ accesskey="&certmgr.edit3.accesskey;"
+ disabled="true" oncommand="editCerts();"/>
+ <button id="ca_addButton"
+ label="&certmgr.restore2.label;"
+ accesskey="&certmgr.restore2.accesskey;"
+ oncommand="addCACerts();"/>
+ <button id="ca_exportButton"
+ label="&certmgr.export.label;"
+ accesskey="&certmgr.export.accesskey;"
+ disabled="true" oncommand="exportCerts();"/>
+ <button id="ca_deleteButton"
+ label="&certmgr.delete_builtin.label;"
+ accesskey="&certmgr.delete_builtin.accesskey;"
+ disabled="true" oncommand="deleteCerts();"/>
+ </hbox>
+ </vbox>
+</overlay>
diff --git a/security/manager/pki/resources/content/MineOverlay.xul b/security/manager/pki/resources/content/MineOverlay.xul
new file mode 100644
index 000000000..57526ac0f
--- /dev/null
+++ b/security/manager/pki/resources/content/MineOverlay.xul
@@ -0,0 +1,64 @@
+<?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 overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="MineOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="myCerts">
+ <description>&certmgr.mine;</description>
+ <separator class="thin"/>
+ <tree id="user-tree" flex="1" enableColumnDrag="true"
+ onselect="mine_enableButtons()">
+ <treecols>
+ <treecol id="certcol" label="&certmgr.certname;" primary="true"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="tokencol" label="&certmgr.tokenname;"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="serialnumcol" label="&certmgr.serial;"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="issuedcol" label="&certmgr.begins;"
+ hidden="true" persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="expiredcol" label="&certmgr.expires;"
+ persist="hidden width ordinal" flex="1"/>
+ </treecols>
+ <treechildren ondblclick="viewCerts();"/>
+ </tree>
+
+ <separator class="thin"/>
+
+ <hbox>
+ <button id="mine_viewButton" class="normal"
+ label="&certmgr.view2.label;"
+ accesskey="&certmgr.view2.accesskey;"
+ disabled="true" oncommand="viewCerts();"/>
+ <button id="mine_backupButton" class="normal"
+ label="&certmgr.backup2.label;"
+ accesskey="&certmgr.backup2.accesskey;"
+ disabled="true" oncommand="backupCerts();"/>
+ <button id="mine_backupAllButton" class="normal"
+ label="&certmgr.backupall2.label;"
+ accesskey="&certmgr.backupall2.accesskey;"
+ oncommand="backupAllCerts();"/>
+ <button id="mine_restoreButton" class="normal"
+ label="&certmgr.restore2.label;"
+ accesskey="&certmgr.restore2.accesskey;"
+ oncommand="restoreCerts();"/>
+ <button id="mine_deleteButton" class="normal"
+ label="&certmgr.delete2.label;"
+ accesskey="&certmgr.delete2.accesskey;"
+ disabled="true" oncommand="deleteCerts();"/>
+ </hbox>
+ </vbox>
+</overlay>
diff --git a/security/manager/pki/resources/content/OrphanOverlay.xul b/security/manager/pki/resources/content/OrphanOverlay.xul
new file mode 100644
index 000000000..a81ca9424
--- /dev/null
+++ b/security/manager/pki/resources/content/OrphanOverlay.xul
@@ -0,0 +1,47 @@
+<?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 overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="OrphanOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="OrphanCerts">
+ <description>&certmgr.orphans;</description>
+ <separator class="thin"/>
+ <tree id="orphan-tree" flex="1" enableColumnDrag="true"
+ onselect="orphan_enableButtons()">
+ <treecols>
+ <treecol id="certcol" label="&certmgr.certname;" primary="true"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="tokencol" label="&certmgr.tokenname;"
+ persist="hidden width ordinal" flex="1"/>
+ </treecols>
+ <treechildren ondblclick="viewCerts();"/>
+ </tree>
+
+ <separator class="thin"/>
+
+ <hbox>
+ <button id="orphan_viewButton" class="normal"
+ label="&certmgr.view2.label;"
+ accesskey="&certmgr.view2.accesskey;"
+ disabled="true" oncommand="viewCerts();"/>
+ <button id="orphan_exportButton" class="normal"
+ label="&certmgr.export.label;"
+ accesskey="&certmgr.export.accesskey;"
+ disabled="true" oncommand="exportCerts();"/>
+ <button id="orphan_deleteButton" class="normal"
+ label="&certmgr.delete2.label;"
+ accesskey="&certmgr.delete2.accesskey;"
+ disabled="true" oncommand="deleteCerts();"/>
+ </hbox>
+ </vbox>
+</overlay>
diff --git a/security/manager/pki/resources/content/OthersOverlay.xul b/security/manager/pki/resources/content/OthersOverlay.xul
new file mode 100644
index 000000000..0f5154277
--- /dev/null
+++ b/security/manager/pki/resources/content/OthersOverlay.xul
@@ -0,0 +1,54 @@
+<?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 overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="OthersOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="othersCerts">
+ <description>&certmgr.others;</description>
+ <separator class="thin"/>
+ <tree id="email-tree" flex="1"
+ onselect="email_enableButtons()">
+ <treecols>
+ <treecol id="certcol" label="&certmgr.certname;" primary="true"
+ flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="emailcol" label="&certmgr.email;"
+ flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="expiredcol" label="&certmgr.expires;"
+ flex="1"/>
+ </treecols>
+ <treechildren flex="1" ondblclick="viewCerts();"/>
+ </tree>
+
+ <separator class="thin"/>
+
+ <hbox>
+ <button id="email_viewButton"
+ label="&certmgr.view2.label;"
+ accesskey="&certmgr.view2.accesskey;"
+ disabled="true" oncommand="viewCerts();"/>
+ <button id="email_addButton"
+ label="&certmgr.restore2.label;"
+ accesskey="&certmgr.restore2.accesskey;"
+ oncommand="addEmailCert();"/>
+ <button id="email_exportButton"
+ label="&certmgr.export.label;"
+ accesskey="&certmgr.export.accesskey;"
+ disabled="true" oncommand="exportCerts();"/>
+ <button id="email_deleteButton"
+ label="&certmgr.delete2.label;"
+ accesskey="&certmgr.delete2.accesskey;"
+ disabled="true" oncommand="deleteCerts();"/>
+ </hbox>
+ </vbox>
+</overlay>
diff --git a/security/manager/pki/resources/content/WebSitesOverlay.xul b/security/manager/pki/resources/content/WebSitesOverlay.xul
new file mode 100644
index 000000000..e0f3ae788
--- /dev/null
+++ b/security/manager/pki/resources/content/WebSitesOverlay.xul
@@ -0,0 +1,57 @@
+<?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 overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="WebSitesOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="webCerts">
+ <description>&certmgr.websites2;</description>
+ <separator class="thin"/>
+ <tree id="server-tree" flex="1" enableColumnDrag="true"
+ onselect="websites_enableButtons()">
+ <treecols>
+ <treecol id="certcol" label="&certmgr.certname;" primary="true"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="sitecol" label="&certmgr.certserver;"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="lifetimecol" label="&certmgr.override_lifetime;"
+ persist="hidden width ordinal" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="expiredcol" label="&certmgr.expires;"
+ persist="hidden width ordinal" flex="1"/>
+ </treecols>
+ <treechildren ondblclick="viewCerts();"/>
+ </tree>
+
+ <separator class="thin"/>
+
+ <hbox>
+ <button id="websites_viewButton"
+ label="&certmgr.view2.label;"
+ accesskey="&certmgr.view2.accesskey;"
+ disabled="true" oncommand="viewCerts();"/>
+ <button id="websites_exportButton"
+ label="&certmgr.export.label;"
+ accesskey="&certmgr.export.accesskey;"
+ disabled="true" oncommand="exportCerts();"/>
+ <button id="websites_deleteButton"
+ label="&certmgr.delete2.label;"
+ accesskey="&certmgr.delete2.accesskey;"
+ disabled="true" oncommand="deleteCerts();"/>
+ <button id="websites_exceptionButton"
+ label="&certmgr.addException.label;"
+ accesskey="&certmgr.addException.accesskey;"
+ oncommand="addException();"/>
+ </hbox>
+ </vbox>
+</overlay>
diff --git a/security/manager/pki/resources/content/certDump.xul b/security/manager/pki/resources/content/certDump.xul
new file mode 100644
index 000000000..74d9b3f8b
--- /dev/null
+++ b/security/manager/pki/resources/content/certDump.xul
@@ -0,0 +1,44 @@
+<?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/. -->
+
+<!DOCTYPE overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="certDumpOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<vbox class="box-padded" id="certPrettyPrint" flex="1">
+ <label class="header" value="&certmgr.hierarchy.label;"
+ control="treesetDump" accesskey="&certmgr.hierarchy.accesskey2;"/>
+ <tree id="treesetDump" onselect="updateCertDump();" flex="1"
+ hidecolumnpicker="true" style="height: 8em;">
+ <treecols>
+ <treecol id="dumpCol" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ </tree>
+
+ <label class="header" value="&certmgr.details.label;"
+ control="prettyDumpTree" accesskey="&certmgr.details.accesskey;"/>
+ <tree id="prettyDumpTree" style="height: 15em" treelines="true" flex="1"
+ onselect="displaySelected();" hidecolumnpicker="true">
+ <treecols>
+ <treecol flex="1" id="certDataCol" primary="true" hideheader="true"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <label class="header" value="&certmgr.fields.label;"
+ control="certDumpVal" accesskey="&certmgr.fields.accesskey;"/>
+ <textbox id="certDumpVal" multiline="true" flex="1"
+ readonly="true" style="height: 11em; font-family: -moz-fixed;"/>
+
+ <separator class="thin"/>
+ <hbox>
+ <button id="export_cert" class="normal" label="&certmgr.export.label;"
+ accesskey="&certmgr.export.accesskey;"
+ oncommand="exportToFile(window, getCurrentCert());"/>
+ </hbox>
+</vbox>
+</overlay>
diff --git a/security/manager/pki/resources/content/certManager.js b/security/manager/pki/resources/content/certManager.js
new file mode 100644
index 000000000..c52477442
--- /dev/null
+++ b/security/manager/pki/resources/content/certManager.js
@@ -0,0 +1,542 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+const nsIFilePicker = Components.interfaces.nsIFilePicker;
+const nsFilePicker = "@mozilla.org/filepicker;1";
+const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
+const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
+const nsIX509Cert = Components.interfaces.nsIX509Cert;
+const nsICertTree = Components.interfaces.nsICertTree;
+const nsCertTree = "@mozilla.org/security/nsCertTree;1";
+
+const gCertFileTypes = "*.p7b; *.crt; *.cert; *.cer; *.pem; *.der";
+
+var { NetUtil } = Components.utils.import("resource://gre/modules/NetUtil.jsm", {});
+var { Services } = Components.utils.import("resource://gre/modules/Services.jsm", {});
+
+var key;
+
+/**
+ * List of certs currently selected in the active tab.
+ * @type nsIX509Cert[]
+ */
+var selected_certs = [];
+var selected_tree_items = [];
+var selected_index = [];
+var certdb;
+
+/**
+ * Cert tree for the "Authorities" tab.
+ * @type nsICertTree
+ */
+var caTreeView;
+/**
+ * Cert tree for the "Servers" tab.
+ * @type nsICertTree
+ */
+var serverTreeView;
+/**
+ * Cert tree for the "People" tab.
+ * @type nsICertTree
+ */
+var emailTreeView;
+/**
+ * Cert tree for the "Your Certificates" tab.
+ * @type nsICertTree
+ */
+var userTreeView;
+/**
+ * Cert tree for the "Other" tab.
+ * @type nsICertTree
+ */
+var orphanTreeView;
+
+var smartCardObserver = {
+ observe: function() {
+ onSmartCardChange();
+ }
+};
+
+function DeregisterSmartCardObservers()
+{
+ Services.obs.removeObserver(smartCardObserver, "smartcard-insert");
+ Services.obs.removeObserver(smartCardObserver, "smartcard-remove");
+}
+
+function LoadCerts()
+{
+ Services.obs.addObserver(smartCardObserver, "smartcard-insert", false);
+ Services.obs.addObserver(smartCardObserver, "smartcard-remove", false);
+
+ certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
+ var certcache = certdb.getCerts();
+
+ caTreeView = Components.classes[nsCertTree]
+ .createInstance(nsICertTree);
+ caTreeView.loadCertsFromCache(certcache, nsIX509Cert.CA_CERT);
+ document.getElementById('ca-tree').view = caTreeView;
+
+ serverTreeView = Components.classes[nsCertTree]
+ .createInstance(nsICertTree);
+ serverTreeView.loadCertsFromCache(certcache, nsIX509Cert.SERVER_CERT);
+ document.getElementById('server-tree').view = serverTreeView;
+
+ emailTreeView = Components.classes[nsCertTree]
+ .createInstance(nsICertTree);
+ emailTreeView.loadCertsFromCache(certcache, nsIX509Cert.EMAIL_CERT);
+ document.getElementById('email-tree').view = emailTreeView;
+
+ userTreeView = Components.classes[nsCertTree]
+ .createInstance(nsICertTree);
+ userTreeView.loadCertsFromCache(certcache, nsIX509Cert.USER_CERT);
+ document.getElementById('user-tree').view = userTreeView;
+
+ orphanTreeView = Components.classes[nsCertTree]
+ .createInstance(nsICertTree);
+ orphanTreeView.loadCertsFromCache(certcache, nsIX509Cert.UNKNOWN_CERT);
+ document.getElementById('orphan-tree').view = orphanTreeView;
+
+ enableBackupAllButton();
+}
+
+function enableBackupAllButton()
+{
+ let backupAllButton = document.getElementById("mine_backupAllButton");
+ backupAllButton.disabled = userTreeView.rowCount < 1;
+}
+
+function getSelectedCerts()
+{
+ var ca_tab = document.getElementById("ca_tab");
+ var mine_tab = document.getElementById("mine_tab");
+ var others_tab = document.getElementById("others_tab");
+ var websites_tab = document.getElementById("websites_tab");
+ var orphan_tab = document.getElementById("orphan_tab");
+ var items = null;
+ if (ca_tab.selected) {
+ items = caTreeView.selection;
+ } else if (mine_tab.selected) {
+ items = userTreeView.selection;
+ } else if (others_tab.selected) {
+ items = emailTreeView.selection;
+ } else if (websites_tab.selected) {
+ items = serverTreeView.selection;
+ } else if (orphan_tab.selected) {
+ items = orphanTreeView.selection;
+ }
+ selected_certs = [];
+ var cert = null;
+ var nr = 0;
+ if (items != null) nr = items.getRangeCount();
+ if (nr > 0) {
+ for (let i = 0; i < nr; i++) {
+ var o1 = {};
+ var o2 = {};
+ items.getRangeAt(i, o1, o2);
+ var min = o1.value;
+ var max = o2.value;
+ for (let j = min; j <= max; j++) {
+ if (ca_tab.selected) {
+ cert = caTreeView.getCert(j);
+ } else if (mine_tab.selected) {
+ cert = userTreeView.getCert(j);
+ } else if (others_tab.selected) {
+ cert = emailTreeView.getCert(j);
+ } else if (websites_tab.selected) {
+ cert = serverTreeView.getCert(j);
+ } else if (orphan_tab.selected) {
+ cert = orphanTreeView.getCert(j);
+ }
+ if (cert) {
+ var sc = selected_certs.length;
+ selected_certs[sc] = cert;
+ selected_index[sc] = j;
+ }
+ }
+ }
+ }
+}
+
+function getSelectedTreeItems()
+{
+ var ca_tab = document.getElementById("ca_tab");
+ var mine_tab = document.getElementById("mine_tab");
+ var others_tab = document.getElementById("others_tab");
+ var websites_tab = document.getElementById("websites_tab");
+ var orphan_tab = document.getElementById("orphan_tab");
+ var items = null;
+ if (ca_tab.selected) {
+ items = caTreeView.selection;
+ } else if (mine_tab.selected) {
+ items = userTreeView.selection;
+ } else if (others_tab.selected) {
+ items = emailTreeView.selection;
+ } else if (websites_tab.selected) {
+ items = serverTreeView.selection;
+ } else if (orphan_tab.selected) {
+ items = orphanTreeView.selection;
+ }
+ selected_certs = [];
+ selected_tree_items = [];
+ selected_index = [];
+ var tree_item = null;
+ var nr = 0;
+ if (items != null) nr = items.getRangeCount();
+ if (nr > 0) {
+ for (let i = 0; i < nr; i++) {
+ var o1 = {};
+ var o2 = {};
+ items.getRangeAt(i, o1, o2);
+ var min = o1.value;
+ var max = o2.value;
+ for (let j = min; j <= max; j++) {
+ if (ca_tab.selected) {
+ tree_item = caTreeView.getTreeItem(j);
+ } else if (mine_tab.selected) {
+ tree_item = userTreeView.getTreeItem(j);
+ } else if (others_tab.selected) {
+ tree_item = emailTreeView.getTreeItem(j);
+ } else if (websites_tab.selected) {
+ tree_item = serverTreeView.getTreeItem(j);
+ } else if (orphan_tab.selected) {
+ tree_item = orphanTreeView.getTreeItem(j);
+ }
+ if (tree_item) {
+ var sc = selected_tree_items.length;
+ selected_tree_items[sc] = tree_item;
+ selected_index[sc] = j;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Returns true if nothing in the given cert tree is selected or if the
+ * selection includes a container. Returns false otherwise.
+ *
+ * @param {nsICertTree} certTree
+ * @returns {Boolean}
+ */
+function nothingOrContainerSelected(certTree)
+{
+ var certTreeSelection = certTree.selection;
+ var numSelectionRanges = certTreeSelection.getRangeCount();
+
+ if (numSelectionRanges == 0) {
+ return true;
+ }
+
+ for (var i = 0; i < numSelectionRanges; i++) {
+ var o1 = {};
+ var o2 = {};
+ certTreeSelection.getRangeAt(i, o1, o2);
+ var minIndex = o1.value;
+ var maxIndex = o2.value;
+ for (var j = minIndex; j <= maxIndex; j++) {
+ if (certTree.isContainer(j)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Enables or disables buttons corresponding to a cert tree depending on what
+ * is selected in the cert tree.
+ *
+ * @param {nsICertTree} certTree
+ * @param {Array} idList A list of string identifiers for button elements to
+ * enable or disable.
+ */
+function enableButtonsForCertTree(certTree, idList)
+{
+ let disableButtons = nothingOrContainerSelected(certTree);
+
+ for (let id of idList) {
+ document.getElementById(id).setAttribute("disabled", disableButtons);
+ }
+}
+
+function ca_enableButtons()
+{
+ let idList = [
+ "ca_viewButton",
+ "ca_editButton",
+ "ca_exportButton",
+ "ca_deleteButton",
+ ];
+ enableButtonsForCertTree(caTreeView, idList);
+}
+
+function mine_enableButtons()
+{
+ let idList = [
+ "mine_viewButton",
+ "mine_backupButton",
+ "mine_deleteButton",
+ ];
+ enableButtonsForCertTree(userTreeView, idList);
+}
+
+function websites_enableButtons()
+{
+ let idList = [
+ "websites_viewButton",
+ "websites_exportButton",
+ "websites_deleteButton",
+ ];
+ enableButtonsForCertTree(serverTreeView, idList);
+}
+
+function email_enableButtons()
+{
+ let idList = [
+ "email_viewButton",
+ "email_exportButton",
+ "email_deleteButton",
+ ];
+ enableButtonsForCertTree(emailTreeView, idList);
+}
+
+function orphan_enableButtons()
+{
+ let idList = [
+ "orphan_viewButton",
+ "orphan_exportButton",
+ "orphan_deleteButton",
+ ];
+ enableButtonsForCertTree(orphanTreeView, idList);
+}
+
+function backupCerts()
+{
+ getSelectedCerts();
+ var numcerts = selected_certs.length;
+ if (numcerts == 0) {
+ return;
+ }
+
+ var bundle = document.getElementById("pippki_bundle");
+ var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker);
+ fp.init(window,
+ bundle.getString("chooseP12BackupFileDialog"),
+ nsIFilePicker.modeSave);
+ fp.appendFilter(bundle.getString("file_browse_PKCS12_spec"),
+ "*.p12");
+ fp.appendFilters(nsIFilePicker.filterAll);
+ var rv = fp.show();
+ if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
+ certdb.exportPKCS12File(null, fp.file, selected_certs.length,
+ selected_certs);
+ }
+}
+
+function backupAllCerts()
+{
+ // Select all rows, then call doBackup()
+ var items = userTreeView.selection.selectAll();
+ backupCerts();
+}
+
+function editCerts()
+{
+ getSelectedCerts();
+
+ for (let cert of selected_certs) {
+ window.openDialog("chrome://pippki/content/editcacert.xul", "",
+ "chrome,centerscreen,modal", cert);
+ }
+}
+
+function restoreCerts()
+{
+ var bundle = document.getElementById("pippki_bundle");
+ var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker);
+ fp.init(window,
+ bundle.getString("chooseP12RestoreFileDialog2"),
+ nsIFilePicker.modeOpen);
+ fp.appendFilter(bundle.getString("file_browse_PKCS12_spec"),
+ "*.p12; *.pfx");
+ fp.appendFilter(bundle.getString("file_browse_Certificate_spec"),
+ gCertFileTypes);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ if (fp.show() == nsIFilePicker.returnOK) {
+ // If this is an X509 user certificate, import it as one.
+
+ var isX509FileType = false;
+ var fileTypesList = gCertFileTypes.slice(1).split('; *');
+ for (var type of fileTypesList) {
+ if (fp.file.path.endsWith(type)) {
+ isX509FileType = true;
+ break;
+ }
+ }
+
+ if (isX509FileType) {
+ let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fstream.init(fp.file, -1, 0, 0);
+ let dataString = NetUtil.readInputStreamToString(fstream, fstream.available());
+ let dataArray = [];
+ for (let i = 0; i < dataString.length; i++) {
+ dataArray.push(dataString.charCodeAt(i));
+ }
+ fstream.close();
+ let prompter = Services.ww.getNewPrompter(window);
+ let interfaceRequestor = {
+ getInterface: function() {
+ return prompter;
+ }
+ };
+ certdb.importUserCertificate(dataArray, dataArray.length, interfaceRequestor);
+ } else {
+ // Otherwise, assume it's a PKCS12 file and import it that way.
+ certdb.importPKCS12File(null, fp.file);
+ }
+
+ var certcache = certdb.getCerts();
+ userTreeView.loadCertsFromCache(certcache, nsIX509Cert.USER_CERT);
+ userTreeView.selection.clearSelection();
+ caTreeView.loadCertsFromCache(certcache, nsIX509Cert.CA_CERT);
+ caTreeView.selection.clearSelection();
+ enableBackupAllButton();
+ }
+}
+
+function exportCerts()
+{
+ getSelectedCerts();
+
+ for (let cert of selected_certs) {
+ exportToFile(window, cert);
+ }
+}
+
+/**
+ * Deletes the selected certs in the active tab.
+ */
+function deleteCerts() {
+ getSelectedTreeItems();
+ let numcerts = selected_tree_items.length;
+ if (numcerts == 0) {
+ return;
+ }
+
+ const treeViewMap = {
+ "mine_tab": userTreeView,
+ "websites_tab": serverTreeView,
+ "ca_tab": caTreeView,
+ "others_tab": emailTreeView,
+ "orphan_tab": orphanTreeView,
+ };
+ let selTab = document.getElementById("certMgrTabbox").selectedItem;
+ let selTabID = selTab.getAttribute("id");
+
+ if (!(selTabID in treeViewMap)) {
+ return;
+ }
+
+ let retVals = {
+ deleteConfirmed: false,
+ };
+ window.openDialog("chrome://pippki/content/deletecert.xul", "",
+ "chrome,centerscreen,modal", selTabID, selected_tree_items,
+ retVals);
+
+ if (retVals.deleteConfirmed) {
+ let treeView = treeViewMap[selTabID];
+
+ for (let t = numcerts - 1; t >= 0; t--) {
+ treeView.deleteEntryObject(selected_index[t]);
+ }
+
+ selected_tree_items = [];
+ selected_index = [];
+ treeView.selection.clearSelection();
+ if (selTabID == "mine_tab") {
+ enableBackupAllButton();
+ }
+ }
+}
+
+function viewCerts()
+{
+ getSelectedCerts();
+
+ for (let cert of selected_certs) {
+ viewCertHelper(window, cert);
+ }
+}
+
+function addCACerts()
+{
+ var bundle = document.getElementById("pippki_bundle");
+ var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker);
+ fp.init(window,
+ bundle.getString("importCACertsPrompt"),
+ nsIFilePicker.modeOpen);
+ fp.appendFilter(bundle.getString("file_browse_Certificate_spec"),
+ gCertFileTypes);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ if (fp.show() == nsIFilePicker.returnOK) {
+ certdb.importCertsFromFile(fp.file, nsIX509Cert.CA_CERT);
+ caTreeView.loadCerts(nsIX509Cert.CA_CERT);
+ caTreeView.selection.clearSelection();
+ }
+}
+
+function onSmartCardChange()
+{
+ var certcache = certdb.getCerts();
+ // We've change the state of the smart cards inserted or removed
+ // that means the available certs may have changed. Update the display
+ userTreeView.loadCertsFromCache(certcache, nsIX509Cert.USER_CERT);
+ userTreeView.selection.clearSelection();
+ caTreeView.loadCertsFromCache(certcache, nsIX509Cert.CA_CERT);
+ caTreeView.selection.clearSelection();
+ serverTreeView.loadCertsFromCache(certcache, nsIX509Cert.SERVER_CERT);
+ serverTreeView.selection.clearSelection();
+ emailTreeView.loadCertsFromCache(certcache, nsIX509Cert.EMAIL_CERT);
+ emailTreeView.selection.clearSelection();
+ orphanTreeView.loadCertsFromCache(certcache, nsIX509Cert.UNKNOWN_CERT);
+ orphanTreeView.selection.clearSelection();
+}
+
+function addEmailCert()
+{
+ var bundle = document.getElementById("pippki_bundle");
+ var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker);
+ fp.init(window,
+ bundle.getString("importEmailCertPrompt"),
+ nsIFilePicker.modeOpen);
+ fp.appendFilter(bundle.getString("file_browse_Certificate_spec"),
+ gCertFileTypes);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ if (fp.show() == nsIFilePicker.returnOK) {
+ certdb.importCertsFromFile(fp.file, nsIX509Cert.EMAIL_CERT);
+ var certcache = certdb.getCerts();
+ emailTreeView.loadCertsFromCache(certcache, nsIX509Cert.EMAIL_CERT);
+ emailTreeView.selection.clearSelection();
+ caTreeView.loadCertsFromCache(certcache, nsIX509Cert.CA_CERT);
+ caTreeView.selection.clearSelection();
+ }
+}
+
+function addException()
+{
+ window.openDialog('chrome://pippki/content/exceptionDialog.xul', "",
+ 'chrome,centerscreen,modal');
+ var certcache = certdb.getCerts();
+ serverTreeView.loadCertsFromCache(certcache, nsIX509Cert.SERVER_CERT);
+ serverTreeView.selection.clearSelection();
+ orphanTreeView.loadCertsFromCache(certcache, nsIX509Cert.UNKNOWN_CERT);
+ orphanTreeView.selection.clearSelection();
+}
diff --git a/security/manager/pki/resources/content/certManager.xul b/security/manager/pki/resources/content/certManager.xul
new file mode 100644
index 000000000..3ea5862e4
--- /dev/null
+++ b/security/manager/pki/resources/content/certManager.xul
@@ -0,0 +1,51 @@
+<?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"?>
+
+<?xul-overlay href="chrome://pippki/content/MineOverlay.xul"?>
+<?xul-overlay href="chrome://pippki/content/OthersOverlay.xul"?>
+<?xul-overlay href="chrome://pippki/content/WebSitesOverlay.xul"?>
+<?xul-overlay href="chrome://pippki/content/CAOverlay.xul"?>
+<?xul-overlay href="chrome://pippki/content/OrphanOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<dialog id="certmanager"
+ windowtype="mozilla:certmanager"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&certmgr.title;"
+ onload="LoadCerts();"
+ onunload="DeregisterSmartCardObservers();"
+ buttons="accept"
+ style="width: 63em; height: 32em;"
+ persist="screenX screenY width height">
+
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+ <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+ <script type="application/javascript" src="chrome://pippki/content/certManager.js"/>
+
+ <vbox flex="1">
+ <tabbox id="certmanagertabs" flex="1" style="margin:5px" persist="selectedIndex">
+ <tabs id="certMgrTabbox">
+ <tab id="mine_tab" label="&certmgr.tab.mine;"/>
+ <tab id="others_tab" label="&certmgr.tab.others2;"/>
+ <tab id="websites_tab" label="&certmgr.tab.websites3;"/>
+ <tab id="ca_tab" label="&certmgr.tab.ca;" selected="true"/>
+ <tab id="orphan_tab" label="&certmgr.tab.orphan2;"/>
+ </tabs>
+ <tabpanels flex="1">
+ <vbox id="myCerts" flex="1"/>
+ <vbox id="othersCerts" flex="1"/>
+ <vbox id="webCerts" flex="1"/>
+ <vbox id="CACerts" flex="1"/>
+ <vbox id="OrphanCerts" flex="1"/>
+ </tabpanels>
+ </tabbox>
+
+ </vbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/certViewer.js b/security/manager/pki/resources/content/certViewer.js
new file mode 100644
index 000000000..c62507694
--- /dev/null
+++ b/security/manager/pki/resources/content/certViewer.js
@@ -0,0 +1,354 @@
+/* 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";
+
+/**
+ * @file Implements functionality for certViewer.xul and its tabs certDump.xul
+ * and viewCertDetails.xul: a dialog that allows various attributes of a
+ * certificate to be viewed.
+ * @argument {nsISupports} window.arguments[0]
+ * The cert to view, queryable to nsIX509Cert.
+ */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+const nsIX509Cert = Ci.nsIX509Cert;
+const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
+const nsIX509CertDB = Ci.nsIX509CertDB;
+const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
+const nsIPK11TokenDB = Ci.nsIPK11TokenDB;
+const nsIASN1Object = Ci.nsIASN1Object;
+const nsIASN1Sequence = Ci.nsIASN1Sequence;
+const nsIASN1PrintableItem = Ci.nsIASN1PrintableItem;
+const nsIASN1Tree = Ci.nsIASN1Tree;
+const nsASN1Tree = "@mozilla.org/security/nsASN1Tree;1";
+
+var bundle;
+
+function doPrompt(msg)
+{
+ let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService);
+ prompts.alert(window, null, msg);
+}
+
+/**
+ * Fills out the "Certificate Hierarchy" tree of the cert viewer "Details" tab.
+ *
+ * @param {tree} node
+ * Parent tree node to append to.
+ * @param {nsIArray<nsIX509Cert>} chain
+ * Chain where cert element n is issued by cert element n + 1.
+ */
+function AddCertChain(node, chain)
+{
+ var child = document.getElementById(node);
+ var currCert;
+ var displayVal;
+ for (let i = chain.length - 1; i >= 0; i--) {
+ currCert = chain.queryElementAt(i, nsIX509Cert);
+ if (currCert.commonName) {
+ displayVal = currCert.commonName;
+ } else {
+ displayVal = currCert.windowTitle;
+ }
+ let addTwistie = i != 0;
+ child = addChildrenToTree(child, displayVal, currCert.dbKey, addTwistie);
+ }
+}
+
+/**
+ * Adds a "verified usage" of a cert to the "General" tab of the cert viewer.
+ *
+ * @param {String} usage
+ * Verified usage to add.
+ */
+function AddUsage(usage)
+{
+ let verifyInfoBox = document.getElementById("verify_info_box");
+ let text = document.createElement("textbox");
+ text.setAttribute("value", usage);
+ text.setAttribute("style", "margin: 2px 5px");
+ text.setAttribute("readonly", "true");
+ text.setAttribute("class", "scrollfield");
+ verifyInfoBox.appendChild(text);
+}
+
+function setWindowName()
+{
+ bundle = document.getElementById("pippki_bundle");
+
+ let cert = window.arguments[0].QueryInterface(Ci.nsIX509Cert);
+ document.title = bundle.getFormattedString("certViewerTitle",
+ [cert.windowTitle]);
+
+ //
+ // Set the cert attributes for viewing
+ //
+
+ // The chain of trust
+ AddCertChain("treesetDump", cert.getChain());
+ DisplayGeneralDataFromCert(cert);
+ BuildPrettyPrint(cert);
+
+ asyncDetermineUsages(cert);
+}
+
+// Certificate usages we care about in the certificate viewer.
+const certificateUsageSSLClient = 0x0001;
+const certificateUsageSSLServer = 0x0002;
+const certificateUsageSSLCA = 0x0008;
+const certificateUsageEmailSigner = 0x0010;
+const certificateUsageEmailRecipient = 0x0020;
+const certificateUsageObjectSigner = 0x0040;
+
+// A map from the name of a certificate usage to the value of the usage.
+// Useful for printing debugging information and for enumerating all supported
+// usages.
+const certificateUsages = {
+ certificateUsageSSLClient,
+ certificateUsageSSLServer,
+ certificateUsageSSLCA,
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient,
+ certificateUsageObjectSigner,
+};
+
+// Map of certificate usage name to localization identifier.
+const certificateUsageToStringBundleName = {
+ certificateUsageSSLClient: "VerifySSLClient",
+ certificateUsageSSLServer: "VerifySSLServer",
+ certificateUsageSSLCA: "VerifySSLCA",
+ certificateUsageEmailSigner: "VerifyEmailSigner",
+ certificateUsageEmailRecipient: "VerifyEmailRecip",
+ certificateUsageObjectSigner: "VerifyObjSign",
+};
+
+const PRErrorCodeSuccess = 0;
+
+const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11;
+const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12;
+const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
+const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20;
+const SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21;
+const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30;
+const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
+
+/**
+ * Kicks off asynchronous verifications of the given certificate to determine
+ * what usages it is currently valid for. Updates the usage display area when
+ * complete.
+ *
+ * @param {nsIX509Cert} cert
+ * The certificate to determine valid usages for.
+ */
+function asyncDetermineUsages(cert) {
+ let promises = [];
+ let now = Date.now() / 1000;
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+ Object.keys(certificateUsages).forEach(usageString => {
+ promises.push(new Promise((resolve, reject) => {
+ let usage = certificateUsages[usageString];
+ certdb.asyncVerifyCertAtTime(cert, usage, 0, null, now,
+ (aPRErrorCode, aVerifiedChain, aHasEVPolicy) => {
+ resolve({ usageString: usageString, errorCode: aPRErrorCode });
+ });
+ }));
+ });
+ Promise.all(promises).then(displayUsages);
+}
+
+/**
+ * Updates the usage display area given the results from asyncDetermineUsages.
+ *
+ * @param {Array} results
+ * An array of objects with the properties "usageString" and "errorCode".
+ * usageString is a string that is a key in the certificateUsages map.
+ * errorCode is either an NSPR error code or PRErrorCodeSuccess (which is
+ * a pseudo-NSPR error code with the value 0 that indicates success).
+ */
+function displayUsages(results) {
+ document.getElementById("verify_pending").setAttribute("hidden", "true");
+ let verified = document.getElementById("verified");
+ let someSuccess = results.some(result =>
+ result.errorCode == PRErrorCodeSuccess
+ );
+ if (someSuccess) {
+ let verifystr = bundle.getString("certVerified");
+ verified.textContent = verifystr;
+ let pipnssBundle = Services.strings.createBundle(
+ "chrome://pipnss/locale/pipnss.properties");
+ results.forEach(result => {
+ if (result.errorCode != PRErrorCodeSuccess) {
+ return;
+ }
+ let bundleName = certificateUsageToStringBundleName[result.usageString];
+ let usage = pipnssBundle.GetStringFromName(bundleName);
+ AddUsage(usage);
+ });
+ } else {
+ const errorRankings = [
+ { error: SEC_ERROR_REVOKED_CERTIFICATE,
+ bundleString: "certNotVerified_CertRevoked" },
+ { error: SEC_ERROR_UNTRUSTED_CERT,
+ bundleString: "certNotVerified_CertNotTrusted" },
+ { error: SEC_ERROR_UNTRUSTED_ISSUER,
+ bundleString: "certNotVerified_IssuerNotTrusted" },
+ { error: SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
+ bundleString: "certNotVerified_AlgorithmDisabled" },
+ { error: SEC_ERROR_EXPIRED_CERTIFICATE,
+ bundleString: "certNotVerified_CertExpired" },
+ { error: SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE,
+ bundleString: "certNotVerified_CAInvalid" },
+ { error: SEC_ERROR_UNKNOWN_ISSUER,
+ bundleString: "certNotVerified_IssuerUnknown" },
+ ];
+ let verifystr;
+ for (let errorRanking of errorRankings) {
+ let errorPresent = results.some(result =>
+ result.errorCode == errorRanking.error
+ );
+ if (errorPresent) {
+ verifystr = bundle.getString(errorRanking.bundleString);
+ break;
+ }
+ }
+ if (!verifystr) {
+ verifystr = bundle.getString("certNotVerified_Unknown");
+ }
+ verified.textContent = verifystr;
+ }
+ // Notify that we are done determining the certificate's valid usages (this
+ // should be treated as an implementation detail that enables tests to run
+ // efficiently - other code in the browser probably shouldn't rely on this).
+ Services.obs.notifyObservers(window, "ViewCertDetails:CertUsagesDone", null);
+}
+
+function addChildrenToTree(parentTree, label, value, addTwistie)
+{
+ let treeChild1 = document.createElement("treechildren");
+ let treeElement = addTreeItemToTreeChild(treeChild1, label, value,
+ addTwistie);
+ parentTree.appendChild(treeChild1);
+ return treeElement;
+}
+
+function addTreeItemToTreeChild(treeChild, label, value, addTwistie)
+{
+ let treeElem1 = document.createElement("treeitem");
+ if (addTwistie) {
+ treeElem1.setAttribute("container", "true");
+ treeElem1.setAttribute("open", "true");
+ }
+ let treeRow = document.createElement("treerow");
+ let treeCell = document.createElement("treecell");
+ treeCell.setAttribute("label", label);
+ if (value) {
+ treeCell.setAttribute("display", value);
+ }
+ treeRow.appendChild(treeCell);
+ treeElem1.appendChild(treeRow);
+ treeChild.appendChild(treeElem1);
+ return treeElem1;
+}
+
+function displaySelected() {
+ var asn1Tree = document.getElementById('prettyDumpTree')
+ .view.QueryInterface(nsIASN1Tree);
+ var items = asn1Tree.selection;
+ var certDumpVal = document.getElementById('certDumpVal');
+ if (items.currentIndex != -1) {
+ var value = asn1Tree.getDisplayData(items.currentIndex);
+ certDumpVal.value = value;
+ } else {
+ certDumpVal.value = "";
+ }
+}
+
+function BuildPrettyPrint(cert)
+{
+ var certDumpTree = Components.classes[nsASN1Tree].
+ createInstance(nsIASN1Tree);
+ certDumpTree.loadASN1Structure(cert.ASN1Structure);
+ document.getElementById('prettyDumpTree').view = certDumpTree;
+}
+
+function addAttributeFromCert(nodeName, value)
+{
+ var node = document.getElementById(nodeName);
+ if (!value) {
+ value = bundle.getString('notPresent');
+ }
+ node.setAttribute('value', value);
+}
+
+/**
+ * Displays information about a cert in the "General" tab of the cert viewer.
+ *
+ * @param {nsIX509Cert} cert
+ * Cert to display information about.
+ */
+function DisplayGeneralDataFromCert(cert)
+{
+ addAttributeFromCert("commonname", cert.commonName);
+ addAttributeFromCert("organization", cert.organization);
+ addAttributeFromCert("orgunit", cert.organizationalUnit);
+ addAttributeFromCert("serialnumber", cert.serialNumber);
+ addAttributeFromCert("sha256fingerprint", cert.sha256Fingerprint);
+ addAttributeFromCert("sha1fingerprint", cert.sha1Fingerprint);
+ addAttributeFromCert("validitystart", cert.validity.notBeforeLocalDay);
+ addAttributeFromCert("validityend", cert.validity.notAfterLocalDay);
+
+ addAttributeFromCert("issuercommonname", cert.issuerCommonName);
+ addAttributeFromCert("issuerorganization", cert.issuerOrganization);
+ addAttributeFromCert("issuerorgunit", cert.issuerOrganizationUnit);
+}
+
+function updateCertDump()
+{
+ var asn1Tree = document.getElementById('prettyDumpTree')
+ .view.QueryInterface(nsIASN1Tree);
+
+ var tree = document.getElementById('treesetDump');
+ if (tree.currentIndex < 0) {
+ doPrompt("No items are selected."); //This should never happen.
+ } else {
+ var item = tree.contentView.getItemAtIndex(tree.currentIndex);
+ var dbKey = item.firstChild.firstChild.getAttribute('display');
+ // Get the cert from the cert database
+ var certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
+ var cert = certdb.findCertByDBKey(dbKey);
+ asn1Tree.loadASN1Structure(cert.ASN1Structure);
+ }
+ displaySelected();
+}
+
+function getCurrentCert()
+{
+ var realIndex;
+ var tree = document.getElementById('treesetDump');
+ if (tree.view.selection.isSelected(tree.currentIndex)
+ && document.getElementById('prettyprint_tab').selected) {
+ /* if the user manually selected a cert on the Details tab,
+ then take that one */
+ realIndex = tree.currentIndex;
+ } else {
+ /* otherwise, take the one at the bottom of the chain
+ (i.e. the one of the end-entity, unless we're displaying
+ CA certs) */
+ realIndex = tree.view.rowCount - 1;
+ }
+ if (realIndex >= 0) {
+ var item = tree.contentView.getItemAtIndex(realIndex);
+ var dbKey = item.firstChild.firstChild.getAttribute('display');
+ var certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
+ var cert = certdb.findCertByDBKey(dbKey);
+ return cert;
+ }
+ /* shouldn't really happen */
+ return null;
+}
diff --git a/security/manager/pki/resources/content/certViewer.xul b/security/manager/pki/resources/content/certViewer.xul
new file mode 100644
index 000000000..3f2cd3bde
--- /dev/null
+++ b/security/manager/pki/resources/content/certViewer.xul
@@ -0,0 +1,40 @@
+<?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 SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<?xul-overlay href="chrome://pippki/content/viewCertDetails.xul"?>
+<?xul-overlay href="chrome://pippki/content/certDump.xul"?>
+
+<dialog id="certDetails"
+ title="&certmgr.certdetail.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept"
+ buttonlabelaccept="&certmgr.close.label;"
+ buttonaccesskeyaccept="&certmgr.close.accesskey;"
+ onload="setWindowName();">
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript"
+ src="chrome://pippki/content/certViewer.js"/>
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+
+ <tabbox flex="1">
+ <tabs>
+ <tab id="general_tab" label="&certmgr.detail.general_tab.title;"
+ accesskey="&certmgr.detail.general_tab.accesskey;"/>
+ <tab id="prettyprint_tab" label="&certmgr.detail.prettyprint_tab.title;"
+ accesskey="&certmgr.detail.prettyprint_tab.accesskey;"/>
+ </tabs>
+ <tabpanels flex="1">
+ <vbox id="general_info"/>
+ <vbox id="certPrettyPrint"/>
+ </tabpanels>
+ </tabbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/changepassword.js b/security/manager/pki/resources/content/changepassword.js
new file mode 100644
index 000000000..823145697
--- /dev/null
+++ b/security/manager/pki/resources/content/changepassword.js
@@ -0,0 +1,270 @@
+/* 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 nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
+const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
+const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+const nsPKCS11ModuleDB = "@mozilla.org/security/pkcs11moduledb;1";
+const nsIPKCS11ModuleDB = Components.interfaces.nsIPKCS11ModuleDB;
+const nsIPKCS11Slot = Components.interfaces.nsIPKCS11Slot;
+const nsIPK11Token = Components.interfaces.nsIPK11Token;
+
+var params;
+var tokenName = "";
+var pw1;
+
+function doPrompt(msg)
+{
+ let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService);
+ prompts.alert(window, null, msg);
+}
+
+function onLoad()
+{
+ document.documentElement.getButton("accept").disabled = true;
+
+ pw1 = document.getElementById("pw1");
+ try {
+ params = window.arguments[0].QueryInterface(nsIDialogParamBlock);
+ tokenName = params.GetString(1);
+ } catch (e) {
+ // this should not happen.
+ // previously we had self.name, but self.name was a bad idea
+ // as window name must be a subset of ascii, and the code was
+ // previously trying to assign unicode to the window's name.
+ // I checked all the places where we get a password prompt and
+ // all of them pass an argument as part of this patch.
+ tokenName = "";
+ }
+
+ if (tokenName == "") {
+ let tokenDB = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+ let tokenList = tokenDB.listTokens();
+ let i = 0;
+ let menu = document.getElementById("tokenMenu");
+ while (tokenList.hasMoreElements()) {
+ let token = tokenList.getNext().QueryInterface(nsIPK11Token);
+ if (token.needsLogin() || !(token.needsUserInit)) {
+ let menuItemNode = document.createElement("menuitem");
+ menuItemNode.setAttribute("value", token.tokenName);
+ menuItemNode.setAttribute("label", token.tokenName);
+ menu.firstChild.appendChild(menuItemNode);
+ if (i == 0) {
+ menu.selectedItem = menuItemNode;
+ tokenName = token.tokenName;
+ }
+ i++;
+ }
+ }
+ } else {
+ var sel = document.getElementById("tokenMenu");
+ sel.setAttribute("hidden", "true");
+ var tag = document.getElementById("tokenName");
+ tag.setAttribute("value", tokenName);
+ }
+
+ process();
+}
+
+function onMenuChange()
+{
+ //get the selected token
+ var list = document.getElementById("tokenMenu");
+ tokenName = list.value;
+
+ process();
+}
+
+
+function process()
+{
+ var secmoddb = Components.classes[nsPKCS11ModuleDB].getService(nsIPKCS11ModuleDB);
+ var bundle = document.getElementById("pippki_bundle");
+
+ // If the token is unitialized, don't use the old password box.
+ // Otherwise, do.
+
+ var slot = secmoddb.findSlotByName(tokenName);
+ if (slot) {
+ var oldpwbox = document.getElementById("oldpw");
+ var msgBox = document.getElementById("message");
+ var status = slot.status;
+ if (status == nsIPKCS11Slot.SLOT_UNINITIALIZED
+ || status == nsIPKCS11Slot.SLOT_READY) {
+
+ oldpwbox.setAttribute("hidden", "true");
+ msgBox.setAttribute("value", bundle.getString("password_not_set"));
+ msgBox.setAttribute("hidden", "false");
+
+ if (status == nsIPKCS11Slot.SLOT_READY) {
+ oldpwbox.setAttribute("inited", "empty");
+ } else {
+ oldpwbox.setAttribute("inited", "true");
+ }
+
+ // Select first password field
+ document.getElementById('pw1').focus();
+ } else {
+ // Select old password field
+ oldpwbox.setAttribute("hidden", "false");
+ msgBox.setAttribute("hidden", "true");
+ oldpwbox.setAttribute("inited", "false");
+ oldpwbox.focus();
+ }
+ }
+
+ if (params) {
+ // Return value 0 means "canceled"
+ params.SetInt(1, 0);
+ }
+
+ checkPasswords();
+}
+
+function setPassword()
+{
+ var pk11db = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+ var token = pk11db.findTokenByName(tokenName);
+
+ var oldpwbox = document.getElementById("oldpw");
+ var initpw = oldpwbox.getAttribute("inited");
+ var bundle = document.getElementById("pippki_bundle");
+
+ var success = false;
+
+ if (initpw == "false" || initpw == "empty") {
+ try {
+ var oldpw = "";
+ var passok = 0;
+
+ if (initpw == "empty") {
+ passok = 1;
+ } else {
+ oldpw = oldpwbox.value;
+ passok = token.checkPassword(oldpw);
+ }
+
+ if (passok) {
+ if (initpw == "empty" && pw1.value == "") {
+ // checkPasswords() should have prevented this path from being reached.
+ } else {
+ if (pw1.value == "") {
+ var secmoddb = Components.classes[nsPKCS11ModuleDB].getService(nsIPKCS11ModuleDB);
+ if (secmoddb.isFIPSEnabled) {
+ // empty passwords are not allowed in FIPS mode
+ doPrompt(bundle.getString("pw_change2empty_in_fips_mode"));
+ passok = 0;
+ }
+ }
+ if (passok) {
+ token.changePassword(oldpw, pw1.value);
+ if (pw1.value == "") {
+ doPrompt(bundle.getString("pw_erased_ok")
+ + " "
+ + bundle.getString("pw_empty_warning"));
+ } else {
+ doPrompt(bundle.getString("pw_change_ok"));
+ }
+ success = true;
+ }
+ }
+ } else {
+ oldpwbox.focus();
+ oldpwbox.setAttribute("value", "");
+ doPrompt(bundle.getString("incorrect_pw"));
+ }
+ } catch (e) {
+ doPrompt(bundle.getString("failed_pw_change"));
+ }
+ } else {
+ token.initPassword(pw1.value);
+ if (pw1.value == "") {
+ doPrompt(bundle.getString("pw_not_wanted") + " " +
+ bundle.getString("pw_empty_warning"));
+ }
+ success = true;
+ }
+
+ if (success && params) {
+ // Return value 1 means "successfully executed ok"
+ params.SetInt(1, 1);
+ }
+
+ // Terminate dialog
+ return success;
+}
+
+function setPasswordStrength()
+{
+ // We weigh the quality of the password by checking the number of:
+ // - Characters
+ // - Numbers
+ // - Non-alphanumeric chars
+ // - Upper and lower case characters
+
+ let pw = document.getElementById("pw1").value;
+
+ let pwlength = pw.length;
+ if (pwlength > 5) {
+ pwlength = 5;
+ }
+
+ let numnumeric = pw.replace(/[0-9]/g, "");
+ let numeric = pw.length - numnumeric.length;
+ if (numeric > 3) {
+ numeric = 3;
+ }
+
+ let symbols = pw.replace(/\W/g, "");
+ let numsymbols = pw.length - symbols.length;
+ if (numsymbols > 3) {
+ numsymbols = 3;
+ }
+
+ let numupper = pw.replace(/[A-Z]/g, "");
+ let upper = pw.length - numupper.length;
+ if (upper > 3) {
+ upper = 3;
+ }
+
+ let pwstrength = (pwlength * 10) - 20 + (numeric * 10) + (numsymbols * 15) +
+ (upper * 10);
+
+ // Clamp strength to [0, 100].
+ if (pwstrength < 0) {
+ pwstrength = 0;
+ }
+ if (pwstrength > 100) {
+ pwstrength = 100;
+ }
+
+ let meter = document.getElementById("pwmeter");
+ meter.setAttribute("value", pwstrength);
+
+ return;
+}
+
+function checkPasswords()
+{
+ let pw1 = document.getElementById("pw1").value;
+ let pw2 = document.getElementById("pw2").value;
+
+ var oldpwbox = document.getElementById("oldpw");
+ if (oldpwbox) {
+ var initpw = oldpwbox.getAttribute("inited");
+
+ if (initpw == "empty" && pw1 == "") {
+ // The token has already been initialized, therefore this dialog
+ // was called with the intention to change the password.
+ // The token currently uses an empty password.
+ // We will not allow changing the password from empty to empty.
+ document.documentElement.getButton("accept").disabled = true;
+ return;
+ }
+ }
+
+ document.documentElement.getButton("accept").disabled = (pw1 != pw2);
+}
diff --git a/security/manager/pki/resources/content/changepassword.xul b/security/manager/pki/resources/content/changepassword.xul
new file mode 100644
index 000000000..7f0878ec1
--- /dev/null
+++ b/security/manager/pki/resources/content/changepassword.xul
@@ -0,0 +1,78 @@
+<?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 SYSTEM "chrome://pippki/locale/pippki.dtd">
+
+<dialog id="set_password" title="&setPassword.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ ondialogaccept="return setPassword();"
+ onload="onLoad();">
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript"
+ src="chrome://pippki/content/changepassword.js"/>
+
+<hbox align="center">
+ <label value="&setPassword.tokenName.label;: "/>
+ <label id="tokenName" />
+ <menulist id="tokenMenu" oncommand="onMenuChange()">
+ <menupopup/>
+ </menulist>
+</hbox>
+
+
+<!--
+ <menulist id="signerList" disabled="true">
+ <menupopup>
+ <menuitem id="token-menu" label="Built-in private key database"/>
+ <menuitem label="Bob Lord's iButton"/>
+ </menupopup>
+ </menulist>
+-->
+<separator/>
+
+<groupbox>
+<grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <label value="&setPassword.oldPassword.label;"/>
+ <textbox id="oldpw" type="password"/>
+ <!-- This textbox is inserted as a workaround to the fact that making the 'type'
+ & 'disabled' property of the 'oldpw' textbox toggle between ['password' &
+ 'false'] and ['text' & 'true'] - as would be necessary if the menu has more
+ than one tokens, some initialized and some not - does not work properly. So,
+ either the textbox 'oldpw' or the textbox 'message' would be displayed,
+ depending on the state of the token selected
+ -->
+ <textbox id="message" disabled="true" />
+ </row>
+ <row>
+ <label value="&setPassword.newPassword.label;"/>
+ <textbox id="pw1" type="password"
+ oninput="setPasswordStrength(); checkPasswords();"/>
+ </row>
+ <row>
+ <label value="&setPassword.reenterPassword.label;"/>
+ <textbox id="pw2" type="password" oninput="checkPasswords();"/>
+ </row>
+ </rows>
+</grid>
+</groupbox>
+
+<groupbox>
+ <caption label="&setPassword.meter.label;"/>
+ <progressmeter id="pwmeter" mode="determined"
+ value="0"/>
+</groupbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/choosetoken.js b/security/manager/pki/resources/content/choosetoken.js
new file mode 100644
index 000000000..42e8565da
--- /dev/null
+++ b/security/manager/pki/resources/content/choosetoken.js
@@ -0,0 +1,44 @@
+/* -*- 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/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+
+var dialogParams;
+
+function onLoad()
+{
+ dialogParams = window.arguments[0].QueryInterface(nsIDialogParamBlock);
+ let selectElement = document.getElementById("tokens");
+ let count = dialogParams.GetInt(0);
+ for (let i = 0; i < count; i++) {
+ let menuItemNode = document.createElement("menuitem");
+ let token = dialogParams.GetString(i);
+ menuItemNode.setAttribute("value", token);
+ menuItemNode.setAttribute("label", token);
+ selectElement.firstChild.appendChild(menuItemNode);
+ if (i == 0) {
+ selectElement.selectedItem = menuItemNode;
+ }
+ }
+}
+
+function doOK()
+{
+ let tokenList = document.getElementById("tokens");
+ // Signal that the user accepted.
+ dialogParams.SetInt(0, 1);
+ // Signal the name of the token the user chose.
+ dialogParams.SetString(0, tokenList.value);
+ return true;
+}
+
+function doCancel()
+{
+ dialogParams.SetInt(0, 0); // Signal that the user cancelled.
+ return true;
+}
diff --git a/security/manager/pki/resources/content/choosetoken.xul b/security/manager/pki/resources/content/choosetoken.xul
new file mode 100644
index 000000000..55d9c108b
--- /dev/null
+++ b/security/manager/pki/resources/content/choosetoken.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://global/skin/" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
+%pippkiDTD;
+]>
+
+
+<dialog id="ssl_warning" title="&chooseToken.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 40em;"
+ buttons="accept,cancel"
+ ondialogaccept="return doOK();"
+ ondialogcancel="return doCancel();"
+ onload="onLoad();">
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+<script type="application/javascript" src="chrome://pippki/content/choosetoken.js"/>
+
+ <groupbox>
+ <description>&chooseToken.message1;</description>
+ <menulist id="tokens">
+ <menupopup/>
+ </menulist>
+ </groupbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/clientauthask.js b/security/manager/pki/resources/content/clientauthask.js
new file mode 100644
index 000000000..55a3c5b77
--- /dev/null
+++ b/security/manager/pki/resources/content/clientauthask.js
@@ -0,0 +1,158 @@
+/* -*- tab-width: 2; 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/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+/**
+ * @file Implements the functionality of clientauthask.xul: a dialog that allows
+ * a user pick a client certificate for TLS client authentication.
+ * @argument {String} window.arguments[0]
+ * The hostname of the server requesting client authentication.
+ * @argument {String} window.arguments[1]
+ * The Organization of the server cert.
+ * @argument {String} window.arguments[2]
+ * The Organization of the issuer of the server cert.
+ * @argument {Number} window.arguments[3]
+ * The port of the server.
+ * @argument {nsISupports} window.arguments[4]
+ * List of certificates the user can choose from, queryable to
+ * nsIArray<nsIX509Cert>.
+ * @argument {nsISupports} window.arguments[5]
+ * Object to set the return values of calling the dialog on, queryable
+ * to the underlying type of ClientAuthAskReturnValues.
+ */
+
+/**
+ * @typedef ClientAuthAskReturnValues
+ * @type nsIWritablePropertyBag2
+ * @property {Boolean} certChosen
+ * Set to true if the user chose a cert and accepted the dialog, false
+ * otherwise.
+ * @property {Boolean} rememberSelection
+ * Set to true if the user wanted their cert selection to be
+ * remembered, false otherwise.
+ * @property {Number} selectedIndex
+ * The index the chosen cert is at for the given cert list. Undefined
+ * value if |certChosen| is not true.
+ */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+/**
+ * The pippki <stringbundle> element.
+ * @type <stringbundle>
+ */
+var bundle;
+/**
+ * The array of certs the user can choose from.
+ * @type nsIArray<nsIX509Cert>
+ */
+var certArray;
+/**
+ * The checkbox storing whether the user wants to remember the selected cert.
+ * @type nsIDOMXULCheckboxElement
+ */
+var rememberBox;
+
+function onLoad() {
+ bundle = document.getElementById("pippki_bundle");
+ let rememberSetting =
+ Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");
+
+ rememberBox = document.getElementById("rememberBox");
+ rememberBox.label = bundle.getString("clientAuthRemember");
+ rememberBox.checked = rememberSetting;
+
+ let hostname = window.arguments[0];
+ let org = window.arguments[1];
+ let issuerOrg = window.arguments[2];
+ let port = window.arguments[3];
+ let formattedOrg = bundle.getFormattedString("clientAuthMessage1", [org]);
+ let formattedIssuerOrg = bundle.getFormattedString("clientAuthMessage2",
+ [issuerOrg]);
+ let formattedHostnameAndPort =
+ bundle.getFormattedString("clientAuthHostnameAndPort",
+ [hostname, port.toString()]);
+ setText("hostname", formattedHostnameAndPort);
+ setText("organization", formattedOrg);
+ setText("issuer", formattedIssuerOrg);
+
+ let selectElement = document.getElementById("nicknames");
+ certArray = window.arguments[4].QueryInterface(Ci.nsIArray);
+ for (let i = 0; i < certArray.length; i++) {
+ let menuItemNode = document.createElement("menuitem");
+ let cert = certArray.queryElementAt(i, Ci.nsIX509Cert);
+ let nickAndSerial =
+ bundle.getFormattedString("clientAuthNickAndSerial",
+ [cert.nickname, cert.serialNumber]);
+ menuItemNode.setAttribute("value", i);
+ menuItemNode.setAttribute("label", nickAndSerial); // This is displayed.
+ selectElement.firstChild.appendChild(menuItemNode);
+ if (i == 0) {
+ selectElement.selectedItem = menuItemNode;
+ }
+ }
+
+ setDetails();
+
+ Services.obs.notifyObservers(document.getElementById("certAuthAsk"),
+ "cert-dialog-loaded", null);
+}
+
+/**
+ * Populates the details section with information concerning the selected cert.
+ */
+function setDetails() {
+ let index = parseInt(document.getElementById("nicknames").value);
+ let cert = certArray.queryElementAt(index, Ci.nsIX509Cert);
+
+ let detailLines = [
+ bundle.getFormattedString("clientAuthIssuedTo", [cert.subjectName]),
+ bundle.getFormattedString("clientAuthSerial", [cert.serialNumber]),
+ bundle.getFormattedString("clientAuthValidityPeriod",
+ [cert.validity.notBeforeLocalTime,
+ cert.validity.notAfterLocalTime]),
+ ];
+ let keyUsages = cert.keyUsages;
+ if (keyUsages) {
+ detailLines.push(bundle.getFormattedString("clientAuthKeyUsages",
+ [keyUsages]));
+ }
+ let emailAddresses = cert.getEmailAddresses({});
+ if (emailAddresses.length > 0) {
+ let joinedAddresses = emailAddresses.join(", ");
+ detailLines.push(bundle.getFormattedString("clientAuthEmailAddresses",
+ [joinedAddresses]));
+ }
+ detailLines.push(bundle.getFormattedString("clientAuthIssuedBy",
+ [cert.issuerName]));
+ detailLines.push(bundle.getFormattedString("clientAuthStoredOn",
+ [cert.tokenName]));
+
+ document.getElementById("details").value = detailLines.join("\n");
+}
+
+function onCertSelected() {
+ setDetails();
+}
+
+function doOK() {
+ let retVals = window.arguments[5].QueryInterface(Ci.nsIWritablePropertyBag2);
+ retVals.setPropertyAsBool("certChosen", true);
+ let index = parseInt(document.getElementById("nicknames").value);
+ retVals.setPropertyAsUint32("selectedIndex", index);
+ retVals.setPropertyAsBool("rememberSelection", rememberBox.checked);
+ return true;
+}
+
+function doCancel() {
+ let retVals = window.arguments[5].QueryInterface(Ci.nsIWritablePropertyBag2);
+ retVals.setPropertyAsBool("certChosen", false);
+ retVals.setPropertyAsBool("rememberSelection", rememberBox.checked);
+ return true;
+}
diff --git a/security/manager/pki/resources/content/clientauthask.xul b/security/manager/pki/resources/content/clientauthask.xul
new file mode 100644
index 000000000..2d6ba54c2
--- /dev/null
+++ b/security/manager/pki/resources/content/clientauthask.xul
@@ -0,0 +1,49 @@
+<?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 % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
+%pippkiDTD;
+]>
+
+
+<dialog id="certAuthAsk" title="&clientAuthAsk.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ ondialogaccept="return doOK();"
+ ondialogcancel="return doCancel();"
+ onload="onLoad();">
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+</stringbundleset>
+
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+<script type="application/javascript" src="chrome://pippki/content/clientauthask.js"/>
+
+ <groupbox>
+ <description style="font-weight: bold;">&clientAuthAsk.message1;</description>
+ <description id="hostname"/>
+ <description id="organization"/>
+ <description id="issuer"/>
+ </groupbox>
+ <groupbox>
+ <description style="font-weight: bold;">&clientAuthAsk.message2;</description>
+ <broadcaster id="certSelected" oncommand="onCertSelected();"/>
+ <!-- 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>
+ <description>&clientAuthAsk.message3;</description>
+ <textbox readonly="true" id="details" multiline="true"
+ style="height: 11em;"/>
+ <checkbox id="rememberBox" checked="true"/>
+ </groupbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/createCertInfo.js b/security/manager/pki/resources/content/createCertInfo.js
new file mode 100644
index 000000000..5e7e9052c
--- /dev/null
+++ b/security/manager/pki/resources/content/createCertInfo.js
@@ -0,0 +1,39 @@
+/* -*- 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/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+var keygenThread;
+
+function onLoad()
+{
+ keygenThread = window.arguments[0].QueryInterface(Components.interfaces.nsIKeygenThread);
+
+ if (!keygenThread) {
+ window.close();
+ return;
+ }
+
+ window.setCursor("wait");
+
+ var obs = {
+ observe: function keygenListenerObserve(subject, topic, data) {
+ if (topic == "keygen-finished") {
+ window.close();
+ }
+ }
+ };
+
+ keygenThread.startKeyGeneration(obs);
+}
+
+function onClose()
+{
+ window.setCursor("auto");
+
+ var alreadyClosed = {};
+ keygenThread.userCanceled(alreadyClosed);
+}
diff --git a/security/manager/pki/resources/content/createCertInfo.xul b/security/manager/pki/resources/content/createCertInfo.xul
new file mode 100644
index 000000000..66f4ba9c6
--- /dev/null
+++ b/security/manager/pki/resources/content/createCertInfo.xul
@@ -0,0 +1,30 @@
+<?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 window SYSTEM "chrome://pippki/locale/pippki.dtd">
+
+<window
+ id="domainMismatch" title="&createCertInfo.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad();"
+ onclose="onClose();"
+>
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript" src="pippki.js" />
+<script type="application/javascript" src="createCertInfo.js" />
+
+<vbox style="margin: 5px; max-width: 50em;">
+
+ <description>&createCertInfo.msg1;</description>
+ <separator/>
+ <description style="font-weight: bold; text-align: center;">&createCertInfo.msg2;</description>
+ <separator/>
+
+</vbox>
+</window>
diff --git a/security/manager/pki/resources/content/deletecert.js b/security/manager/pki/resources/content/deletecert.js
new file mode 100644
index 000000000..784001ea2
--- /dev/null
+++ b/security/manager/pki/resources/content/deletecert.js
@@ -0,0 +1,134 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+/**
+ * @file Implements the functionality of deletecert.xul: a dialog that allows a
+ * user to confirm whether to delete certain certificates.
+ * @argument {String} window.arguments[0]
+ * One of the tab IDs listed in certManager.xul.
+ * @argument {nsICertTreeItem[]} window.arguments[1]
+ * An array of cert tree items representing the certs to delete.
+ * @argument {DeleteCertReturnValues} window.arguments[2]
+ * Object holding the return values of calling the dialog.
+ */
+
+/**
+ * @typedef DeleteCertReturnValues
+ * @type Object
+ * @property {Boolean} deleteConfirmed
+ * Set to true if the user confirmed deletion of the given certs,
+ * false otherwise.
+ */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+/**
+ * Returns the most appropriate string to represent the given nsICertTreeItem.
+ * @param {nsICertTreeItem} certTreeItem
+ * The item to represent.
+ * @returns {String}
+ * A representative string.
+ */
+function certTreeItemToString(certTreeItem) {
+ let cert = certTreeItem.cert;
+ if (!cert) {
+ return certTreeItem.hostPort;
+ }
+
+ const attributes = [
+ cert.commonName,
+ cert.organizationalUnit,
+ cert.organization,
+ cert.subjectName,
+ ];
+ for (let attribute of attributes) {
+ if (attribute) {
+ return attribute;
+ }
+ }
+
+ let bundle = document.getElementById("pippki_bundle");
+ return bundle.getFormattedString("certWithSerial", [cert.serialNumber]);
+}
+
+/**
+ * onload() handler.
+ */
+function onLoad() {
+ let typeFlag = window.arguments[0];
+ let bundle = document.getElementById("pippki_bundle");
+ let title;
+ let confirm;
+ let impact;
+
+ switch (typeFlag) {
+ case "mine_tab":
+ title = bundle.getString("deleteUserCertTitle");
+ confirm = bundle.getString("deleteUserCertConfirm");
+ impact = bundle.getString("deleteUserCertImpact");
+ break;
+ case "websites_tab":
+ title = bundle.getString("deleteSslCertTitle3");
+ confirm = bundle.getString("deleteSslCertConfirm3");
+ impact = bundle.getString("deleteSslCertImpact3");
+ break;
+ case "ca_tab":
+ title = bundle.getString("deleteCaCertTitle2");
+ confirm = bundle.getString("deleteCaCertConfirm2");
+ impact = bundle.getString("deleteCaCertImpactX2");
+ break;
+ case "others_tab":
+ title = bundle.getString("deleteEmailCertTitle");
+ confirm = bundle.getString("deleteEmailCertConfirm");
+ impact = bundle.getString("deleteEmailCertImpactDesc");
+ break;
+ case "orphan_tab":
+ title = bundle.getString("deleteOrphanCertTitle");
+ confirm = bundle.getString("deleteOrphanCertConfirm");
+ impact = "";
+ break;
+ default:
+ return;
+ }
+
+ document.title = title;
+
+ setText("confirm", confirm);
+
+ let box = document.getElementById("certlist");
+ let certTreeItems = window.arguments[1];
+ for (let certTreeItem of certTreeItems) {
+ let listItem = document.createElement("richlistitem");
+ let label = document.createElement("label");
+ label.setAttribute("value", certTreeItemToString(certTreeItem));
+ listItem.appendChild(label);
+ box.appendChild(listItem);
+ }
+
+ setText("impact", impact);
+}
+
+/**
+ * ondialogaccept() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogAccept() {
+ let retVals = window.arguments[2];
+ retVals.deleteConfirmed = true;
+ return true;
+}
+
+/**
+ * ondialogcancel() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogCancel() {
+ let retVals = window.arguments[2];
+ retVals.deleteConfirmed = false;
+ return true;
+}
diff --git a/security/manager/pki/resources/content/deletecert.xul b/security/manager/pki/resources/content/deletecert.xul
new file mode 100644
index 000000000..40b0c081b
--- /dev/null
+++ b/security/manager/pki/resources/content/deletecert.xul
@@ -0,0 +1,28 @@
+<?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 SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<dialog id="deleteCertificate"
+ title="&certmgr.deletecert.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad();"
+ buttons="accept,cancel"
+ ondialogaccept="return onDialogAccept();"
+ ondialogcancel="return onDialogCancel();">
+
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+ <script type="application/javascript" src="chrome://pippki/content/deletecert.js"/>
+ <script type="application/javascript" src="pippki.js" />
+
+ <description id="confirm" style="width: 400px;"/>
+ <richlistbox id="certlist" class="box-padded" flex="1"
+ style="min-height: 8em; height: 8em; min-width: 35em;"/>
+ <description id="impact" style="width: 400px;"/>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/device_manager.js b/security/manager/pki/resources/content/device_manager.js
new file mode 100644
index 000000000..7d16c5888
--- /dev/null
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -0,0 +1,509 @@
+/* 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 nsIFilePicker = Components.interfaces.nsIFilePicker;
+const nsFilePicker = "@mozilla.org/filepicker;1";
+const nsIPKCS11Slot = Components.interfaces.nsIPKCS11Slot;
+const nsIPKCS11Module = Components.interfaces.nsIPKCS11Module;
+const nsPKCS11ModuleDB = "@mozilla.org/security/pkcs11moduledb;1";
+const nsIPKCS11ModuleDB = Components.interfaces.nsIPKCS11ModuleDB;
+const nsIPK11Token = Components.interfaces.nsIPK11Token;
+const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
+const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
+const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+const nsDialogParamBlock = "@mozilla.org/embedcomp/dialogparam;1";
+const nsIPKCS11 = Components.interfaces.nsIPKCS11;
+const nsPKCS11ContractID = "@mozilla.org/security/pkcs11;1";
+
+var { Services } = Components.utils.import("resource://gre/modules/Services.jsm", {});
+
+var bundle;
+var secmoddb;
+var skip_enable_buttons = false;
+
+var smartCardObserver = {
+ observe: function() {
+ onSmartCardChange();
+ }
+};
+
+function DeregisterSmartCardObservers()
+{
+ Services.obs.removeObserver(smartCardObserver, "smartcard-insert");
+ Services.obs.removeObserver(smartCardObserver, "smartcard-remove");
+}
+
+/* Do the initial load of all PKCS# modules and list them. */
+function LoadModules()
+{
+ bundle = document.getElementById("pippki_bundle");
+ secmoddb = Components.classes[nsPKCS11ModuleDB].getService(nsIPKCS11ModuleDB);
+ Services.obs.addObserver(smartCardObserver, "smartcard-insert", false);
+ Services.obs.addObserver(smartCardObserver, "smartcard-remove", false);
+
+ RefreshDeviceList();
+}
+
+function getPKCS11()
+{
+ return Components.classes[nsPKCS11ContractID].getService(nsIPKCS11);
+}
+
+function getNSSString(name)
+{
+ return document.getElementById("pipnss_bundle").getString(name);
+}
+
+function doPrompt(msg)
+{
+ let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService);
+ prompts.alert(window, null, msg);
+}
+
+function doConfirm(msg)
+{
+ let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService);
+ return prompts.confirm(window, null, msg);
+}
+
+function RefreshDeviceList()
+{
+ let modules = secmoddb.listModules();
+ while (modules.hasMoreElements()) {
+ let module = modules.getNext().QueryInterface(nsIPKCS11Module);
+ let slotnames = [];
+ let slots = module.listSlots();
+ while (slots.hasMoreElements()) {
+ let slot = slots.getNext().QueryInterface(nsIPKCS11Slot);
+ // Token names are preferred because NSS prefers lookup by token name.
+ slotnames.push(slot.tokenName ? slot.tokenName : slot.name);
+ }
+ AddModule(module.name, slotnames);
+ }
+
+ // Set the text on the FIPS button.
+ SetFIPSButton();
+}
+
+function SetFIPSButton()
+{
+ var fipsButton = document.getElementById("fipsbutton");
+ var label;
+ if (secmoddb.isFIPSEnabled) {
+ label = bundle.getString("disable_fips");
+ } else {
+ label = bundle.getString("enable_fips");
+ }
+ fipsButton.setAttribute("label", label);
+
+ var can_toggle = secmoddb.canToggleFIPS;
+ if (can_toggle) {
+ fipsButton.removeAttribute("disabled");
+ } else {
+ fipsButton.setAttribute("disabled", "true");
+ }
+}
+
+/* Add a module to the tree. slots is the array of slots in the module,
+ * to be represented as children.
+ */
+function AddModule(module, slots)
+{
+ var tree = document.getElementById("device_list");
+ var item = document.createElement("treeitem");
+ var row = document.createElement("treerow");
+ var cell = document.createElement("treecell");
+ cell.setAttribute("label", module);
+ row.appendChild(cell);
+ item.appendChild(row);
+ var parent = document.createElement("treechildren");
+ for (let slot of slots) {
+ var child_item = document.createElement("treeitem");
+ var child_row = document.createElement("treerow");
+ var child_cell = document.createElement("treecell");
+ child_cell.setAttribute("label", slot);
+ child_row.appendChild(child_cell);
+ child_item.appendChild(child_row);
+ child_item.setAttribute("pk11kind", "slot");
+ parent.appendChild(child_item);
+ }
+ item.appendChild(parent);
+ item.setAttribute("pk11kind", "module");
+ item.setAttribute("open", "true");
+ item.setAttribute("container", "true");
+ tree.appendChild(item);
+}
+
+var selected_slot;
+var selected_module;
+
+/* get the slot selected by the user (can only be one-at-a-time) */
+function getSelectedItem()
+{
+ var tree = document.getElementById('device_tree');
+ if (tree.currentIndex < 0) return;
+ var item = tree.contentView.getItemAtIndex(tree.currentIndex);
+ selected_slot = null;
+ selected_module = null;
+ if (item) {
+ var kind = item.getAttribute("pk11kind");
+ var module_name;
+ if (kind == "slot") {
+ // get the module cell for this slot cell
+ var cell = item.parentNode.parentNode.firstChild.firstChild;
+ module_name = cell.getAttribute("label");
+ var module = secmoddb.findModuleByName(module_name);
+ // get the cell for the selected row (the slot to display)
+ cell = item.firstChild.firstChild;
+ var slot_name = cell.getAttribute("label");
+ selected_slot = module.findSlotByName(slot_name);
+ } else { // (kind == "module")
+ // get the cell for the selected row (the module to display)
+ cell = item.firstChild.firstChild;
+ module_name = cell.getAttribute("label");
+ selected_module = secmoddb.findModuleByName(module_name);
+ }
+ }
+}
+
+function enableButtons()
+{
+ if (skip_enable_buttons) {
+ return;
+ }
+
+ var login_toggle = "true";
+ var logout_toggle = "true";
+ var pw_toggle = "true";
+ var unload_toggle = "true";
+ getSelectedItem();
+ if (selected_module) {
+ unload_toggle = "false";
+ showModuleInfo();
+ } else if (selected_slot) {
+ // here's the workaround - login functions are all with token,
+ // so grab the token type
+ var selected_token = selected_slot.getToken();
+ if (selected_token != null) {
+ if (selected_token.needsLogin() || !(selected_token.needsUserInit)) {
+ pw_toggle = "false";
+ if (selected_token.needsLogin()) {
+ if (selected_token.isLoggedIn()) {
+ logout_toggle = "false";
+ } else {
+ login_toggle = "false";
+ }
+ }
+ }
+ }
+ showSlotInfo();
+ }
+ var thebutton = document.getElementById('login_button');
+ thebutton.setAttribute("disabled", login_toggle);
+ thebutton = document.getElementById('logout_button');
+ thebutton.setAttribute("disabled", logout_toggle);
+ thebutton = document.getElementById('change_pw_button');
+ thebutton.setAttribute("disabled", pw_toggle);
+ thebutton = document.getElementById('unload_button');
+ thebutton.setAttribute("disabled", unload_toggle);
+ // not implemented
+ //thebutton = document.getElementById('change_slotname_button');
+ //thebutton.setAttribute("disabled", toggle);
+}
+
+// clear the display of information for the slot
+function ClearInfoList()
+{
+ let infoList = document.getElementById("info_list");
+ while (infoList.hasChildNodes()) {
+ infoList.removeChild(infoList.firstChild);
+ }
+}
+
+function ClearDeviceList()
+{
+ ClearInfoList();
+
+ skip_enable_buttons = true;
+ var tree = document.getElementById('device_tree');
+ tree.view.selection.clearSelection();
+ skip_enable_buttons = false;
+
+ // Remove the existing listed modules so that a refresh doesn't display the
+ // module that just changed.
+ let deviceList = document.getElementById("device_list");
+ while (deviceList.hasChildNodes()) {
+ deviceList.removeChild(deviceList.firstChild);
+ }
+}
+
+
+// show a list of info about a slot
+function showSlotInfo()
+{
+ var present = true;
+ ClearInfoList();
+ switch (selected_slot.status) {
+ case nsIPKCS11Slot.SLOT_DISABLED:
+ AddInfoRow(bundle.getString("devinfo_status"),
+ bundle.getString("devinfo_stat_disabled"),
+ "tok_status");
+ present = false;
+ break;
+ case nsIPKCS11Slot.SLOT_NOT_PRESENT:
+ AddInfoRow(bundle.getString("devinfo_status"),
+ bundle.getString("devinfo_stat_notpresent"),
+ "tok_status");
+ present = false;
+ break;
+ case nsIPKCS11Slot.SLOT_UNINITIALIZED:
+ AddInfoRow(bundle.getString("devinfo_status"),
+ bundle.getString("devinfo_stat_uninitialized"),
+ "tok_status");
+ break;
+ case nsIPKCS11Slot.SLOT_NOT_LOGGED_IN:
+ AddInfoRow(bundle.getString("devinfo_status"),
+ bundle.getString("devinfo_stat_notloggedin"),
+ "tok_status");
+ break;
+ case nsIPKCS11Slot.SLOT_LOGGED_IN:
+ AddInfoRow(bundle.getString("devinfo_status"),
+ bundle.getString("devinfo_stat_loggedin"),
+ "tok_status");
+ break;
+ case nsIPKCS11Slot.SLOT_READY:
+ AddInfoRow(bundle.getString("devinfo_status"),
+ bundle.getString("devinfo_stat_ready"),
+ "tok_status");
+ break;
+ }
+ AddInfoRow(bundle.getString("devinfo_desc"),
+ selected_slot.desc, "slot_desc");
+ AddInfoRow(bundle.getString("devinfo_manID"),
+ selected_slot.manID, "slot_manID");
+ AddInfoRow(bundle.getString("devinfo_hwversion"),
+ selected_slot.HWVersion, "slot_hwv");
+ AddInfoRow(bundle.getString("devinfo_fwversion"),
+ selected_slot.FWVersion, "slot_fwv");
+ if (present) {
+ showTokenInfo();
+ }
+}
+
+function showModuleInfo()
+{
+ ClearInfoList();
+ AddInfoRow(bundle.getString("devinfo_modname"),
+ selected_module.name, "module_name");
+ AddInfoRow(bundle.getString("devinfo_modpath"),
+ selected_module.libName, "module_path");
+}
+
+// add a row to the info list, as [col1 col2] (ex.: ["status" "logged in"])
+function AddInfoRow(col1, col2, cell_id)
+{
+ var tree = document.getElementById("info_list");
+ var item = document.createElement("treeitem");
+ var row = document.createElement("treerow");
+ var cell1 = document.createElement("treecell");
+ cell1.setAttribute("label", col1);
+ cell1.setAttribute("crop", "never");
+ row.appendChild(cell1);
+ var cell2 = document.createElement("treecell");
+ cell2.setAttribute("label", col2);
+ cell2.setAttribute("crop", "never");
+ cell2.setAttribute("id", cell_id);
+ row.appendChild(cell2);
+ item.appendChild(row);
+ tree.appendChild(item);
+}
+
+// log in to a slot
+function doLogin()
+{
+ getSelectedItem();
+ // here's the workaround - login functions are with token
+ var selected_token = selected_slot.getToken();
+ try {
+ selected_token.login(false);
+ var tok_status = document.getElementById("tok_status");
+ if (selected_token.isLoggedIn()) {
+ tok_status.setAttribute("label",
+ bundle.getString("devinfo_stat_loggedin"));
+ } else {
+ tok_status.setAttribute("label",
+ bundle.getString("devinfo_stat_notloggedin"));
+ }
+ } catch (e) {
+ doPrompt(bundle.getString("login_failed"));
+ }
+ enableButtons();
+}
+
+// log out of a slot
+function doLogout()
+{
+ getSelectedItem();
+ // here's the workaround - login functions are with token
+ var selected_token = selected_slot.getToken();
+ try {
+ selected_token.logoutAndDropAuthenticatedResources();
+ var tok_status = document.getElementById("tok_status");
+ if (selected_token.isLoggedIn()) {
+ tok_status.setAttribute("label",
+ bundle.getString("devinfo_stat_loggedin"));
+ } else {
+ tok_status.setAttribute("label",
+ bundle.getString("devinfo_stat_notloggedin"));
+ }
+ } catch (e) {
+ }
+ enableButtons();
+}
+
+// load a new device
+function doLoad()
+{
+ window.open("load_device.xul", "loaddevice", "chrome,centerscreen,modal");
+ ClearDeviceList();
+ RefreshDeviceList();
+}
+
+function deleteSelected()
+{
+ getSelectedItem();
+ if (selected_module &&
+ doConfirm(getNSSString("DelModuleWarning"))) {
+ try {
+ getPKCS11().deleteModule(selected_module.name);
+ }
+ catch (e) {
+ doPrompt(getNSSString("DelModuleError"));
+ return false;
+ }
+ selected_module = null;
+ return true;
+ }
+ return false;
+}
+
+function doUnload()
+{
+ if (deleteSelected()) {
+ ClearDeviceList();
+ RefreshDeviceList();
+ }
+}
+
+// handle card insertion and removal
+function onSmartCardChange()
+{
+ var tree = document.getElementById('device_tree');
+ var index = tree.currentIndex;
+ tree.currentIndex = 0;
+ ClearDeviceList();
+ RefreshDeviceList();
+ tree.currentIndex = index;
+ enableButtons();
+}
+
+function changePassword()
+{
+ getSelectedItem();
+ let params = Components.classes[nsDialogParamBlock]
+ .createInstance(nsIDialogParamBlock);
+ params.SetString(1, selected_slot.tokenName);
+ window.openDialog("changepassword.xul", "", "chrome,centerscreen,modal",
+ params);
+ showSlotInfo();
+ enableButtons();
+}
+
+// browse fs for PKCS#11 device
+function doBrowseFiles()
+{
+ var srbundle = document.getElementById("pippki_bundle");
+ var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker);
+ fp.init(window,
+ srbundle.getString("loadPK11TokenDialog"),
+ nsIFilePicker.modeOpen);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ if (fp.show() == nsIFilePicker.returnOK) {
+ var pathbox = document.getElementById("device_path");
+ pathbox.setAttribute("value", fp.file.path);
+ }
+}
+
+function doLoadDevice()
+{
+ var name_box = document.getElementById("device_name");
+ var path_box = document.getElementById("device_path");
+ try {
+ getPKCS11().addModule(name_box.value, path_box.value, 0, 0);
+ } catch (e) {
+ if (e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) {
+ doPrompt(getNSSString("AddModuleDup"));
+ } else {
+ doPrompt(getNSSString("AddModuleFailure"));
+ }
+
+ return false;
+ }
+ return true;
+}
+
+// ------------------------------------- Old code
+
+function showTokenInfo()
+{
+ //ClearInfoList();
+ var selected_token = selected_slot.getToken();
+ AddInfoRow(bundle.getString("devinfo_label"),
+ selected_token.tokenLabel, "tok_label");
+ AddInfoRow(bundle.getString("devinfo_manID"),
+ selected_token.tokenManID, "tok_manID");
+ AddInfoRow(bundle.getString("devinfo_serialnum"),
+ selected_token.tokenSerialNumber, "tok_sNum");
+ AddInfoRow(bundle.getString("devinfo_hwversion"),
+ selected_token.tokenHWVersion, "tok_hwv");
+ AddInfoRow(bundle.getString("devinfo_fwversion"),
+ selected_token.tokenFWVersion, "tok_fwv");
+}
+
+function toggleFIPS()
+{
+ if (!secmoddb.isFIPSEnabled) {
+ // A restriction of FIPS mode is, the password must be set
+ // In FIPS mode the password must be non-empty.
+ // This is different from what we allow in NON-Fips mode.
+
+ var tokendb = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+ var internal_token = tokendb.getInternalKeyToken(); // nsIPK11Token
+ var slot = secmoddb.findSlotByName(internal_token.tokenName);
+ switch (slot.status) {
+ case nsIPKCS11Slot.SLOT_UNINITIALIZED:
+ case nsIPKCS11Slot.SLOT_READY:
+ // Token has either no or an empty password.
+ doPrompt(bundle.getString("fips_nonempty_password_required"));
+ return;
+ }
+ }
+
+ try {
+ secmoddb.toggleFIPSMode();
+ }
+ catch (e) {
+ doPrompt(bundle.getString("unable_to_toggle_fips"));
+ return;
+ }
+
+ // Remove the existing listed modules so that a refresh doesn't display the
+ // module that just changed.
+ ClearDeviceList();
+
+ RefreshDeviceList();
+}
diff --git a/security/manager/pki/resources/content/device_manager.xul b/security/manager/pki/resources/content/device_manager.xul
new file mode 100644
index 000000000..0a5b4ca91
--- /dev/null
+++ b/security/manager/pki/resources/content/device_manager.xul
@@ -0,0 +1,90 @@
+<?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 % deviceManangerDTD SYSTEM "chrome://pippki/locale/deviceManager.dtd">
+%deviceManangerDTD;
+<!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
+%pippkiDTD;
+]>
+
+<dialog id="devicemanager"
+ windowtype="mozilla:devicemanager"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&devmgr.title;"
+ style="&devmgr.style2;"
+ persist="screenX screenY width height"
+ onload="LoadModules();"
+ onunload="DeregisterSmartCardObservers();"
+ buttons="accept">
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+ <stringbundle id="pipnss_bundle" src="chrome://pipnss/locale/pipnss.properties"/>
+</stringbundleset>
+
+<script type="application/javascript" src="chrome://pippki/content/device_manager.js"/>
+
+<grid flex="1" style="margin:5px">
+ <columns>
+ <column flex="1"/>
+ <column flex="3"/>
+ <column/>
+ </columns>
+ <rows>
+ <row flex="1">
+ <vbox> <!-- List of devices -->
+ <tree id="device_tree" seltype="single"
+ onselect="enableButtons();" hidecolumnpicker="true"
+ flex="1" style="min-width: 15em">
+ <treecols>
+ <treecol id="deviceCol" flex="1" primary="true" label="&devmgr.devlist.label;"/>
+ </treecols>
+ <treechildren id="device_list"/>
+ </tree>
+ </vbox> <!-- / List of devices -->
+ <vbox> <!-- Device status -->
+ <tree id="info_tree" seltype="single" hidecolumnpicker="true"
+ flex="1" style="min-width: 10em">
+ <treecols>
+ <treecol id="title1Col" flex="5" primary="true" label="&devmgr.details.title;"/>
+ <treecol id="title2Col" flex="7" label="&devmgr.details.title2;"/>
+ </treecols>
+ <treechildren id="info_list"/>
+ </tree>
+ </vbox> <!-- / Device status -->
+ <vbox> <!-- Buttons for manipulating devices -->
+ <button id="login_button"
+ label="&devmgr.button.login.label;"
+ accesskey="&devmgr.button.login.accesskey;"
+ oncommand="doLogin();" disabled="true"/>
+ <button id="logout_button"
+ label="&devmgr.button.logout.label;"
+ accesskey="&devmgr.button.logout.accesskey;"
+ oncommand="doLogout();" disabled="true"/>
+ <button id="change_pw_button"
+ label="&devmgr.button.changepw.label;"
+ accesskey="&devmgr.button.changepw.accesskey;"
+ oncommand="changePassword();" disabled="true"/>
+ <button id="load_button"
+ label="&devmgr.button.load.label;"
+ accesskey="&devmgr.button.load.accesskey;"
+ oncommand="doLoad();"/>
+ <button id="unload_button"
+ label="&devmgr.button.unload.label;"
+ accesskey="&devmgr.button.unload.accesskey;"
+ oncommand="doUnload();" disabled="true"/>
+ <button id="fipsbutton"
+ label=""
+ accesskey="&devmgr.button.fips.accesskey;"
+ oncommand="toggleFIPS();"/>
+ </vbox> <!-- / Buttons for manipulating devices -->
+ </row>
+ </rows>
+</grid>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/downloadcert.js b/security/manager/pki/resources/content/downloadcert.js
new file mode 100644
index 000000000..892e34be0
--- /dev/null
+++ b/security/manager/pki/resources/content/downloadcert.js
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+/**
+ * @file Implements the functionality of downloadcert.xul: a dialog that allows
+ * a user to confirm whether to import a certificate, and if so what trust
+ * to give it.
+ * @argument {nsISupports} window.arguments[0]
+ * Certificate to confirm import of, queryable to nsIX509Cert.
+ * @argument {nsISupports} window.arguments[1]
+ * Object to set the return values of calling the dialog on, queryable
+ * to the underlying type of DownloadCertReturnValues.
+ */
+
+/**
+ * @typedef DownloadCertReturnValues
+ * @type nsIWritablePropertyBag2
+ * @property {Boolean} importConfirmed
+ * Set to true if the user confirmed import of the cert and accepted
+ * the dialog, false otherwise.
+ * @property {Boolean} trustForSSL
+ * Set to true if the cert should be trusted for SSL, false otherwise.
+ * Undefined value if |importConfirmed| is not true.
+ * @property {Boolean} trustForEmail
+ * Set to true if the cert should be trusted for e-mail, false
+ * otherwise. Undefined value if |importConfirmed| is not true.
+ * @property {Boolean} trustForObjSign
+ * Set to true if the cert should be trusted for object signing, false
+ * otherwise. Undefined value if |importConfirmed| is not true.
+ */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+/**
+ * The cert to potentially import.
+ * @type nsIX509Cert
+ */
+var gCert;
+
+/**
+ * onload() handler.
+ */
+function onLoad() {
+ gCert = window.arguments[0].QueryInterface(Ci.nsIX509Cert);
+
+ let bundle = document.getElementById("pippki_bundle");
+ let caName = gCert.commonName;
+ if (caName.length == 0) {
+ caName = bundle.getString("unnamedCA");
+ }
+
+ setText("trustHeader", bundle.getFormattedString("newCAMessage1", [caName]));
+}
+
+/**
+ * Handler for the "View Cert" button.
+ */
+function viewCert() {
+ viewCertHelper(window, gCert);
+}
+
+/**
+ * ondialogaccept() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogAccept() {
+ let checkSSL = document.getElementById("trustSSL");
+ let checkEmail = document.getElementById("trustEmail");
+ let checkObjSign = document.getElementById("trustObjSign");
+
+ let retVals = window.arguments[1].QueryInterface(Ci.nsIWritablePropertyBag2);
+ retVals.setPropertyAsBool("importConfirmed", true);
+ retVals.setPropertyAsBool("trustForSSL", checkSSL.checked);
+ retVals.setPropertyAsBool("trustForEmail", checkEmail.checked);
+ retVals.setPropertyAsBool("trustForObjSign", checkObjSign.checked);
+ return true;
+}
+
+/**
+ * ondialogcancel() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogCancel() {
+ let retVals = window.arguments[1].QueryInterface(Ci.nsIWritablePropertyBag2);
+ retVals.setPropertyAsBool("importConfirmed", false);
+ return true;
+}
diff --git a/security/manager/pki/resources/content/downloadcert.xul b/security/manager/pki/resources/content/downloadcert.xul
new file mode 100644
index 000000000..9ddc88a17
--- /dev/null
+++ b/security/manager/pki/resources/content/downloadcert.xul
@@ -0,0 +1,69 @@
+<?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 SYSTEM "chrome://pippki/locale/pippki.dtd">
+
+<dialog id="download_cert"
+ title="&downloadCert.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 46em;"
+ buttons="accept,cancel"
+ ondialogaccept="return onDialogAccept();"
+ ondialogcancel="return onDialogCancel();"
+ onload="onLoad();">
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript" src="chrome://pippki/content/downloadcert.js"/>
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+
+
+ <!-- Let 'em know what they're doing -->
+ <vbox>
+ <description>&downloadCert.message1;</description>
+ </vbox>
+
+ <separator/>
+
+ <!-- checkboxes for trust bits
+ - "do you want to?"
+ - * trust for SSL
+ - * trust for email
+ - * trust for object signing
+ -->
+ <vbox>
+ <description id="trustHeader"/>
+ <checkbox label="&downloadCert.trustSSL;"
+ id="trustSSL"/>
+ <checkbox label="&downloadCert.trustEmail;"
+ id="trustEmail"/>
+ <checkbox label="&downloadCert.trustObjSign;"
+ id="trustObjSign"/>
+ </vbox>
+
+ <separator/>
+
+ <vbox>
+ <description>&downloadCert.message3;</description>
+ <separator/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <button id="viewC-button"
+ label="&downloadCert.viewCert.label;"
+ oncommand="viewCert();"/>
+ <description style="margin: 4px;">&downloadCert.viewCert.text;</description>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/editcacert.js b/security/manager/pki/resources/content/editcacert.js
new file mode 100644
index 000000000..405d5281a
--- /dev/null
+++ b/security/manager/pki/resources/content/editcacert.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+var gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+/**
+ * Cert to edit the trust of.
+ * @type nsIX509Cert
+ */
+var gCert;
+
+/**
+ * onload() handler.
+ */
+function onLoad() {
+ gCert = window.arguments[0];
+
+ let bundle = document.getElementById("pippki_bundle");
+ setText("certmsg",
+ bundle.getFormattedString("editTrustCA", [gCert.commonName]));
+
+ let sslCheckbox = document.getElementById("trustSSL");
+ sslCheckbox.checked = gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL);
+
+ let emailCheckbox = document.getElementById("trustEmail");
+ emailCheckbox.checked = gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL);
+
+ let objSignCheckbox = document.getElementById("trustObjSign");
+ objSignCheckbox.checked =
+ gCertDB.isCertTrusted(gCert, Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_OBJSIGN);
+}
+
+/**
+ * ondialogaccept() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogAccept() {
+ let sslCheckbox = document.getElementById("trustSSL");
+ let emailCheckbox = document.getElementById("trustEmail");
+ let objSignCheckbox = document.getElementById("trustObjSign");
+ let trustSSL = sslCheckbox.checked ? Ci.nsIX509CertDB.TRUSTED_SSL : 0;
+ let trustEmail = emailCheckbox.checked ? Ci.nsIX509CertDB.TRUSTED_EMAIL : 0;
+ let trustObjSign = objSignCheckbox.checked ? Ci.nsIX509CertDB.TRUSTED_OBJSIGN
+ : 0;
+
+ gCertDB.setCertTrust(gCert, Ci.nsIX509Cert.CA_CERT,
+ trustSSL | trustEmail | trustObjSign);
+ return true;
+}
diff --git a/security/manager/pki/resources/content/editcacert.xul b/security/manager/pki/resources/content/editcacert.xul
new file mode 100644
index 000000000..46ea4f1f9
--- /dev/null
+++ b/security/manager/pki/resources/content/editcacert.xul
@@ -0,0 +1,36 @@
+<?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 SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<dialog id="editCaCert"
+ title="&certmgr.editcacert.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ ondialogaccept="return onDialogAccept();"
+ onload="onLoad();"
+>
+
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+ <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+ <script type="application/javascript"
+ src="chrome://pippki/content/editcacert.js"/>
+
+ <description id="certmsg"/>
+ <separator/>
+ <description>&certmgr.editcert.edittrust;</description>
+ <vbox align="start">
+ <checkbox label="&certmgr.editcert.trustssl;"
+ id="trustSSL"/>
+ <checkbox label="&certmgr.editcert.trustemail;"
+ id="trustEmail"/>
+ <checkbox label="&certmgr.editcert.trustobjsign;"
+ id="trustObjSign"/>
+ </vbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/exceptionDialog.js b/security/manager/pki/resources/content/exceptionDialog.js
new file mode 100644
index 000000000..0ca24a614
--- /dev/null
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -0,0 +1,368 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+var gDialog;
+var gBundleBrand;
+var gPKIBundle;
+var gSSLStatus;
+var gCert;
+var gChecking;
+var gBroken;
+var gNeedReset;
+var gSecHistogram;
+var gNsISecTel;
+
+Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+function badCertListener() {}
+badCertListener.prototype = {
+ getInterface: function (aIID) {
+ return this.QueryInterface(aIID);
+ },
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Components.interfaces.nsIBadCertListener2) ||
+ aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
+ aIID.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+
+ throw new Error(Components.results.NS_ERROR_NO_INTERFACE);
+ },
+ handle_test_result: function () {
+ if (gSSLStatus) {
+ gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert;
+ }
+ },
+ notifyCertProblem: function MSR_notifyCertProblem(socketInfo, sslStatus, targetHost) {
+ gBroken = true;
+ gSSLStatus = sslStatus;
+ this.handle_test_result();
+ return true; // suppress error UI
+ }
+};
+
+function initExceptionDialog() {
+ gNeedReset = false;
+ gDialog = document.documentElement;
+ gBundleBrand = document.getElementById("brand_bundle");
+ gPKIBundle = document.getElementById("pippki_bundle");
+ gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"].
+ getService(Components.interfaces.nsITelemetry).
+ getHistogramById("SECURITY_UI");
+ gNsISecTel = Components.interfaces.nsISecurityUITelemetry;
+
+ var brandName = gBundleBrand.getString("brandShortName");
+ setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName]));
+ gDialog.getButton("extra1").disabled = true;
+
+ var args = window.arguments;
+ if (args && args[0]) {
+ if (args[0].location) {
+ // We were pre-seeded with a location.
+ document.getElementById("locationTextBox").value = args[0].location;
+ document.getElementById('checkCertButton').disabled = false;
+
+ if (args[0].sslStatus) {
+ gSSLStatus = args[0].sslStatus;
+ gCert = gSSLStatus.serverCert;
+ gBroken = true;
+ updateCertStatus();
+ } else if (args[0].prefetchCert) {
+ // We can optionally pre-fetch the certificate too. Don't do this
+ // synchronously, since it would prevent the window from appearing
+ // until the fetch is completed, which could be multiple seconds.
+ // Instead, let's use a timer to spawn the actual fetch, but update
+ // the dialog to "checking..." state right away, so that the UI
+ // is appropriately responsive. Bug 453855
+ document.getElementById("checkCertButton").disabled = true;
+ gChecking = true;
+ updateCertStatus();
+
+ window.setTimeout(checkCert, 0);
+ }
+ }
+
+ // Set out parameter to false by default
+ args[0].exceptionAdded = false;
+ }
+}
+
+/**
+ * Attempt to download the certificate for the location specified, and populate
+ * the Certificate Status section with the result.
+ */
+function checkCert() {
+ gCert = null;
+ gSSLStatus = null;
+ gChecking = true;
+ gBroken = false;
+ updateCertStatus();
+
+ var uri = getURI();
+
+ var req = new XMLHttpRequest();
+ try {
+ if (uri) {
+ req.open('GET', uri.prePath, false);
+ req.channel.notificationCallbacks = new badCertListener();
+ req.send(null);
+ }
+ } catch (e) {
+ // We *expect* exceptions if there are problems with the certificate
+ // presented by the site. Log it, just in case, but we can proceed here,
+ // with appropriate sanity checks
+ Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " +
+ "This results in a (mostly harmless) exception being thrown. " +
+ "Logged for information purposes only: " + e);
+ } finally {
+ gChecking = false;
+ }
+
+ if (req.channel && req.channel.securityInfo) {
+ const Ci = Components.interfaces;
+ gSSLStatus = req.channel.securityInfo
+ .QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+ gCert = gSSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
+ }
+
+ updateCertStatus();
+}
+
+/**
+ * Build and return a URI, based on the information supplied in the
+ * Certificate Location fields
+ */
+function getURI() {
+ // Use fixup service instead of just ioservice's newURI since it's quite
+ // likely that the host will be supplied without a protocol prefix, resulting
+ // in malformed uri exceptions being thrown.
+ let fus = Components.classes["@mozilla.org/docshell/urifixup;1"]
+ .getService(Components.interfaces.nsIURIFixup);
+ let locationTextBox = document.getElementById("locationTextBox");
+ let uri = fus.createFixupURI(locationTextBox.value, 0);
+
+ if (!uri) {
+ return null;
+ }
+
+ if (uri.scheme == "http") {
+ uri.scheme = "https";
+ }
+
+ if (uri.port == -1) {
+ uri.port = 443;
+ }
+
+ return uri;
+}
+
+function resetDialog() {
+ document.getElementById("viewCertButton").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ gDialog.getButton("extra1").disabled = true;
+ setText("headerDescription", "");
+ setText("statusDescription", "");
+ setText("statusLongDescription", "");
+ setText("status2Description", "");
+ setText("status2LongDescription", "");
+ setText("status3Description", "");
+ setText("status3LongDescription", "");
+}
+
+/**
+ * Called by input textboxes to manage UI state
+ */
+function handleTextChange() {
+ var checkCertButton = document.getElementById('checkCertButton');
+ checkCertButton.disabled = !(document.getElementById("locationTextBox").value);
+ if (gNeedReset) {
+ gNeedReset = false;
+ resetDialog();
+ }
+}
+
+function updateCertStatus() {
+ var shortDesc, longDesc;
+ var shortDesc2, longDesc2;
+ var shortDesc3, longDesc3;
+ var use2 = false;
+ var use3 = false;
+ let bucketId = gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_BASE;
+ if (gCert) {
+ if (gBroken) {
+ var mms = "addExceptionDomainMismatchShort";
+ var mml = "addExceptionDomainMismatchLong2";
+ var exs = "addExceptionExpiredShort";
+ var exl = "addExceptionExpiredLong2";
+ var uts = "addExceptionUnverifiedOrBadSignatureShort";
+ var utl = "addExceptionUnverifiedOrBadSignatureLong2";
+ var use1 = false;
+ if (gSSLStatus.isDomainMismatch) {
+ bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_DOMAIN;
+ use1 = true;
+ shortDesc = mms;
+ longDesc = mml;
+ }
+ if (gSSLStatus.isNotValidAtThisTime) {
+ bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_TIME;
+ if (!use1) {
+ use1 = true;
+ shortDesc = exs;
+ longDesc = exl;
+ }
+ else {
+ use2 = true;
+ shortDesc2 = exs;
+ longDesc2 = exl;
+ }
+ }
+ if (gSSLStatus.isUntrusted) {
+ bucketId += gNsISecTel.WARNING_BAD_CERT_TOP_ADD_EXCEPTION_FLAG_UNTRUSTED;
+ if (!use1) {
+ use1 = true;
+ shortDesc = uts;
+ longDesc = utl;
+ } else if (!use2) {
+ use2 = true;
+ shortDesc2 = uts;
+ longDesc2 = utl;
+ } else {
+ use3 = true;
+ shortDesc3 = uts;
+ longDesc3 = utl;
+ }
+ }
+ gSecHistogram.add(bucketId);
+
+ // In these cases, we do want to enable the "Add Exception" button
+ gDialog.getButton("extra1").disabled = false;
+
+ // If the Private Browsing service is available and the mode is active,
+ // don't store permanent exceptions, since they would persist after
+ // private browsing mode was disabled.
+ var inPrivateBrowsing = inPrivateBrowsingMode();
+ var pe = document.getElementById("permanent");
+ pe.disabled = inPrivateBrowsing;
+ pe.checked = !inPrivateBrowsing;
+
+ setText("headerDescription", gPKIBundle.getString("addExceptionInvalidHeader"));
+ }
+ else {
+ shortDesc = "addExceptionValidShort";
+ longDesc = "addExceptionValidLong";
+ gDialog.getButton("extra1").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ }
+
+ // We're done checking the certificate, so allow the user to check it again.
+ document.getElementById("checkCertButton").disabled = false;
+ document.getElementById("viewCertButton").disabled = false;
+
+ // Notify observers about the availability of the certificate
+ Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .notifyObservers(null, "cert-exception-ui-ready", null);
+ }
+ else if (gChecking) {
+ shortDesc = "addExceptionCheckingShort";
+ longDesc = "addExceptionCheckingLong2";
+ // We're checking the certificate, so we disable the Get Certificate
+ // button to make sure that the user can't interrupt the process and
+ // trigger another certificate fetch.
+ document.getElementById("checkCertButton").disabled = true;
+ document.getElementById("viewCertButton").disabled = true;
+ gDialog.getButton("extra1").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ }
+ else {
+ shortDesc = "addExceptionNoCertShort";
+ longDesc = "addExceptionNoCertLong2";
+ // We're done checking the certificate, so allow the user to check it again.
+ document.getElementById("checkCertButton").disabled = false;
+ document.getElementById("viewCertButton").disabled = true;
+ gDialog.getButton("extra1").disabled = true;
+ document.getElementById("permanent").disabled = true;
+ }
+
+ setText("statusDescription", gPKIBundle.getString(shortDesc));
+ setText("statusLongDescription", gPKIBundle.getString(longDesc));
+
+ if (use2) {
+ setText("status2Description", gPKIBundle.getString(shortDesc2));
+ setText("status2LongDescription", gPKIBundle.getString(longDesc2));
+ }
+
+ if (use3) {
+ setText("status3Description", gPKIBundle.getString(shortDesc3));
+ setText("status3LongDescription", gPKIBundle.getString(longDesc3));
+ }
+
+ gNeedReset = true;
+}
+
+/**
+ * Handle user request to display certificate details
+ */
+function viewCertButtonClick() {
+ gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_CLICK_VIEW_CERT);
+ if (gCert) {
+ viewCertHelper(this, gCert);
+ }
+}
+
+/**
+ * Handle user request to add an exception for the specified cert
+ */
+function addException() {
+ if (!gCert || !gSSLStatus) {
+ return;
+ }
+
+ var overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
+ .getService(Components.interfaces.nsICertOverrideService);
+ var flags = 0;
+ let confirmBucketId = gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_BASE;
+ if (gSSLStatus.isUntrusted) {
+ flags |= overrideService.ERROR_UNTRUSTED;
+ confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_UNTRUSTED;
+ }
+ if (gSSLStatus.isDomainMismatch) {
+ flags |= overrideService.ERROR_MISMATCH;
+ confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_DOMAIN;
+ }
+ if (gSSLStatus.isNotValidAtThisTime) {
+ flags |= overrideService.ERROR_TIME;
+ confirmBucketId += gNsISecTel.WARNING_BAD_CERT_TOP_CONFIRM_ADD_EXCEPTION_FLAG_TIME;
+ }
+
+ var permanentCheckbox = document.getElementById("permanent");
+ var shouldStorePermanently = permanentCheckbox.checked && !inPrivateBrowsingMode();
+ if (!permanentCheckbox.checked) {
+ gSecHistogram.add(gNsISecTel.WARNING_BAD_CERT_TOP_DONT_REMEMBER_EXCEPTION);
+ }
+
+ gSecHistogram.add(confirmBucketId);
+ var uri = getURI();
+ overrideService.rememberValidityOverride(
+ uri.asciiHost, uri.port,
+ gCert,
+ flags,
+ !shouldStorePermanently);
+
+ let args = window.arguments;
+ if (args && args[0]) {
+ args[0].exceptionAdded = true;
+ }
+
+ gDialog.acceptDialog();
+}
+
+/**
+ * Returns true if this dialog is in private browsing mode.
+ */
+function inPrivateBrowsingMode() {
+ return PrivateBrowsingUtils.isWindowPrivate(window);
+}
diff --git a/security/manager/pki/resources/content/exceptionDialog.xul b/security/manager/pki/resources/content/exceptionDialog.xul
new file mode 100644
index 000000000..6d417f360
--- /dev/null
+++ b/security/manager/pki/resources/content/exceptionDialog.xul
@@ -0,0 +1,89 @@
+<?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 SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<dialog id="exceptiondialog"
+ windowtype="mozilla:exceptiondialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&exceptionMgr.title;"
+ buttons="cancel,extra1,extra2"
+ buttonlabelextra1="&exceptionMgr.exceptionButton.label;"
+ buttonaccesskeyextra1="&exceptionMgr.exceptionButton.accesskey;"
+ style="width: 46em; min-height: 38em;"
+ onload="initExceptionDialog();"
+ ondialogextra1="addException();"
+ ondialogextra2="checkCert();"
+ persist="screenX screenY width height"
+ defaultButton="extra2">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+ <stringbundle id="brand_bundle" src="chrome://branding/locale/brand.properties"/>
+ </stringbundleset>
+
+ <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+ <script type="application/javascript" src="chrome://pippki/content/exceptionDialog.js"/>
+
+ <hbox>
+ <vbox>
+#ifdef MOZ_WIDGET_GTK
+ <image src="moz-icon://stock/gtk-dialog-warning?size=dialog"/>
+#else
+ <image src="chrome://global/skin/icons/warning-large.png"/>
+#endif
+ <spacer flex="1"/>
+ </vbox>
+ <vbox flex="1">
+ <!-- Note that because of the styling, there must be no whitespace within
+ the description tags -->
+ <description id="warningText"
+ style="white-space: pre-wrap"/>
+ <description id="warningSupplemental"
+ style="font-weight: bold; white-space: pre-wrap;"
+ >&exceptionMgr.supplementalWarning;</description>
+ </vbox>
+ </hbox>
+
+ <groupbox id="locationGroupBox">
+ <caption label="&exceptionMgr.certlocation.caption2;"/>
+ <hbox align="center">
+ <label control="locationTextBox" value="&exceptionMgr.certlocation.url;"/>
+ <textbox id="locationTextBox" flex="1" oninput="handleTextChange();"
+ value="https://" class="uri-element"/>
+ <button id="checkCertButton" disabled="true" dlgtype="extra2"
+ accesskey="&exceptionMgr.certlocation.accesskey;"
+ label="&exceptionMgr.certlocation.download;"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox id="certStatusGroupBox" flex="1">
+ <caption label="&exceptionMgr.certstatus.caption;"/>
+ <hbox>
+ <description id="headerDescription" style="white-space: pre-wrap;"
+ flex="1"/>
+ <vbox>
+ <button id="viewCertButton" label="&exceptionMgr.certstatus.viewCert;"
+ accesskey="&exceptionMgr.certstatus.accesskey;"
+ disabled="true" oncommand="viewCertButtonClick();"/>
+ </vbox>
+ </hbox>
+ <description id="statusDescription"
+ style="font-weight: bold; padding-bottom: 1em;"/>
+ <description id="statusLongDescription" style="white-space: pre-wrap;"/>
+ <description id="status2Description"
+ style="font-weight: bold; padding-bottom: 1em;"/>
+ <description id="status2LongDescription" style="white-space: pre-wrap;"/>
+ <description id="status3Description"
+ style="font-weight: bold; padding-bottom: 1em;"/>
+ <description id="status3LongDescription" style="white-space: pre-wrap;"/>
+ <spacer flex="1"/>
+ <checkbox id="permanent" disabled="true"
+ label="&exceptionMgr.permanent.label;"
+ accesskey="&exceptionMgr.permanent.accesskey;"/>
+ </groupbox>
+</dialog>
diff --git a/security/manager/pki/resources/content/load_device.xul b/security/manager/pki/resources/content/load_device.xul
new file mode 100644
index 000000000..45fffcee7
--- /dev/null
+++ b/security/manager/pki/resources/content/load_device.xul
@@ -0,0 +1,42 @@
+<?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 % deviceManangerDTD SYSTEM "chrome://pippki/locale/deviceManager.dtd">
+%deviceManangerDTD;
+<!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
+%pippkiDTD;
+]>
+
+<dialog id="loaddevice"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&loaddevice.title;"
+ buttons="accept,cancel"
+ ondialogaccept="return doLoadDevice();">
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+ <stringbundle id="pipnss_bundle" src="chrome://pipnss/locale/pipnss.properties"/>
+</stringbundleset>
+
+ <script type="application/javascript" src="chrome://pippki/content/device_manager.js"/>
+
+ <description>&loaddevice.info;</description>
+ <hbox align="center">
+ <label value="&loaddevice.modname;" accesskey="&loaddevice.modname.accesskey;"
+ control="device_name"/>
+ <textbox id="device_name" flex="1" value="&loaddevice.modname.default;"/>
+ </hbox>
+ <hbox align="center">
+ <label value="&loaddevice.filename;" accesskey="&loaddevice.filename.accesskey;"
+ control="device_path"/>
+ <textbox id="device_path" flex="1"/>
+ <button label="&loaddevice.browse;" flex="1"
+ accesskey="&loaddevice.browse.accesskey;" oncommand="doBrowseFiles();"/>
+ </hbox>
+
+</dialog>
diff --git a/security/manager/pki/resources/content/pippki.js b/security/manager/pki/resources/content/pippki.js
new file mode 100644
index 000000000..b660d2d93
--- /dev/null
+++ b/security/manager/pki/resources/content/pippki.js
@@ -0,0 +1,191 @@
+/* -*- 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";
+
+/*
+ * These are helper functions to be included
+ * pippki UI js files.
+ */
+
+function setText(id, value) {
+ let element = document.getElementById(id);
+ if (!element) {
+ return;
+ }
+ if (element.hasChildNodes()) {
+ element.removeChild(element.firstChild);
+ }
+ element.appendChild(document.createTextNode(value));
+}
+
+const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
+const nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1";
+
+function viewCertHelper(parent, cert) {
+ if (!cert) {
+ return;
+ }
+
+ var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs);
+ cd.viewCert(parent, cert);
+}
+
+function getDERString(cert)
+{
+ var length = {};
+ var derArray = cert.getRawDER(length);
+ var derString = '';
+ for (var i = 0; i < derArray.length; i++) {
+ derString += String.fromCharCode(derArray[i]);
+ }
+ return derString;
+}
+
+function getPKCS7String(cert, chainMode)
+{
+ var length = {};
+ var pkcs7Array = cert.exportAsCMS(chainMode, length);
+ var pkcs7String = '';
+ for (var i = 0; i < pkcs7Array.length; i++) {
+ pkcs7String += String.fromCharCode(pkcs7Array[i]);
+ }
+ return pkcs7String;
+}
+
+function getPEMString(cert)
+{
+ var derb64 = btoa(getDERString(cert));
+ // Wrap the Base64 string into lines of 64 characters with CRLF line breaks
+ // (as specified in RFC 1421).
+ var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n");
+ return "-----BEGIN CERTIFICATE-----\r\n"
+ + wrapped
+ + "\r\n-----END CERTIFICATE-----\r\n";
+}
+
+function alertPromptService(title, message)
+{
+ var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Components.interfaces.nsIPromptService);
+ ps.alert(window, title, message);
+}
+
+const DEFAULT_CERT_EXTENSION = "crt";
+
+/**
+ * Generates a filename for a cert suitable to set as the |defaultString|
+ * attribute on an nsIFilePicker.
+ *
+ * @param {nsIX509Cert} cert
+ * The cert to generate a filename for.
+ * @returns {String}
+ * Generated filename.
+ */
+function certToFilename(cert) {
+ let filename = cert.commonName;
+ if (!filename) {
+ filename = cert.windowTitle;
+ }
+
+ // Remove unneeded and/or unsafe characters.
+ filename = filename.replace(/\s/g, "")
+ .replace(/\./g, "")
+ .replace(/\\/g, "")
+ .replace(/\//g, "");
+
+ // nsIFilePicker.defaultExtension is more of a suggestion to some
+ // implementations, so we include the extension in the file name as well. This
+ // is what the documentation for nsIFilePicker.defaultString says we should do
+ // anyways.
+ return `${filename}.${DEFAULT_CERT_EXTENSION}`;
+}
+
+function exportToFile(parent, cert)
+{
+ var bundle = document.getElementById("pippki_bundle");
+ if (!cert) {
+ return;
+ }
+
+ var nsIFilePicker = Components.interfaces.nsIFilePicker;
+ var fp = Components.classes["@mozilla.org/filepicker;1"].
+ createInstance(nsIFilePicker);
+ fp.init(parent, bundle.getString("SaveCertAs"),
+ nsIFilePicker.modeSave);
+ fp.defaultString = certToFilename(cert);
+ fp.defaultExtension = DEFAULT_CERT_EXTENSION;
+ fp.appendFilter(bundle.getString("CertFormatBase64"), "*.crt; *.pem");
+ fp.appendFilter(bundle.getString("CertFormatBase64Chain"), "*.crt; *.pem");
+ fp.appendFilter(bundle.getString("CertFormatDER"), "*.der");
+ fp.appendFilter(bundle.getString("CertFormatPKCS7"), "*.p7c");
+ fp.appendFilter(bundle.getString("CertFormatPKCS7Chain"), "*.p7c");
+ fp.appendFilters(nsIFilePicker.filterAll);
+ var res = fp.show();
+ if (res != nsIFilePicker.returnOK && res != nsIFilePicker.returnReplace) {
+ return;
+ }
+
+ var content = '';
+ switch (fp.filterIndex) {
+ case 1:
+ content = getPEMString(cert);
+ var chain = cert.getChain();
+ for (let i = 1; i < chain.length; i++) {
+ content += getPEMString(chain.queryElementAt(i, Components.interfaces.nsIX509Cert));
+ }
+ break;
+ case 2:
+ content = getDERString(cert);
+ break;
+ case 3:
+ content = getPKCS7String(cert, Components.interfaces.nsIX509Cert.CMS_CHAIN_MODE_CertOnly);
+ break;
+ case 4:
+ content = getPKCS7String(cert, Components.interfaces.nsIX509Cert.CMS_CHAIN_MODE_CertChainWithRoot);
+ break;
+ case 0:
+ default:
+ content = getPEMString(cert);
+ break;
+ }
+ var msg;
+ var written = 0;
+ try {
+ var file = Components.classes["@mozilla.org/file/local;1"].
+ createInstance(Components.interfaces.nsILocalFile);
+ file.initWithPath(fp.file.path);
+ var fos = Components.classes["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Components.interfaces.nsIFileOutputStream);
+ // flags: PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE
+ fos.init(file, 0x02 | 0x08 | 0x20, 0o0644, 0);
+ written = fos.write(content, content.length);
+ fos.close();
+ } catch (e) {
+ switch (e.result) {
+ case Components.results.NS_ERROR_FILE_ACCESS_DENIED:
+ msg = bundle.getString("writeFileAccessDenied");
+ break;
+ case Components.results.NS_ERROR_FILE_IS_LOCKED:
+ msg = bundle.getString("writeFileIsLocked");
+ break;
+ case Components.results.NS_ERROR_FILE_NO_DEVICE_SPACE:
+ case Components.results.NS_ERROR_FILE_DISK_FULL:
+ msg = bundle.getString("writeFileNoDeviceSpace");
+ break;
+ default:
+ msg = e.message;
+ break;
+ }
+ }
+ if (written != content.length) {
+ if (msg.length == 0) {
+ msg = bundle.getString("writeFileUnknownError");
+ }
+ alertPromptService(bundle.getString("writeFileFailure"),
+ bundle.getFormattedString("writeFileFailed",
+ [fp.file.path, msg]));
+ }
+}
diff --git a/security/manager/pki/resources/content/protectedAuth.js b/security/manager/pki/resources/content/protectedAuth.js
new file mode 100644
index 000000000..3ffd96a83
--- /dev/null
+++ b/security/manager/pki/resources/content/protectedAuth.js
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+function onLoad()
+{
+ let protectedAuthThread =
+ window.arguments[0].QueryInterface(Components.interfaces.nsIProtectedAuthThread);
+
+ if (!protectedAuthThread) {
+ window.close();
+ return;
+ }
+
+ try {
+ let tokenName = protectedAuthThread.getTokenName();
+
+ let tag = document.getElementById("tokenName");
+ tag.setAttribute("value", tokenName);
+
+ window.setCursor("wait");
+
+ let obs = {
+ observe: function protectedAuthListenerObserve(subject, topic, data) {
+ if (topic == "operation-completed") {
+ window.close();
+ }
+ }
+ };
+
+ protectedAuthThread.login(obs);
+ } catch (exception) {
+ window.close();
+ }
+}
+
+function onClose()
+{
+ window.setCursor("auto");
+}
diff --git a/security/manager/pki/resources/content/protectedAuth.xul b/security/manager/pki/resources/content/protectedAuth.xul
new file mode 100644
index 000000000..46aba7cce
--- /dev/null
+++ b/security/manager/pki/resources/content/protectedAuth.xul
@@ -0,0 +1,33 @@
+<?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 window SYSTEM "chrome://pippki/locale/pippki.dtd">
+
+<window
+ id="protectedAuth" title="&protectedAuth.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad();"
+ onclose="onClose();"
+>
+
+<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+<script type="application/javascript" src="pippki.js" />
+<script type="application/javascript" src="protectedAuth.js" />
+<script type="application/javascript" src="chrome://help/content/help.js" />
+
+<vbox style="margin: 5px; max-width: 50em;">
+
+ <description>&protectedAuth.msg;</description>
+
+ <hbox>
+ <description>&protectedAuth.tokenName.label;</description>
+ <description id="tokenName"></description>
+ </hbox>
+
+</vbox>
+</window>
diff --git a/security/manager/pki/resources/content/resetpassword.js b/security/manager/pki/resources/content/resetpassword.js
new file mode 100644
index 000000000..35dc5b2e5
--- /dev/null
+++ b/security/manager/pki/resources/content/resetpassword.js
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from pippki.js */
+"use strict";
+
+const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
+const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
+const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+
+var tokenName;
+
+function onLoad()
+{
+ if ("arguments" in window) {
+ var params = window.arguments[0].QueryInterface(nsIDialogParamBlock);
+ tokenName = params.GetString(1);
+ } else {
+ tokenName = self.name;
+ }
+}
+
+function resetPassword()
+{
+ var pk11db = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+ var token = pk11db.findTokenByName(tokenName);
+ token.reset();
+
+ try {
+ var loginManager = Components.classes["@mozilla.org/login-manager;1"].
+ getService(Components.interfaces.nsILoginManager);
+ loginManager.removeAllLogins();
+ } catch (e) {
+ }
+
+ var bundle = document.getElementById("pippki_bundle");
+ var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
+ promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
+ if (promptService && bundle) {
+ promptService.alert(window,
+ bundle.getString("resetPasswordConfirmationTitle"),
+ bundle.getString("resetPasswordConfirmationMessage"));
+ }
+
+ return true;
+}
+
diff --git a/security/manager/pki/resources/content/resetpassword.xul b/security/manager/pki/resources/content/resetpassword.xul
new file mode 100644
index 000000000..6da79b0d9
--- /dev/null
+++ b/security/manager/pki/resources/content/resetpassword.xul
@@ -0,0 +1,35 @@
+<?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 SYSTEM "chrome://pippki/locale/pippki.dtd">
+
+<dialog id="reset_password" title="&resetPassword.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ buttonlabelaccept="&resetPasswordButtonLabel;"
+ defaultButton="cancel"
+ ondialogaccept="return resetPassword();"
+ style="width: 40em;" onload="onLoad();">
+
+ <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
+
+ <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
+ <script type="application/javascript" src="chrome://pippki/content/resetpassword.js"/>
+
+ <hbox flex="1">
+ <vbox>
+ <image class="alert-icon" style="margin: 5px;"/>
+ </vbox>
+ <vbox style="margin: 5px;" flex="1">
+ <hbox flex="1">
+ <vbox flex="1">
+ <description>&resetPassword.text;</description>
+ </vbox>
+ </hbox>
+ </vbox>
+ </hbox>
+</dialog>
diff --git a/security/manager/pki/resources/content/setp12password.js b/security/manager/pki/resources/content/setp12password.js
new file mode 100644
index 000000000..5a4836735
--- /dev/null
+++ b/security/manager/pki/resources/content/setp12password.js
@@ -0,0 +1,128 @@
+/* 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";
+
+/**
+ * @file Implements the functionality of setp12password.xul: a dialog that lets
+ * the user confirm the password to set on a PKCS #12 file.
+ * @argument {nsISupports} window.arguments[0]
+ * Object to set the return values of calling the dialog on, queryable
+ * to the underlying type of SetP12PasswordReturnValues.
+ */
+
+/**
+ * @typedef SetP12PasswordReturnValues
+ * @type nsIWritablePropertyBag2
+ * @property {Boolean} confirmedPassword
+ * Set to true if the user entered two matching passwords and
+ * confirmed the dialog.
+ * @property {String} password
+ * The password the user entered. Undefined value if
+ * |confirmedPassword| is not true.
+ */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+/**
+ * onload() handler.
+ */
+function onLoad() {
+ // Ensure the first password textbox has focus.
+ document.getElementById("pw1").focus();
+}
+
+/**
+ * ondialogaccept() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogAccept() {
+ let password = document.getElementById("pw1").value;
+
+ let retVals = window.arguments[0].QueryInterface(Ci.nsIWritablePropertyBag2);
+ retVals.setPropertyAsBool("confirmedPassword", true);
+ retVals.setPropertyAsAString("password", password);
+ return true;
+}
+
+/**
+ * ondialogcancel() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogCancel() {
+ let retVals = window.arguments[0].QueryInterface(Ci.nsIWritablePropertyBag2);
+ retVals.setPropertyAsBool("confirmedPassword", false);
+ return true;
+}
+
+/**
+ * Calculates the strength of the given password, suitable for use in updating
+ * a progress bar that represents said strength.
+ *
+ * The strength of the password is calculated by checking the number of:
+ * - Characters
+ * - Numbers
+ * - Non-alphanumeric chars
+ * - Upper case characters
+ *
+ * @param {String} password
+ * The password to calculate the strength of.
+ * @returns {Number}
+ * The strength of the password in the range [0, 100].
+ */
+function getPasswordStrength(password) {
+ let lengthStrength = password.length;
+ if (lengthStrength > 5) {
+ lengthStrength = 5;
+ }
+
+ let nonNumericChars = password.replace(/[0-9]/g, "");
+ let numericStrength = password.length - nonNumericChars.length;
+ if (numericStrength > 3) {
+ numericStrength = 3;
+ }
+
+ let nonSymbolChars = password.replace(/\W/g, "");
+ let symbolStrength = password.length - nonSymbolChars.length;
+ if (symbolStrength > 3) {
+ symbolStrength = 3;
+ }
+
+ let nonUpperAlphaChars = password.replace(/[A-Z]/g, "");
+ let upperAlphaStrength = password.length - nonUpperAlphaChars.length;
+ if (upperAlphaStrength > 3) {
+ upperAlphaStrength = 3;
+ }
+
+ let strength = (lengthStrength * 10) - 20 + (numericStrength * 10) +
+ (symbolStrength * 15) + (upperAlphaStrength * 10);
+ if (strength < 0) {
+ strength = 0;
+ }
+ if (strength > 100) {
+ strength = 100;
+ }
+
+ return strength;
+}
+
+/**
+ * oninput() handler for both password textboxes.
+ *
+ * @param {Boolean} recalculatePasswordStrength
+ * Whether to recalculate the strength of the first password.
+ */
+function onPasswordInput(recalculatePasswordStrength) {
+ let pw1 = document.getElementById("pw1").value;
+
+ if (recalculatePasswordStrength) {
+ document.getElementById("pwmeter").value = getPasswordStrength(pw1);
+ }
+
+ // Disable the accept button if the two passwords don't match, and enable it
+ // if the passwords do match.
+ let pw2 = document.getElementById("pw2").value;
+ document.documentElement.getButton("accept").disabled = (pw1 != pw2);
+}
diff --git a/security/manager/pki/resources/content/setp12password.xul b/security/manager/pki/resources/content/setp12password.xul
new file mode 100644
index 000000000..2bdcd5fe9
--- /dev/null
+++ b/security/manager/pki/resources/content/setp12password.xul
@@ -0,0 +1,51 @@
+<?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 SYSTEM "chrome://pippki/locale/pippki.dtd">
+
+<dialog id="setp12password"
+ title="&pkcs12.setpassword.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width: 48em;"
+ buttons="accept,cancel"
+ ondialogaccept="return onDialogAccept();"
+ ondialogcancel="return onDialogCancel();"
+ onload="onLoad();">
+
+ <script type="application/javascript"
+ src="chrome://pippki/content/setp12password.js"/>
+
+ <description>&pkcs12.setpassword.message;</description>
+ <separator />
+ <grid>
+ <columns> <column/> <column/> </columns>
+ <rows>
+ <row>
+ <label value="&pkcs12.setpassword.label1;"/>
+ <textbox id="pw1" type="password" oninput="onPasswordInput(true);"/>
+ </row>
+ <row>
+ <label value="&pkcs12.setpassword.label2;"/>
+ <textbox id="pw2" type="password" oninput="onPasswordInput(false);"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ <description>&pkcs12.setpassword.reminder;</description>
+ <separator/>
+ <label value="&setPassword.meter.label;"/>
+ <grid style="margin: 4px;">
+ <rows> <row/> </rows>
+ <columns>
+ <column style="margin: 5px;">
+ <progressmeter flex="1" id="pwmeter" mode="determined" value="0"
+ orient="horizontal"
+ style="width: 20em; foreground-color: red"/>
+ </column>
+ </columns>
+ </grid>
+</dialog>
diff --git a/security/manager/pki/resources/content/viewCertDetails.xul b/security/manager/pki/resources/content/viewCertDetails.xul
new file mode 100644
index 000000000..86acb751c
--- /dev/null
+++ b/security/manager/pki/resources/content/viewCertDetails.xul
@@ -0,0 +1,104 @@
+<?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/. -->
+<!DOCTYPE overlay SYSTEM "chrome://pippki/locale/certManager.dtd">
+
+<overlay id="certViewerOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cert="http://netscape.com/rdf-cert#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<vbox class="box-padded" id="general_info">
+ <vbox id="verify_info_box">
+ <label id="verify_pending" value="&certmgr.pending.label;"/>
+ <label class="header" id="verified"/>
+ </vbox>
+ <separator class="groove"/>
+ <vbox flex="1">
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <label class="header" value="&certmgr.subjectinfo.label;"/>
+ <spacer/>
+ <spacer/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.cn;"/>
+ <textbox id="commonname" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.o;"/>
+ <textbox id="organization" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.ou;"/>
+ <textbox id="orgunit" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.serialnumber;"/>
+ <textbox id="serialnumber" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ <spacer/>
+ </row>
+ <row>
+ <label class="header" value="&certmgr.issuerinfo.label;"/>
+ <spacer/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.cn;"/>
+ <textbox id="issuercommonname" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.o;"/>
+ <textbox id="issuerorganization" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.ou;"/>
+ <textbox id="issuerorgunit" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ <spacer/>
+ </row>
+ <row>
+ <label class ="header" value="&certmgr.periodofvalidity.label;"/>
+ <spacer/>
+ </row>
+ <row>
+ <label value="&certmgr.begins;"/>
+ <textbox id="validitystart" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <label value="&certmgr.expires;"/>
+ <textbox id="validityend" class="plain" readonly="true"/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ <spacer/>
+ </row>
+ <row>
+ <label class="header" value="&certmgr.fingerprints.label;"/>
+ <spacer/>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.sha256fingerprint;"/>
+ <hbox>
+ <textbox id="sha256fingerprint" class="plain" readonly="true" multiline="true"
+ style="height: 6ex; width: 48ch; font-family: monospace;"/>
+ </hbox>
+ </row>
+ <row>
+ <label value="&certmgr.certdetail.sha1fingerprint;"/>
+ <textbox id="sha1fingerprint" class="plain" readonly="true" style="min-width:34em;"/>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+</vbox>
+</overlay>
diff --git a/security/manager/pki/resources/jar.mn b/security/manager/pki/resources/jar.mn
new file mode 100644
index 000000000..36018524f
--- /dev/null
+++ b/security/manager/pki/resources/jar.mn
@@ -0,0 +1,43 @@
+# 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/.
+
+pippki.jar:
+% content pippki %content/pippki/
+ content/pippki/CAOverlay.xul (content/CAOverlay.xul)
+ content/pippki/MineOverlay.xul (content/MineOverlay.xul)
+ content/pippki/OrphanOverlay.xul (content/OrphanOverlay.xul)
+ content/pippki/OthersOverlay.xul (content/OthersOverlay.xul)
+ content/pippki/WebSitesOverlay.xul (content/WebSitesOverlay.xul)
+ content/pippki/certDump.xul (content/certDump.xul)
+ content/pippki/certManager.js (content/certManager.js)
+ content/pippki/certManager.xul (content/certManager.xul)
+ content/pippki/certViewer.js (content/certViewer.js)
+ content/pippki/certViewer.xul (content/certViewer.xul)
+ content/pippki/changepassword.js (content/changepassword.js)
+ content/pippki/changepassword.xul (content/changepassword.xul)
+ content/pippki/choosetoken.js (content/choosetoken.js)
+ content/pippki/choosetoken.xul (content/choosetoken.xul)
+ content/pippki/clientauthask.js (content/clientauthask.js)
+ content/pippki/clientauthask.xul (content/clientauthask.xul)
+ content/pippki/createCertInfo.js (content/createCertInfo.js)
+ content/pippki/createCertInfo.xul (content/createCertInfo.xul)
+ content/pippki/deletecert.js (content/deletecert.js)
+ content/pippki/deletecert.xul (content/deletecert.xul)
+ content/pippki/device_manager.js (content/device_manager.js)
+ content/pippki/device_manager.xul (content/device_manager.xul)
+ content/pippki/downloadcert.js (content/downloadcert.js)
+ content/pippki/downloadcert.xul (content/downloadcert.xul)
+ content/pippki/editcacert.js (content/editcacert.js)
+ content/pippki/editcacert.xul (content/editcacert.xul)
+ content/pippki/exceptionDialog.js (content/exceptionDialog.js)
+* content/pippki/exceptionDialog.xul (content/exceptionDialog.xul)
+ content/pippki/load_device.xul (content/load_device.xul)
+ content/pippki/pippki.js (content/pippki.js)
+ content/pippki/protectedAuth.js (content/protectedAuth.js)
+ content/pippki/protectedAuth.xul (content/protectedAuth.xul)
+ content/pippki/resetpassword.js (content/resetpassword.js)
+ content/pippki/resetpassword.xul (content/resetpassword.xul)
+ content/pippki/setp12password.js (content/setp12password.js)
+ content/pippki/setp12password.xul (content/setp12password.xul)
+ content/pippki/viewCertDetails.xul (content/viewCertDetails.xul)
diff --git a/security/manager/pki/resources/moz.build b/security/manager/pki/resources/moz.build
new file mode 100644
index 000000000..eb4454d28
--- /dev/null
+++ b/security/manager/pki/resources/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file