Bug 1594855 - Import additional parts of Enigmail. r=patrick DONTBUILD
authorKai Engert <kaie@kuix.de>
Fri, 22 Nov 2019 11:57:04 +0100
changeset 37581 f0114d8e2104e1080991df7361bfe2fcef0ec37a
parent 37580 cb5b2500f62187226ff94bbf6b9937fedfaf08be
child 37582 736117fc5641a89df4b114b58def785352607037
push id396
push userclokep@gmail.com
push dateMon, 06 Jan 2020 23:11:57 +0000
reviewerspatrick
bugs1594855
Bug 1594855 - Import additional parts of Enigmail. r=patrick DONTBUILD
mail/extensions/openpgp/content/strings/am-enigprefs.properties
mail/extensions/openpgp/content/ui/am-enigprefs.js
mail/extensions/openpgp/content/ui/enigmailEditIdentity.js
mail/extensions/openpgp/content/ui/enigmailEditIdentity.xul
mail/extensions/openpgp/content/ui/enigmailKeygen.js
mail/extensions/openpgp/content/ui/enigmailKeygen.xul
mail/extensions/openpgp/content/ui/enigmailMsgBox.js
mail/extensions/openpgp/content/ui/enigmailMsgBox.xul
mail/extensions/openpgp/content/ui/keyDetailsDlg.js
mail/extensions/openpgp/content/ui/keyDetailsDlg.xul
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/strings/am-enigprefs.properties
@@ -0,0 +1,2 @@
+# Strings used in the Mozill AccountManager
+prefPanel-enigprefs=OpenPGP Security
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/am-enigprefs.js
@@ -0,0 +1,106 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+var EnigmailLog = ChromeUtils.import("chrome://openpgp/content/modules/log.jsm").EnigmailLog;
+var EnigmailCore = ChromeUtils.import("chrome://openpgp/content/modules/core.jsm").EnigmailCore;
+var Overlays = ChromeUtils.import("chrome://openpgp/content/modules/overlays.jsm").Overlays;
+
+if (!Enigmail) var Enigmail = {};
+
+var gPref = null;
+
+function onInit() {
+  EnigmailLog.DEBUG("am-enigprefs.js: onInit()\n");
+  Enigmail.overlayInitialized = true;
+
+  if (Enigmail.overlayLoaded) performInit();
+}
+
+function performInit() {
+  EnigmailLog.DEBUG("am-enigprefs.js: performInit()\n");
+
+  Enigmail.edit.onInit();
+}
+
+function onAcceptEditor() {
+  EnigmailLog.DEBUG("am-enigprefs.js: onAcceptEditor()\n");
+  Enigmail.edit.onSave();
+  saveChanges();
+  return true;
+}
+
+function onPreInit(account, accountValues) {
+  EnigmailLog.DEBUG("am-enigprefs.js: onPreInit()\n");
+
+  Enigmail.overlayLoaded = false;
+  Enigmail.overlayInitialized = false;
+
+  if (!EnigmailCore.getService()) {
+    return;
+  }
+
+  let foundEnigmail = document.getElementById("enigmail_enablePgp");
+
+  if (!foundEnigmail) {
+    // Enigmail Overlay not yet loaded
+    Overlays.loadOverlays("enigmail-am", window, ["chrome://openpgp/content/ui/enigmailEditIdentity.xul"]).then(
+      nLoaded => {
+        EnigmailLog.DEBUG("am-enigprefs.js: onPreInit: XUL loaded\n");
+
+        Enigmail.edit.identity = account.defaultIdentity;
+        Enigmail.edit.account = account;
+        Enigmail.overlayLoaded = true;
+
+        try {
+          if (Enigmail.overlayInitialized) performInit();
+        } catch (ex) {
+          EnigmailLog.ERROR("am-enigprefs.js: onPreInit: error: " + ex.message + "\n");
+        }
+      }
+    );
+  } else {
+    // Enigmail Overlay already loaded
+    Enigmail.edit.identity = account.defaultIdentity;
+    Enigmail.edit.account = account;
+    Enigmail.overlayLoaded = true;
+  }
+}
+
+function onSave() {
+  EnigmailLog.DEBUG("am-enigprefs.js: onSave()\n");
+
+  Enigmail.edit.onSave();
+  saveChanges();
+  return true;
+}
+
+function onLockPreference() {
+  // do nothing
+}
+
+// Does the work of disabling an element given the array which contains xul id/prefstring pairs.
+// Also saves the id/locked state in an array so that other areas of the code can avoid
+// stomping on the disabled state indiscriminately.
+function disableIfLocked(prefstrArray) {
+  // do nothing
+}
+
+function enigmailOnAcceptEditor() {
+  EnigmailLog.DEBUG("am-enigprefs.js: enigmailOnAcceptEditor()\n");
+
+  Enigmail.edit.onSave();
+
+  return true; // allow to close dialog in all cases
+}
+
+
+function saveChanges() {}
+
+document.addEventListener("dialogaccept", function(event) {
+  Enigmail.edit.onAcceptEditor();
+});
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/enigmailEditIdentity.js
@@ -0,0 +1,244 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/* global gAccount: false, gIdentity: false, onOk: false, smimeOnAcceptEditor: false */
+
+"use strict";
+
+var EnigmailFuncs = ChromeUtils.import("chrome://openpgp/content/modules/funcs.jsm").EnigmailFuncs;
+var EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
+var EnigmailWindows = ChromeUtils.import("chrome://openpgp/content/modules/windows.jsm").EnigmailWindows;
+var EnigmailDialog = ChromeUtils.import("chrome://openpgp/content/modules/dialog.jsm").EnigmailDialog;
+
+if (!Enigmail) var Enigmail = {};
+
+Enigmail.edit = {
+  account: null,
+  identity: null,
+  enablePgp: null,
+  pgpKeyMode: null,
+  pgpKeyId: null,
+  cryptoChoicesEnabled: null,
+  signingPolicy: null, // account specific: by default sign
+  encryptionPolicy: null, // account specific: by default encrypt
+  pgpMimeMode: null, // account specific: by default pgp/mime
+  pgpSignPlainPolicy: null,
+  pgpSignEncPolicy: null,
+  autoEncryptDrafts: null,
+  openPgpSendKeyWithMsg: null,
+
+  onInit: function() {
+    // initialize all of our elements based on the current identity values....
+    EnigmailFuncs.collapseAdvanced(document.getElementById("enigmail_PrefsBox"), "hidden");
+
+    this.enablePgp = document.getElementById("enigmail_enablePgp");
+    this.pgpKeyMode = document.getElementById("enigmail_pgpKeyMode");
+    this.pgpKeyId = document.getElementById("enigmail_identity.pgpkeyId");
+    this.signingPolicy = document.getElementById("enigmail_sign_ifPossible");
+    this.encryptionPolicy = document.getElementById("enigmail_encrypt_ifPossible");
+    this.pgpMimeMode = document.getElementById("enigmail_pgpMimeMode");
+    this.pgpSignEncPolicy = document.getElementById("enigmail_sign_encrypted");
+    this.pgpSignPlainPolicy = document.getElementById("enigmail_sign_notEncrypted");
+    this.autoEncryptDrafts = document.getElementById("enigmail_autoEncryptDrafts");
+    this.mimePreferOpenPGP = document.getElementById("enigmail_mimePreferOpenPGP");
+    this.enableAc = document.getElementById("enigmail_enableAutocrypt");
+    this.acPreferEncrypt = document.getElementById("enigmail_acPreferEncrypt");
+    this.isSingleIdEditor = document.getElementById("enigmail_singleId") ? true : false;
+    this.openPgpSendKeyWithMsg = document.getElementById("openpgp.sendKeyWithMsg");
+
+    document.getElementById("enigmail_amPrefAutocryptDesc").innerHTML = EnigmailLocale.getString("amPrefAutocrypt.desc");
+
+    if (this.isSingleIdEditor) {
+      let acTab = document.getElementById("enigmail_autocryptTab");
+      acTab.setAttribute("collapsed", "true");
+    }
+
+    if (this.identity) {
+      this.enablePgp.checked = this.identity.getBoolAttribute("enablePgp");
+      this.cryptoChoicesEnabled = this.enablePgp.checked;
+
+      var selectedItemId = null;
+      var keyPolicy = this.identity.getIntAttribute("pgpKeyMode");
+      switch (keyPolicy) {
+        case 1:
+          selectedItemId = 'enigmail_keymode_usePgpkeyId';
+          break;
+        default:
+          selectedItemId = 'enigmail_keymode_useFromAddress';
+          break;
+      }
+      this.pgpKeyMode.selectedItem = document.getElementById(selectedItemId);
+
+      var mimePolicy = this.identity.getIntAttribute("mimePreferOpenPGP");
+      switch (mimePolicy) {
+        case 1:
+          selectedItemId = "enigmail_mime_preferEnigmail";
+          break;
+        default:
+          selectedItemId = "enigmail_mime_preferSMime";
+          break;
+      }
+      this.mimePreferOpenPGP.selectedItem = document.getElementById(selectedItemId);
+
+      this.pgpKeyId.value = this.identity.getCharAttribute("pgpkeyId");
+      this.signingPolicy.checked = (this.identity.getIntAttribute("defaultSigningPolicy") > 0);
+      this.encryptionPolicy.checked = (this.identity.getIntAttribute("defaultEncryptionPolicy") > 0);
+      this.pgpMimeMode.checked = this.identity.getBoolAttribute("pgpMimeMode");
+      this.pgpSignEncPolicy.checked = this.identity.getBoolAttribute("pgpSignEncrypted");
+      this.pgpSignPlainPolicy.checked = this.identity.getBoolAttribute("pgpSignPlain");
+      this.autoEncryptDrafts.checked = this.identity.getBoolAttribute("autoEncryptDrafts");
+
+    } else {
+      this.enablePgp.checked = false;
+      this.cryptoChoicesEnabled = false;
+      this.pgpMimeMode.checked = true;
+      this.pgpSignEncPolicy.checked = true;
+      this.autoEncryptDrafts.checked = true;
+    }
+
+    if (this.account) {
+      this.enableAc.checked = this.account.incomingServer.getBoolValue("enableAutocrypt");
+      this.acPreferEncrypt.checked = (this.account.incomingServer.getIntValue("acPreferEncrypt") > 0);
+    } else {
+      this.enableAc.checked = true;
+    }
+
+
+    // Disable all locked elements on the panel
+    //onLockPreference();
+    this.enableAllPrefs();
+  },
+
+  onLoadEditor: function() {
+    if (typeof(gAccount) == "object") {
+      this.account = gAccount;
+      this.identity = gIdentity;
+    } else if ("arguments" in window) {
+      this.identity = window.arguments[0].identity;
+      this.account = window.arguments[0].account;
+    }
+
+    if (this.identity) {
+      var idLabel = EnigmailLocale.getString("identityName", [this.identity.identityName]);
+      document.getElementById("enigmail_identityName").value = idLabel;
+    }
+
+    var dlg = document.getElementsByTagName("dialog")[0];
+    dlg.setAttribute("ondialogaccept", "return Enigmail.edit.onAcceptEditor();");
+
+    this.onInit();
+  },
+
+  onAcceptEditor: function() {
+    try {
+      if (onOk() === false) {
+        return false;
+      }
+    } catch (ex) {}
+    this.onSave();
+    if (typeof(smimeOnAcceptEditor) == "function") {
+      return smimeOnAcceptEditor();
+    } else
+      return true;
+  },
+
+  onSave: function() {
+    if (!this.identity) {
+      this.identity = gIdentity;
+    }
+    this.identity.setBoolAttribute("enablePgp", this.enablePgp.checked);
+    //To attach OpenPGP Key with the mail
+    this.identity.setBoolAttribute("attachPgpKey", this.openPgpSendKeyWithMsg.checked);
+
+    if (this.enablePgp.checked) {
+      // PGP is enabled
+      this.identity.setIntAttribute("pgpKeyMode", this.pgpKeyMode.selectedItem.value);
+      this.identity.setIntAttribute("mimePreferOpenPGP", this.mimePreferOpenPGP.selectedItem.value);
+      this.identity.setCharAttribute("pgpkeyId", this.pgpKeyId.value);
+      this.identity.setIntAttribute("defaultSigningPolicy", (this.signingPolicy.checked ? 1 : 0));
+      this.identity.setIntAttribute("defaultEncryptionPolicy", (this.encryptionPolicy.checked ? 1 : 0));
+      this.identity.setBoolAttribute("pgpMimeMode", this.pgpMimeMode.checked);
+      this.identity.setBoolAttribute("pgpSignEncrypted", this.pgpSignEncPolicy.checked);
+      this.identity.setBoolAttribute("pgpSignPlain", this.pgpSignPlainPolicy.checked);
+      this.identity.setBoolAttribute("autoEncryptDrafts", this.autoEncryptDrafts.checked);
+    }
+
+    if (!this.isSingleIdEditor) {
+      this.account.incomingServer.setBoolValue("enableAutocrypt", this.enableAc.checked);
+      this.account.incomingServer.setIntValue("acPreferEncrypt", this.acPreferEncrypt.checked ? 1 : 0);
+    }
+  },
+
+  toggleEnable: function() {
+    let newCryptoEnabled = (!this.cryptoChoicesEnabled);
+
+    this.cryptoChoicesEnabled = newCryptoEnabled;
+    this.enableAllPrefs();
+  },
+
+  enableAllPrefs: function() {
+    var elem = document.getElementById("enigmail_bcEnablePgp");
+    if (this.cryptoChoicesEnabled) {
+      if (elem) elem.removeAttribute("disabled");
+    } else {
+      if (elem) elem.setAttribute("disabled", "true");
+    }
+
+    this.enableKeySel(this.cryptoChoicesEnabled && (this.pgpKeyMode.value == 1));
+    this.enableAcSettings();
+  },
+
+  enableKeySel: function(enable) {
+    if (enable) {
+      document.getElementById("enigmail_bcUseKeyId").removeAttribute("disabled");
+    } else {
+      document.getElementById("enigmail_bcUseKeyId").setAttribute("disabled", "true");
+    }
+  },
+
+  enableAcSettings: function() {
+    if (this.cryptoChoicesEnabled && this.enableAc.checked) {
+      this.acPreferEncrypt.removeAttribute("disabled");
+    } else {
+      this.acPreferEncrypt.setAttribute("disabled", "true");
+    }
+  },
+
+  handleClick: function(event) {
+    if (event.target.hasAttribute("href")) {
+      EnigmailWindows.openMailTab(event.target.getAttribute("href"));
+    }
+  },
+
+  selectKeyId: function() {
+    var resultObj = {};
+    var inputObj = {};
+    inputObj.dialogHeader = EnigmailLocale.getString("encryptKeyHeader");
+    inputObj.options = "single,hidexpired,private,nosending";
+    var button = document.getElementById("enigmail_selectPgpKey");
+    var label = button.getAttribute("label");
+    inputObj.options += ",sendlabel=" + label;
+    inputObj.options += ",";
+
+    window.openDialog("chrome://openpgp/content/ui/enigmailKeySelection.xul", "", "dialog,modal,centerscreen,resizable", inputObj, resultObj);
+    try {
+      if (resultObj.cancelled) return;
+      var selKey = resultObj.userList[0];
+      //selKey = "0x"+selKey.substring(10,18);
+      this.pgpKeyId.value = selKey;
+    } catch (ex) {
+      // cancel pressed -> don't send mail
+      return;
+    }
+  }
+
+};
+
+window.addEventListener("load-enigmail", Enigmail.edit.onLoadEditor.bind(Enigmail.edit), false);
+
+document.addEventListener("dialogaccept", function(event) {
+  Enigmail.edit.onAcceptEditor();
+});
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/enigmailEditIdentity.xul
@@ -0,0 +1,148 @@
+<?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 https://mozilla.org/MPL/2.0/.
+-->
+
+<?xml-stylesheet href="chrome://openpgp/skin/enigmail.css" type="text/css"?>
+
+<!DOCTYPE window SYSTEM "chrome://openpgp/locale/enigmail.dtd" >
+
+<overlay id="enigmail_IdentityEditOvl"
+    xmlns:html="http://www.w3.org/1999/xhtml"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/x-javascript" src="chrome://openpgp/content/ui/enigmailEditIdentity.js"/>
+
+  <vbox id="enigmail_IdentityEdit">
+    <checkbox id="enigmail_enablePgp"
+              prefstring="mail.identity.%identitykey%.enablePgp"
+              preftype="bool" prefattribute="value"
+              label="&enigmail.amPrefEnablePgp.label;"
+              oncommand="Enigmail.edit.toggleEnable();"/>
+    <vbox>
+      <vbox class="enigmailGroupbox" id="enigmail_encryption.titleBox">
+        <radiogroup id="enigmail_pgpKeyMode" aria-labelledby="enablePgp">
+          <radio id="enigmail_keymode_useFromAddress"
+                 label="&enigmail.amPrefUseFromAddr.label;"
+                 value="0"
+                 observes="enigmail_bcEnablePgp"
+                 oncommand="Enigmail.edit.enableKeySel(false);"/>
+          <vbox>
+            <radio id="enigmail_keymode_usePgpkeyId"
+                   label="&enigmail.amPrefUseKeyId.label;"
+                   observes="enigmail_bcEnablePgp"
+                   value="1"
+                   oncommand="Enigmail.edit.enableKeySel(true);"/>
+            <hbox flex="1">
+              <textbox id="enigmail_identity.pgpkeyId" aria-labelledby="keymode_usePgpkeyId"
+                       prefstring="mail.identity.%identitykey%.pgpkeyId"
+                       observes="enigmail_bcUseKeyId"
+                       readonly="true"
+                       flex="1"
+                       preftype="wstring" prefattribute="value"/>
+              <button id="enigmail_selectPgpKey" label="&enigmail.amPrefSelectKey.label;"
+                      observes="enigmail_bcUseKeyId"
+                      oncommand="Enigmail.edit.selectKeyId()"/>
+            </hbox>
+          </vbox>
+        </radiogroup>
+
+        <tabbox flex="1" id="enigmail_tabs">
+          <tabs id="enigmail_tabBox">
+            <tab id="enigmail_msgCompTab"   label="&enigmail.amPrefMsgComp.label;"/>
+            <tab id="enigmail_autocryptTab" label="&enigmail.autocrypt.label;"/>
+          </tabs>
+
+          <tabpanels flex="1" id="enigmail_tabPanels">
+            <!-- Message Composition -->
+            <vbox flex="1">
+              <caption label="&enigmail.amPrefDefaultEncrypt.label;"/>
+
+              <checkbox id="enigmail_encrypt_ifPossible"
+                        checked="false"
+                        observes="enigmail_bcEnablePgp"
+                        label="&enigmail.defaultEncryption.label;"/>
+              <checkbox id="enigmail_sign_ifPossible"
+                        checked="false"
+                        observes="enigmail_bcEnablePgp"
+                        label="&enigmail.defaultSigning.label;"/>
+              <checkbox id="enigmail_pgpMimeMode"
+                        checked="false"
+                        observes="enigmail_bcEnablePgp"
+                        label="&enigmail.usePGPMimeAlways.label;"/>
+
+              <separator/>
+
+              <label value="&enigmail.afterDefaultsAndRules.label;"/>
+              <hbox flex="1">
+                <checkbox id="enigmail_sign_notEncrypted"
+                          checked="false"
+                          observes="enigmail_bcEnablePgp"
+                          label="&enigmail.finallySignNotEncrypted.label;"/>
+                <checkbox id="enigmail_sign_encrypted"
+                          checked="false"
+                          observes="enigmail_bcEnablePgp"
+                          label="&enigmail.finallySignEncrypted.label;"/>
+              </hbox>
+
+              <separator/>
+
+              <checkbox id="enigmail_autoEncryptDrafts" checked="false"
+                        label="&enigmail.autoEncryptDrafts.label;"/>
+
+              <separator/>
+
+              <label id="mimePreferOpenPGP" value="&enigmail.amPrefMimePreferProto.label;"/>
+              <radiogroup id="enigmail_mimePreferOpenPGP" aria-labelledby="mimePreferOpenPGP">
+                <hbox flex="1">
+                  <radio id="enigmail_mime_preferSMime"
+                         label="&enigmail.amPrefMimePreferSMime.label;"
+                         value="0"
+                         observes="enigmail_bcEnablePgp"/>
+                  <radio id="enigmail_mime_preferEnigmail"
+                         label="&enigmail.amPrefMimePreferEnigmail.label;"
+                         value="1"
+                         observes="enigmail_bcEnablePgp"/>
+                </hbox>
+              </radiogroup>
+
+              <separator/>
+
+              <checkbox id="openpgp.sendKeyWithMsg"
+                          label="&enigmail.amPrefPgp.sendKeyWithMsg.label;"
+                          checked="false"/>
+            </vbox>
+
+            <!-- Autocrypt tab -->
+            <vbox flex="1" align="start">
+              <html:p id="enigmail_amPrefAutocryptDesc" onclick="Enigmail.edit.handleClick(event)"></html:p>
+
+              <checkbox id="enigmail_enableAutocrypt"
+                  prefstring="mail.server.%serverkey%.enableAutocrypt"
+                  preftype="bool" prefattribute="value"
+                  observes="enigmail_bcEnablePgp"
+                  oncommand="Enigmail.edit.enableAcSettings()"
+                  label="&enigmail.enableAutocrypt.label;"/>
+
+              <checkbox id="enigmail_acPreferEncrypt"
+                  prefstring="mail.server.%serverkey%.acPreferEncrypt"
+                  preftype="bool" prefattribute="value"
+                  label="&enigmail.acPreferEncrypt.label;"/>
+            </vbox>
+          </tabpanels>
+        </tabbox>
+
+        <hbox autostretch="never" id="enigmail_PrefsBox">
+          <spacer flex="1"/>
+          <button class="dialog"
+                  id="enigmail_openpgpPrefsButton"
+                  observes="enigmail_bcEnablePgp"
+                  label="&enigmail.openpgpPrefsButton.label;"
+                  oncommand="EnigmailWindows.openPrefWindow(window, true, 'sendingTab');"/>
+        </hbox>
+      </vbox>
+    </vbox>
+  </vbox>
+</overlay>
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/enigmailKeygen.js
@@ -0,0 +1,516 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+// Uses: chrome://openpgp/content/ui/enigmailCommon.js
+
+"use strict";
+
+var Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+// modules
+/* global EnigmailData: false, EnigmailLog: false, EnigmailLocale: false, EnigmailGpg: false, EnigmailKeyEditor: false */
+/* global EnigmailOS: false, EnigmailPrefs: false, EnigmailGpgAgent: false, EnigmailApp: false, EnigmailKeyRing: false */
+/* global EnigmailDialog: false, EnigmailFuncs: false */
+
+// from enigmailCommon.js:
+/* global EnigGetWindowOptions: false, EnigConfirm: false, EnigGetString: false, GetEnigmailSvc: false */
+/* global EnigLongAlert: false, EnigAlert: false, EnigInitCommon: false, ENIG_ACCOUNT_MANAGER_CONTRACTID: false */
+/* global EnigGetPref: false, EnigSetPref: false, EnigSavePrefs: false, EnigFilePicker: false, EnigGetFilePath: false */
+/* global EnigmailWindows: false, EnigCreateRevokeCert: false */
+
+// Initialize enigmailCommon
+EnigInitCommon("enigmailKeygen");
+
+var gAccountManager = Components.classes[ENIG_ACCOUNT_MANAGER_CONTRACTID].getService(Components.interfaces.nsIMsgAccountManager);
+
+var gUserIdentityList;
+var gUserIdentityListPopup;
+var gUseForSigning;
+
+var gKeygenRequest;
+var gAllData = "";
+var gGeneratedKey = null;
+var gUsedId;
+
+const KEYGEN_CANCELLED = "cancelled";
+
+function enigmailKeygenLoad() {
+  EnigmailLog.DEBUG("enigmailKeygen.js: Load\n");
+
+  gUserIdentityList = document.getElementById("userIdentity");
+  gUserIdentityListPopup = document.getElementById("userIdentityPopup");
+  gUseForSigning = document.getElementById("useForSigning");
+
+  var noPassphrase = document.getElementById("noPassphrase");
+
+  if (!EnigmailGpg.getGpgFeature("keygen-passphrase")) {
+    document.getElementById("passphraseRow").setAttribute("collapsed", "true");
+    noPassphrase.setAttribute("collapsed", "true");
+  }
+
+  if (EnigmailGpg.getGpgFeature("supports-ecc-keys")) {
+    let eccElem = document.getElementById("keyType_ecc");
+    eccElem.removeAttribute("hidden");
+    updateKeySizeSel(eccElem);
+    document.getElementById("keyType").selectedItem = eccElem;
+  }
+
+
+  if (gUserIdentityListPopup) {
+    fillIdentityListPopup();
+  }
+  gUserIdentityList.focus();
+
+  // restore safe setting, which you ALWAYS explicitly have to overrule,
+  // if you don't want them:
+  // - specify passphrase
+  // - specify expiry date
+  noPassphrase.checked = false;
+  EnigSetPref("noPassphrase", noPassphrase.checked);
+  var noExpiry = document.getElementById("noExpiry");
+  noExpiry.checked = false;
+
+  enigmailKeygenUpdate(true, false);
+
+  var enigmailSvc = GetEnigmailSvc();
+  if (!enigmailSvc) {
+    EnigAlert(EnigGetString("accessError"));
+  }
+
+  if (EnigmailGpgAgent.agentType != "gpg") {
+    EnigAlert(EnigGetString("onlyGPG"));
+    return;
+  }
+}
+
+function updateKeySizeSel(selectedObj) {
+  if (selectedObj.id === "keyType_ecc") {
+    document.getElementById("keySize").setAttribute("disabled", "true");
+  } else {
+    document.getElementById("keySize").removeAttribute("disabled");
+  }
+}
+
+function enigmailOnClose() {
+  var closeWin = true;
+  if (gKeygenRequest) {
+    closeWin = EnigConfirm(EnigGetString("keyAbort"), EnigGetString("keyMan.button.generateKeyAbort"), EnigGetString("keyMan.button.generateKeyContinue"));
+  }
+  if (closeWin) abortKeyGeneration();
+  return closeWin;
+}
+
+function enigmailKeygenUnload() {
+  EnigmailLog.DEBUG("enigmailKeygen.js: Unload\n");
+
+  enigmailKeygenCloseRequest();
+}
+
+
+function enigmailKeygenUpdate(getPrefs, setPrefs) {
+  EnigmailLog.DEBUG("enigmailKeygen.js: Update: " + getPrefs + ", " + setPrefs + "\n");
+
+  var noPassphrase = document.getElementById("noPassphrase");
+  var noPassphraseChecked = getPrefs ? EnigGetPref("noPassphrase") : noPassphrase.checked;
+
+  if (setPrefs) {
+    EnigSetPref("noPassphrase", noPassphraseChecked);
+  }
+
+  noPassphrase.checked = noPassphraseChecked;
+
+  var passphrase1 = document.getElementById("passphrase");
+  var passphrase2 = document.getElementById("passphraseRepeat");
+  passphrase1.disabled = noPassphraseChecked;
+  passphrase2.disabled = noPassphraseChecked;
+}
+
+function enigmailKeygenTerminate(exitCode) {
+  EnigmailLog.DEBUG("enigmailKeygen.js: Terminate:\n");
+
+  var curId = gUsedId;
+
+  gKeygenRequest = null;
+
+  if ((!gGeneratedKey) || gGeneratedKey == KEYGEN_CANCELLED) {
+    if (!gGeneratedKey)
+      EnigAlert(EnigGetString("keyGenFailed"));
+    return;
+  }
+
+  var progMeter = document.getElementById("keygenProgress");
+  progMeter.setAttribute("value", 100);
+
+  if (gGeneratedKey) {
+    if (gUseForSigning.checked) {
+      curId.setBoolAttribute("enablePgp", true);
+      curId.setIntAttribute("pgpKeyMode", 1);
+      curId.setCharAttribute("pgpkeyId", "0x" + gGeneratedKey);
+
+      enigmailKeygenUpdate(false, true);
+
+      EnigSavePrefs();
+
+      EnigmailWindows.keyManReloadKeys();
+
+      if (EnigConfirm(EnigGetString("keygenComplete", curId.email) + "\n\n" + EnigGetString("revokeCertRecommended"), EnigGetString("keyMan.button.generateCert"))) {
+        EnigCreateRevokeCert(gGeneratedKey, curId.email, closeAndReset);
+      } else
+        closeAndReset();
+    } else {
+      if (EnigConfirm(EnigGetString("genCompleteNoSign") + "\n\n" + EnigGetString("revokeCertRecommended"), EnigGetString("keyMan.button.generateCert"))) {
+        EnigCreateRevokeCert(gGeneratedKey, curId.email, closeAndReset);
+        genAndSaveRevCert(gGeneratedKey, curId.email).then(
+          function _resolve() {
+            closeAndReset();
+          },
+          function _reject() {
+            // do nothing
+          }
+        );
+      } else
+        closeAndReset();
+    }
+  } else {
+    EnigAlert(EnigGetString("keyGenFailed"));
+    window.close();
+  }
+}
+
+/**
+ * generate and save a revokation certificate.
+ *
+ * return: Promise object
+ */
+
+function genAndSaveRevCert(keyId, uid) {
+  EnigmailLog.DEBUG("enigmailKeygen.js: genAndSaveRevCert\n");
+
+  return new Promise(
+    function(resolve, reject) {
+
+      let keyFile = EnigmailApp.getProfileDirectory();
+      keyFile.append("0x" + keyId + "_rev.asc");
+
+      // create a revokation cert in the TB profile directoy
+      EnigmailKeyEditor.genRevokeCert(window, "0x" + keyId, keyFile, "1", "",
+        function _revokeCertCb(exitCode, errorMsg) {
+          if (exitCode !== 0) {
+            EnigAlert(EnigGetString("revokeCertFailed") + "\n\n" + errorMsg);
+            reject(1);
+          }
+          saveRevCert(keyFile, keyId, uid, resolve, reject);
+        });
+    }
+  );
+}
+
+/**
+ *  create a copy of the revokation cert at a user defined location
+ */
+function saveRevCert(inputKeyFile, keyId, uid, resolve, reject) {
+
+  let defaultFileName = uid.replace(/[\\/<>]/g, "");
+  defaultFileName += " (0x" + keyId + ") rev.asc";
+
+  let outFile = EnigFilePicker(EnigGetString("saveRevokeCertAs"),
+    "", true, "*.asc",
+    defaultFileName, [EnigGetString("asciiArmorFile"), "*.asc"]);
+
+  if (outFile) {
+    try {
+      inputKeyFile.copyToFollowingLinks(outFile.parent, outFile.leafName);
+      EnigmailDialog.info(window, EnigGetString("revokeCertOK"));
+    } catch (ex) {
+      EnigAlert(EnigGetString("revokeCertFailed"));
+      reject(2);
+    }
+  }
+  resolve();
+}
+
+function closeAndReset() {
+  EnigmailKeyRing.clearCache();
+  window.close();
+}
+
+// Cleanup
+function enigmailKeygenCloseRequest() {
+  EnigmailLog.DEBUG("enigmailKeygen.js: CloseRequest\n");
+
+  if (gKeygenRequest) {
+    var p = gKeygenRequest;
+    gKeygenRequest = null;
+    p.kill(false);
+  }
+}
+
+function enigmailCheckPassphrase() {
+  var passphraseElement = document.getElementById("passphrase");
+  var passphrase2Element = document.getElementById("passphraseRepeat");
+
+  var passphrase = passphraseElement.value;
+
+  if (passphrase != passphrase2Element.value) {
+    EnigAlert(EnigGetString("passNoMatch"));
+    return null;
+  }
+
+  if (passphrase.search(/[^\x20-\x7E]/) >= 0) {
+    if (!EnigmailDialog.confirmDlg(window, EnigmailLocale.getString("keygen.passCharProblem"),
+        EnigmailLocale.getString("dlg.button.ignore"), EnigmailLocale.getString("dlg.button.cancel"))) {
+      return null;
+    }
+  }
+  if ((passphrase.search(/^\s/) === 0) || (passphrase.search(/\s$/) >= 0)) {
+    EnigAlert(EnigGetString("passSpaceProblem"));
+    return null;
+  }
+
+  return passphrase;
+}
+
+
+
+function enigmailKeygenStart() {
+  EnigmailLog.DEBUG("enigmailKeygen.js: Start\n");
+
+
+  if (gKeygenRequest) {
+    let req = gKeygenRequest.QueryInterface(Components.interfaces.nsIRequest);
+    if (req.isPending()) {
+      EnigmailDialog.info(window, EnigGetString("genGoing"));
+      return;
+    }
+  }
+
+  gGeneratedKey = null;
+  gAllData = "";
+
+  var enigmailSvc = GetEnigmailSvc();
+  if (!enigmailSvc) {
+    EnigAlert(EnigGetString("accessError"));
+    return;
+  }
+
+  var passphrase;
+  // gpg >= 2.1 queries passphrase using gpg-agent only
+  if (EnigmailGpg.getGpgFeature("keygen-passphrase")) {
+    var noPassphraseElement = document.getElementById("noPassphrase");
+    var passphraseElement = document.getElementById("passphrase");
+
+    if (!noPassphraseElement.checked) {
+      if (passphraseElement.value.trim() === "") {
+        EnigmailDialog.info(window, EnigGetString("passCheckBox"));
+        return;
+      }
+
+      passphrase = enigmailCheckPassphrase();
+      if (passphrase === null) return;
+    }
+
+  } else {
+    passphrase = "";
+  }
+
+  var noExpiry = document.getElementById("noExpiry");
+  var expireInput = document.getElementById("expireInput");
+  var timeScale = document.getElementById("timeScale");
+
+  var expiryTime = 0;
+  if (!noExpiry.checked) {
+    expiryTime = Number(expireInput.value) * Number(timeScale.value);
+    if (expiryTime > 36500) {
+      EnigmailDialog.info(window, EnigGetString("expiryTooLong"));
+      return;
+    }
+    if (expiryTime <= 0) {
+      EnigmailDialog.info(window, EnigGetString("expiryTooShort"));
+      return;
+    }
+  }
+  var keySize = Number(document.getElementById("keySize").value);
+  var keyType = document.getElementById("keyType").value;
+
+  var curId = getCurrentIdentity();
+  gUsedId = curId;
+
+  var userName = curId.fullName;
+  var userEmail = curId.email;
+
+  if (!userName) {
+    EnigmailDialog.info(window, EnigGetString("keygen.missingUserName"));
+    return;
+  }
+
+  var idString = userName;
+
+  idString += " <" + userEmail + ">";
+
+  var confirmMsg = EnigGetString("keyConfirm", idString);
+
+  if (!EnigConfirm(confirmMsg, EnigGetString("keyMan.button.generateKey"))) {
+    return;
+  }
+
+  var proc = null;
+
+  var listener = {
+    onStartRequest: function() {},
+    onStopRequest: function(status) {
+      enigmailKeygenTerminate(status);
+    },
+    onDataAvailable: function(data) {
+      EnigmailLog.DEBUG("enigmailKeygen.js: onDataAvailable() " + data + "\n");
+
+      gAllData += data;
+      var keyCreatedIndex = gAllData.indexOf("[GNUPG:] KEY_CREATED");
+      if (keyCreatedIndex > 0) {
+        gGeneratedKey = gAllData.substr(keyCreatedIndex);
+        gGeneratedKey = gGeneratedKey.replace(/(.*\[GNUPG:\] KEY_CREATED . )([a-fA-F0-9]+)([\n\r].*)*/, "$2");
+        gAllData = gAllData.replace(/\[GNUPG:\] KEY_CREATED . [a-fA-F0-9]+[\n\r]/, "");
+      }
+      gAllData = gAllData.replace(/[\r\n]*\[GNUPG:\] GOOD_PASSPHRASE/g, "").replace(/([\r\n]*\[GNUPG:\] PROGRESS primegen )(.)( \d+ \d+)/g, "$2");
+      var progMeter = document.getElementById("keygenProgress");
+      var progValue = Number(progMeter.value);
+      progValue += (1 + (100 - progValue) / 200);
+      if (progValue >= 95) progValue = 10;
+      progMeter.setAttribute("value", progValue);
+    }
+  };
+
+  try {
+    gKeygenRequest = EnigmailKeyRing.generateKey(
+      EnigmailData.convertFromUnicode(userName),
+      "", // user id comment
+      EnigmailData.convertFromUnicode(userEmail),
+      expiryTime,
+      keySize,
+      keyType,
+      EnigmailData.convertFromUnicode(passphrase),
+      listener);
+  } catch (ex) {
+    EnigmailLog.DEBUG("enigmailKeygen.js: generateKey() failed with " + ex.toString() + "\n" + ex.stack + "\n");
+  }
+
+  if (!gKeygenRequest) {
+    EnigAlert(EnigGetString("keyGenFailed"));
+  }
+
+  EnigmailLog.WRITE("enigmailKeygen.js: Start: gKeygenRequest = " + gKeygenRequest + "\n");
+}
+
+function abortKeyGeneration() {
+  gGeneratedKey = KEYGEN_CANCELLED;
+  enigmailKeygenCloseRequest();
+}
+
+function enigmailKeygenCancel() {
+  EnigmailLog.DEBUG("enigmailKeygen.js: Cancel\n");
+  var closeWin = false;
+
+  if (gKeygenRequest) {
+    closeWin = EnigConfirm(EnigGetString("keyAbort"), EnigGetString("keyMan.button.generateKeyAbort"), EnigGetString("keyMan.button.generateKeyContinue"));
+    if (closeWin) abortKeyGeneration();
+  } else {
+    closeWin = true;
+  }
+
+  if (closeWin) window.close();
+}
+
+function onNoExpiry() {
+  var noExpiry = document.getElementById("noExpiry");
+  var expireInput = document.getElementById("expireInput");
+  var timeScale = document.getElementById("timeScale");
+
+  expireInput.disabled = noExpiry.checked;
+  timeScale.disabled = noExpiry.checked;
+}
+
+
+function queryISupArray(supportsArray, iid) {
+  var result = [];
+  var i;
+  // Gecko > 20
+  for (i = 0; i < supportsArray.length; i++) {
+    result.push(supportsArray.queryElementAt(i, iid));
+  }
+
+  return result;
+}
+
+function getCurrentIdentity() {
+  var item = gUserIdentityList.selectedItem;
+  var identityKey = item.getAttribute('id');
+
+  var identity = gAccountManager.getIdentity(identityKey);
+
+  return identity;
+}
+
+function fillIdentityListPopup() {
+  EnigmailLog.DEBUG("enigmailKeygen.js: fillIdentityListPopup\n");
+
+  try {
+    var idSupports = gAccountManager.allIdentities;
+    var identities = queryISupArray(idSupports,
+      Components.interfaces.nsIMsgIdentity);
+
+    EnigmailLog.DEBUG("enigmailKeygen.js: fillIdentityListPopup: " + identities + "\n");
+
+    // Default identity
+    let defIdentity = EnigmailFuncs.getDefaultIdentity();
+
+    EnigmailLog.DEBUG("enigmailKeygen.js: fillIdentityListPopup: default=" + defIdentity.key + "\n");
+
+    var selected = false;
+    for (var i = 0; i < identities.length; i++) {
+      var identity = identities[i];
+
+      EnigmailLog.DEBUG("id.valid=" + identity.valid + "\n");
+      if (!identity.valid || !identity.email)
+        continue;
+
+      var serverSupports, inServer;
+      // Gecko >= 20
+      serverSupports = gAccountManager.getServersForIdentity(identity);
+      if (serverSupports.length > 0) {
+        inServer = serverSupports.queryElementAt(0, Components.interfaces.nsIMsgIncomingServer);
+      }
+
+      if (inServer) {
+        var accountName = " - " + inServer.prettyName;
+
+        EnigmailLog.DEBUG("enigmailKeygen.js: accountName=" + accountName + "\n");
+        EnigmailLog.DEBUG("enigmailKeygen.js: email=" + identity.email + "\n");
+
+        var item = document.createXULElement('menuitem');
+        //      item.setAttribute('label', identity.identityName);
+        item.setAttribute('label', identity.identityName + accountName);
+        item.setAttribute('class', 'identity-popup-item');
+        item.setAttribute('accountname', accountName);
+        item.setAttribute('id', identity.key);
+        item.setAttribute('email', identity.email);
+
+        gUserIdentityListPopup.appendChild(item);
+
+        if (!selected)
+          gUserIdentityList.selectedItem = item;
+
+        if (identity.key == defIdentity.key) {
+          gUserIdentityList.selectedItem = item;
+          selected = true;
+        }
+      }
+    }
+  }
+  catch(ex) {
+    EnigmailLog.writeException("enigmailKeygen.js: fillIdentityListPopup: exception\n", ex);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/enigmailKeygen.xul
@@ -0,0 +1,164 @@
+<?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 https://mozilla.org/MPL/2.0/.
+-->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://openpgp/skin/enigmail.css" type="text/css"?>
+
+<?xul-overlay href="chrome://global/content/dialogOverlay.xul"?>
+
+<!DOCTYPE window SYSTEM "chrome://openpgp/locale/enigmail.dtd" >
+
+<window
+     id="enigmailKeygen"
+     title="&enigmail.keygenTitle.label;"
+     windowtype="enigmail:keygen"
+     width="600" height="480"
+     orient="vertical"
+     onload="enigmailKeygenLoad()"
+     onclose="return enigmailOnClose();"
+     xmlns:html="http://www.w3.org/1999/xhtml"
+     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script type="application/x-javascript" src="chrome://global/content/dialogOverlay.js" />
+
+<script type="application/x-javascript" src="chrome://openpgp/content/ui/enigmailCommon.js"/>
+<script type="application/x-javascript" src="chrome://openpgp/content/ui/enigmailKeygen.js"/>
+
+<popupset id="aTooltipSet"/>
+
+<vbox class="enigmailGroupbox" id="userIdBox">
+  <hbox orient="horizontal" align="center">
+    <label value="&enigmail.keyUserId.label;" control="userIdentity"/>
+    <menulist id="userIdentity" label="..." flex="1">
+      <menupopup id="userIdentityPopup"/>
+    </menulist>
+  </hbox>
+
+  <checkbox id="useForSigning"
+            label="&enigmail.useForSigning.label;"
+            checked="true" />
+</vbox>
+
+<checkbox id="noPassphrase"
+          label="&enigmail.keyNoPassphrase.label;"
+          oncommand="enigmailKeygenUpdate(false, false);" />
+
+<grid>
+  <columns>
+    <column />
+    <column flex="1"/>
+  </columns>
+
+  <rows>
+    <row id="passphraseRow">
+      <hbox id="passphraseBox" align="center">
+        <label control="passphrase" value="&enigmail.keyPassphrase.label;" />
+      </hbox>
+      <hbox align="center">
+        <textbox id="passphrase" type="password" />
+        <label control="passphraseRepeat" value="&enigmail.keyPassphraseRepeat.label;" />
+        <textbox id="passphraseRepeat" type="password" />
+      </hbox>
+    </row>
+  </rows>
+</grid>
+
+<tabbox flex="1">
+  <tabs id="settingsTabBox">
+    <tab id="basicTab"    label="&enigmail.keyGen.expiry.title;"/>
+    <tab id="advancedTab" label="&enigmail.advancedPrefsButton.label;"/>
+  </tabs>
+
+  <tabpanels flex="1">
+
+    <hbox> <!-- Basic Tab -->
+      <hbox align="center">
+        <label value="&enigmail.keyGen.expire.label;" control="expireInput"/>
+      </hbox>
+      <hbox align="center">
+        <textbox id="expireInput" size="5" maxlength="5" value="5"/>
+        <menulist id="timeScale" label="&enigmail.keyGen.years.label;" value="365">
+          <menupopup id="timeScalePopup">
+            <menuitem id="years" value="365" label="&enigmail.keyGen.years.label;" selected="true"/>
+            <menuitem id="months" value="30" label="&enigmail.keyGen.months.label;"/>
+            <menuitem id="days" value="1" label="&enigmail.keyGen.days.label;"/>
+          </menupopup>
+        </menulist>
+        <checkbox label="&enigmail.keyGen.noExpiry.label;"
+                  id="noExpiry" oncommand="onNoExpiry()"/>
+      </hbox>
+    </hbox>
+
+    <vbox> <!-- Advanced Tab -->
+      <grid>
+        <columns>
+          <column/>
+          <column flex="1"/>
+        </columns>
+
+        <rows>
+          <row>
+            <hbox align="center">
+              <label value="&enigmail.keyGen.keyType.label;" control="keyType"/>
+            </hbox>
+            <hbox flex="0">
+              <menulist id="keyType" label="&enigmail.keyGen.keyType.rsa;" value="RSA" >
+                <menupopup id="keyTypePopup">
+                  <menuitem id="keySize_rsa" value="RSA" label="&enigmail.keyGen.keyType.rsa;" selected="true" oncommand="updateKeySizeSel(this)"/>
+                  <menuitem id="keyType_ecc" value="ECC" label="&enigmail.keyGen.keyType.ecc;" oncommand="updateKeySizeSel(this)" hidden="true"/>
+                </menupopup>
+              </menulist>
+            </hbox>
+          </row>
+          <row>
+            <hbox align="center">
+              <label value="&enigmail.keyGen.keySize.label;" control="keySize"/>
+            </hbox>
+            <hbox flex="0">
+              <menulist id="keySize" label="4096" value="4096" >
+                <menupopup id="keySizePopup">
+                  <menuitem id="keySize_3072" value="3072" label="3072"/>
+                  <menuitem id="keySize_4096" value="4096" label="4096" selected="true"/>
+                </menupopup>
+              </menulist>
+            </hbox>
+          </row>
+        </rows>
+      </grid>
+  </vbox>
+  </tabpanels>
+</tabbox>
+
+<separator/>
+
+<hbox autostretch="never">
+  <button label="&enigmail.generateKey.label;"
+          class="dialog"
+          tooltip="aTooltip"
+          tooltiptext="&enigmail.generateKey.tooltip;"
+          oncommand="enigmailKeygenStart();" />
+
+  <button label="&enigmail.cancelKey.label;"
+          class="dialog"
+          tooltip="aTooltip"
+          tooltiptext="&enigmail.cancelKey.tooltip;"
+          oncommand="enigmailKeygenCancel();" />
+</hbox>
+
+<separator/>
+
+<vbox class="enigmailCaptionbox" id="keygenConsoleBox" orient="vertical" flex="1">
+  <html:h1><html:span>&enigmail.keyGen.console.label;</html:span></html:h1>
+
+  <description>&enigmail.keygen.desc;</description>
+
+  <vbox class="enigmailGroupbox">
+    <html:progress id="keygenProgress" max="100" value="0"/>
+  </vbox>
+</vbox>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/enigmailMsgBox.js
@@ -0,0 +1,190 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+var Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+var EnigmailClipboard = ChromeUtils.import("chrome://openpgp/content/modules/clipboard.jsm").EnigmailClipboard;
+var EnigmailOS = ChromeUtils.import("chrome://openpgp/content/modules/os.jsm").EnigmailOS;
+var EnigmailLocale = ChromeUtils.import("chrome://openpgp/content/modules/locale.jsm").EnigmailLocale;
+var EnigmailEvents = ChromeUtils.import("chrome://openpgp/content/modules/events.jsm").EnigmailEvents;
+
+function onLoad() {
+  var dlg = document.getElementById("enigmailMsgBox");
+  dlg.getButton("help").setAttribute("hidden", "true");
+  dlg.getButton("cancel").setAttribute("hidden", "true");
+  dlg.getButton("extra1").setAttribute("hidden", "true");
+  dlg.getButton("extra2").setAttribute("hidden", "true");
+
+  document.getElementById("filler").maxWidth = screen.availWidth - 50;
+  //dlg.maxHeight = screen.availHeight;
+
+  let args = window.arguments[0];
+  let msgtext = args.msgtext;
+  let button1 = args.button1;
+  let button2 = args.button2;
+  let button3 = args.button3;
+  let buttonCancel = args.cancelButton;
+  let checkboxLabel = args.checkboxLabel;
+  let iconType = args.iconType;
+
+  if (args.iconType) {
+    let icn = document.getElementById("infoImage");
+    icn.removeAttribute("collapsed");
+    let iconClass = "";
+
+    switch (args.iconType) {
+      case 2:
+        iconClass = "question-icon";
+        break;
+      case 3:
+        iconClass = "alert-icon";
+        break;
+      case 4:
+        iconClass = "error-icon";
+        break;
+      default:
+        iconClass = "message-icon";
+    }
+    icn.setAttribute("class", "spaced " + iconClass);
+  }
+
+  if (args.dialogTitle) {
+    if (EnigmailOS.isMac) {
+      let t = document.getElementById("macosDialogTitle");
+      t.setAttribute("value", args.dialogTitle);
+      t.removeAttribute("collapsed");
+    }
+
+    dlg.setAttribute("title", args.dialogTitle);
+  } else {
+    dlg.setAttribute("title", EnigmailLocale.getString("enigAlert"));
+  }
+
+  if (button1) {
+    setButton("accept", button1);
+  }
+  if (button2) {
+    setButton("extra1", button2);
+  }
+  if (button3) {
+    setButton("extra2", button3);
+  }
+  if (buttonCancel) {
+    setButton("cancel", buttonCancel);
+  }
+
+  if (checkboxLabel) {
+    let checkboxElem = document.getElementById("theCheckBox");
+    checkboxElem.setAttribute("label", checkboxLabel);
+    document.getElementById("checkboxContainer").removeAttribute("hidden");
+  }
+
+  dlg.getButton("accept").focus();
+  let textbox = document.getElementById("msgtext");
+  textbox.appendChild(textbox.ownerDocument.createTextNode(msgtext));
+
+  window.addEventListener("keypress", onKeyPress);
+  EnigmailEvents.dispatchEvent(resizeDlg, 0);
+}
+
+function resizeDlg() {
+  let availHeight = screen.availHeight;
+  if (window.outerHeight > availHeight - 100) {
+    let box = document.getElementById("msgContainer");
+    let dlg = document.getElementById("enigmailMsgBox");
+    let btnHeight = dlg.getButton("accept").parentNode.clientHeight + 20;
+    let boxHeight = box.clientHeight;
+    let dlgHeight = dlg.clientHeight;
+
+    box.setAttribute("style", "overflow: auto;");
+    box.setAttribute("height", boxHeight - btnHeight - (dlgHeight - availHeight));
+    window.outerHeight = availHeight;
+  }
+}
+
+function centerDialog() {
+  if (!EnigmailOS.isMac)
+    document.getElementById("enigmailMsgBox").centerWindowOnScreen();
+}
+
+function setButton(buttonId, label) {
+  var labelType = buttonId;
+
+  var dlg = document.getElementById("enigmailMsgBox");
+  var elem = dlg.getButton(labelType);
+
+  var i = label.indexOf(":");
+  if (i === 0) {
+    elem = dlg.getButton(label.substr(1));
+    elem.setAttribute("hidden", "false");
+    elem.setAttribute("oncommand", "dlgClose('" + buttonId + "')");
+    return;
+  }
+  if (i > 0) {
+    labelType = label.substr(0, i);
+    label = label.substr(i + 1);
+    elem = dlg.getButton(labelType);
+  }
+  i = label.indexOf("&");
+  if (i >= 0) {
+    var c = label.substr(i + 1, 1);
+    if (c != "&") {
+      elem.setAttribute("accesskey", c);
+    }
+    label = label.substr(0, i) + label.substr(i + 1);
+  }
+  elem.setAttribute("label", label);
+  elem.setAttribute("oncommand", "dlgClose('" + buttonId + "')");
+  elem.removeAttribute("hidden");
+}
+
+function dlgClose(buttonId) {
+  let buttonNumber = 99;
+
+  switch (buttonId) {
+    case "accept":
+      buttonNumber = 0;
+      break;
+    case "extra1":
+      buttonNumber = 1;
+      break;
+    case "extra2":
+      buttonNumber = 2;
+      break;
+    case "cancel":
+      buttonNumber = -1;
+  }
+
+  window.arguments[1].value = buttonNumber;
+  window.arguments[1].checked = (document.getElementById("theCheckBox").getAttribute("checked") == "true");
+  window.close();
+}
+
+function checkboxCb() {
+  // do nothing
+}
+
+
+function copyToClipbrd() {
+  let s = window.getSelection().toString();
+
+  EnigmailClipboard.setClipboardContent(s);
+}
+
+function onKeyPress(event) {
+  if (event.key == "c" && event.getModifierState("Accel")) {
+    copyToClipbrd();
+    event.stopPropagation();
+  }
+}
+
+document.addEventListener("dialogaccept", function(event) {
+  dlgClose('accept');
+});
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/enigmailMsgBox.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 https://mozilla.org/MPL/2.0/.
+-->
+
+<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://openpgp/skin/enigmail.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % enigMailDTD SYSTEM "chrome://openpgp/locale/enigmail.dtd" >
+%enigMailDTD;
+<!ENTITY % utilityDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+%utilityDTD;
+]>
+
+<dialog id="enigmailMsgBox"
+        title=""
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        buttons="accept,help,cancel,extra1,extra2"
+        onload="onLoad();"
+        xmlns:html="http://www.w3.org/1999/xhtml"
+        buttonpack="center">
+
+  <script type="application/x-javascript" src="chrome://openpgp/content/ui/enigmailMsgBox.js"/>
+
+  <popupset>
+    <menupopup id="ctxmenu">
+      <menuitem label="&copyCmd.label;" oncommand="copyToClipbrd()"/>
+    </menupopup>
+  </popupset>
+
+  <hbox id="filler" style="min-width: 0%;">
+    <spacer style="width: 29em;"/>
+  </hbox>
+
+<grid>
+  <columns>
+    <column/>
+    <column flex="1"/>
+  </columns>
+
+  <rows>
+    <row>
+      <hbox id="iconContainer" align="start">
+        <image id="infoImage" class="spaced" collapsed="true"/>
+      </hbox>
+      <vbox id="infoContainer" pack="center">
+        <label id="macosDialogTitle" collapsed="true" class="enigmailDialogTitle"/>
+        <vbox id="msgContainer" style="max-width: 45em;">
+          <description id="msgtext" context="ctxmenu" noinitialfocus="true" class="enigmailDialogBody"/>
+        </vbox>
+      </vbox>
+    </row>
+    <row id="checkboxContainer" hidden="true">
+      <spacer/>
+      <checkbox id="theCheckBox" checked="false" oncommand="checkboxCb()"/>
+    </row>
+  </rows>
+</grid>
+</dialog>
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/keyDetailsDlg.js
@@ -0,0 +1,577 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+
+/* global EnigmailLog: false, EnigmailLocale: false, EnigmailKey: false, EnigmailKeyRing: false */
+
+// from enigmailCommon.js:
+/* global GetEnigmailSvc: false, EnigAlert: false, EnigConvertGpgToUnicode: false */
+/* global EnigCleanGuiList: false, EnigGetTrustLabel: false, EnigShowPhoto: false, EnigSignKey: false */
+/* global EnigEditKeyExpiry: false, EnigEditKeyTrust: false, EnigChangeKeyPwd: false, EnigRevokeKey: false */
+/* global EnigCreateRevokeCert: false, EnigmailTimer: false */
+
+// from enigmailKeyManager.js:
+/* global keyMgrAddPhoto: false, EnigmailCompat: false */
+
+"use strict";
+
+var Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+var gKeyId = null;
+var gUserId = null;
+var gKeyList = null;
+var gTreeFuncs = null;
+
+function onLoad() {
+  window.arguments[1].refresh = false;
+
+  gKeyId = window.arguments[0].keyId;
+
+  let accept = document.getElementById("enigmailKeyDetailsDlg").getButton("accept");
+  accept.focus();
+
+  reloadData();
+}
+
+/***
+ * Set the label text of a HTML element
+ */
+
+function setText(elementId, label) {
+  let node = document.getElementById(elementId);
+  node.textContent = label;
+}
+
+function setLabel(elementId, label) {
+  let node = document.getElementById(elementId);
+  node.setAttribute("value", label);
+}
+
+function reloadData() {
+  var enigmailSvc = GetEnigmailSvc();
+  if (!enigmailSvc) {
+    EnigAlert(EnigmailLocale.getString("accessError"));
+    window.close();
+    return;
+  }
+  var exitCodeObj = {};
+  var statusFlagsObj = {};
+  var errorMsgObj = {};
+
+  gUserId = null;
+
+  var fingerprint = "";
+  var subKeyLen = "";
+  var subAlgo = "";
+  var treeChildren = document.getElementById("keyListChildren");
+  var uidList = document.getElementById("additionalUid");
+  var photoImg = document.getElementById("photoIdImg");
+
+  // clean lists
+  EnigCleanGuiList(treeChildren);
+  EnigCleanGuiList(uidList);
+
+  let keyObj = EnigmailKeyRing.getKeyById(gKeyId);
+  if (keyObj) {
+
+    if (keyObj.secretAvailable) {
+      setLabel("keyType", EnigmailLocale.getString("keyTypePair"));
+      document.getElementById("ownKeyCommands").removeAttribute("hidden");
+    } else {
+      document.getElementById("ownKeyCommands").setAttribute("hidden", "true");
+      setLabel("keyType", EnigmailLocale.getString("keyTypePublic"));
+    }
+
+    if (keyObj.photoAvailable === true) {
+      let pFile = keyObj.getPhotoFile(0);
+
+      if (pFile && pFile.isFile() && pFile.isReadable()) {
+        const photoUri = Cc["@mozilla.org/network/io-service;1"].
+        getService(Ci.nsIIOService).newFileURI(pFile).spec;
+
+        photoImg.setAttribute("src", photoUri);
+        photoImg.removeAttribute("hidden");
+      }
+    } else {
+      photoImg.setAttribute("hidden", "true");
+    }
+
+    if (keyObj.isOwnerTrustUseful()) {
+      document.getElementById("setOwnerTrust").removeAttribute("collapsed");
+    } else {
+      document.getElementById("setOwnerTrust").setAttribute("collapsed", "true");
+    }
+
+    if (keyObj.hasSubUserIds()) {
+      document.getElementById("alsoknown").removeAttribute("collapsed");
+      createUidData(uidList, keyObj);
+    } else {
+      document.getElementById("alsoknown").setAttribute("collapsed", "true");
+    }
+
+    if (keyObj.signatures) {
+      let sigListViewObj = new SigListView(keyObj);
+      let tree = document.getElementById("signatures_tree");
+      tree.view = sigListViewObj;
+      gTreeFuncs = EnigmailCompat.getTreeCompatibleFuncs(tree, sigListViewObj);
+    }
+
+    let subkeyListViewObj = new SubkeyListView(keyObj);
+    document.getElementById("subkeyList").view = subkeyListViewObj;
+
+    gUserId = keyObj.userId;
+    let expiryDate = keyObj.expiry;
+    if (expiryDate.length === 0) {
+      expiryDate = EnigmailLocale.getString("keyDoesNotExpire");
+    }
+    setLabel("userId", gUserId);
+    setText("keyValidity", getTrustLabel(keyObj.keyTrust));
+    setText("ownerTrust", getTrustLabel(keyObj.ownerTrust));
+    setText("keyCreated", keyObj.created);
+    setText("keyExpiry", expiryDate);
+    if (keyObj.fpr) {
+      setLabel("fingerprint", EnigmailKey.formatFpr(keyObj.fpr));
+    }
+  }
+}
+
+
+function createUidData(listNode, keyDetails) {
+  for (let i = 1; i < keyDetails.userIds.length; i++) {
+    if (keyDetails.userIds[i].type === "uid") {
+      let item = listNode.appendItem(keyDetails.userIds[i].userId);
+      item.setAttribute("label", keyDetails.userIds[i].userId);
+      if ("dre".search(keyDetails.userIds[i].keyTrust) >= 0) {
+        item.setAttribute("class", "enigmailDisabled");
+      }
+    }
+  }
+}
+
+function getTrustLabel(trustCode) {
+  var trustTxt = EnigGetTrustLabel(trustCode);
+  if (trustTxt == "-" || trustTxt.length === 0) {
+    trustTxt = EnigmailLocale.getString("keyValid.unknown");
+  }
+  return trustTxt;
+}
+
+function setAttr(attribute, value) {
+  var elem = document.getElementById(attribute);
+  if (elem) {
+    elem.value = value;
+  }
+}
+
+function enableRefresh() {
+  window.arguments[1].refresh = true;
+}
+
+// ------------------ onCommand Functions  -----------------
+
+function showPhoto() {
+  EnigShowPhoto(gKeyId, gUserId, 0);
+}
+
+function keyDetailsAddPhoto() {
+  keyMgrAddPhoto(gUserId, gKeyId);
+}
+
+function signKey() {
+  if (EnigSignKey(gUserId, gKeyId, null)) {
+    enableRefresh();
+    reloadData();
+  }
+}
+
+
+function changeExpirationDate() {
+  if (EnigEditKeyExpiry([gUserId], [gKeyId])) {
+    enableRefresh();
+    reloadData();
+  }
+}
+
+
+function setOwnerTrust() {
+
+  if (EnigEditKeyTrust([gUserId], [gKeyId])) {
+    enableRefresh();
+    reloadData();
+  }
+}
+
+function manageUids() {
+  let keyObj = EnigmailKeyRing.getKeyById(gKeyId);
+
+  var inputObj = {
+    keyId: keyObj.keyId,
+    ownKey: keyObj.secretAvailable
+  };
+
+  var resultObj = {
+    refresh: false
+  };
+  window.openDialog("chrome://openpgp/content/ui/enigmailManageUidDlg.xul",
+    "", "dialog,modal,centerscreen,resizable=yes", inputObj, resultObj);
+  if (resultObj.refresh) {
+    enableRefresh();
+    reloadData();
+  }
+}
+
+function changePassword() {
+  EnigChangeKeyPwd(gKeyId, gUserId);
+}
+
+function revokeKey() {
+  EnigRevokeKey(gKeyId, gUserId, function _revokeKeyCb(success) {
+    if (success) {
+      enableRefresh();
+      reloadData();
+    }
+  });
+}
+
+function genRevocationCert() {
+  EnigCreateRevokeCert(gKeyId, gUserId);
+}
+
+
+function SigListView(keyObj) {
+  this.keyObj = [];
+
+  let sigObj = keyObj.signatures;
+  for (let i in sigObj) {
+    let k = {
+      uid: sigObj[i].userId,
+      fpr: sigObj[i].fpr,
+      created: sigObj[i].created,
+      expanded: true,
+      sigList: []
+    };
+
+    for (let j in sigObj[i].sigList) {
+      let s = sigObj[i].sigList[j];
+      if (s.sigKnown) {
+        let sig = EnigmailKeyRing.getKeyById(s.signerKeyId);
+        k.sigList.push({
+          uid: s.userId,
+          created: s.created,
+          fpr: sig ? sig.fpr : "",
+          sigType: s.sigType
+        });
+      }
+    }
+    this.keyObj.push(k);
+  }
+
+  this.prevKeyObj = null;
+  this.prevRow = -1;
+
+  this.updateRowCount();
+}
+
+// implements nsITreeView
+SigListView.prototype = {
+
+  updateRowCount: function() {
+    let rc = 0;
+
+    for (let i in this.keyObj) {
+      rc += this.keyObj[i].expanded ? this.keyObj[i].sigList.length + 1 : 1;
+    }
+
+    this.rowCount = rc;
+  },
+
+  setLastKeyObj: function(keyObj, row) {
+    this.prevKeyObj = keyObj;
+    this.prevRow = row;
+    return keyObj;
+  },
+
+  getSigAtIndex: function(row) {
+    if (this.lastIndex == row) {
+      return this.lastKeyObj;
+    }
+
+    let j = 0,
+      l = 0;
+
+    for (let i in this.keyObj) {
+      if (j === row) return this.setLastKeyObj(this.keyObj[i], row);
+      j++;
+
+      if (this.keyObj[i].expanded) {
+        l = this.keyObj[i].sigList.length;
+
+        if (j + l >= row && row - j < l) {
+          return this.setLastKeyObj(this.keyObj[i].sigList[row - j], row);
+        } else {
+          j += l;
+        }
+      }
+    }
+
+    return null;
+  },
+
+  getCellText: function(row, column) {
+    let s = this.getSigAtIndex(row);
+
+    if (s) {
+      switch (column.id) {
+        case "sig_uid_col":
+          return s.uid;
+        case "sig_fingerprint_col":
+          return EnigmailKey.formatFpr(s.fpr);
+        case "sig_created_col":
+          return s.created;
+      }
+    }
+
+    return "";
+  },
+
+  setTree: function(treebox) {
+    this.treebox = treebox;
+  },
+
+  isContainer: function(row) {
+    let s = this.getSigAtIndex(row);
+    return ("sigList" in s);
+  },
+
+  isSeparator: function(row) {
+    return false;
+  },
+
+  isSorted: function() {
+    return false;
+  },
+
+  getLevel: function(row) {
+    let s = this.getSigAtIndex(row);
+    return ("sigList" in s ? 0 : 1);
+  },
+
+  cycleHeader: function(col, elem) {},
+
+  getImageSrc: function(row, col) {
+    return null;
+  },
+
+  getRowProperties: function(row, props) {},
+
+  getCellProperties: function(row, col) {
+    if (col.id === "sig_fingerprint_col") {
+      return "fixedWidthFont";
+    }
+
+    return "";
+  },
+
+  canDrop: function(row, orientation, data) {
+    return false;
+  },
+
+  getColumnProperties: function(colid, col, props) {},
+
+  isContainerEmpty: function(row) {
+    return false;
+  },
+
+  getParentIndex: function(idx) {
+    return -1;
+  },
+
+  getProgressMode: function(row, col) {},
+
+  isContainerOpen: function(row) {
+    let s = this.getSigAtIndex(row);
+    return s.expanded;
+  },
+
+  isSelectable: function(row, col) {
+    return true;
+  },
+
+  toggleOpenState: function(row) {
+    let s = this.getSigAtIndex(row);
+    s.expanded = !s.expanded;
+    let r = this.rowCount;
+    this.updateRowCount();
+    gTreeFuncs.rowCountChanged(row, this.rowCount - r);
+  }
+};
+
+function createSubkeyItem(subkey) {
+
+  // Get expiry state of this subkey
+  let expire;
+  if (subkey.keyTrust === "r") {
+    expire = EnigmailLocale.getString("keyValid.revoked");
+  } else if (subkey.expiryTime === 0) {
+    expire = EnigmailLocale.getString("keyExpiryNever");
+  } else {
+    expire = subkey.expiry;
+  }
+
+  let subkeyType = subkey.type === "pub" ? EnigmailLocale.getString("keyTypePrimary") :
+    EnigmailLocale.getString("keyTypeSubkey");
+
+  let usagetext = "";
+  let i;
+  //  e = encrypt
+  //  s = sign
+  //  c = certify
+  //  a = authentication
+  //  Capital Letters are ignored, as these reflect summary properties of a key
+
+  var singlecode = "";
+  for (i = 0; i < subkey.keyUseFor.length; i++) {
+    singlecode = subkey.keyUseFor.substr(i, 1);
+    switch (singlecode) {
+      case "e":
+        if (usagetext.length > 0) {
+          usagetext = usagetext + ", ";
+        }
+        usagetext = usagetext + EnigmailLocale.getString("keyUsageEncrypt");
+        break;
+      case "s":
+        if (usagetext.length > 0) {
+          usagetext = usagetext + ", ";
+        }
+        usagetext = usagetext + EnigmailLocale.getString("keyUsageSign");
+        break;
+      case "c":
+        if (usagetext.length > 0) {
+          usagetext = usagetext + ", ";
+        }
+        usagetext = usagetext + EnigmailLocale.getString("keyUsageCertify");
+        break;
+      case "a":
+        if (usagetext.length > 0) {
+          usagetext = usagetext + ", ";
+        }
+        usagetext = usagetext + EnigmailLocale.getString("keyUsageAuthentication");
+        break;
+    } // * case *
+  } // * for *
+
+  let keyObj = {
+    keyType: subkeyType,
+    keyId: "0x" + subkey.keyId,
+    algo: subkey.algoSym,
+    size: subkey.keySize,
+    creationDate: subkey.created,
+    expiry: expire,
+    usage: usagetext
+  };
+
+  return keyObj;
+}
+
+function SubkeyListView(keyObj) {
+  this.subkeys = [];
+  this.rowCount = keyObj.subKeys.length + 1;
+  this.subkeys.push(createSubkeyItem(keyObj));
+
+  for (let i = 0; i < keyObj.subKeys.length; i++) {
+    this.subkeys.push(createSubkeyItem(keyObj.subKeys[i]));
+  }
+
+}
+
+// implements nsITreeView
+SubkeyListView.prototype = {
+
+  getCellText: function(row, column) {
+    let s = this.subkeys[row];
+
+    if (s) {
+      switch (column.id) {
+        case "keyTypeCol":
+          return s.keyType;
+        case "keyIdCol":
+          return s.keyId;
+        case "algoCol":
+          return s.algo;
+        case "sizeCol":
+          return s.size;
+        case "createdCol":
+          return s.creationDate;
+        case "expiryCol":
+          return s.expiry;
+        case "keyUsageCol":
+          return s.usage;
+      }
+    }
+
+    return "";
+  },
+
+  setTree: function(treebox) {
+    this.treebox = treebox;
+  },
+
+  isContainer: function(row) {
+    return false;
+  },
+
+  isSeparator: function(row) {
+    return false;
+  },
+
+  isSorted: function() {
+    return false;
+  },
+
+  getLevel: function(row) {
+    return 0;
+  },
+
+  cycleHeader: function(col, elem) {},
+
+  getImageSrc: function(row, col) {
+    return null;
+  },
+
+  getRowProperties: function(row, props) {},
+
+  getCellProperties: function(row, col) {
+    return "";
+  },
+
+  canDrop: function(row, orientation, data) {
+    return false;
+  },
+
+  getColumnProperties: function(colid, col, props) {},
+
+  isContainerEmpty: function(row) {
+    return false;
+  },
+
+  getParentIndex: function(idx) {
+    return -1;
+  },
+
+  getProgressMode: function(row, col) {},
+
+  isContainerOpen: function(row) {
+    return false;
+  },
+
+  isSelectable: function(row, col) {
+    return true;
+  },
+
+  toggleOpenState: function(row) {}
+};
new file mode 100644
--- /dev/null
+++ b/mail/extensions/openpgp/content/ui/keyDetailsDlg.xul
@@ -0,0 +1,212 @@
+<?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 https://mozilla.org/MPL/2.0/.
+-->
+
+<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://openpgp/skin/enigmail.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % enigMailDTD SYSTEM "chrome://openpgp/locale/enigmail.dtd" >
+%enigMailDTD;
+]>
+
+<dialog id="enigmailKeyDetailsDlg"
+        title="&enigmail.keyDetails.title;"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        xmlns:html="http://www.w3.org/1999/xhtml"
+        buttons="accept"
+        minwidth="450px"
+        persist="width height"
+        buttonlabelaccept="&enigmail.cardDetails.closeWindow.label;"
+        onload="onLoad();">
+
+
+  <script type="application/x-javascript" src="chrome://openpgp/content/ui/enigmailCommon.js"/>
+  <script type="application/x-javascript" src="chrome://openpgp/content/ui/keyDetailsDlg.js"/>
+  <script type="application/x-javascript" src="chrome://openpgp/content/ui/enigmailKeyManager.js"/>
+
+  <broadcasterset>
+    <broadcaster id="ownKeyCommands" hidden="true"/>
+  </broadcasterset>
+
+  <hbox >
+    <vbox>
+      <grid>
+        <columns>
+          <column style="min-width:15%;" flex="1"/>
+          <column flex="1"/>
+        </columns>
+        <rows>
+          <row>
+            <label value="&enigmail.keyDetails.userId.label;" control="userId"/>
+            <textbox id="userId" class="plain" style="white-space: pre;"
+              readonly="true" value="?" multiline="false" size="60"/>
+          </row>
+          <row>
+            <label value="&enigmail.keyDetails.keyType.label;" control="keyType"/>
+            <textbox id="keyType" class="plain" style="white-space: pre;"
+              readonly="true" value="?" multiline="false" size="60"/>
+          </row>
+          <row>
+            <label value="&enigmail.keyDetails.fingerprint.label;" control="fingerprint"/>
+            <textbox id="fingerprint" class="plain" style="white-space: pre;"
+              readonly="true" value="?" multiline="false" size="60"/>
+          </row>
+        </rows>
+      </grid>
+      <vbox class="enigmailCaptionbox" id="alsoknown" flex="1">
+        <html:h1><html:span>&enigmail.keyDetails.alsoKnown.label;:</html:span></html:h1>
+        <richlistbox id="additionalUid" style="height: 4em;" flex="1"/>
+      </vbox>
+    </vbox>
+    <vbox flex="1" align="end">
+      <hbox flex="1" align="end">
+        <image src="" id="photoIdImg" style="display: block; height: auto; width: auto; max-width: 100px; max-height: 120px;"/>
+      </hbox>
+    </vbox>
+  </hbox>
+
+  <tabbox flex="1" style="margin:5px" id="mainTabs">
+    <tabs id="mainTabBox">
+      <tab id="basicTab"       label="&enigmail.basic.label;"/>
+      <tab id="signaturesTab"  label="&enigmail.keyDetails.signaturesTab;"/>
+      <tab id="structureTab"   label="&enigmail.keyDetails.structureTab;"/>
+    </tabs>
+
+    <tabpanels flex="1" id="mainTabPanel">
+       <!-- Basic Tab -->
+      <vbox id="basicPanel">
+        <html:table style="width: 100%;">
+          <html:colgroup>
+            <html:col/>
+            <html:col style="width: 100%;"/>
+            <html:col style="text-align: right;"/>
+          </html:colgroup>
+          <html:tr>
+            <html:td style="white-space: nowrap; padding: 0 15px 0 0;">
+              &enigmail.keyDetails.created.label;
+            </html:td>
+            <html:td id="keyCreated"/>
+          </html:tr>
+          <html:tr>
+            <html:td style="white-space: nowrap; padding: 0 15px 0 0;">
+              &enigmail.keyDetails.expiry.label;
+            </html:td>
+            <html:td id="keyExpiry"/>
+            <html:td>
+              <button observes="ownKeyCommands" label="&enigmail.keyDetails.change.label;" oncommand="changeExpirationDate()"/>
+            </html:td>
+          </html:tr>
+          <html:tr>
+            <html:td>
+              &enigmail.keyDetails.keyValidity.label;
+            </html:td>
+            <html:td id="keyValidity"/>
+            <html:td>
+              <button label="&enigmail.keyDetails.signKey.label;" oncommand="signKey()"/>
+            </html:td>
+          </html:tr>
+          <html:tr>
+            <html:td style="white-space: nowrap; padding: 0 15px 0 0;">
+              &enigmail.keyDetails.trustStatus.label;
+            </html:td>
+            <html:td id="ownerTrust"/>
+            <html:td>
+              <button id="setOwnerTrust" label="&enigmail.keyDetails.change.label;" oncommand="setOwnerTrust()"/>
+            </html:td>
+          </html:tr>
+        </html:table>
+      </vbox>
+
+      <!-- certifications tab -->
+      <vbox id="signaturesPanel">
+        <tree id="signatures_tree" flex="1"
+          hidecolumnpicker="true"
+          ondblclick="sigHandleDblClick(event)">
+
+          <treecols>
+            <treecol id="sig_uid_col" flex="1"
+                label="&enigmail.keyDetails.uidCertifiedCol;"
+                primary="true"/>
+            <splitter class="tree-splitter"/>
+            <splitter class="tree-splitter"/>
+            <treecol id="sig_fingerprint_col"
+                     label="&enigmail.keyDetails.fingerprint.label;"
+                     persist="width"/>
+            <treecol id="sig_created_col"
+                     label="&enigmail.keyDetails.created.label;"
+                     persist="width"/>
+          </treecols>
+
+          <treechildren/>
+        </tree>
+      </vbox>
+
+      <!-- structure tab -->
+      <vbox id ="structurePanel">
+        <hbox flex="1">
+          <tree id="subkeyList" flex="1"
+            enableColumnDrag="true"
+            style="height:100px"
+            hidecolumnpicker="true">
+
+            <treecols>
+              <treecol id="keyTypeCol" primary="true"
+                  label="&enigmail.keyDetails.keyPart.label;"
+                  style="width:71px"
+                  persist="width"/>
+              <splitter class="tree-splitter"/>
+              <treecol id="keyUsageCol"
+                       label="&enigmail.keyDetails.usage.label;"
+                       flex="1"/>
+              <splitter class="tree-splitter"/>
+              <treecol id="keyIdCol" style="width:77px"
+                       label="&enigmail.keyDetails.ID.label;"
+                       persist="width"/>
+              <splitter class="tree-splitter"/>
+              <treecol id="algoCol" style="width:60px"
+                       label="&enigmail.keyDetails.algorithm.label;"
+                       persist="width"/>
+              <splitter class="tree-splitter"/>
+              <treecol id="sizeCol" style="width:37px"
+                       label="&enigmail.keyDetails.size.label;"
+                       persist="width"/>
+              <splitter class="tree-splitter"/>
+              <treecol id="createdCol" style="width:70px"
+                       label="&enigmail.keyDetails.created.label;"
+                       persist="width"/>
+              <splitter class="tree-splitter"/>
+              <treecol id="expiryCol" style="width:70px"
+                       label="&enigmail.keyDetails.expiry.label;"
+                       persist="width"/>
+            </treecols>
+
+            <treechildren id="keyListChildren"/>
+
+          </tree>
+        </hbox>
+      </vbox>
+    </tabpanels>
+  </tabbox>
+
+  <separator/>
+
+  <hbox flex="0" observes="ownKeyCommands">
+    <button id="actions" label="&enigmail.keyDetails.selAction.label;"
+            accesskey="&enigmail.keyDetails.selAction.accesskey;" type="menu">
+      <menupopup id="actionPopup">
+        <menuitem label="&enigmail.keyMan.manageUid.label;" oncommand="manageUids()"/>
+        <menuitem label="&enigmail.keyMan.addPhoto.label;" oncommand="keyDetailsAddPhoto()"/>
+        <menuitem label="&enigmail.keyMan.changePwd.label;" oncommand="changePassword()"/>
+        <menuitem label="&enigmail.keyMan.revokeKey.label;" oncommand="revokeKey()"/>
+        <menuitem label="&enigmail.keyMan.ctxGenRevoke.label;" oncommand="genRevocationCert()"/>
+      </menupopup>
+    </button>
+  </hbox>
+
+</dialog>