/* 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/. */ // chat/content/imAccountOptionsHelper.js /* globals accountOptionsHelper */ const { ChatIcons } = ChromeUtils.importESModule( "resource:///modules/chatIcons.sys.mjs" ); ChromeUtils.defineESModuleGetters(this, { ChatEncryption: "resource:///modules/ChatEncryption.sys.mjs", OTR: "resource:///modules/OTR.sys.mjs", OTRUI: "resource:///modules/OTRUI.sys.mjs", }); var autoJoinPref = "autoJoin"; function onPreInit(aAccount, aAccountValue) { account.init(aAccount.incomingServer.wrappedJSObject.imAccount); } function onBeforeUnload() { if (account.encryptionObserver) { Services.obs.removeObserver( account.encryptionObserver, "account-sessions-changed" ); Services.obs.removeObserver( account.encryptionObserver, "account-encryption-status-changed" ); } } var account = { async init(aAccount) { let title = document.querySelector(".dialogheader .dialogheader-title"); let defaultTitle = title.getAttribute("defaultTitle"); let titleValue; if (aAccount.name) { titleValue = defaultTitle + " - <" + aAccount.name + ">"; } else { titleValue = defaultTitle; } title.setAttribute("value", titleValue); document.title = titleValue; this.account = aAccount; this.proto = this.account.protocol; document.getElementById("accountName").value = this.account.name; document.getElementById("protocolName").value = this.proto.name || this.proto.id; document.getElementById("protocolIcon").src = ChatIcons.getProtocolIconURI( this.proto, 48 ); let password = document.getElementById("server.password"); let passwordBox = document.getElementById("passwordBox"); if (this.proto.noPassword) { passwordBox.hidden = true; password.removeAttribute("wsm_persist"); } else { passwordBox.hidden = false; try { // Should we force layout here to ensure password.value works? // Will throw if we don't have a protocol plugin for the account. password.value = this.account.password; password.setAttribute("wsm_persist", "true"); } catch (e) { passwordBox.hidden = true; password.removeAttribute("wsm_persist"); } } document.getElementById("server.alias").value = this.account.alias; if (ChatEncryption.canConfigureEncryption(this.account.protocol)) { document.getElementById("imTabEncryption").hidden = false; document.querySelector(".otr-settings").hidden = !OTRUI.enabled; document.getElementById("server.otrAllowMsgLog").value = this.account.otrAllowMsgLog; if (OTRUI.enabled) { 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) { fp = await document.l10n.formatValue("otr-not-yet-available"); } document.getElementById("otrFingerprint").value = fp; } document.querySelector(".chat-encryption-settings").hidden = !this.account.protocol.canEncrypt; if (this.account.protocol.canEncrypt) { document.l10n.setAttributes( document.getElementById("chat-encryption-description"), "chat-encryption-description", { protocol: this.proto.name, } ); this.buildEncryptionStatus(); this.buildAccountSessionsList(); this.encryptionObserver = { observe: (subject, topic) => { if ( topic === "account-sessions-changed" && subject.id === this.account.id ) { this.buildAccountSessionsList(); } else if ( topic === "account-encryption-status-changed" && subject.id === this.account.id ) { this.buildEncryptionStatus(); } }, QueryInterface: ChromeUtils.generateQI([ "nsIObserver", "nsISupportsWeakReference", ]), }; Services.obs.addObserver( this.encryptionObserver, "account-sessions-changed", true ); Services.obs.addObserver( this.encryptionObserver, "account-encryption-status-changed", true ); } } let protoId = this.proto.id; let canAutoJoin = protoId == "prpl-irc" || protoId == "prpl-jabber" || protoId == "prpl-gtalk"; document.getElementById("autojoinBox").hidden = !canAutoJoin; let autojoin = document.getElementById("server.autojoin"); if (canAutoJoin) { autojoin.setAttribute("wsm_persist", "true"); } else { autojoin.removeAttribute("wsm_persist"); } this.prefs = Services.prefs.getBranch( "messenger.account." + this.account.id + ".options." ); this.populateProtoSpecificBox(); }, encryptionObserver: null, buildEncryptionStatus() { const encryptionStatus = document.querySelector(".chat-encryption-status"); if (this.account.encryptionStatus.length) { encryptionStatus.replaceChildren( ...this.account.encryptionStatus.map(status => { const item = document.createElementNS( "http://www.w3.org/1999/xhtml", "li" ); item.textContent = status; return item; }) ); } else { const placeholder = document.createElementNS( "http://www.w3.org/1999/xhtml", "li" ); document.l10n.setAttributes(placeholder, "chat-encryption-placeholder"); encryptionStatus.replaceChildren(placeholder); } }, buildAccountSessionsList() { const sessions = this.account.getSessions(); document.querySelector(".chat-encryption-sessions-container").hidden = sessions.length === 0; const sessionList = document.querySelector(".chat-encryption-sessions"); sessionList.replaceChildren( ...sessions.map(session => { const button = document.createElementNS( "http://www.w3.org/1999/xhtml", "button" ); document.l10n.setAttributes( button, "chat-encryption-session-" + (session.trusted ? "trusted" : "verify") ); button.disabled = session.trusted; if (!button.disabled) { button.addEventListener("click", async () => { try { const sessionInfo = await session.verify(); parent.gSubDialog.open( "chrome://messenger/content/chat/verify.xhtml", { features: "resizable=no" }, sessionInfo ); } catch (error) { // Verification was probably aborted by the other side. this.account.prplAccount.wrappedJSObject.WARN(error); } }); } const sessionLabel = document.createElementNS( "http://www.w3.org/1999/xhtml", "span" ); sessionLabel.textContent = session.id; const row = document.createElementNS( "http://www.w3.org/1999/xhtml", "li" ); row.append(sessionLabel, button); row.classList.toggle("chat-current-session", session.currentSession); return row; }) ); }, populateProtoSpecificBox() { let attributes = {}; attributes[Ci.prplIPref.typeBool] = [ { name: "wsm_persist", value: "true" }, { name: "preftype", value: "bool" }, { name: "genericattr", value: "true" }, ]; attributes[Ci.prplIPref.typeInt] = [ { name: "wsm_persist", value: "true" }, { name: "preftype", value: "int" }, { name: "genericattr", value: "true" }, ]; attributes[Ci.prplIPref.typeString] = attributes[Ci.prplIPref.typeList] = [ { name: "wsm_persist", value: "true" }, { name: "preftype", value: "wstring" }, { name: "genericattr", value: "true" }, ]; let haveOptions = accountOptionsHelper.addOptions( "server.", this.proto.getOptions(), attributes ); let advanced = document.getElementById("advanced"); if (advanced.hidden && haveOptions) { advanced.hidden = false; // Force textbox XBL binding attachment by forcing layout, // otherwise setFormElementValue from AccountManager.js sets // properties that don't exist when restoring values. document.getElementById("protoSpecific").getBoundingClientRect(); } else if (!haveOptions) { advanced.hidden = true; } let inputElements = document.querySelectorAll( "#protoSpecific :is(checkbox, input, menulist)" ); // Because the elements are added after the document loaded we have to // notify the parent document that there are prefs to save. for (let input of inputElements) { if (input.localName == "input" || input.localName == "textarea") { input.addEventListener("change", event => { document.dispatchEvent(new CustomEvent("prefchange")); }); } else { input.addEventListener("command", event => { document.dispatchEvent(new CustomEvent("prefchange")); }); } } }, viewFingerprintKeys() { let otrAccount = { account: this.account }; parent.gSubDialog.open( "chrome://chat/content/otr-finger.xhtml", undefined, otrAccount ); }, };