Bug 1552177 - OTR chat: private key generation should be done in the background, remove status UI. r=mkmelin
authorKai Engert <kaie@kuix.de>
Tue, 04 Jun 2019 22:58:00 +0200
changeset 35769 fe88d825d3c671d9b24f56f1477e30302c7cebca
parent 35768 0146e4f2fdaf0c60393d24808e76047e0b7ee32a
child 35770 0cddb77c70798d62b4d355c31e073fdb302a293f
push id392
push userclokep@gmail.com
push dateMon, 02 Sep 2019 20:17:19 +0000
reviewersmkmelin
bugs1552177
Bug 1552177 - OTR chat: private key generation should be done in the background, remove status UI. r=mkmelin Differential Revision: https://phabricator.services.mozilla.com/D32838
chat/content/jar.mn
chat/content/otr-generate-key.js
chat/content/otr-generate-key.xul
chat/content/otr/generate-key.ftl
chat/content/otr/otrUI.ftl
chat/modules/OTR.jsm
chat/modules/OTRUI.jsm
mail/components/im/content/am-im.js
mail/locales/jar.mn
--- a/chat/content/jar.mn
+++ b/chat/content/jar.mn
@@ -13,11 +13,9 @@ chat.jar:
 	content/chat/conversation-browser.js
 	content/chat/conv.html
 	content/chat/otr-add-fingerprint.js
 	content/chat/otr-add-fingerprint.xul
 	content/chat/otr-auth.js
 	content/chat/otr-auth.xul
 	content/chat/otr-finger.js
 	content/chat/otr-finger.xul
-	content/chat/otr-generate-key.js
-	content/chat/otr-generate-key.xul
 	content/chat/otrWorker.js
deleted file mode 100644
--- a/chat/content/otr-generate-key.js
+++ /dev/null
@@ -1,50 +0,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/. */
-
-const {
-  XPCOMUtils,
-  l10nHelper,
-} = ChromeUtils.import("resource:///modules/imXPCOMUtils.jsm");
-const {Services} = ChromeUtils.import("resource:///modules/imServices.jsm");
-const {OTR} = ChromeUtils.import("resource:///modules/OTR.jsm");
-
-var otrPriv = {
-
-  async onload() {
-    let args = window.arguments[0].wrappedJSObject;
-    let priv = document.getElementById("priv");
-
-    let protocolNameToShow;
-
-    // args.protocol is the normalized protocol name.
-    // However, we don't want to show normalized names like "jabber",
-    // but want to show the terms used in the UI like "XMPP".
-
-    let protocols = Services.core.getProtocols();
-    while (protocols.hasMoreElements()) {
-      let protocol = protocols.getNext();
-      if (protocol.normalizedName === args.protocol) {
-        protocolNameToShow = protocol.name;
-        break;
-      }
-    }
-
-    if (!protocolNameToShow) {
-      protocolNameToShow = args.protocol;
-    }
-
-    let text = await document.l10n.formatValue(
-      "otr-genkey-account", {name: args.account, protocol: protocolNameToShow});
-    priv.textContent = text;
-
-    OTR.generatePrivateKey(args.account, args.protocol).then(function() {
-      document.documentElement.getButton("accept").disabled = false;
-      document.documentElement.acceptDialog();
-    }).catch(async function(err) {
-      priv.textContent = await document.l10n.formatValue(
-          "otr-genkey-failed", {error: String(err)});
-      document.documentElement.getButton("accept").disabled = false;
-    });
-  },
-};
deleted file mode 100644
--- a/chat/content/otr-generate-key.xul
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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>
-
-<dialog
-  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-  xmlns:html="http://www.w3.org/1999/xhtml"
-  id="otrPrivDialog"
-  data-l10n-id="otr-generate-key"
-  data-l10n-attrs="buttonlabelaccept"
-  windowtype="OTR:Priv"
-  buttons="accept"
-  onload="otrPriv.onload()"
-  buttondisabledaccept="true">
-
-  <linkset>
-    <html:link rel="localization" href="messenger/otr/generate-key.ftl"/>
-  </linkset>
-
-  <script src="chrome://chat/content/otr-generate-key.js" />
-  <description id="priv" style="width: 300px; white-space: pre-wrap;"></description>
-
-</dialog>
deleted file mode 100644
--- a/chat/content/otr/generate-key.ftl
+++ /dev/null
@@ -1,16 +0,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/.
-
-otr-generate-key =
-    .title = Generating private key
-    .buttonlabelaccept = Done
-
-# Variables:
-#   $name (String) - the name of the user's own chat account
-#   $protocol (String) - the chat communication protocol used by that account
-otr-genkey-account = Generating private key for { $name } ({ $protocol }) …
-
-# Variables:
-#   $error (String) - contains an error message that describes the cause of the failure
-otr-genkey-failed = Generating key failed: { $error }
--- a/chat/content/otr/otrUI.ftl
+++ b/chat/content/otr/otrUI.ftl
@@ -70,8 +70,13 @@ afterauth-private = You have verified th
 afterauth-unverified = The identity of { $name } has not been verified.
 
 verify-title = Verify your contact's identity
 error-title = Error
 success-title = End to End Encryption
 successThem-title = Verify your contact's identity
 fail-title = Unable to verify
 waiting-title = Verification request sent
+
+# Do not translate 'OTR' (name of an encryption protocol)
+# Variables:
+#   $error (String) - contains an error message that describes the cause of the failure
+otr-genkey-failed = Generating OTR private key failed: { $error }
--- a/chat/modules/OTR.jsm
+++ b/chat/modules/OTR.jsm
@@ -193,16 +193,39 @@ var OTR = {
         throw new Error("otrl_privkey_generate_calculate (" + err + ")");
     }).catch(function(err) {
       if (!newkey.isNull())
         OTRLib.otrl_privkey_generate_cancelled(OTR.userstate, newkey);
       throw err;
     });
   },
 
+  generatePrivateKeySync(account, protocol) {
+    let newkey = new ctypes.void_t.ptr();
+    let err = OTRLib.otrl_privkey_generate_start(
+      OTR.userstate, account, protocol, newkey.address()
+    );
+    if (err || newkey.isNull())
+      return "otrl_privkey_generate_start (" + err + ")";
+
+    err = OTRLib.otrl_privkey_generate_calculate(newkey);
+    if (!err) {
+      err = OTRLib.otrl_privkey_generate_finish(
+        OTR.userstate, newkey, OTR.privateKeyPath);
+    }
+    if (err && !newkey.isNull()) {
+      OTRLib.otrl_privkey_generate_cancelled(OTR.userstate, newkey);
+    }
+
+    if (err) {
+      return "otrl_privkey_generate_calculate (" + err + ")";
+    }
+    return null;
+  },
+
   // write fingerprints to file synchronously
   writeFingerprints() {
     if (OTRLib.otrl_privkey_write_fingerprints(
       this.userstate, this.fingerprintsPath
     )) throw new Error("Failed to write fingerprints.");
   },
 
   // generate instance tag synchronously
--- a/chat/modules/OTRUI.jsm
+++ b/chat/modules/OTRUI.jsm
@@ -16,21 +16,20 @@ const syncL10n = new LocalizationSync([
 function _str(id) {
   return syncL10n.formatValue(id);
 }
 
 function _strArgs(id, args) {
   return syncL10n.formatValue(id, args);
 }
 
-const privDialog = "chrome://chat/content/otr-generate-key.xul";
-const authDialog = "chrome://chat/content/otr-auth.xul";
-const addFingerDialog = "chrome://chat/content/otr-add-fingerprint.xul";
+const OTR_AUTH_DIALOG_URL = "chrome://chat/content/otr-auth.xul";
+const OTR_ADD_FINGER_DIALOG_URL = "chrome://chat/content/otr-add-fingerprint.xul";
 
-const AuthVerify = "otr-auth-unverified";
+const AUTH_STATUS_UNVERIFIED = "otr-auth-unverified";
 var authLabelMap;
 var authTitleMap;
 var trustMap;
 
 function initStrings() {
   authLabelMap = new Map([
     ["otr:auth-error", _str("auth-error")],
     ["otr:auth-success", _str("auth-success")],
@@ -147,17 +146,17 @@ var OTRUI = {
     menuitem.setAttribute("id", "otrcont");
     menuitem.addEventListener("command", () => {
       let target = buddyContextMenu.triggerNode;
       if (target.localName == "richlistitem") {
         let contact = target.contact;
         let args = OTRUI.contactWrapper(contact);
         args.wrappedJSObject = args;
         let features = "chrome,modal,centerscreen,resizable=no,minimizable=no";
-        Services.ww.openWindow(null, addFingerDialog, "", features, args);
+        Services.ww.openWindow(null, OTR_ADD_FINGER_DIALOG_URL, "", features, args);
       }
     });
 
     buddyContextMenu.addEventListener("popupshowing", (e) => {
       let target = e.target.triggerNode;
       if (target.localName == "richlistitem") {
         menuitem.hidden = false;
         sep.hidden = false;
@@ -177,16 +176,56 @@ var OTRUI = {
       s.remove();
     }
     let p = doc.getElementById("otrcont");
     if (p) {
       p.remove();
     }
   },
 
+  loopKeyGenSuccess() {
+    ChromeUtils.idleDispatch(OTRUI.genNextMissingKey);
+  },
+
+  loopKeyGenFailure(param) {
+    ChromeUtils.idleDispatch(OTRUI.genNextMissingKey);
+    OTRUI.reportKeyGenFailure(param);
+  },
+
+  reportKeyGenFailure(param) {
+    throw new Error(_strArgs("otr-genkey-failed", {error: String(param)}));
+  },
+
+  accountsToGenKey: [],
+
+  genNextMissingKey() {
+    if (!OTRUI.accountsToGenKey) {
+      return;
+    }
+
+    let acc = OTRUI.accountsToGenKey.pop();
+    let fp = OTR.privateKeyFingerprint(acc.name, acc.prot);
+    if (!fp) {
+      OTR.generatePrivateKey(acc.name, acc.prot)
+      .then(OTRUI.loopKeyGenSuccess, OTRUI.loopKeyGenFailure);
+    } else {
+      ChromeUtils.idleDispatch(OTRUI.genNextMissingKey);
+    }
+  },
+
+  genMissingKeys() {
+    for (let acc of Services.accounts.getAccounts()) {
+      OTRUI.accountsToGenKey.push({
+        name: acc.normalizedName,
+        prot: acc.protocol.normalizedName,
+      });
+    }
+    ChromeUtils.idleDispatch(OTRUI.genNextMissingKey);
+  },
+
   async init() {
     if (!OTRUI.stringsLoaded) {
       initStrings();
       OTRUI.stringsLoaded = true;
     }
 
     this.debug = Services.prefs.getBoolPref("chat.otr.trace", false);
 
@@ -209,16 +248,18 @@ var OTRUI = {
       Services.obs.addObserver(OTRUI, "prpl-quit");
 
       let conversations = Services.conversations.getConversations();
       while (conversations.hasMoreElements()) {
       let aConv = conversations.getNext();
       OTRUI.initConv(aConv);
       }
       OTRUI.addMenuObserver();
+
+      ChromeUtils.idleDispatch(OTRUI.genMissingKeys);
     }).catch(function(err) {
       // console.log("===> " + err + "\n");
       throw err;
     });
   },
 
   disconnect(aConv) {
     if (aConv)
@@ -235,17 +276,17 @@ var OTRUI = {
     }
     return allGood;
   },
 
   openAuth(window, name, mode, uiConv, contactInfo) {
     let otrAuth = this.globalDoc.querySelector(".otr-auth");
     otrAuth.disabled = true;
     let win = window.openDialog(
-      authDialog,
+      OTR_AUTH_DIALOG_URL,
       "auth=" + name,
       "centerscreen,resizable=no,minimizable=no",
       mode,
       uiConv,
       contactInfo
     );
     windowRefs.set(name, win);
     window.addEventListener("beforeunload", function() {
@@ -414,17 +455,17 @@ var OTRUI = {
 
   closeUnverified(context) {
     let uiConv = OTR.getUIConvFromContext(context);
     if (!uiConv) return;
 
     let notifications = this.globalBox.allNotifications;
     for (let i = notifications.length - 1; i >= 0; i--) {
       if (context.username == notifications[i].getAttribute("user") &&
-          notifications[i].getAttribute("value") == AuthVerify) {
+          notifications[i].getAttribute("value") == AUTH_STATUS_UNVERIFIED) {
         notifications[i].close();
       }
     }
   },
 
   hideUserNotifications(context) {
     let notifications = this.globalBox.allNotifications;
     for (let i = notifications.length - 1; i >= 0; i--) {
@@ -466,17 +507,17 @@ var OTRUI = {
         return true;
       },
     }];
 
     let priority = this.globalBox.PRIORITY_WARNING_MEDIUM;
     this.globalBox.appendNotification(msg, context.username, null, priority, buttons, null);
 
     let verifyTitle = syncL10n.formatValue("verify-title");
-    this.updateNotificationUI(context, verifyTitle, context.username, AuthVerify);
+    this.updateNotificationUI(context, verifyTitle, context.username, AUTH_STATUS_UNVERIFIED);
 
     if (context.username !== this.visibleConv.normalizedName) {
       this.hideUserNotifications(context);
     }
   },
 
   updateNotificationUI(context, typeTitle, username, key) {
     let notification = this.globalBox.getNotificationWithValue(username);
@@ -578,28 +619,23 @@ var OTRUI = {
       OTRUI.notifyVerification(aObj.context, key, false);
     } else {
       // TODO: show the aObj.progress to the user with a
       //   <progressmeter mode="determined" value="10" />
       OTRUI.notifyVerification(aObj.context, "otr:auth-waiting", true);
     }
   },
 
-  generate(args) {
-    let features = "chrome,modal,centerscreen,resizable=no,minimizable=no";
-    args.wrappedJSObject = args;
-    Services.ww.openWindow(null, privDialog, "", features, args);
-  },
-
   onAccountCreated(acc) {
     let account = acc.normalizedName;
     let protocol = acc.protocol.normalizedName;
     Promise.resolve();
     if (OTR.privateKeyFingerprint(account, protocol) === null)
-      OTR.generatePrivateKey(account, protocol);
+      OTR.generatePrivateKey(account, protocol)
+      .catch(OTRUI.reportKeyGenFailure);
   },
 
   contactWrapper(contact) {
     // If the conversation already started.
     if (contact.buddy) {
       return {
         account: contact.buddy.normalizedName,
         protocol: contact.buddy.buddy.protocol.normalizedName,
@@ -616,17 +652,17 @@ var OTRUI = {
   },
 
   onContactAdded(contact) {
     let args = OTRUI.contactWrapper(contact);
     if (OTR.getFingerprintsForRecipient(args.account, args.protocol, args.screenname).length > 0)
       return;
     args.wrappedJSObject = args;
     let features = "chrome,modal,centerscreen,resizable=no,minimizable=no";
-    Services.ww.openWindow(null, addFingerDialog, "", features, args);
+    Services.ww.openWindow(null, OTR_ADD_FINGER_DIALOG_URL, "", features, args);
   },
 
   observe(aObject, aTopic, aMsg) {
     let doc;
     // console.log("====> observing topic: " + aTopic + " with msg: " + aMsg);
     // console.log(aObject);
 
     switch (aTopic) {
@@ -650,19 +686,24 @@ var OTRUI = {
       // case "contact-signed-off":
       //  break;
       case "prpl-quit":
         OTRUI.disconnect(null);
         break;
       case "domwindowopened":
         OTRUI.addMenus(aObject);
         break;
-      case "otr:generate":
-        OTRUI.generate(aObject);
+      case "otr:generate": {
+        let result =
+          OTR.generatePrivateKeySync(aObject.account, aObject.protocol);
+        if (result != null) {
+          OTRUI.reportKeyGenFailure(result);
+        }
         break;
+      }
       case "otr:disconnected":
       case "otr:msg-state":
         if (aTopic === "otr:disconnected" ||
             OTR.trust(aObject) !== OTR.trustState.TRUST_UNVERIFIED) {
           OTRUI.closeAuth(aObject);
           OTRUI.closeUnverified(aObject);
           OTRUI.closeVerification(aObject);
         }
--- a/mail/components/im/content/am-im.js
+++ b/mail/components/im/content/am-im.js
@@ -61,17 +61,16 @@ var account = {
       document.getElementById("imTabOTR").hidden = false;
       document.getElementById("server.otrVerifyNudge").value = this.account.otrVerifyNudge;
       document.getElementById("server.otrRequireEncryption").value = this.account.otrRequireEncryption;
 
       let fpa = this.account.normalizedName;
       let fpp = this.account.protocol.normalizedName;
       let fp = OTR.privateKeyFingerprint(fpa, fpp);
       if (!fp) {
-        OTR.generatePrivateKey(fpa, fpp);
         fp = await document.l10n.formatValue("otr-notYetAvailable");
       }
       document.getElementById("otrFingerprint").value = fp;
     }
 
     let protoId = this.proto.id;
     let canAutoJoin = protoId == "prpl-irc" ||
                       protoId == "prpl-jabber" ||
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -12,17 +12,16 @@
   # When ready for l10n, move chat/content/otr/*.ftl
   #                        to mail/locales/en-US/messenger/otr/
   # and remove the following otr/ lines.
   messenger/otr/am-im-otr.ftl                                           (../../chat/content/otr/am-im-otr.ftl)
   messenger/otr/finger.ftl                                              (../../chat/content/otr/finger.ftl)
   messenger/otr/add-finger.ftl                                          (../../chat/content/otr/add-finger.ftl)
   messenger/otr/auth.ftl                                                (../../chat/content/otr/auth.ftl)
   messenger/otr/chat.ftl                                                (../../chat/content/otr/chat.ftl)
-  messenger/otr/generate-key.ftl                                        (../../chat/content/otr/generate-key.ftl)
   messenger/otr/otr.ftl                                                 (../../chat/content/otr/otr.ftl)
   messenger/otr/otrUI.ftl                                               (../../chat/content/otr/otrUI.ftl)
   messenger                                                             (%messenger/**/*.ftl)
 
 
 @AB_CD@.jar:
 % locale messenger @AB_CD@ %locale/@AB_CD@/messenger/
   locale/@AB_CD@/messenger/aboutDialog.dtd                              (%chrome/messenger/aboutDialog.dtd)