diff options
Diffstat (limited to 'comm/chat/content/otr-auth.js')
-rw-r--r-- | comm/chat/content/otr-auth.js | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/comm/chat/content/otr-auth.js b/comm/chat/content/otr-auth.js new file mode 100644 index 0000000000..24199a6acc --- /dev/null +++ b/comm/chat/content/otr-auth.js @@ -0,0 +1,198 @@ +/* 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 } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); +const { l10nHelper } = ChromeUtils.importESModule( + "resource:///modules/imXPCOMUtils.sys.mjs" +); +const { OTR } = ChromeUtils.importESModule("resource:///modules/OTR.sys.mjs"); + +window.addEventListener("DOMContentLoaded", event => { + otrAuth.onload(); +}); + +var [mode, uiConv, contactInfo] = window.arguments; + +function showSection(selected, hideMenu) { + document.getElementById("how").hidden = !!hideMenu; + ["questionAndAnswer", "sharedSecret", "manualVerification", "ask"].forEach( + function (key) { + document.getElementById(key).hidden = key !== selected; + } + ); + window.sizeToContent(); +} + +function startSMP(context, answer, question) { + OTR.sendSecret(context, answer, question); + OTR.authUpdate(context, 10); +} + +function manualVerification(fingerprint, context) { + let opts = document.getElementById("verifiedOption"); + let trust = opts.selectedItem.value === "yes"; + OTR.setTrust(fingerprint, trust, context); +} + +async function populateFingers(context, theirs, trust) { + let yours = OTR.privateKeyFingerprint(context.account, context.protocol); + if (!yours) { + throw new Error("Fingerprint should already be generated."); + } + + let [yourFPLabel, theirFPLabel] = await document.l10n.formatValues([ + { id: "auth-your-fp-value", args: { own_name: context.account } }, + { id: "auth-their-fp-value", args: { their_name: context.username } }, + ]); + + document.getElementById("yourFPLabel").value = yourFPLabel; + document.getElementById("theirFPLabel").value = theirFPLabel; + + document.getElementById("yourFPValue").value = yours; + document.getElementById("theirFPValue").value = theirs; + + let opts = document.getElementById("verifiedOption"); + let verified = trust ? "yes" : "no"; + for (let item of opts.menupopup.children) { + if (verified === item.value) { + opts.selectedItem = item; + break; + } + } +} + +var otrAuth = { + async onload() { + // This window implements the interactive authentication of a buddy's + // key. At open time, we're given several parameters, and the "mode" + // parameter tells us from where we've been called. + // mode == "pref" means that we have been opened from the preferences, + // and it means we cannot rely on the other user being online, and + // we there might be no uiConv active currently, so we fall back. + + let nameSource = + mode === "pref" ? contactInfo.screenname : uiConv.normalizedName; + let title = await document.l10n.formatValue("auth-title", { + name: nameSource, + }); + document.title = title; + + document.addEventListener("dialogaccept", () => { + return this.accept(); + }); + + document.addEventListener("dialogcancel", () => { + return this.cancel(); + }); + + let context, theirs; + switch (mode) { + case "start": + context = OTR.getContext(uiConv.target); + theirs = OTR.hashToHuman(context.fingerprint); + populateFingers(context, theirs, context.trust); + showSection("questionAndAnswer"); + break; + case "pref": + context = OTR.getContextFromRecipient( + contactInfo.account, + contactInfo.protocol, + contactInfo.screenname + ); + theirs = contactInfo.fingerprint; + populateFingers(context, theirs, contactInfo.trust); + showSection("manualVerification", true); + this.oninput({ value: true }); + break; + case "ask": + let receivedQuestionLabel = document.getElementById( + "receivedQuestionLabel" + ); + let receivedQuestionDisplay = + document.getElementById("receivedQuestion"); + let responseLabel = document.getElementById("responseLabel"); + if (contactInfo.question) { + receivedQuestionLabel.hidden = false; + receivedQuestionDisplay.hidden = false; + receivedQuestionDisplay.value = contactInfo.question; + responseLabel.value = await document.l10n.formatValue("auth-answer"); + } else { + receivedQuestionLabel.hidden = true; + receivedQuestionDisplay.hidden = true; + responseLabel.value = await document.l10n.formatValue("auth-secret"); + } + showSection("ask", true); + break; + } + }, + + accept() { + // uiConv may not be present in pref mode + let context = uiConv ? OTR.getContext(uiConv.target) : null; + if (mode === "pref") { + manualVerification(contactInfo.fpointer, context); + } else if (mode === "start") { + let how = document.getElementById("howOption"); + switch (how.selectedItem.value) { + case "questionAndAnswer": + let question = document.getElementById("question").value; + let answer = document.getElementById("answer").value; + startSMP(context, answer, question); + break; + case "sharedSecret": + let secret = document.getElementById("secret").value; + startSMP(context, secret); + break; + case "manualVerification": + manualVerification(context.fingerprint, context); + break; + default: + throw new Error("Unreachable!"); + } + } else if (mode === "ask") { + let response = document.getElementById("response").value; + OTR.sendResponse(context, response); + OTR.authUpdate(context, contactInfo.progress); + } else { + throw new Error("Unreachable!"); + } + return true; + }, + + cancel() { + if (mode === "ask") { + let context = OTR.getContext(uiConv.target); + OTR.abortSMP(context); + // Close the ask-auth notification if it was previously triggered. + OTR.notifyObservers( + { + context, + }, + "otr:cancel-ask-auth" + ); + } + }, + + oninput(e) { + document.querySelector("dialog").getButton("accept").disabled = !e.value; + }, + + how() { + let how = document.getElementById("howOption").selectedItem.value; + switch (how) { + case "questionAndAnswer": + this.oninput(document.getElementById("answer")); + break; + case "sharedSecret": + this.oninput(document.getElementById("secret")); + break; + case "manualVerification": + this.oninput({ value: true }); + break; + } + showSection(how); + }, +}; |