This is an automated email from the git hooks/post-receive script.
pierov pushed a commit to branch main in repository torbutton.
The following commit(s) were added to refs/heads/main by this push: new 9f2e434a Bug 40012: Linted torbutton 9f2e434a is described below
commit 9f2e434a7f09d2f53c529e7293bf715c4a6b71e6 Author: Pier Angelo Vendrame pierov@torproject.org AuthorDate: Wed Aug 24 16:28:30 2022 +0200
Bug 40012: Linted torbutton --- chrome/content/aboutTor/aboutTor-content.js | 64 +- chrome/content/preferences-mobile.js | 49 +- chrome/content/tor-circuit-display.js | 907 +++++++++------- chrome/content/torbutton.js | 1561 ++++++++++++++------------- components/domain-isolator.js | 98 +- components/dragDropFilter.js | 39 +- components/external-app-blocker.js | 75 +- components/startup-observer.js | 270 ++--- components/torCheckService.js | 119 +- components/torbutton-logger.js | 201 ++-- modules/tor-control-port.js | 457 ++++---- modules/utils.js | 238 ++-- 12 files changed, 2217 insertions(+), 1861 deletions(-)
diff --git a/chrome/content/aboutTor/aboutTor-content.js b/chrome/content/aboutTor/aboutTor-content.js index 601b817c..55bf4413 100644 --- a/chrome/content/aboutTor/aboutTor-content.js +++ b/chrome/content/aboutTor/aboutTor-content.js @@ -14,9 +14,14 @@ * AboutTor:ChromeData privileged data chrome -> content */
+/* globals content, addMessageListener, sendAsyncMessage, + removeMessageListener */ + const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-let { bindPrefAndInit, show_torbrowser_manual } = ChromeUtils.import("resource://torbutton/modules/utils.js", {}); +const { bindPrefAndInit, show_torbrowser_manual } = ChromeUtils.import( + "resource://torbutton/modules/utils.js" +);
var AboutTorListener = { kAboutTorLoadedMessage: "AboutTor:Loaded", @@ -26,13 +31,14 @@ var AboutTorListener = { return content.document.documentURI.toLowerCase() == "about:tor"; },
- init: function(aChromeGlobal) { + init(aChromeGlobal) { aChromeGlobal.addEventListener("AboutTorLoad", this, false, true); },
- handleEvent: function(aEvent) { - if (!this.isAboutTor) + handleEvent(aEvent) { + if (!this.isAboutTor) { return; + }
switch (aEvent.type) { case "AboutTorLoad": @@ -44,9 +50,10 @@ var AboutTorListener = { } },
- receiveMessage: function(aMessage) { - if (!this.isAboutTor) + receiveMessage(aMessage) { + if (!this.isAboutTor) { return; + }
switch (aMessage.name) { case this.kAboutTorChromeDataMessage: @@ -55,7 +62,7 @@ var AboutTorListener = { } },
- onPageLoad: function() { + onPageLoad() { // Arrange to update localized text and links. bindPrefAndInit("intl.locale.requested", () => { const aNewVal = Services.locale.requestedLocale; @@ -72,48 +79,53 @@ var AboutTorListener = { sendAsyncMessage(this.kAboutTorLoadedMessage); },
- onPageHide: function() { + onPageHide() { removeEventListener("resize", this, false); removeEventListener("pagehide", this, false); removeMessageListener(this.kAboutTorChromeDataMessage, this); },
- onChromeDataUpdate: function(aData) { + onChromeDataUpdate(aData) { let body = content.document.body;
// Update status: tor on/off, Tor Browser manual shown. - if (aData.torOn) + if (aData.torOn) { body.setAttribute("toron", "yes"); - else + } else { body.removeAttribute("toron"); + }
- if (show_torbrowser_manual()) + if (show_torbrowser_manual()) { body.setAttribute("showmanual", "yes"); - else + } else { body.removeAttribute("showmanual"); + }
- if (aData.updateChannel) + if (aData.updateChannel) { body.setAttribute("updatechannel", aData.updateChannel); - else + } else { body.removeAttribute("updatechannel"); + }
if (aData.hasBeenUpdated) { body.setAttribute("hasbeenupdated", "yes"); - content.document.getElementById("update-infolink").setAttribute("href", - aData.updateMoreInfoURL); + content.document + .getElementById("update-infolink") + .setAttribute("href", aData.updateMoreInfoURL); }
- if (aData.mobile) + if (aData.mobile) { body.setAttribute("mobile", "yes"); + }
// Setting body.initialized="yes" displays the body. body.setAttribute("initialized", "yes"); },
- onLocaleChange: function(aLocale) { + onLocaleChange(aLocale) { // Set localized "Get Involved" link. content.document.getElementById("getInvolvedLink").href = - "https://community.torproject.org/" + aLocale; + "https://community.torproject.org/" + aLocale;
// Display the Tor Browser product name and version. try { @@ -123,12 +135,14 @@ var AboutTorListener = { let tbbVersion = Services.prefs.getCharPref("torbrowser.version"); let elem = content.document.getElementById("torbrowser-version");
- while (elem.firstChild) - elem.removeChild(elem.firstChild); - elem.appendChild(content.document.createTextNode(productName + ' ' - + tbbVersion)); + while (elem.firstChild) { + elem.firstChild.remove(); + } + elem.appendChild( + content.document.createTextNode(productName + " " + tbbVersion) + ); } catch (e) {} - } + }, };
AboutTorListener.init(this); diff --git a/chrome/content/preferences-mobile.js b/chrome/content/preferences-mobile.js index fa79dce8..92564e90 100644 --- a/chrome/content/preferences-mobile.js +++ b/chrome/content/preferences-mobile.js @@ -2,26 +2,30 @@
// Utilities const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { getBoolPref, getIntPref, setBoolPref, setIntPref, getCharPref } - = Services.prefs; +const { + getBoolPref, + getIntPref, + setBoolPref, + setIntPref, + getCharPref, +} = Services.prefs;
-let { getLocale, show_torbrowser_manual } = - ChromeUtils.import("resource://torbutton/modules/utils.js", {}); +let { getLocale, show_torbrowser_manual } = ChromeUtils.import( + "resource://torbutton/modules/utils.js" +);
// Description elements have the follow names. -const descNames = - [, "desc_standard", "desc_safer", "desc_safest"]; +const descNames = ["", "desc_standard", "desc_safer", "desc_safest"]; // "Learn-more"-elements have the follow names. -const linkNames = - [, "link_standard", "link_safer", "link_safest"]; +const linkNames = ["", "link_standard", "link_safer", "link_safest"]; // A single `state` object that reflects the user settings in this UI.
-let state = { slider : 0, custom : false}; +let state = { slider: 0, custom: false };
// Utility functions to convert between the legacy 4-value pref index // and the 3-valued security slider. -let sliderPositionToPrefSetting = pos => [, 4, 2, 1][pos]; -let prefSettingToSliderPosition = pref => [, 3, 2, 2, 1][pref]; +let sliderPositionToPrefSetting = pos => [0, 4, 2, 1][pos]; +let prefSettingToSliderPosition = pref => [0, 3, 2, 2, 1][pref];
// Set the desired slider value and update UI. function torbutton_set_slider(sliderValue) { @@ -31,9 +35,9 @@ function torbutton_set_slider(sliderValue) { let descs = descNames.map(name => document.getElementById(name)); descs.forEach((desc, i) => { if (state.slider !== i) { - desc.style.display = 'none'; + desc.style.display = "none"; } else { - desc.style.display = 'block'; + desc.style.display = "block"; } }); torbutton_save_security_settings(); @@ -42,15 +46,20 @@ function torbutton_set_slider(sliderValue) { // Read prefs 'extensions.torbutton.security_slider' and // 'extensions.torbutton.security_custom', and initialize the UI. function torbutton_init_security_ui() { - torbutton_set_slider(prefSettingToSliderPosition( - getIntPref("extensions.torbutton.security_slider"))); + torbutton_set_slider( + prefSettingToSliderPosition( + getIntPref("extensions.torbutton.security_slider") + ) + ); torbutton_set_learn_more_links(); }
// Write the two prefs from the current settings. function torbutton_save_security_settings() { - setIntPref("extensions.torbutton.security_slider", - sliderPositionToPrefSetting(state.slider)); + setIntPref( + "extensions.torbutton.security_slider", + sliderPositionToPrefSetting(state.slider) + ); setBoolPref("extensions.torbutton.security_custom", state.custom); }
@@ -59,15 +68,15 @@ function torbutton_save_security_settings() { // let's show the "Learn more"-link, otherwise hide it. function torbutton_set_learn_more_links() { let show_manual = show_torbrowser_manual(); - let locale = "" + let locale = ""; if (show_manual) { locale = getLocale(); } let links = linkNames.map(name => document.getElementById(name)); links.forEach(link => { if (show_manual && locale != "") { - link.href= "https:/tb-manual.torproject.org/" + locale + - "/security-slider.html"; + link.href = + "https:/tb-manual.torproject.org/" + locale + "/security-slider.html"; link.hidden = false; } else { link.hidden = true; diff --git a/chrome/content/tor-circuit-display.js b/chrome/content/tor-circuit-display.js index 14e3da5b..e4b5ceea 100644 --- a/chrome/content/tor-circuit-display.js +++ b/chrome/content/tor-circuit-display.js @@ -21,28 +21,38 @@ // a previous call to configureControlPortModule(), and binds to a named // bool pref whose value determines whether the circuit display is enabled // or disabled. -let createTorCircuitDisplay = (function () { - -"use strict"; - -// Mozilla utilities -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - -// Import the controller code. -let { wait_for_controller } = ChromeUtils.import("resource://torbutton/modules/tor-control-port.js", {}); - -// Utility functions -let { bindPrefAndInit, observe, getLocale, getDomainForBrowser, torbutton_get_property_string } = ChromeUtils.import("resource://torbutton/modules/utils.js", {}); - -// Make the TorButton logger available. -let logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; - -// ## Circuit/stream credentials and node monitoring - -// A mutable map that stores the current nodes for each -// SOCKS username/password pair. -let credentialsToNodeDataMap = new Map(), +let createTorCircuitDisplay = (function() { + "use strict"; + + // Mozilla utilities + const { Services } = ChromeUtils.import( + "resource://gre/modules/Services.jsm" + ); + + // Import the controller code. + const { wait_for_controller } = ChromeUtils.import( + "resource://torbutton/modules/tor-control-port.js" + ); + + // Utility functions + let { + bindPrefAndInit, + observe, + getLocale, + getDomainForBrowser, + torbutton_get_property_string, + } = ChromeUtils.import("resource://torbutton/modules/utils.js"); + + // Make the TorButton logger available. + let logger = Cc["@torproject.org/torbutton-logger;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + + // ## Circuit/stream credentials and node monitoring + + // A mutable map that stores the current nodes for each + // SOCKS username/password pair. + let credentialsToNodeDataMap = new Map(), // A mutable map that reports `true` for IDs of "mature" circuits // (those that have conveyed a stream). knownCircuitIDs = new Map(), @@ -50,424 +60,477 @@ let credentialsToNodeDataMap = new Map(), // latest channels for each browser + domain. browserToCredentialsMap = new Map();
-// __trimQuotes(s)__. -// Removes quotation marks around a quoted string. -let trimQuotes = s => s ? s.match(/^"(.*)"$/)[1] : undefined; - -// __getBridge(id)__. -// Gets the bridge parameters for a given node ID. If the node -// is not currently used as a bridge, returns null. -let getBridge = async function (controller, id) { - let bridges = await controller.getConf("bridge"); - if (bridges) { - for (let bridge of bridges) { - if (bridge.ID && bridge.ID.toUpperCase() === id.toUpperCase()) { - return bridge; + // __trimQuotes(s)__. + // Removes quotation marks around a quoted string. + let trimQuotes = s => (s ? s.match(/^"(.*)"$/)[1] : undefined); + + // __getBridge(id)__. + // Gets the bridge parameters for a given node ID. If the node + // is not currently used as a bridge, returns null. + let getBridge = async function(controller, id) { + let bridges = await controller.getConf("bridge"); + if (bridges) { + for (let bridge of bridges) { + if (bridge.ID && bridge.ID.toUpperCase() === id.toUpperCase()) { + return bridge; + } } } - } - return null; -}; - -// nodeDataForID(controller, id)__. -// Returns the type, IP addresses and country code of a node with given ID. -// Example: `nodeDataForID(controller, "20BC91DC525C3DC9974B29FBEAB51230DE024C44")` -// => `{ type: "default", ipAddrs: ["12.23.34.45", "2001:db8::"], countryCode: "fr" }` -let nodeDataForID = async function (controller, id) { - let result = {ipAddrs: []}; - const bridge = await getBridge(controller, id); // type, ip, countryCode; - const addrRe = /^[?([^]]+)]?:\d+$/ - if (bridge) { - result.type = "bridge"; - result.bridgeType = bridge.type; - // Attempt to get an IP address from bridge address string. - try { - const ip = bridge.address.match(addrRe)[1]; - if (!ip.startsWith("0.")) { - result.ipAddrs = [ip]; - } - } catch (e) { - } - } else { - // either dealing with a relay, or a bridge whose fingerprint is not saved in torrc - try { - const statusMap = await controller.getInfo("ns/id/" + id); - result.type = "default"; - if (!statusMap.IP.startsWith("0.")) { - result.ipAddrs.push(statusMap.IP); - } + return null; + }; + + // nodeDataForID(controller, id)__. + // Returns the type, IP addresses and country code of a node with given ID. + // Example: `nodeDataForID(controller, "20BC91DC525C3DC9974B29FBEAB51230DE024C44")` + // => `{ type: "default", ipAddrs: ["12.23.34.45", "2001:db8::"], countryCode: "fr" }` + let nodeDataForID = async function(controller, id) { + let result = { ipAddrs: [] }; + const bridge = await getBridge(controller, id); // type, ip, countryCode; + const addrRe = /^[?([^]]+)]?:\d+$/; + if (bridge) { + result.type = "bridge"; + result.bridgeType = bridge.type; + // Attempt to get an IP address from bridge address string. + try { + const ip = bridge.address.match(addrRe)[1]; + if (!ip.startsWith("0.")) { + result.ipAddrs = [ip]; + } + } catch (e) {} + } else { + // either dealing with a relay, or a bridge whose fingerprint is not saved in torrc try { - result.ipAddrs.push(statusMap.IPv6.match(addrRe)[1]); + const statusMap = await controller.getInfo("ns/id/" + id); + result.type = "default"; + if (!statusMap.IP.startsWith("0.")) { + result.ipAddrs.push(statusMap.IP); + } + try { + result.ipAddrs.push(statusMap.IPv6.match(addrRe)[1]); + } catch (e) {} } catch (e) { + // getInfo will throw if the given id is not a relay + // this probably means we are dealing with a user-provided bridge with no fingerprint + result.type = "bridge"; + // we don't know the ip/ipv6 or type, so leave blank + result.ipAddrs = []; + result.bridgeType = ""; } - } catch (e) { - // getInfo will throw if the given id is not a relay - // this probably means we are dealing with a user-provided bridge with no fingerprint - result.type = "bridge"; - // we don't know the ip/ipv6 or type, so leave blank - result.ipAddrs = []; - result.bridgeType = ""; } - } - if (result.ipAddrs.length > 0) { - // Get the country code for the node's IP address. - try { - const countryCode = await controller.getInfo("ip-to-country/" + result.ipAddrs[0]); - result.countryCode = countryCode === "??" ? null : countryCode; - } catch (e) { } - } - return result; -}; - -// __nodeDataForCircuit(controller, circuitEvent)__. -// Gets the information for a circuit. -let nodeDataForCircuit = async function (controller, circuitEvent) { - let rawIDs = circuitEvent.circuit.map(circ => circ[0]), + if (result.ipAddrs.length) { + // Get the country code for the node's IP address. + try { + const countryCode = await controller.getInfo( + "ip-to-country/" + result.ipAddrs[0] + ); + result.countryCode = countryCode === "??" ? null : countryCode; + } catch (e) {} + } + return result; + }; + + // __nodeDataForCircuit(controller, circuitEvent)__. + // Gets the information for a circuit. + let nodeDataForCircuit = async function(controller, circuitEvent) { + let rawIDs = circuitEvent.circuit.map(circ => circ[0]), // Remove the leading '$' if present. - ids = rawIDs.map(id => id[0] === "$" ? id.substring(1) : id); - // Get the node data for all IDs in circuit. - return Promise.all(ids.map(id => nodeDataForID(controller, id))); -}; - -// __getCircuitStatusByID(aController, circuitID)__ -// Returns the circuit status for the circuit with the given ID. -let getCircuitStatusByID = async function (aController, circuitID) { - let circuitStatuses = await aController.getInfo("circuit-status"); - if (circuitStatuses) { - for (let circuitStatus of circuitStatuses) { - if (circuitStatus.id === circuitID) { - return circuitStatus; + ids = rawIDs.map(id => (id[0] === "$" ? id.substring(1) : id)); + // Get the node data for all IDs in circuit. + return Promise.all(ids.map(id => nodeDataForID(controller, id))); + }; + + // __getCircuitStatusByID(aController, circuitID)__ + // Returns the circuit status for the circuit with the given ID. + let getCircuitStatusByID = async function(aController, circuitID) { + let circuitStatuses = await aController.getInfo("circuit-status"); + if (circuitStatuses) { + for (let circuitStatus of circuitStatuses) { + if (circuitStatus.id === circuitID) { + return circuitStatus; + } } } - } - return null; -}; - -// __collectIsolationData(aController, updateUI)__. -// Watches for STREAM SENTCONNECT events. When a SENTCONNECT event occurs, then -// we assume isolation settings (SOCKS username+password) are now fixed for the -// corresponding circuit. Whenever the first stream on a new circuit is seen, -// looks up u+p and records the node data in the credentialsToNodeDataMap. -// We need to update the circuit display immediately after any new node data -// is received. So the `updateUI` callback will be called at that point. -// See https://trac.torproject.org/projects/tor/ticket/15493 -let collectIsolationData = function (aController, updateUI) { - return aController.watchEvent( - "STREAM", - streamEvent => streamEvent.StreamStatus === "SENTCONNECT", - async (streamEvent) => { - if (!knownCircuitIDs.get(streamEvent.CircuitID)) { - logger.eclog(3, "streamEvent.CircuitID: " + streamEvent.CircuitID); - knownCircuitIDs.set(streamEvent.CircuitID, true); - let circuitStatus = await getCircuitStatusByID(aController, streamEvent.CircuitID), - credentials = circuitStatus ? - (trimQuotes(circuitStatus.SOCKS_USERNAME) + "|" + - trimQuotes(circuitStatus.SOCKS_PASSWORD)) : - null; - if (credentials) { - let nodeData = await nodeDataForCircuit(aController, circuitStatus); - credentialsToNodeDataMap.set(credentials, nodeData); - updateUI(); + return null; + }; + + // __collectIsolationData(aController, updateUI)__. + // Watches for STREAM SENTCONNECT events. When a SENTCONNECT event occurs, then + // we assume isolation settings (SOCKS username+password) are now fixed for the + // corresponding circuit. Whenever the first stream on a new circuit is seen, + // looks up u+p and records the node data in the credentialsToNodeDataMap. + // We need to update the circuit display immediately after any new node data + // is received. So the `updateUI` callback will be called at that point. + // See https://trac.torproject.org/projects/tor/ticket/15493 + let collectIsolationData = function(aController, updateUI) { + return aController.watchEvent( + "STREAM", + streamEvent => streamEvent.StreamStatus === "SENTCONNECT", + async streamEvent => { + if (!knownCircuitIDs.get(streamEvent.CircuitID)) { + logger.eclog(3, "streamEvent.CircuitID: " + streamEvent.CircuitID); + knownCircuitIDs.set(streamEvent.CircuitID, true); + let circuitStatus = await getCircuitStatusByID( + aController, + streamEvent.CircuitID + ), + credentials = circuitStatus + ? trimQuotes(circuitStatus.SOCKS_USERNAME) + + "|" + + trimQuotes(circuitStatus.SOCKS_PASSWORD) + : null; + if (credentials) { + let nodeData = await nodeDataForCircuit(aController, circuitStatus); + credentialsToNodeDataMap.set(credentials, nodeData); + updateUI(); + } } } - }); -}; - -// __browserForChannel(channel)__. -// Returns the browser that loaded a given channel. -let browserForChannel = function (channel) { - if (!channel) return null; - let chan = channel.QueryInterface(Ci.nsIChannel); - let callbacks = chan.notificationCallbacks; - if (!callbacks) return null; - let loadContext; - try { - loadContext = callbacks.getInterface(Ci.nsILoadContext); - } catch (e) { - // Ignore - return null; - } - if (!loadContext) return null; - return loadContext.topFrameElement; -}; - -// __collectBrowserCredentials()__. -// Starts observing http channels. Each channel's proxyInfo -// username and password is recorded for the channel's browser. -let collectBrowserCredentials = function () { - return observe("http-on-modify-request", chan => { + ); + }; + + // __browserForChannel(channel)__. + // Returns the browser that loaded a given channel. + let browserForChannel = function(channel) { + if (!channel) { + return null; + } + let chan = channel.QueryInterface(Ci.nsIChannel); + let callbacks = chan.notificationCallbacks; + if (!callbacks) { + return null; + } + let loadContext; try { - let proxyInfo = chan.QueryInterface(Ci.nsIProxiedChannel).proxyInfo; - let browser = browserForChannel(chan); - if (browser && proxyInfo) { - if (!browserToCredentialsMap.has(browser)) { - browserToCredentialsMap.set(browser, new Map()); + loadContext = callbacks.getInterface(Ci.nsILoadContext); + } catch (e) { + // Ignore + return null; + } + if (!loadContext) { + return null; + } + return loadContext.topFrameElement; + }; + + // __collectBrowserCredentials()__. + // Starts observing http channels. Each channel's proxyInfo + // username and password is recorded for the channel's browser. + let collectBrowserCredentials = function() { + return observe("http-on-modify-request", chan => { + try { + let proxyInfo = chan.QueryInterface(Ci.nsIProxiedChannel).proxyInfo; + let browser = browserForChannel(chan); + if (browser && proxyInfo) { + if (!browserToCredentialsMap.has(browser)) { + browserToCredentialsMap.set(browser, new Map()); + } + let domainMap = browserToCredentialsMap.get(browser); + domainMap.set(proxyInfo.username, [ + proxyInfo.username, + proxyInfo.password, + ]); } - let domainMap = browserToCredentialsMap.get(browser); - domainMap.set(proxyInfo.username, [proxyInfo.username, - proxyInfo.password]); + } catch (e) { + logger.eclog( + 3, + `Error collecting browser credentials: ${e.message}, ${chan.URI.spec}` + ); } + }); + }; + + // ## User interface + + // __uiString__. + // Read the localized strings for this UI. + let uiString = function(shortName) { + return torbutton_get_property_string( + "torbutton.circuit_display." + shortName + ); + }; + + // __localizedCountryNameFromCode(countryCode)__. + // Convert a country code to a localized country name. + // Example: `'de'` -> `'Deutschland'` in German locale. + let localizedCountryNameFromCode = function(countryCode) { + if (!countryCode) { + return uiString("unknown_country"); + } + try { + return Services.intl.getRegionDisplayNames(undefined, [countryCode])[0]; } catch (e) { - logger.eclog(3, `Error collecting browser credentials: ${e.message}, ${chan.URI.spec}`); + return countryCode.toUpperCase(); } - }); -}; - -// ## User interface - -// __uiString__. -// Read the localized strings for this UI. -let uiString = function (shortName) { - return torbutton_get_property_string("torbutton.circuit_display." + shortName); -}; - -// __localizedCountryNameFromCode(countryCode)__. -// Convert a country code to a localized country name. -// Example: `'de'` -> `'Deutschland'` in German locale. -let localizedCountryNameFromCode = function (countryCode) { - if (!countryCode) return uiString("unknown_country"); - try { - return Services.intl.getRegionDisplayNames(undefined, [countryCode])[0]; - } catch (e) { - return countryCode.toUpperCase(); - } -}; - -// __showCircuitDisplay(show)__. -// If show === true, makes the circuit display visible. -let showCircuitDisplay = function (show) { - document.getElementById("circuit-display-container").style.display = show ? - 'block' : 'none'; -}; - -// __xmlTree(ns, data)__. -// Takes an xml namespace, ns, and a -// data structure representing xml elements like -// [tag, { attr-key: attr-value }, ...xml-children] -// and returns nested xml element objects. -let xmlTree = function xmlTree (ns, data) { - let [type, attrs, ...children] = data; - let element = type.startsWith("html:") - ? document.createXULElement(type) - : document.createElementNS(ns, type); - for (let [key, val] of Object.entries(attrs)) { - element.setAttribute(key, val); - } - for (let child of children) { - if (child !== null && child !== undefined) { - element.append(typeof child === "string" ? child : xmlTree(ns, child)); + }; + + // __showCircuitDisplay(show)__. + // If show === true, makes the circuit display visible. + let showCircuitDisplay = function(show) { + document.getElementById("circuit-display-container").style.display = show + ? "block" + : "none"; + }; + + // __xmlTree(ns, data)__. + // Takes an xml namespace, ns, and a + // data structure representing xml elements like + // [tag, { attr-key: attr-value }, ...xml-children] + // and returns nested xml element objects. + let xmlTree = function xmlTree(ns, data) { + let [type, attrs, ...children] = data; + let element = type.startsWith("html:") + ? document.createXULElement(type) + : document.createElementNS(ns, type); + for (let [key, val] of Object.entries(attrs)) { + element.setAttribute(key, val); } - } - return element; -}; - -// __htmlTree(data)__. -// Takes a data structure representing html elements like -// [tag, { attr-key: attr-value }, ...html-children] -// and returns nested html element objects. -let htmlTree = data => xmlTree("http://www.w3.org/1999/xhtml", data); - -// __appendHtml(parent, data)__. -// Takes a data structure representing html elements like -// [tag, { attr-key: attr-value }, ...html-children] -// and appends nested html element objects to the parent element. -let appendHtml = (parent, data) => parent.appendChild(htmlTree(data)); - -// __circuitCircuitData()__. -// Obtains the circuit used by the given browser. -let currentCircuitData = function (browser) { - if (browser) { - let firstPartyDomain = getDomainForBrowser(browser); - let domain = firstPartyDomain || "--unknown--"; - let domainMap = browserToCredentialsMap.get(browser); - let credentials = domainMap && domainMap.get(domain); - if (credentials) { - let [SOCKS_username, SOCKS_password] = credentials; - let nodeData = credentialsToNodeDataMap.get(`${SOCKS_username}|${SOCKS_password}`); - let domain = SOCKS_username; - if (browser.documentURI.host.endsWith(".tor.onion")) { - const service = Cc["@torproject.org/onion-alias-service;1"].getService( - Ci.IOnionAliasService - ); - domain = service.getOnionAlias(browser.documentURI.host); + for (let child of children) { + if (child !== null && child !== undefined) { + element.append(typeof child === "string" ? child : xmlTree(ns, child)); } - return { domain, nodeData }; } - } - return { domain: null, nodeData: null }; -}; - -// __updateCircuitDisplay()__. -// Updates the Tor circuit display, showing the current domain -// and the relay nodes for that domain. -let updateCircuitDisplay = function () { - let { domain, nodeData } = currentCircuitData(gBrowser.selectedBrowser); - if (domain && nodeData) { - // Update the displayed information for the relay nodes. - let nodeHtmlList = document.getElementById("circuit-display-nodes"); - let li = (...data) => appendHtml(nodeHtmlList, ["li", {}, ...data]); - nodeHtmlList.innerHTML = ""; - li(uiString("this_browser")); - for (let i = 0; i < nodeData.length; ++i) { - let relayText; - if (nodeData[i].type === "bridge") { - relayText = uiString("tor_bridge"); - let bridgeType = nodeData[i].bridgeType; - if (bridgeType === "meek_lite") { - relayText += ": meek"; - } - else if (bridgeType !== "vanilla" && bridgeType !== "") { - relayText += ": " + bridgeType; + return element; + }; + + // __htmlTree(data)__. + // Takes a data structure representing html elements like + // [tag, { attr-key: attr-value }, ...html-children] + // and returns nested html element objects. + let htmlTree = data => xmlTree("http://www.w3.org/1999/xhtml", data); + + // __appendHtml(parent, data)__. + // Takes a data structure representing html elements like + // [tag, { attr-key: attr-value }, ...html-children] + // and appends nested html element objects to the parent element. + let appendHtml = (parent, data) => parent.appendChild(htmlTree(data)); + + // __circuitCircuitData()__. + // Obtains the circuit used by the given browser. + let currentCircuitData = function(browser) { + if (browser) { + let firstPartyDomain = getDomainForBrowser(browser); + let domain = firstPartyDomain || "--unknown--"; + let domainMap = browserToCredentialsMap.get(browser); + let credentials = domainMap && domainMap.get(domain); + if (credentials) { + let [SOCKS_username, SOCKS_password] = credentials; + let nodeData = credentialsToNodeDataMap.get( + `${SOCKS_username}|${SOCKS_password}` + ); + let domain = SOCKS_username; + if (browser.documentURI.host.endsWith(".tor.onion")) { + const service = Cc[ + "@torproject.org/onion-alias-service;1" + ].getService(Ci.IOnionAliasService); + domain = service.getOnionAlias(browser.documentURI.host); } - } else if (nodeData[i].type == "default") { - relayText = localizedCountryNameFromCode(nodeData[i].countryCode); + return { domain, nodeData }; } - const ipAddrs = nodeData[i].ipAddrs.join(", "); - li(relayText, " ", ["span", { class: "circuit-ip-address" }, ipAddrs], " ", - (i === 0 && nodeData[0].type !== "bridge") ? - ["span", { class: "circuit-guard-info" }, uiString("guard")] : null); } + return { domain: null, nodeData: null }; + };
- let domainParts = []; - if (domain.endsWith(".onion")) { - for (let i = 0; i < 3; ++i) { - li(uiString("relay")); + // __updateCircuitDisplay()__. + // Updates the Tor circuit display, showing the current domain + // and the relay nodes for that domain. + let updateCircuitDisplay = function() { + let { domain, nodeData } = currentCircuitData(gBrowser.selectedBrowser); + if (domain && nodeData) { + // Update the displayed information for the relay nodes. + let nodeHtmlList = document.getElementById("circuit-display-nodes"); + let li = (...data) => appendHtml(nodeHtmlList, ["li", {}, ...data]); + nodeHtmlList.innerHTML = ""; + li(uiString("this_browser")); + for (let i = 0; i < nodeData.length; ++i) { + let relayText; + if (nodeData[i].type === "bridge") { + relayText = uiString("tor_bridge"); + let bridgeType = nodeData[i].bridgeType; + if (bridgeType === "meek_lite") { + relayText += ": meek"; + } else if (bridgeType !== "vanilla" && bridgeType !== "") { + relayText += ": " + bridgeType; + } + } else if (nodeData[i].type == "default") { + relayText = localizedCountryNameFromCode(nodeData[i].countryCode); + } + const ipAddrs = nodeData[i].ipAddrs.join(", "); + li( + relayText, + " ", + ["span", { class: "circuit-ip-address" }, ipAddrs], + " ", + i === 0 && nodeData[0].type !== "bridge" + ? ["span", { class: "circuit-guard-info" }, uiString("guard")] + : null + ); } - if (domain.length > 22) { - domainParts.push(domain.slice(0, 7), "…", domain.slice(-12)); + + let domainParts = []; + if (domain.endsWith(".onion")) { + for (let i = 0; i < 3; ++i) { + li(uiString("relay")); + } + if (domain.length > 22) { + domainParts.push(domain.slice(0, 7), "…", domain.slice(-12)); + } else { + domainParts.push(domain); + } } else { domainParts.push(domain); } - } else { - domainParts.push(domain); - }
- // We use a XUL html:span element so that the tooltiptext is displayed. - li([ - "html:span", - { - class: "circuit-onion", - onclick: ` + // We use a XUL html:span element so that the tooltiptext is displayed. + li([ + "html:span", + { + class: "circuit-onion", + onclick: ` this.classList.add("circuit-onion-copied"); Cc[ "@mozilla.org/widget/clipboardhelper;1" ].getService(Ci.nsIClipboardHelper).copyString(this.getAttribute("data-onion")) `, - "data-onion": domain, - "data-text-clicktocopy": torbutton_get_property_string("torbutton.circuit_display.click_to_copy"), - "data-text-copied": torbutton_get_property_string("torbutton.circuit_display.copied"), - tooltiptext: domain, - }, - ...domainParts, - ]); - - // Hide the note about guards if we are using a bridge. - document.getElementById("circuit-guard-note-container").style.display = - (nodeData[0].type === "bridge") ? "none" : "block"; - } else { - // Only show the Tor circuit if we have credentials and node data. - logger.eclog(4, "no SOCKS credentials found for current document."); - } - showCircuitDisplay(domain && nodeData); -}; - -// __syncDisplayWithSelectedTab(syncOn)__. -// Whenever the user starts to open the popup menu, make sure the display -// is the correct one for this tab. It's also possible that a new site -// can be loaded while the popup menu is open. -// Update the display if this happens. -let syncDisplayWithSelectedTab = (function() { - let listener = { onLocationChange : function (aBrowser) { - if (aBrowser === gBrowser.selectedBrowser) { - updateCircuitDisplay(); - } - } }; - return function (syncOn) { - let popupMenu = document.getElementById("identity-popup"); - if (syncOn) { - // Update the circuit display just before the popup menu is shown. - popupMenu.addEventListener("popupshowing", updateCircuitDisplay); - // If the currently selected tab has been sent to a new location, - // update the circuit to reflect that. - gBrowser.addTabsProgressListener(listener); + "data-onion": domain, + "data-text-clicktocopy": torbutton_get_property_string( + "torbutton.circuit_display.click_to_copy" + ), + "data-text-copied": torbutton_get_property_string( + "torbutton.circuit_display.copied" + ), + tooltiptext: domain, + }, + ...domainParts, + ]); + + // Hide the note about guards if we are using a bridge. + document.getElementById("circuit-guard-note-container").style.display = + nodeData[0].type === "bridge" ? "none" : "block"; } else { - // Stop syncing. - gBrowser.removeTabsProgressListener(listener); - popupMenu.removeEventListener("popupshowing", updateCircuitDisplay); - // Hide the display. - showCircuitDisplay(false); + // Only show the Tor circuit if we have credentials and node data. + logger.eclog(4, "no SOCKS credentials found for current document."); } + showCircuitDisplay(domain && nodeData); }; -})();
-// __setupGuardNote()__. -// Call once to show the Guard note as intended. -let setupGuardNote = function () { - let guardNote = document.getElementById("circuit-guard-note-container"); - let guardNoteString = uiString("guard_note"); - let learnMoreString = uiString("learn_more"); - let [noteBefore, name, noteAfter] = guardNoteString.split(/[[]]/); - let localeCode = getLocale(); - appendHtml(guardNote, - ["div", {}, - noteBefore, ["span", {class: "circuit-guard-name"}, name], - noteAfter, " ", - ["span", {onclick: `gBrowser.selectedTab = gBrowser.addWebTab('https://support.torproject.org/$%7BlocaleCode%7D/tbb/tbb-2/%27);%60, - class: "circuit-link"}, - learnMoreString]]); -}; - -// __ensureCorrectPopupDimensions()__. -// Make sure the identity popup always displays with the correct height. -let ensureCorrectPopupDimensions = function () { - let setDimensions = () => { - setTimeout(() => { - let view = document.querySelector("#identity-popup-multiView .panel-viewcontainer"); - let stack = document.querySelector("#identity-popup-multiView .panel-viewstack"); + // __syncDisplayWithSelectedTab(syncOn)__. + // Whenever the user starts to open the popup menu, make sure the display + // is the correct one for this tab. It's also possible that a new site + // can be loaded while the popup menu is open. + // Update the display if this happens. + let syncDisplayWithSelectedTab = (function() { + let listener = { + onLocationChange(aBrowser) { + if (aBrowser === gBrowser.selectedBrowser) { + updateCircuitDisplay(); + } + }, + }; + return function(syncOn) { + let popupMenu = document.getElementById("identity-popup"); + if (syncOn) { + // Update the circuit display just before the popup menu is shown. + popupMenu.addEventListener("popupshowing", updateCircuitDisplay); + // If the currently selected tab has been sent to a new location, + // update the circuit to reflect that. + gBrowser.addTabsProgressListener(listener); + } else { + // Stop syncing. + gBrowser.removeTabsProgressListener(listener); + popupMenu.removeEventListener("popupshowing", updateCircuitDisplay); + // Hide the display. + showCircuitDisplay(false); + } + }; + })(); + + // __setupGuardNote()__. + // Call once to show the Guard note as intended. + let setupGuardNote = function() { + let guardNote = document.getElementById("circuit-guard-note-container"); + let guardNoteString = uiString("guard_note"); + let learnMoreString = uiString("learn_more"); + let [noteBefore, name, noteAfter] = guardNoteString.split(/[[]]/); + let localeCode = getLocale(); + appendHtml(guardNote, [ + "div", + {}, + noteBefore, + ["span", { class: "circuit-guard-name" }, name], + noteAfter, + " ", + [ + "span", + { + onclick: `gBrowser.selectedTab = gBrowser.addWebTab('https://support.torproject.org/$%7BlocaleCode%7D/tbb/tbb-2/%27);%60, + class: "circuit-link", + }, + learnMoreString, + ], + ]); + }; + + // __ensureCorrectPopupDimensions()__. + // Make sure the identity popup always displays with the correct height. + let ensureCorrectPopupDimensions = function() { + let setDimensions = () => { + setTimeout(() => { + let view = document.querySelector( + "#identity-popup-multiView .panel-viewcontainer" + ); + let stack = document.querySelector( + "#identity-popup-multiView .panel-viewstack" + ); + let view2 = document.getElementById("identity-popup-mainView"); + if (view && stack && view2) { + let newWidth = Math.max( + ...[...view2.children].map(el => el.clientWidth) + ); + let newHeight = stack.clientHeight; + stack.setAttribute("width", newWidth); + view2.style.minWidth = view2.style.maxWidth = newWidth + "px"; + view.setAttribute("width", newWidth); + view.setAttribute("height", newHeight); + } + }, 0); + }; + let removeDimensions = () => { + let view = document.querySelector( + "#identity-popup-multiView .panel-viewcontainer" + ); + let stack = document.querySelector( + "#identity-popup-multiView .panel-viewstack" + ); let view2 = document.getElementById("identity-popup-mainView"); if (view && stack && view2) { - let newWidth = Math.max(...[...view2.children].map(el => el.clientWidth)); - let newHeight = stack.clientHeight; - stack.setAttribute("width", newWidth); - view2.style.minWidth = view2.style.maxWidth = newWidth + "px"; - view.setAttribute("width", newWidth); - view.setAttribute("height", newHeight); + view.removeAttribute("width"); + view.removeAttribute("height"); + stack.removeAttribute("width"); + view2.style.minWidth = view2.style.maxWidth = ""; } - }, 0); + }; + let popupMenu = document.getElementById("identity-popup"); + popupMenu.addEventListener("popupshowing", setDimensions); + popupMenu.addEventListener("popuphiding", removeDimensions); + return () => { + popupMenu.removeEventListener("popupshowing", setDimensions); + popupMenu.removeEventListener("popuphiding", removeDimensions); + }; }; - let removeDimensions = () => { - let view = document.querySelector("#identity-popup-multiView .panel-viewcontainer"); - let stack = document.querySelector("#identity-popup-multiView .panel-viewstack"); - let view2 = document.getElementById("identity-popup-mainView"); - if (view && stack && view2) { - view.removeAttribute("width"); - view.removeAttribute("height"); - stack.removeAttribute("width"); - view2.style.minWidth = view2.style.maxWidth = ""; + + // ## Main function + + // __setupDisplay(enablePrefName)__. + // Once called, the Tor circuit display will be started whenever + // the "enablePref" is set to true, and stopped when it is set to false. + // A reference to this function (called createTorCircuitDisplay) is exported as a global. + let setupDisplay = function(enablePrefName) { + // From 79 on the identity popup is initialized lazily + if (gIdentityHandler._initializePopup) { + gIdentityHandler._initializePopup(); } - }; - let popupMenu = document.getElementById("identity-popup"); - popupMenu.addEventListener("popupshowing", setDimensions); - popupMenu.addEventListener("popuphiding", removeDimensions); - return () => { - popupMenu.removeEventListener("popupshowing", setDimensions); - popupMenu.removeEventListener("popuphiding", removeDimensions); - }; -}; - -// ## Main function - -// __setupDisplay(enablePrefName)__. -// Once called, the Tor circuit display will be started whenever -// the "enablePref" is set to true, and stopped when it is set to false. -// A reference to this function (called createTorCircuitDisplay) is exported as a global. -let setupDisplay = function (enablePrefName) { - // From 79 on the identity popup is initialized lazily - if (gIdentityHandler._initializePopup) { - gIdentityHandler._initializePopup(); - } - setupGuardNote(); - let myController = null, + setupGuardNote(); + let myController = null, stopCollectingIsolationData = null, stopCollectingBrowserCredentials = null, stopEnsuringCorrectPopupDimensions = null, @@ -486,35 +549,47 @@ let setupDisplay = function (enablePrefName) { myController = null; } }, - start = async function () { + start = async function() { if (!myController) { try { myController = await wait_for_controller(); syncDisplayWithSelectedTab(true); - stopCollectingIsolationData = collectIsolationData(myController, updateCircuitDisplay); + stopCollectingIsolationData = collectIsolationData( + myController, + updateCircuitDisplay + ); stopCollectingBrowserCredentials = collectBrowserCredentials(); stopEnsuringCorrectPopupDimensions = ensureCorrectPopupDimensions(); } catch (err) { logger.eclog(5, err); - logger.eclog(5, "Disabling tor display circuit because of an error."); + logger.eclog( + 5, + "Disabling tor display circuit because of an error." + ); myController.close(); stop(); } - } - }; - try { - let unbindPref = bindPrefAndInit(enablePrefName, on => { if (on) start(); else stop(); }); - // When this chrome window is unloaded, we need to unbind the pref. - window.addEventListener("unload", function () { - unbindPref(); - stop(); - }); - } catch (e) { - logger.eclog(5, "Error: " + e.message + "\n" + e.stack); - } -}; + } + }; + try { + let unbindPref = bindPrefAndInit(enablePrefName, on => { + if (on) { + start(); + } else { + stop(); + } + }); + // When this chrome window is unloaded, we need to unbind the pref. + window.addEventListener("unload", function() { + unbindPref(); + stop(); + }); + } catch (e) { + logger.eclog(5, "Error: " + e.message + "\n" + e.stack); + } + };
-return setupDisplay; + return setupDisplay;
-// Finish createTorCircuitDisplay() + // Finish createTorCircuitDisplay() })(); diff --git a/chrome/content/torbutton.js b/chrome/content/torbutton.js index fde5e1fa..ec2680f2 100644 --- a/chrome/content/torbutton.js +++ b/chrome/content/torbutton.js @@ -3,121 +3,105 @@ var torbutton_init; var torbutton_new_circuit;
(() => { -// Bug 1506 P1-P5: This is the main Torbutton overlay file. Much needs to be -// preserved here, but in an ideal world, most of this code should perhaps be -// moved into an XPCOM service, and much can also be tossed. See also -// individual 1506 comments for details. + // Bug 1506 P1-P5: This is the main Torbutton overlay file. Much needs to be + // preserved here, but in an ideal world, most of this code should perhaps be + // moved into an XPCOM service, and much can also be tossed. See also + // individual 1506 comments for details.
-// TODO: check for leaks: http://www.mozilla.org/scriptable/avoiding-leaks.html -// TODO: Double-check there are no strange exploits to defeat: -// http://kb.mozillazine.org/Links_to_local_pages_don%27t_work + // TODO: check for leaks: http://www.mozilla.org/scriptable/avoiding-leaks.html + // TODO: Double-check there are no strange exploits to defeat: + // http://kb.mozillazine.org/Links_to_local_pages_don%27t_work
-/* global gBrowser, CustomizableUI, + /* global gBrowser, CustomizableUI, createTorCircuitDisplay, gFindBarInitialized, gFindBar, OpenBrowserWindow, PrivateBrowsingUtils, Services, AppConstants */
-let { - show_torbrowser_manual, - unescapeTorString, - bindPrefAndInit, - getDomainForBrowser, - torbutton_safelog, - torbutton_log, - torbutton_get_property_string, -} = ChromeUtils.import("resource://torbutton/modules/utils.js", {}); -let { configureControlPortModule, wait_for_controller } = Cu.import("resource://torbutton/modules/tor-control-port.js", {}); - -const k_tb_tor_check_failed_topic = "Torbutton:TorCheckFailed"; - -var m_tb_prefs = Services.prefs; - -// status -var m_tb_wasinited = false; -var m_tb_is_main_window = false; - -var m_tb_control_ipc_file = null; // Set if using IPC (UNIX domain socket). -var m_tb_control_port = null; // Set if using TCP. -var m_tb_control_host = null; // Set if using TCP. -var m_tb_control_pass = null; -var m_tb_control_desc = null; // For logging. - -var m_tb_domWindowUtils = window.windowUtils; - -async function clearData(flags) { - return new Promise((resolve, reject) => { - Services.clearData.deleteData(flags, { - onDataDeleted(code) { - if (code === Cr.NS_OK) { - resolve(); - } else { - reject(new Error(`Error deleting data with flags ${flags}: ${code}`)); - } - }, - }); - }); -} - -// Bug 1506 P2: This object keeps Firefox prefs in sync with Torbutton prefs. -// It probably could stand some simplification (See #3100). It also belongs -// in a component, not the XUL overlay. -var torbutton_unique_pref_observer = -{ - register: function() - { - this.forced_ua = false; - m_tb_prefs.addObserver("extensions.torbutton", this, false); - m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this, false); - m_tb_prefs.addObserver("javascript", this, false); - m_tb_prefs.addObserver("privacy.resistFingerprinting", this, false); - m_tb_prefs.addObserver("privacy.resistFingerprinting.letterboxing", this, false); + let { + show_torbrowser_manual, + unescapeTorString, + bindPrefAndInit, + getDomainForBrowser, + torbutton_log, + torbutton_get_property_string, + } = ChromeUtils.import("resource://torbutton/modules/utils.js"); + let { configureControlPortModule, wait_for_controller } = ChromeUtils.import( + "resource://torbutton/modules/tor-control-port.js" + ); + + const k_tb_tor_check_failed_topic = "Torbutton:TorCheckFailed"; + + var m_tb_prefs = Services.prefs; + + // status + var m_tb_wasinited = false; + var m_tb_is_main_window = false; + + var m_tb_control_ipc_file = null; // Set if using IPC (UNIX domain socket). + var m_tb_control_port = null; // Set if using TCP. + var m_tb_control_host = null; // Set if using TCP. + var m_tb_control_pass = null; + + // Bug 1506 P2: This object keeps Firefox prefs in sync with Torbutton prefs. + // It probably could stand some simplification (See #3100). It also belongs + // in a component, not the XUL overlay. + var torbutton_unique_pref_observer = { + register() { + this.forced_ua = false; + m_tb_prefs.addObserver("extensions.torbutton", this); + m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this); + m_tb_prefs.addObserver("javascript", this); + m_tb_prefs.addObserver("privacy.resistFingerprinting", this); + m_tb_prefs.addObserver("privacy.resistFingerprinting.letterboxing", this); },
- unregister: function() - { - m_tb_prefs.removeObserver("extensions.torbutton", this); - m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this); - m_tb_prefs.removeObserver("javascript", this); - m_tb_prefs.removeObserver("privacy.resistFingerprinting", this); - m_tb_prefs.removeObserver("privacy.resistFingerprinting.letterboxing", this); + unregister() { + m_tb_prefs.removeObserver("extensions.torbutton", this); + m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this); + m_tb_prefs.removeObserver("javascript", this); + m_tb_prefs.removeObserver("privacy.resistFingerprinting", this); + m_tb_prefs.removeObserver( + "privacy.resistFingerprinting.letterboxing", + this + ); },
// topic: what event occurred // subject: what nsIPrefBranch we're observing // data: which pref has been changed (relative to subject) - observe: function(subject, topic, data) - { - if (topic !== "nsPref:changed") return; - switch (data) { - case "browser.privatebrowsing.autostart": - torbutton_update_disk_prefs(); - break; - case "extensions.torbutton.use_nontor_proxy": - torbutton_use_nontor_proxy(); - break; - case "privacy.resistFingerprinting": - case "privacy.resistFingerprinting.letterboxing": - torbutton_update_fingerprinting_prefs(); - break; - } - } -} + observe(subject, topic, data) { + if (topic !== "nsPref:changed") { + return; + } + switch (data) { + case "browser.privatebrowsing.autostart": + torbutton_update_disk_prefs(); + break; + case "extensions.torbutton.use_nontor_proxy": + torbutton_use_nontor_proxy(); + break; + case "privacy.resistFingerprinting": + case "privacy.resistFingerprinting.letterboxing": + torbutton_update_fingerprinting_prefs(); + break; + } + }, + };
-var torbutton_tor_check_observer = { + var torbutton_tor_check_observer = { register() { - this._obsSvc = Services.obs; - this._obsSvc.addObserver(this, k_tb_tor_check_failed_topic); + this._obsSvc = Services.obs; + this._obsSvc.addObserver(this, k_tb_tor_check_failed_topic); },
- unregister: function() - { - if (this._obsSvc) - this._obsSvc.removeObserver(this, k_tb_tor_check_failed_topic); + unregister() { + if (this._obsSvc) { + this._obsSvc.removeObserver(this, k_tb_tor_check_failed_topic); + } },
- observe: function(subject, topic, data) - { + observe(subject, topic, data) { if (topic === k_tb_tor_check_failed_topic) { // Update all open about:tor pages. torbutton_abouttor_message_handler.updateAllOpenPages(); @@ -129,9 +113,9 @@ var torbutton_tor_check_observer = { if (win == window) { let foundTab = false; let tabBrowser = top.gBrowser; - for (let i = 0; !foundTab && (i < tabBrowser.browsers.length); ++i) { + for (let i = 0; !foundTab && i < tabBrowser.browsers.length; ++i) { let b = tabBrowser.getBrowserAtIndex(i); - foundTab = (b.currentURI.spec.toLowerCase() == "about:tor"); + foundTab = b.currentURI.spec.toLowerCase() == "about:tor"; }
if (!foundTab) { @@ -140,82 +124,102 @@ var torbutton_tor_check_observer = { } } }, -}; + };
-var torbutton_new_identity_observers = { - register() { - Services.obs.addObserver(this, "new-identity-requested"); - }, + var torbutton_new_identity_observers = { + register() { + Services.obs.addObserver(this, "new-identity-requested"); + },
- observe(aSubject, aTopic, aData) { - if (aTopic !== "new-identity-requested") { - return; - } + observe(aSubject, aTopic, aData) { + if (aTopic !== "new-identity-requested") { + return; + }
- // Clear the domain isolation state. - torbutton_log(3, "Clearing domain isolator"); - const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService( - Ci.nsISupports - ).wrappedJSObject; - domainIsolator.clearIsolation(); - - torbutton_log(3, "New Identity: Sending NEWNYM"); - // We only support TBB for newnym. - if (!m_tb_control_pass || (!m_tb_control_ipc_file && !m_tb_control_port)) { - var warning = torbutton_get_property_string("torbutton.popup.no_newnym"); - torbutton_log(5, "Torbutton cannot safely newnym. It does not have access to the Tor Control Port."); - window.alert(warning); - } else { - var warning = torbutton_get_property_string("torbutton.popup.no_newnym"); - torbutton_send_ctrl_cmd("SIGNAL NEWNYM").then(res => { - if (!res) { - torbutton_log(5, "Torbutton was unable to request a new circuit from Tor"); - window.alert(warning); - } - }).catch(e => { - torbutton_log(5, "Torbutton was unable to request a new circuit from Tor " + e); + // Clear the domain isolation state. + torbutton_log(3, "Clearing domain isolator"); + const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + domainIsolator.clearIsolation(); + + torbutton_log(3, "New Identity: Sending NEWNYM"); + // We only support TBB for newnym. + if ( + !m_tb_control_pass || + (!m_tb_control_ipc_file && !m_tb_control_port) + ) { + const warning = torbutton_get_property_string( + "torbutton.popup.no_newnym" + ); + torbutton_log( + 5, + "Torbutton cannot safely newnym. It does not have access to the Tor Control Port." + ); window.alert(warning); - }); - } - }, -} + } else { + const warning = torbutton_get_property_string( + "torbutton.popup.no_newnym" + ); + torbutton_send_ctrl_cmd("SIGNAL NEWNYM") + .then(res => { + if (!res) { + torbutton_log( + 5, + "Torbutton was unable to request a new circuit from Tor" + ); + window.alert(warning); + } + }) + .catch(e => { + torbutton_log( + 5, + "Torbutton was unable to request a new circuit from Tor " + e + ); + window.alert(warning); + }); + } + }, + };
-function torbutton_is_mobile() { + function torbutton_is_mobile() { return Services.appinfo.OS === "Android"; -} + }
-// Bug 1506 P2-P4: This code sets some version variables that are irrelevant. -// It does read out some important environment variables, though. It is -// called once per browser window.. This might belong in a component. -torbutton_init = function() { - torbutton_log(3, 'called init()'); + // Bug 1506 P2-P4: This code sets some version variables that are irrelevant. + // It does read out some important environment variables, though. It is + // called once per browser window.. This might belong in a component. + torbutton_init = function() { + torbutton_log(3, "called init()");
if (m_tb_wasinited) { - return; + return; } m_tb_wasinited = true;
let tlps; try { - tlps = Cc["@torproject.org/torlauncher-protocol-service;1"] - .getService(Ci.nsISupports).wrappedJSObject; - } catch(e) {} + tlps = Cc["@torproject.org/torlauncher-protocol-service;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + } catch (e) {}
// Bug 1506 P4: These vars are very important for New Identity - var environ = Cc["@mozilla.org/process/environment;1"] - .getService(Ci.nsIEnvironment); + var environ = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment + );
if (environ.exists("TOR_CONTROL_PASSWD")) { - m_tb_control_pass = environ.get("TOR_CONTROL_PASSWD"); + m_tb_control_pass = environ.get("TOR_CONTROL_PASSWD"); } else if (environ.exists("TOR_CONTROL_COOKIE_AUTH_FILE")) { - var cookie_path = environ.get("TOR_CONTROL_COOKIE_AUTH_FILE"); - try { - if ("" != cookie_path) { - m_tb_control_pass = torbutton_read_authentication_cookie(cookie_path); - } - } catch(e) { - torbutton_log(4, 'unable to read authentication cookie'); + var cookie_path = environ.get("TOR_CONTROL_COOKIE_AUTH_FILE"); + try { + if ("" != cookie_path) { + m_tb_control_pass = torbutton_read_authentication_cookie(cookie_path); } + } catch (e) { + torbutton_log(4, "unable to read authentication cookie"); + } } else { try { // Try to get password from Tor Launcher. @@ -227,47 +231,47 @@ torbutton_init = function() { // since Tor Launcher knows how to handle its own preferences and how to // resolve relative paths. try { - m_tb_control_ipc_file = tlps.TorGetControlIPCFile(); - } catch(e) {} - - if (m_tb_control_ipc_file) { - m_tb_control_desc = m_tb_control_ipc_file.path; - } else { - if (environ.exists("TOR_CONTROL_PORT")) { - m_tb_control_port = environ.get("TOR_CONTROL_PORT"); - } else { - try { - const kTLControlPortPref = "extensions.torlauncher.control_port"; - m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref); - } catch(e) { - // Since we want to disable some features when Tor Launcher is - // not installed (e.g., New Identity), we do not set a default - // port value here. - } - } + m_tb_control_ipc_file = tlps.TorGetControlIPCFile(); + } catch (e) {}
- if (m_tb_control_port) { - m_tb_control_desc = "" + m_tb_control_port; + if (!m_tb_control_ipc_file) { + if (environ.exists("TOR_CONTROL_PORT")) { + m_tb_control_port = environ.get("TOR_CONTROL_PORT"); + } else { + try { + const kTLControlPortPref = "extensions.torlauncher.control_port"; + m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref); + } catch (e) { + // Since we want to disable some features when Tor Launcher is + // not installed (e.g., New Identity), we do not set a default + // port value here. } + }
- if (environ.exists("TOR_CONTROL_HOST")) { - m_tb_control_host = environ.get("TOR_CONTROL_HOST"); - } else { - try { - const kTLControlHostPref = "extensions.torlauncher.control_host"; - m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref); - } catch(e) { - m_tb_control_host = "127.0.0.1"; - } + if (environ.exists("TOR_CONTROL_HOST")) { + m_tb_control_host = environ.get("TOR_CONTROL_HOST"); + } else { + try { + const kTLControlHostPref = "extensions.torlauncher.control_host"; + m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref); + } catch (e) { + m_tb_control_host = "127.0.0.1"; } + } }
- configureControlPortModule(m_tb_control_ipc_file, m_tb_control_host, - m_tb_control_port, m_tb_control_pass); + configureControlPortModule( + m_tb_control_ipc_file, + m_tb_control_host, + m_tb_control_port, + m_tb_control_pass + );
// Add about:tor IPC message listener. - window.messageManager.addMessageListener("AboutTor:Loaded", - torbutton_abouttor_message_handler); + window.messageManager.addMessageListener( + "AboutTor:Loaded", + torbutton_abouttor_message_handler + );
setupPreferencesForMobile();
@@ -275,361 +279,387 @@ torbutton_init = function() { torbutton_tor_check_observer.register();
try { - createTorCircuitDisplay("extensions.torbutton.display_circuit"); - } catch(e) { - torbutton_log(4, "Error creating the tor circuit display " + e); + createTorCircuitDisplay("extensions.torbutton.display_circuit"); + } catch (e) { + torbutton_log(4, "Error creating the tor circuit display " + e); }
try { - torbutton_init_user_manual_links(); - } catch(e) { - torbutton_log(4, "Error loading the user manual " + e); + torbutton_init_user_manual_links(); + } catch (e) { + torbutton_log(4, "Error loading the user manual " + e); }
// Arrange for our about:tor content script to be loaded in each frame. window.messageManager.loadFrameScript( - "chrome://torbutton/content/aboutTor/aboutTor-content.js", true); + "chrome://torbutton/content/aboutTor/aboutTor-content.js", + true + );
torbutton_new_identity_observers.register();
- torbutton_log(3, 'init completed'); -} - -var torbutton_abouttor_message_handler = { - // Receive IPC messages from the about:tor content script. - receiveMessage: async function(aMessage) { - switch(aMessage.name) { - case "AboutTor:Loaded": - aMessage.target.messageManager.sendAsyncMessage("AboutTor:ChromeData", - await this.getChromeData(true)); - break; - } - }, - - // Send privileged data to all of the about:tor content scripts. - updateAllOpenPages: async function() { - window.messageManager.broadcastAsyncMessage("AboutTor:ChromeData", - await this.getChromeData(false)); - }, - - // The chrome data contains all of the data needed by the about:tor - // content process that is only available here (in the chrome process). - // It is sent to the content process when an about:tor window is opened - // and in response to events such as the browser noticing that Tor is - // not working. - getChromeData: async function(aIsRespondingToPageLoad) { - let dataObj = { - mobile: torbutton_is_mobile(), - updateChannel: AppConstants.MOZ_UPDATE_CHANNEL, - torOn: await torbutton_tor_check_ok() - }; - - if (aIsRespondingToPageLoad) { - const kShouldNotifyPref = "torbrowser.post_update.shouldNotify"; - if (m_tb_prefs.getBoolPref(kShouldNotifyPref, false)) { - m_tb_prefs.clearUserPref(kShouldNotifyPref); - dataObj.hasBeenUpdated = true; - dataObj.updateMoreInfoURL = this.getUpdateMoreInfoURL(); + torbutton_log(3, "init completed"); + }; + + var torbutton_abouttor_message_handler = { + // Receive IPC messages from the about:tor content script. + async receiveMessage(aMessage) { + switch (aMessage.name) { + case "AboutTor:Loaded": + aMessage.target.messageManager.sendAsyncMessage( + "AboutTor:ChromeData", + await this.getChromeData(true) + ); + break; } - } + },
- return dataObj; - }, + // Send privileged data to all of the about:tor content scripts. + async updateAllOpenPages() { + window.messageManager.broadcastAsyncMessage( + "AboutTor:ChromeData", + await this.getChromeData(false) + ); + },
- getUpdateMoreInfoURL: function() { - try { - return Services.prefs.getCharPref("torbrowser.post_update.url"); - } catch (e) {} + // The chrome data contains all of the data needed by the about:tor + // content process that is only available here (in the chrome process). + // It is sent to the content process when an about:tor window is opened + // and in response to events such as the browser noticing that Tor is + // not working. + async getChromeData(aIsRespondingToPageLoad) { + let dataObj = { + mobile: torbutton_is_mobile(), + updateChannel: AppConstants.MOZ_UPDATE_CHANNEL, + torOn: await torbutton_tor_check_ok(), + }; + + if (aIsRespondingToPageLoad) { + const kShouldNotifyPref = "torbrowser.post_update.shouldNotify"; + if (m_tb_prefs.getBoolPref(kShouldNotifyPref, false)) { + m_tb_prefs.clearUserPref(kShouldNotifyPref); + dataObj.hasBeenUpdated = true; + dataObj.updateMoreInfoURL = this.getUpdateMoreInfoURL(); + } + } + + return dataObj; + },
- // Use the default URL as a fallback. - return Services.urlFormatter.formatURLPref("startup.homepage_override_url"); + getUpdateMoreInfoURL() { + try { + return Services.prefs.getCharPref("torbrowser.post_update.url"); + } catch (e) {} + + // Use the default URL as a fallback. + return Services.urlFormatter.formatURLPref( + "startup.homepage_override_url" + ); + }, + }; + + // Bug 1506 P4: Control port interaction. Needed for New Identity. + function torbutton_read_authentication_cookie(path) { + var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + file.initWithPath(path); + var fileStream = Cc[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Ci.nsIFileInputStream); + fileStream.init(file, 1, 0, false); + var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance( + Ci.nsIBinaryInputStream + ); + binaryStream.setInputStream(fileStream); + var array = binaryStream.readByteArray(fileStream.available()); + binaryStream.close(); + fileStream.close(); + return torbutton_array_to_hexdigits(array); } -}; - -// Bug 1506 P4: Control port interaction. Needed for New Identity. -function torbutton_socket_readline(input) { - var str = ""; - var bytes; - while((bytes = input.readBytes(1)) != "\n") { - if (bytes != '\r') - str += bytes; + + // Bug 1506 P4: Control port interaction. Needed for New Identity. + function torbutton_array_to_hexdigits(array) { + return array + .map(function(c) { + return String("0" + c.toString(16)).slice(-2); + }) + .join(""); } - return str; -} - -// Bug 1506 P4: Control port interaction. Needed for New Identity. -function torbutton_read_authentication_cookie(path) { - var file = Cc["@mozilla.org/file/local;1"] - .createInstance(Ci.nsIFile); - file.initWithPath(path); - var fileStream = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); - fileStream.init(file, 1, 0, false); - var binaryStream = Cc["@mozilla.org/binaryinputstream;1"] - .createInstance(Ci.nsIBinaryInputStream); - binaryStream.setInputStream(fileStream); - var array = binaryStream.readByteArray(fileStream.available()); - binaryStream.close(); - fileStream.close(); - return torbutton_array_to_hexdigits(array); -} - -// Bug 1506 P4: Control port interaction. Needed for New Identity. -function torbutton_array_to_hexdigits(array) { - return array.map(function(c) { - return String("0" + c.toString(16)).slice(-2) - }).join(''); -}; - -// Bug 1506 P4: Control port interaction. Needed for New Identity. -// -// Asynchronously executes a command on the control port. -// returns the response as a string, or null on error -async function torbutton_send_ctrl_cmd(command) { - const getErrorMessage = e => (e && (e.torMessage || e.message)) || ""; - let response = null; - try { - const avoidCache = true; - let torController = await wait_for_controller(avoidCache); - - let bytes = await torController.sendCommand(command); - if (!bytes.startsWith("250")) { - throw `Unexpected command response on control port '${bytes}'`; - } - response = bytes.slice(4);
- torController.close(); - } catch(err) { - let msg = getErrorMessage(err); - torbutton_log(4, `Error: ${msg}`); + // Bug 1506 P4: Control port interaction. Needed for New Identity. + // + // Asynchronously executes a command on the control port. + // returns the response as a string, or null on error + async function torbutton_send_ctrl_cmd(command) { + const getErrorMessage = e => (e && (e.torMessage || e.message)) || ""; + let response = null; + try { + const avoidCache = true; + let torController = await wait_for_controller(avoidCache); + + let bytes = await torController.sendCommand(command); + if (!bytes.startsWith("250")) { + throw new Error( + `Unexpected command response on control port '${bytes}'` + ); + } + response = bytes.slice(4); + + torController.close(); + } catch (err) { + let msg = getErrorMessage(err); + torbutton_log(4, `Error: ${msg}`); + } + return response; } - return response; -}
-// Bug 1506 P4: Needed for New IP Address -torbutton_new_circuit = function() { - let firstPartyDomain = getDomainForBrowser(gBrowser.selectedBrowser); + // Bug 1506 P4: Needed for New IP Address + torbutton_new_circuit = function() { + let firstPartyDomain = getDomainForBrowser(gBrowser.selectedBrowser);
- let domainIsolator = Cc["@torproject.org/domain-isolator;1"] - .getService(Ci.nsISupports).wrappedJSObject; + let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService( + Ci.nsISupports + ).wrappedJSObject;
- domainIsolator.newCircuitForDomain(firstPartyDomain); + domainIsolator.newCircuitForDomain(firstPartyDomain);
- gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE); -} + gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE); + };
-/* Called when we switch the use_nontor_proxy pref in either direction. - * - * Enables/disables domain isolation and then does new identity - */ -function torbutton_use_nontor_proxy() -{ - let domainIsolator = Cc["@torproject.org/domain-isolator;1"] - .getService(Ci.nsISupports).wrappedJSObject; - - if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) { - // Disable domain isolation - domainIsolator.disableIsolation(); - } else { - domainIsolator.enableIsolation(); + /* Called when we switch the use_nontor_proxy pref in either direction. + * + * Enables/disables domain isolation and then does new identity + */ + function torbutton_use_nontor_proxy() { + let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + + if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) { + // Disable domain isolation + domainIsolator.disableIsolation(); + } else { + domainIsolator.enableIsolation(); + } } -} - -async function torbutton_do_tor_check() -{ - let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] - .getService(Ci.nsISupports).wrappedJSObject; - if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy") || - !m_tb_prefs.getBoolPref("extensions.torbutton.test_enabled")) - return; // Only do the check once. - - // If we have a tor control port and transparent torification is off, - // perform a check via the control port. - const kEnvSkipControlPortTest = "TOR_SKIP_CONTROLPORTTEST"; - const kEnvUseTransparentProxy = "TOR_TRANSPROXY"; - var env = Cc["@mozilla.org/process/environment;1"] - .getService(Ci.nsIEnvironment); - if ((m_tb_control_ipc_file || m_tb_control_port) && + + async function torbutton_do_tor_check() { + let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + if ( + m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy") || + !m_tb_prefs.getBoolPref("extensions.torbutton.test_enabled") + ) { + return; + } // Only do the check once. + + // If we have a tor control port and transparent torification is off, + // perform a check via the control port. + const kEnvSkipControlPortTest = "TOR_SKIP_CONTROLPORTTEST"; + const kEnvUseTransparentProxy = "TOR_TRANSPROXY"; + var env = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment + ); + if ( + (m_tb_control_ipc_file || m_tb_control_port) && !env.exists(kEnvUseTransparentProxy) && !env.exists(kEnvSkipControlPortTest) && - m_tb_prefs.getBoolPref("extensions.torbutton.local_tor_check")) { - if (await torbutton_local_tor_check()) - checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful; - else { - // The check failed. Update toolbar icon and tooltip. - checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; + m_tb_prefs.getBoolPref("extensions.torbutton.local_tor_check") + ) { + if (await torbutton_local_tor_check()) { + checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful; + } else { + // The check failed. Update toolbar icon and tooltip. + checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; + } + } else { + // A local check is not possible, so perform a remote check. + torbutton_initiate_remote_tor_check(); } } - else { - // A local check is not possible, so perform a remote check. - torbutton_initiate_remote_tor_check(); - } -} - -async function torbutton_local_tor_check() -{ - let didLogError = false; - - let proxyType = m_tb_prefs.getIntPref("network.proxy.type"); - if (0 == proxyType) - return false; - - // Ask tor for its SOCKS listener address and port and compare to the - // browser preferences. - const kCmdArg = "net/listeners/socks"; - let resp = await torbutton_send_ctrl_cmd("GETINFO " + kCmdArg); - if (!resp) - return false; - - function logUnexpectedResponse() - { - if (!didLogError) { - didLogError = true; - torbutton_log(5, "Local Tor check: unexpected GETINFO response: " + resp); + + async function torbutton_local_tor_check() { + let didLogError = false; + + let proxyType = m_tb_prefs.getIntPref("network.proxy.type"); + if (0 == proxyType) { + return false; } - }
- function removeBrackets(aStr) - { - // Remove enclosing square brackets if present. - if (aStr.startsWith('[') && aStr.endsWith(']')) - return aStr.substr(1, aStr.length - 2); + // Ask tor for its SOCKS listener address and port and compare to the + // browser preferences. + const kCmdArg = "net/listeners/socks"; + let resp = await torbutton_send_ctrl_cmd("GETINFO " + kCmdArg); + if (!resp) { + return false; + }
- return aStr; - } + function logUnexpectedResponse() { + if (!didLogError) { + didLogError = true; + torbutton_log( + 5, + "Local Tor check: unexpected GETINFO response: " + resp + ); + } + }
- // Sample response: net/listeners/socks="127.0.0.1:9149" "127.0.0.1:9150" - // First, check for and remove the command argument prefix. - if (0 != resp.indexOf(kCmdArg + '=')) { - logUnexpectedResponse(); - return false; - } - resp = resp.substr(kCmdArg.length + 1); - - // Retrieve configured proxy settings and check each listener against them. - // When the SOCKS prefs are set to use IPC (e.g., a Unix domain socket), a - // file URL should be present in network.proxy.socks. - // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1211567 - let socksAddr = m_tb_prefs.getCharPref("network.proxy.socks"); - let socksPort = m_tb_prefs.getIntPref("network.proxy.socks_port"); - let socksIPCPath; - if (socksAddr && socksAddr.startsWith("file:")) { - // Convert the file URL to a file path. - try { - let ioService = Services.io; - let fph = ioService.getProtocolHandler("file") - .QueryInterface(Ci.nsIFileProtocolHandler); - socksIPCPath = fph.getFileFromURLSpec(socksAddr).path; - } catch (e) { - torbutton_log(5, "Local Tor check: IPC file error: " + e); + function removeBrackets(aStr) { + // Remove enclosing square brackets if present. + if (aStr.startsWith("[") && aStr.endsWith("]")) { + return aStr.substr(1, aStr.length - 2); + } + + return aStr; + } + + // Sample response: net/listeners/socks="127.0.0.1:9149" "127.0.0.1:9150" + // First, check for and remove the command argument prefix. + if (0 != resp.indexOf(kCmdArg + "=")) { + logUnexpectedResponse(); return false; } - } else { - socksAddr = removeBrackets(socksAddr); - } + resp = resp.substr(kCmdArg.length + 1); + + // Retrieve configured proxy settings and check each listener against them. + // When the SOCKS prefs are set to use IPC (e.g., a Unix domain socket), a + // file URL should be present in network.proxy.socks. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1211567 + let socksAddr = m_tb_prefs.getCharPref("network.proxy.socks"); + let socksPort = m_tb_prefs.getIntPref("network.proxy.socks_port"); + let socksIPCPath; + if (socksAddr && socksAddr.startsWith("file:")) { + // Convert the file URL to a file path. + try { + let ioService = Services.io; + let fph = ioService + .getProtocolHandler("file") + .QueryInterface(Ci.nsIFileProtocolHandler); + socksIPCPath = fph.getFileFromURLSpec(socksAddr).path; + } catch (e) { + torbutton_log(5, "Local Tor check: IPC file error: " + e); + return false; + } + } else { + socksAddr = removeBrackets(socksAddr); + } + + // Split into quoted strings. This code is adapted from utils.splitAtSpaces() + // within tor-control-port.js; someday this code should use the entire + // tor-control-port.js framework. + let addrArray = []; + resp.replace(/((\S*?"(.*?)")+\S*|\S+)/g, function(a, captured) { + addrArray.push(captured); + });
- // Split into quoted strings. This code is adapted from utils.splitAtSpaces() - // within tor-control-port.js; someday this code should use the entire - // tor-control-port.js framework. - let addrArray = []; - resp.replace(/((\S*?"(.*?)")+\S*|\S+)/g, function (a, captured) { - addrArray.push(captured); - }); - - let foundSocksListener = false; - for (let i = 0; !foundSocksListener && (i < addrArray.length); ++i) { - let addr; - try { addr = unescapeTorString(addrArray[i]); } catch (e) {} - if (!addr) - continue; - - // Remove double quotes if present. - let len = addr.length; - if ((len > 2) && ('"' == addr.charAt(0)) && ('"' == addr.charAt(len - 1))) - addr = addr.substring(1, len - 1); - - if (addr.startsWith("unix:")) { - if (!socksIPCPath) + let foundSocksListener = false; + for (let i = 0; !foundSocksListener && i < addrArray.length; ++i) { + let addr; + try { + addr = unescapeTorString(addrArray[i]); + } catch (e) {} + if (!addr) { continue; + }
- // Check against the configured UNIX domain socket proxy. - let path = addr.substring(5); - torbutton_log(2, "Tor socks listener (Unix domain socket): " + path); - foundSocksListener = (socksIPCPath === path); - } else if (!socksIPCPath) { - // Check against the configured TCP proxy. We expect addr:port where addr - // may be an IPv6 address; that is, it may contain colon characters. - // Also, we remove enclosing square brackets before comparing addresses - // because tor requires them but Firefox does not. - let idx = addr.lastIndexOf(':'); - if (idx < 0) { - logUnexpectedResponse(); - } else { - let torSocksAddr = removeBrackets(addr.substring(0, idx)); - let torSocksPort = parseInt(addr.substring(idx + 1), 10); - if ((torSocksAddr.length < 1) || isNaN(torSocksPort)) { + // Remove double quotes if present. + let len = addr.length; + if (len > 2 && '"' == addr.charAt(0) && '"' == addr.charAt(len - 1)) { + addr = addr.substring(1, len - 1); + } + + if (addr.startsWith("unix:")) { + if (!socksIPCPath) { + continue; + } + + // Check against the configured UNIX domain socket proxy. + let path = addr.substring(5); + torbutton_log(2, "Tor socks listener (Unix domain socket): " + path); + foundSocksListener = socksIPCPath === path; + } else if (!socksIPCPath) { + // Check against the configured TCP proxy. We expect addr:port where addr + // may be an IPv6 address; that is, it may contain colon characters. + // Also, we remove enclosing square brackets before comparing addresses + // because tor requires them but Firefox does not. + let idx = addr.lastIndexOf(":"); + if (idx < 0) { logUnexpectedResponse(); } else { - torbutton_log(2, "Tor socks listener: " + torSocksAddr + ':' - + torSocksPort); - foundSocksListener = ((socksAddr === torSocksAddr) && - (socksPort === torSocksPort)); + let torSocksAddr = removeBrackets(addr.substring(0, idx)); + let torSocksPort = parseInt(addr.substring(idx + 1), 10); + if (torSocksAddr.length < 1 || isNaN(torSocksPort)) { + logUnexpectedResponse(); + } else { + torbutton_log( + 2, + "Tor socks listener: " + torSocksAddr + ":" + torSocksPort + ); + foundSocksListener = + socksAddr === torSocksAddr && socksPort === torSocksPort; + } } } } - } - - return foundSocksListener; -} // torbutton_local_tor_check
+ return foundSocksListener; + } // torbutton_local_tor_check
-function torbutton_initiate_remote_tor_check() { - let obsSvc = Services.obs; - try { - let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] - .getService(Ci.nsISupports).wrappedJSObject; + function torbutton_initiate_remote_tor_check() { + let obsSvc = Services.obs; + try { + let checkSvc = Cc[ + "@torproject.org/torbutton-torCheckService;1" + ].getService(Ci.nsISupports).wrappedJSObject; let req = checkSvc.createCheckRequest(true); // async - req.onreadystatechange = function (aEvent) { - if (req.readyState === 4) { - let ret = checkSvc.parseCheckResponse(req); - - // If we received an error response from check.torproject.org, - // set the status of the tor check to failure (we don't want - // to indicate failure if we didn't receive a response). - if (ret == 2 || ret == 3 || ret == 5 || ret == 6 - || ret == 7 || ret == 8) { - checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; - obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic, null); - } else if (ret == 4) { - checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful; - } // Otherwise, redo the check later - - torbutton_log(3, "Tor remote check done. Result: " + ret); - } + req.onreadystatechange = function(aEvent) { + if (req.readyState === 4) { + let ret = checkSvc.parseCheckResponse(req); + + // If we received an error response from check.torproject.org, + // set the status of the tor check to failure (we don't want + // to indicate failure if we didn't receive a response). + if ( + ret == 2 || + ret == 3 || + ret == 5 || + ret == 6 || + ret == 7 || + ret == 8 + ) { + checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; + obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic); + } else if (ret == 4) { + checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful; + } // Otherwise, redo the check later + + torbutton_log(3, "Tor remote check done. Result: " + ret); + } };
torbutton_log(3, "Sending async Tor remote check"); req.send(null); - } catch(e) { - if (e.result == 0x80004005) // NS_ERROR_FAILURE - torbutton_log(5, "Tor check failed! Is tor running?"); - else - torbutton_log(5, "Tor check failed! Tor internal error: "+e); - - checkSvc.statusOfTorCheck = checkSvc.kCheckFailed; - obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic, null); - } -} // torbutton_initiate_remote_tor_check() + } catch (e) { + if (e.result == 0x80004005) { + // NS_ERROR_FAILURE + torbutton_log(5, "Tor check failed! Is tor running?"); + } else { + torbutton_log(5, "Tor check failed! Tor internal error: " + e); + }
-async function torbutton_tor_check_ok() -{ - await torbutton_do_tor_check(); - let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"] - .getService(Ci.nsISupports).wrappedJSObject; - return (checkSvc.kCheckFailed != checkSvc.statusOfTorCheck); -} + obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic); + } + } // torbutton_initiate_remote_tor_check()
-function torbutton_update_disk_prefs() { + async function torbutton_tor_check_ok() { + await torbutton_do_tor_check(); + let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + return checkSvc.kCheckFailed != checkSvc.statusOfTorCheck; + } + + function torbutton_update_disk_prefs() { var mode = m_tb_prefs.getBoolPref("browser.privatebrowsing.autostart");
m_tb_prefs.setBoolPref("browser.cache.disk.enable", !mode); @@ -647,184 +677,205 @@ function torbutton_update_disk_prefs() {
// Force prefs to be synced to disk Services.prefs.savePrefFile(null); -} + }
-function torbutton_update_fingerprinting_prefs() { + function torbutton_update_fingerprinting_prefs() { var mode = m_tb_prefs.getBoolPref("privacy.resistFingerprinting"); - var letterboxing = m_tb_prefs.getBoolPref("privacy.resistFingerprinting.letterboxing", false); - m_tb_prefs.setBoolPref("extensions.torbutton.resize_new_windows", mode && !letterboxing); + var letterboxing = m_tb_prefs.getBoolPref( + "privacy.resistFingerprinting.letterboxing", + false + ); + m_tb_prefs.setBoolPref( + "extensions.torbutton.resize_new_windows", + mode && !letterboxing + );
// Force prefs to be synced to disk Services.prefs.savePrefFile(null); -} - -// Bug 1506 P1: This function just cleans up prefs that got set badly in previous releases -function torbutton_fixup_old_prefs() -{ - if(m_tb_prefs.getIntPref('extensions.torbutton.pref_fixup_version') < 1) { - // TBB 5.0a3 had bad Firefox code that silently flipped this pref on us - if (m_tb_prefs.prefHasUserValue("browser.newtabpage.enhanced")) { - m_tb_prefs.clearUserPref("browser.newtabpage.enhanced"); - // TBB 5.0a3 users had all the necessary data cached in - // directoryLinks.json. This meant that resetting the pref above - // alone was not sufficient as the tiles features uses the cache - // even if the pref indicates that feature should be disabled. - // We flip the preference below as this forces a refetching which - // effectively results in an empty JSON file due to our spoofed - // URLs. - let matchOS = m_tb_prefs.getBoolPref("intl.locale.matchOS"); - m_tb_prefs.setBoolPref("intl.locale.matchOS", !matchOS); - m_tb_prefs.setBoolPref("intl.locale.matchOS", matchOS); - } + }
- // For some reason, the Share This Page button also survived the - // TBB 5.0a4 update's attempt to remove it. - if (m_tb_prefs.prefHasUserValue("browser.uiCustomization.state")) { - m_tb_prefs.clearUserPref("browser.uiCustomization.state"); - } + // Bug 1506 P1: This function just cleans up prefs that got set badly in previous releases + function torbutton_fixup_old_prefs() { + if (m_tb_prefs.getIntPref("extensions.torbutton.pref_fixup_version") < 1) { + // TBB 5.0a3 had bad Firefox code that silently flipped this pref on us + if (m_tb_prefs.prefHasUserValue("browser.newtabpage.enhanced")) { + m_tb_prefs.clearUserPref("browser.newtabpage.enhanced"); + // TBB 5.0a3 users had all the necessary data cached in + // directoryLinks.json. This meant that resetting the pref above + // alone was not sufficient as the tiles features uses the cache + // even if the pref indicates that feature should be disabled. + // We flip the preference below as this forces a refetching which + // effectively results in an empty JSON file due to our spoofed + // URLs. + let matchOS = m_tb_prefs.getBoolPref("intl.locale.matchOS"); + m_tb_prefs.setBoolPref("intl.locale.matchOS", !matchOS); + m_tb_prefs.setBoolPref("intl.locale.matchOS", matchOS); + }
- m_tb_prefs.setIntPref('extensions.torbutton.pref_fixup_version', 1); + // For some reason, the Share This Page button also survived the + // TBB 5.0a4 update's attempt to remove it. + if (m_tb_prefs.prefHasUserValue("browser.uiCustomization.state")) { + m_tb_prefs.clearUserPref("browser.uiCustomization.state"); + } + + m_tb_prefs.setIntPref("extensions.torbutton.pref_fixup_version", 1); } -} + }
-// ---------------------- Event handlers ----------------- + // ---------------------- Event handlers -----------------
-// Bug 1506 P1-P3: Most of these observers aren't very important. -// See their comments for details -function torbutton_do_main_window_startup() -{ + // Bug 1506 P1-P3: Most of these observers aren't very important. + // See their comments for details + function torbutton_do_main_window_startup() { torbutton_log(3, "Torbutton main window startup"); m_tb_is_main_window = true; torbutton_unique_pref_observer.register(); -} + }
-// Bug 1506 P4: Most of this function is now useless, save -// for the very important SOCKS environment vars at the end. -// Those could probably be rolled into a function with the -// control port vars, though. See 1506 comments inside. -function torbutton_do_startup() -{ - if(m_tb_prefs.getBoolPref("extensions.torbutton.startup")) { - // Bug 1506: Should probably be moved to an XPCOM component - torbutton_do_main_window_startup(); + // Bug 1506 P4: Most of this function is now useless, save + // for the very important SOCKS environment vars at the end. + // Those could probably be rolled into a function with the + // control port vars, though. See 1506 comments inside. + function torbutton_do_startup() { + if (m_tb_prefs.getBoolPref("extensions.torbutton.startup")) { + // Bug 1506: Should probably be moved to an XPCOM component + torbutton_do_main_window_startup();
- // For charsets - torbutton_update_fingerprinting_prefs(); + // For charsets + torbutton_update_fingerprinting_prefs();
- // Bug 30565: sync browser.privatebrowsing.autostart with security.nocertdb - torbutton_update_disk_prefs(); + // Bug 30565: sync browser.privatebrowsing.autostart with security.nocertdb + torbutton_update_disk_prefs();
- // For general pref fixups to handle pref damage in older versions - torbutton_fixup_old_prefs(); + // For general pref fixups to handle pref damage in older versions + torbutton_fixup_old_prefs();
- m_tb_prefs.setBoolPref("extensions.torbutton.startup", false); + m_tb_prefs.setBoolPref("extensions.torbutton.startup", false); } -} - -// Bug 1506 P3: Used to decide if we should resize the window. -// -// Returns true if the window wind is neither maximized, full screen, -// ratpoisioned/evilwmed, nor minimized. -function torbutton_is_windowed(wind) { - torbutton_log(3, "Window: (" + wind.outerWidth + "," + wind.outerHeight + ") ?= (" - + wind.screen.availWidth + "," + wind.screen.availHeight + ")"); - if (wind.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED - || wind.windowState == Ci.nsIDOMChromeWindow.STATE_MAXIMIZED) { - torbutton_log(2, "Window is minimized/maximized"); - return false; + } + + // Bug 1506 P3: Used to decide if we should resize the window. + // + // Returns true if the window wind is neither maximized, full screen, + // ratpoisioned/evilwmed, nor minimized. + function torbutton_is_windowed(wind) { + torbutton_log( + 3, + "Window: (" + + wind.outerWidth + + "," + + wind.outerHeight + + ") ?= (" + + wind.screen.availWidth + + "," + + wind.screen.availHeight + + ")" + ); + if ( + wind.windowState == Ci.nsIDOMChromeWindow.STATE_MINIMIZED || + wind.windowState == Ci.nsIDOMChromeWindow.STATE_MAXIMIZED + ) { + torbutton_log(2, "Window is minimized/maximized"); + return false; } if ("fullScreen" in wind && wind.fullScreen) { - torbutton_log(2, "Window is fullScreen"); - return false; + torbutton_log(2, "Window is fullScreen"); + return false; } - if(wind.outerHeight == wind.screen.availHeight - && wind.outerWidth == wind.screen.availWidth) { - torbutton_log(3, "Window is ratpoisoned/evilwm'ed"); - return false; + if ( + wind.outerHeight == wind.screen.availHeight && + wind.outerWidth == wind.screen.availWidth + ) { + torbutton_log(3, "Window is ratpoisoned/evilwm'ed"); + return false; }
torbutton_log(2, "Window is normal"); return true; -} + }
-function showSecurityPreferencesPanel(chromeWindow) { - const tabBrowser = chromeWindow.BrowserApp; - let settingsTab = null; + function showSecurityPreferencesPanel(chromeWindow) { + const tabBrowser = chromeWindow.BrowserApp; + let settingsTab = null;
- const SECURITY_PREFERENCES_URI = 'chrome://torbutton/content/preferences.xhtml'; + const SECURITY_PREFERENCES_URI = + "chrome://torbutton/content/preferences.xhtml";
- tabBrowser.tabs.some(function (tab) { + tabBrowser.tabs.some(function(tab) { // If the security prefs tab is opened, send the user to it if (tab.browser.currentURI.spec === SECURITY_PREFERENCES_URI) { - settingsTab = tab; - return true; + settingsTab = tab; + return true; } return false; - }); + });
- if (settingsTab === null) { + if (settingsTab === null) { // Open up the settings panel in a new tab. tabBrowser.addTab(SECURITY_PREFERENCES_URI, { - "selected": true, - "parentId": tabBrowser.selectedTab.id, - triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + selected: true, + parentId: tabBrowser.selectedTab.id, + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }); - } else { + } else { // Activate an existing settings panel tab. tabBrowser.selectTab(settingsTab); + } } -}
-function setupPreferencesForMobile() { - if (!torbutton_is_mobile()) { - return; - } + function setupPreferencesForMobile() { + if (!torbutton_is_mobile()) { + return; + }
- torbutton_log(4, "Setting up settings preferences for Android."); + torbutton_log(4, "Setting up settings preferences for Android.");
- const chromeWindow = Services.wm.getMostRecentWindow('navigator:browser'); + const chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
- // Add the extension's chrome menu item to the main browser menu. - chromeWindow.NativeWindow.menu.add({ - 'name': torbutton_get_property_string("torbutton.security_settings.menu.title"), - 'callback': showSecurityPreferencesPanel.bind(this, chromeWindow) - }); -} + // Add the extension's chrome menu item to the main browser menu. + chromeWindow.NativeWindow.menu.add({ + name: torbutton_get_property_string( + "torbutton.security_settings.menu.title" + ), + callback: showSecurityPreferencesPanel.bind(this, chromeWindow), + }); + }
-// Bug 1506 P3: This is needed pretty much only for the window resizing. -// See comments for individual functions for details -function torbutton_new_window(event) -{ + // Bug 1506 P3: This is needed pretty much only for the window resizing. + // See comments for individual functions for details + function torbutton_new_window(event) { torbutton_log(3, "New window"); var browser = window.gBrowser;
- if(!browser) { + if (!browser) { torbutton_log(5, "No browser for new window."); return; }
if (!m_tb_wasinited) { - torbutton_init(); + torbutton_init(); }
torbutton_do_startup();
- let progress = Cc["@mozilla.org/docloaderservice;1"] - .getService(Ci.nsIWebProgress); + let progress = Cc["@mozilla.org/docloaderservice;1"].getService( + Ci.nsIWebProgress + );
if (torbutton_is_windowed(window)) { - progress.addProgressListener(torbutton_resizelistener, - Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); + progress.addProgressListener( + torbutton_resizelistener, + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT + ); } -} + }
-// Bug 1506 P2: This is only needed because we have observers -// in XUL that should be in an XPCOM component -function torbutton_close_window(event) { + // Bug 1506 P2: This is only needed because we have observers + // in XUL that should be in an XPCOM component + function torbutton_close_window(event) { torbutton_tor_check_observer.unregister();
- window.removeEventListener("sizemodechange", m_tb_resize_handler, - false); + window.removeEventListener("sizemodechange", m_tb_resize_handler);
// TODO: This is a real ghetto hack.. When the original window // closes, we need to find another window to handle observing @@ -832,151 +883,179 @@ function torbutton_close_window(event) { // majority of torbutton functionality into a XPCOM component.. // But that is a major overhaul.. if (m_tb_is_main_window) { - torbutton_log(3, "Original window closed. Searching for another"); - var wm = Services.wm; - var enumerator = wm.getEnumerator("navigator:browser"); - while(enumerator.hasMoreElements()) { - var win = enumerator.getNext(); - // For some reason, when New Identity is called from a pref - // observer (ex: torbutton_use_nontor_proxy) on an ASAN build, - // we sometimes don't have this symbol set in the new window yet. - // However, the new window will run this init later in that case, - // as it does in the OSX case. - if(win != window && "torbutton_do_main_window_startup" in win) { - torbutton_log(3, "Found another window"); - win.torbutton_do_main_window_startup(); - m_tb_is_main_window = false; - break; - } + torbutton_log(3, "Original window closed. Searching for another"); + var wm = Services.wm; + var enumerator = wm.getEnumerator("navigator:browser"); + while (enumerator.hasMoreElements()) { + var win = enumerator.getNext(); + // For some reason, when New Identity is called from a pref + // observer (ex: torbutton_use_nontor_proxy) on an ASAN build, + // we sometimes don't have this symbol set in the new window yet. + // However, the new window will run this init later in that case, + // as it does in the OSX case. + if (win != window && "torbutton_do_main_window_startup" in win) { + torbutton_log(3, "Found another window"); + win.torbutton_do_main_window_startup(); + m_tb_is_main_window = false; + break; } + }
- torbutton_unique_pref_observer.unregister(); + torbutton_unique_pref_observer.unregister();
- if(m_tb_is_main_window) { // main window not reset above - // This happens on Mac OS because they allow firefox - // to still persist without a navigator window - torbutton_log(3, "Last window closed. None remain."); - m_tb_prefs.setBoolPref("extensions.torbutton.startup", true); - m_tb_is_main_window = false; - } + if (m_tb_is_main_window) { + // main window not reset above + // This happens on Mac OS because they allow firefox + // to still persist without a navigator window + torbutton_log(3, "Last window closed. None remain."); + m_tb_prefs.setBoolPref("extensions.torbutton.startup", true); + m_tb_is_main_window = false; + } } -} - -window.addEventListener('load',torbutton_new_window,false); -window.addEventListener('unload', torbutton_close_window, false); - -var m_tb_resize_handler = null; -var m_tb_resize_date = null; - -// Bug 1506 P1/P3: Setting a fixed window size is important, but -// probably not for android. -var torbutton_resizelistener = -{ - QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]), - - onLocationChange: function(aProgress, aRequest, aURI) {}, - onStateChange: function(aProgress, aRequest, aFlag, aStatus) { - if (aFlag & Ci.nsIWebProgressListener.STATE_STOP) { - m_tb_resize_handler = async function() { - // Wait for end of execution queue to ensure we have correct windowState. - await new Promise(resolve => setTimeout(resolve, 0)); - if (window.windowState === window.STATE_MAXIMIZED || - window.windowState === window.STATE_FULLSCREEN) { - if (m_tb_prefs.getBoolPref("extensions.torbutton.resize_new_windows") && - m_tb_prefs.getIntPref("extensions.torbutton.maximize_warnings_remaining") > 0) { - - // Do not add another notification if one is already showing. - const kNotificationName = "torbutton-maximize-notification"; - let box = gBrowser.getNotificationBox(); - if (box.getNotificationWithValue(kNotificationName)) - return; - - // Rate-limit showing our notification if needed. - if (m_tb_resize_date === null) { - m_tb_resize_date = Date.now(); - } else { - // We wait at least another second before we show a new - // notification. Should be enough to rule out OSes that call our - // handler rapidly due to internal workings. - if (Date.now() - m_tb_resize_date < 1000) { + } + + window.addEventListener("load", torbutton_new_window); + window.addEventListener("unload", torbutton_close_window); + + var m_tb_resize_handler = null; + var m_tb_resize_date = null; + + // Bug 1506 P1/P3: Setting a fixed window size is important, but + // probably not for android. + var torbutton_resizelistener = { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + + onLocationChange(aProgress, aRequest, aURI) {}, + onStateChange(aProgress, aRequest, aFlag, aStatus) { + if (aFlag & Ci.nsIWebProgressListener.STATE_STOP) { + m_tb_resize_handler = async function() { + // Wait for end of execution queue to ensure we have correct windowState. + await new Promise(resolve => setTimeout(resolve, 0)); + if ( + window.windowState === window.STATE_MAXIMIZED || + window.windowState === window.STATE_FULLSCREEN + ) { + if ( + m_tb_prefs.getBoolPref( + "extensions.torbutton.resize_new_windows" + ) && + m_tb_prefs.getIntPref( + "extensions.torbutton.maximize_warnings_remaining" + ) > 0 + ) { + // Do not add another notification if one is already showing. + const kNotificationName = "torbutton-maximize-notification"; + let box = gBrowser.getNotificationBox(); + if (box.getNotificationWithValue(kNotificationName)) { return; } - // Resizing but we need to reset |m_tb_resize_date| now. - m_tb_resize_date = Date.now(); - }
- // No need to get "OK" translated again. - let sbSvc = Services.strings; - let bundle = sbSvc. - createBundle("chrome://global/locale/commonDialogs.properties"); - let button_label = bundle.GetStringFromName("OK"); - - let buttons = [{ - label: button_label, - accessKey: 'O', - popup: null, - callback: - function() { - m_tb_prefs.setIntPref("extensions.torbutton.maximize_warnings_remaining", - m_tb_prefs.getIntPref("extensions.torbutton.maximize_warnings_remaining") - 1); + // Rate-limit showing our notification if needed. + if (m_tb_resize_date === null) { + m_tb_resize_date = Date.now(); + } else { + // We wait at least another second before we show a new + // notification. Should be enough to rule out OSes that call our + // handler rapidly due to internal workings. + if (Date.now() - m_tb_resize_date < 1000) { + return; } - }]; - - let priority = box.PRIORITY_WARNING_LOW; - let message = - torbutton_get_property_string("torbutton.maximize_warning"); + // Resizing but we need to reset |m_tb_resize_date| now. + m_tb_resize_date = Date.now(); + }
- box.appendNotification(message, kNotificationName, null, - priority, buttons); - return; + // No need to get "OK" translated again. + let sbSvc = Services.strings; + let bundle = sbSvc.createBundle( + "chrome://global/locale/commonDialogs.properties" + ); + let button_label = bundle.GetStringFromName("OK"); + + let buttons = [ + { + label: button_label, + accessKey: "O", + popup: null, + callback() { + m_tb_prefs.setIntPref( + "extensions.torbutton.maximize_warnings_remaining", + m_tb_prefs.getIntPref( + "extensions.torbutton.maximize_warnings_remaining" + ) - 1 + ); + }, + }, + ]; + + let priority = box.PRIORITY_WARNING_LOW; + let message = torbutton_get_property_string( + "torbutton.maximize_warning" + ); + + box.appendNotification( + message, + kNotificationName, + null, + priority, + buttons + ); + } } - } - }; // m_tb_resize_handler - - // We need to handle OSes that auto-maximize windows depending on user - // settings and/or screen resolution in the start-up phase and users that - // try to shoot themselves in the foot by maximizing the window manually. - // We add a listener which is triggerred as soon as the window gets - // maximized (windowState = 1). We are resizing during start-up but not - // later as the user should see only a warning there as a stopgap before - // #14229 lands. - // Alas, the Firefox window code is handling the event not itself: - // "// Note the current implementation of SetSizeMode just stores - // // the new state; it doesn't actually resize. So here we store - // // the state and pass the event on to the OS." - // (See: https://mxr.mozilla.org/mozilla-esr31/source/xpfe/appshell/src/ - // nsWebShellWindow.cpp#348) - // This means we have to cope with race conditions and resizing in the - // sizemodechange listener is likely to fail. Thus, we add a specific - // resize listener that is doing the work for us. It seems (at least on - // Ubuntu) to be the case that maximizing (and then again normalizing) of - // the window triggers more than one resize event the first being not the - // one we need. Thus we can't remove the listener after the first resize - // event got fired. Thus, we have the rather klunky setTimeout() call. - window.addEventListener("sizemodechange", m_tb_resize_handler, false); - - let progress = Cc["@mozilla.org/docloaderservice;1"] - .getService(Ci.nsIWebProgress); - progress.removeProgressListener(this); - } - }, // onStateChange - - onProgressChange: function(aProgress, aRequest, curSelfProgress, - maxSelfProgress, curTotalProgress, - maxTotalProgress) {}, - onStatusChange: function(aProgress, aRequest, stat, message) {}, - onSecurityChange: function() {} -}; - -// Makes sure the item in the Help Menu and the link in about:tor -// for the Tor Browser User Manual are only visible when -// show_torbrowser_manual() returns true. -function torbutton_init_user_manual_links() { - let menuitem = document.getElementById("torBrowserUserManual"); - bindPrefAndInit("intl.locale.requested", val => { - menuitem.hidden = !show_torbrowser_manual(); - torbutton_abouttor_message_handler.updateAllOpenPages(); - }); -} + }; // m_tb_resize_handler + + // We need to handle OSes that auto-maximize windows depending on user + // settings and/or screen resolution in the start-up phase and users that + // try to shoot themselves in the foot by maximizing the window manually. + // We add a listener which is triggerred as soon as the window gets + // maximized (windowState = 1). We are resizing during start-up but not + // later as the user should see only a warning there as a stopgap before + // #14229 lands. + // Alas, the Firefox window code is handling the event not itself: + // "// Note the current implementation of SetSizeMode just stores + // // the new state; it doesn't actually resize. So here we store + // // the state and pass the event on to the OS." + // (See: https://mxr.mozilla.org/mozilla-esr31/source/xpfe/appshell/src/ + // nsWebShellWindow.cpp#348) + // This means we have to cope with race conditions and resizing in the + // sizemodechange listener is likely to fail. Thus, we add a specific + // resize listener that is doing the work for us. It seems (at least on + // Ubuntu) to be the case that maximizing (and then again normalizing) of + // the window triggers more than one resize event the first being not the + // one we need. Thus we can't remove the listener after the first resize + // event got fired. Thus, we have the rather klunky setTimeout() call. + window.addEventListener("sizemodechange", m_tb_resize_handler); + + let progress = Cc["@mozilla.org/docloaderservice;1"].getService( + Ci.nsIWebProgress + ); + progress.removeProgressListener(this); + } + }, // onStateChange + + onProgressChange( + aProgress, + aRequest, + curSelfProgress, + maxSelfProgress, + curTotalProgress, + maxTotalProgress + ) {}, + onStatusChange(aProgress, aRequest, stat, message) {}, + onSecurityChange() {}, + }; + + // Makes sure the item in the Help Menu and the link in about:tor + // for the Tor Browser User Manual are only visible when + // show_torbrowser_manual() returns true. + function torbutton_init_user_manual_links() { + let menuitem = document.getElementById("torBrowserUserManual"); + bindPrefAndInit("intl.locale.requested", val => { + menuitem.hidden = !show_torbrowser_manual(); + torbutton_abouttor_message_handler.updateAllOpenPages(); + }); + } })(); //vim:set ts=4 diff --git a/components/domain-isolator.js b/components/domain-isolator.js index 06fe1e2e..1c77b577 100644 --- a/components/domain-isolator.js +++ b/components/domain-isolator.js @@ -9,15 +9,17 @@ // ### Abbreviations
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +);
XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", });
// Make the logger available. -let logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; +let logger = Cc["@torproject.org/torbutton-logger;1"].getService(Ci.nsISupports) + .wrappedJSObject;
// Import crypto object (FF 37+). Cu.importGlobalProperties(["crypto"]); @@ -29,8 +31,9 @@ let mozilla = {}; // __mozilla.protocolProxyService__. // Mozilla's protocol proxy service, useful for managing proxy connections made // by the browser. -mozilla.protocolProxyService = Cc["@mozilla.org/network/protocol-proxy-service;1"] - .getService(Ci.nsIProtocolProxyService); +mozilla.protocolProxyService = Cc[ + "@mozilla.org/network/protocol-proxy-service;1" +].getService(Ci.nsIProtocolProxyService);
// __mozilla.registerProxyChannelFilter(filterFunction, positionIndex)__. // Registers a proxy channel filter with the Mozilla Protocol Proxy Service, @@ -38,13 +41,16 @@ mozilla.protocolProxyService = Cc["@mozilla.org/network/protocol-proxy-service;1 // The filterFunction should expect two arguments, (aChannel, aProxy), // where aProxy is the proxy or list of proxies that would be used by default // for the given channel, and should return a new Proxy or list of Proxies. -mozilla.registerProxyChannelFilter = function (filterFunction, positionIndex) { +mozilla.registerProxyChannelFilter = function(filterFunction, positionIndex) { let proxyFilter = { - applyFilter : function (aChannel, aProxy, aCallback) { + applyFilter(aChannel, aProxy, aCallback) { aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy)); - } + }, }; - mozilla.protocolProxyService.registerChannelFilter(proxyFilter, positionIndex); + mozilla.protocolProxyService.registerChannelFilter( + proxyFilter, + positionIndex + ); };
// ## tor functionality. @@ -66,24 +72,25 @@ tor.unknownDirtySince = Date.now(); // Takes a proxyInfo object (originalProxy) and returns a new proxyInfo // object with the same properties, except the username is set to the // the domain, and the password is a nonce. -tor.socksProxyCredentials = function (originalProxy, domain) { +tor.socksProxyCredentials = function(originalProxy, domain) { // Check if we already have a nonce. If not, create // one for this domain. if (!tor.noncesForDomains.hasOwnProperty(domain)) { tor.noncesForDomains[domain] = tor.nonce(); } let proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo); - return mozilla.protocolProxyService - .newProxyInfoWithAuth("socks", - proxy.host, - proxy.port, - domain, // username - tor.noncesForDomains[domain], // password - "", // aProxyAuthorizationHeader - "", // aConnectionIsolationKey - proxy.flags, - proxy.failoverTimeout, - proxy.failoverProxy); + return mozilla.protocolProxyService.newProxyInfoWithAuth( + "socks", + proxy.host, + proxy.port, + domain, // username + tor.noncesForDomains[domain], // password + "", // aProxyAuthorizationHeader + "", // aConnectionIsolationKey + proxy.flags, + proxy.failoverTimeout, + proxy.failoverProxy + ); };
tor.nonce = function() { @@ -98,7 +105,7 @@ tor.nonce = function() { let tagStr = ""; for (let i = 0; i < tag.length; i++) { tagStr += (tag[i] >>> 4).toString(16); - tagStr += (tag[i] & 0x0F).toString(16); + tagStr += (tag[i] & 0x0f).toString(16); }
return tagStr; @@ -110,13 +117,16 @@ tor.newCircuitForDomain = function(domain) { domain = "--unknown--"; } tor.noncesForDomains[domain] = tor.nonce(); - logger.eclog(3, "New domain isolation for " + domain + ": " + tor.noncesForDomains[domain]); + logger.eclog( + 3, + "New domain isolation for " + domain + ": " + tor.noncesForDomains[domain] + ); };
// __tor.clearIsolation()_. // Clear the isolation state cache, forcing new circuits to be used for all // subsequent requests. -tor.clearIsolation = function () { +tor.clearIsolation = function() { // Per-domain nonces are stored in a map, so simply re-initialize the map. tor.noncesForDomains = {};
@@ -130,28 +140,38 @@ tor.clearIsolation = function () { // to the SOCKS server (the tor client process) with a username (the first party domain) // and a nonce password. Tor provides a separate circuit for each username+password // combination. -tor.isolateCircuitsByDomain = function () { - mozilla.registerProxyChannelFilter(function (aChannel, aProxy) { +tor.isolateCircuitsByDomain = function() { + mozilla.registerProxyChannelFilter(function(aChannel, aProxy) { if (!tor.isolationEnabled) { return aProxy; } try { let channel = aChannel.QueryInterface(Ci.nsIChannel), - firstPartyDomain = channel.loadInfo.originAttributes.firstPartyDomain; + firstPartyDomain = channel.loadInfo.originAttributes.firstPartyDomain; if (firstPartyDomain === "") { firstPartyDomain = "--unknown--"; - if (Date.now() - tor.unknownDirtySince > 1000*10*60) { - logger.eclog(3, "tor catchall circuit has been dirty for over 10 minutes. Rotating."); + if (Date.now() - tor.unknownDirtySince > 1000 * 10 * 60) { + logger.eclog( + 3, + "tor catchall circuit has been dirty for over 10 minutes. Rotating." + ); tor.newCircuitForDomain("--unknown--"); tor.unknownDirtySince = Date.now(); } } - let replacementProxy = tor.socksProxyCredentials(aProxy, firstPartyDomain); - logger.eclog(3, `tor SOCKS: ${channel.URI.spec} via - ${replacementProxy.username}:${replacementProxy.password}`); + let replacementProxy = tor.socksProxyCredentials( + aProxy, + firstPartyDomain + ); + logger.eclog( + 3, + `tor SOCKS: ${channel.URI.spec} via + ${replacementProxy.username}:${replacementProxy.password}` + ); return replacementProxy; } catch (e) { logger.eclog(4, `tor domain isolator error: ${e.message}`); + return null; } }, 0); }; @@ -164,7 +184,7 @@ const kMODULE_CID = Components.ID("e33fd6d4-270f-475f-a96f-ff3140279f68");
// DomainIsolator object. function DomainIsolator() { - this.wrappedJSObject = this; + this.wrappedJSObject = this; }
// Firefox component requirements @@ -173,7 +193,7 @@ DomainIsolator.prototype = { classDescription: kMODULE_NAME, classID: kMODULE_CID, contractID: kMODULE_CONTRACTID, - observe: function (subject, topic, data) { + observe(subject, topic, data) { if (topic === "profile-after-change") { logger.eclog(3, "domain isolator: set up isolating circuits by domain");
@@ -183,23 +203,23 @@ DomainIsolator.prototype = { tor.isolateCircuitsByDomain(); } }, - newCircuitForDomain: function (domain) { + newCircuitForDomain(domain) { tor.newCircuitForDomain(domain); },
- enableIsolation: function() { + enableIsolation() { tor.isolationEnabled = true; },
- disableIsolation: function() { + disableIsolation() { tor.isolationEnabled = false; },
- clearIsolation: function() { + clearIsolation() { tor.clearIsolation(); },
- wrappedJSObject: null + wrappedJSObject: null, };
// Assign factory to global object. diff --git a/components/dragDropFilter.js b/components/dragDropFilter.js index 361424d1..4b76bd10 100644 --- a/components/dragDropFilter.js +++ b/components/dragDropFilter.js @@ -5,8 +5,9 @@ * access to URLs (a potential proxy bypass vector). *************************************************************************/
- -const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, { @@ -21,8 +22,9 @@ const kMODULE_CID = Components.ID("f605ec27-d867-44b5-ad97-2a29276642c3"); const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo];
function DragDropFilter() { - this.logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; + this.logger = Cc["@torproject.org/torbutton-logger;1"].getService( + Ci.nsISupports + ).wrappedJSObject; this.logger.log(3, "Component Load 0: New DragDropFilter.");
try { @@ -32,8 +34,7 @@ function DragDropFilter() { } }
-DragDropFilter.prototype = -{ +DragDropFilter.prototype = { QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
// make this an nsIClassInfo object @@ -43,23 +44,25 @@ DragDropFilter.prototype = classID: kMODULE_CID,
// method of nsIClassInfo - getInterfaces: function(count) { + getInterfaces(count) { count.value = kInterfaces.length; return kInterfaces; },
// method of nsIClassInfo - getHelperForLanguage: function(count) { return null; }, + getHelperForLanguage(count) { + return null; + },
// method of nsIObserver - observe: function(subject, topic, data) { - if (topic == "on-datatransfer-available") { + observe(subject, topic, data) { + if (topic === "on-datatransfer-available") { this.logger.log(3, "The DataTransfer is available"); - return this.filterDataTransferURLs(subject); + this.filterDataTransferURLs(subject); } },
- filterDataTransferURLs: function(aDataTransfer) { + filterDataTransferURLs(aDataTransfer) { var types = null; var type = ""; var count = aDataTransfer.mozItemCount; @@ -71,16 +74,18 @@ DragDropFilter.prototype = for (var j = 0; j < len; ++j) { type = types[j]; this.logger.log(3, "Type is: " + type); - if (type == "text/x-moz-url" || - type == "text/x-moz-url-data" || - type == "text/uri-list" || - type == "application/x-moz-file-promise-url") { + if ( + type == "text/x-moz-url" || + type == "text/x-moz-url-data" || + type == "text/uri-list" || + type == "application/x-moz-file-promise-url" + ) { aDataTransfer.clearData(type); this.logger.log(3, "Removing " + type); } } } - } + }, };
// Assign factory to global object. diff --git a/components/external-app-blocker.js b/components/external-app-blocker.js index 2fa80d9d..6a53fc01 100644 --- a/components/external-app-blocker.js +++ b/components/external-app-blocker.js @@ -12,15 +12,21 @@ * handle an URL (e.g., when the user clicks on a mailto: URL). *************************************************************************/
-const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { PromptUtils } = ChromeUtils.import("resource://gre/modules/SharedPromptUtils.jsm"); +const { PromptUtils } = ChromeUtils.import( + "resource://gre/modules/SharedPromptUtils.jsm" +);
XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", });
-let { torbutton_get_property_string } = ChromeUtils.import("resource://torbutton/modules/utils.js", {}); +let { torbutton_get_property_string } = ChromeUtils.import( + "resource://torbutton/modules/utils.js" +);
// Module specific constants const kMODULE_NAME = "Torbutton External App Handler"; @@ -30,16 +36,19 @@ const kMODULE_CID = Components.ID("3da0269f-fc29-4e9e-a678-c3b1cafcf13f"); const kInterfaces = [Ci.nsIObserver, Ci.nsIClassInfo];
function ExternalAppBlocker() { - this.logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; + this.logger = Cc["@torproject.org/torbutton-logger;1"].getService( + Ci.nsISupports + ).wrappedJSObject; this.logger.log(3, "Component Load 0: New ExternalAppBlocker."); }
-ExternalAppBlocker.prototype = -{ +ExternalAppBlocker.prototype = { _helperAppLauncher: undefined,
- QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsIHelperAppWarningDialog]), + QueryInterface: ChromeUtils.generateQI([ + Ci.nsIObserver, + Ci.nsIHelperAppWarningDialog, + ]),
// make this an nsIClassInfo object flags: Ci.nsIClassInfo.DOM_OBJECT, @@ -48,17 +57,18 @@ ExternalAppBlocker.prototype = classID: kMODULE_CID,
// method of nsIClassInfo - getInterfaces: function(count) { + getInterfaces(count) { count.value = kInterfaces.length; return kInterfaces; },
// method of nsIClassInfo - getHelperForLanguage: function(count) { return null; }, + getHelperForLanguage(count) { + return null; + },
// method of nsIHelperAppWarningDialog - maybeShow: function(aLauncher, aWindowContext) - { + maybeShow(aLauncher, aWindowContext) { // Hold a reference to the object that called this component. This is // important not just because we need to later invoke the // continueRequest() or cancelRequest() callback on aLauncher, but also @@ -80,7 +90,7 @@ ExternalAppBlocker.prototype = * on chrome://global/content/commonDialog.xhtml as well as some of the code * in resource://gre/modules/SharedPromptUtils.jsm. */ - _showPrompt: function(aWindowContext) { + _showPrompt(aWindowContext) { let parentWin; try { parentWin = aWindowContext.getInterface(Ci.nsIDOMWindow); @@ -91,20 +101,22 @@ ExternalAppBlocker.prototype = let title = torbutton_get_property_string("torbutton.popup.external.title"); let app = torbutton_get_property_string("torbutton.popup.external.app"); let note = torbutton_get_property_string("torbutton.popup.external.note"); - let suggest = torbutton_get_property_string("torbutton.popup.external.suggest"); + let suggest = torbutton_get_property_string( + "torbutton.popup.external.suggest" + ); let launch = torbutton_get_property_string("torbutton.popup.launch"); let cancel = torbutton_get_property_string("torbutton.popup.cancel"); let dontask = torbutton_get_property_string("torbutton.popup.dontask");
let args = { - promptType: "confirmEx", - title: title, - text: app+note+suggest+" ", - checkLabel: dontask, - checked: false, - ok: false, - button0Label: launch, - button1Label: cancel, + promptType: "confirmEx", + title, + text: app + note + suggest + " ", + checkLabel: dontask, + checked: false, + ok: false, + button0Label: launch, + button1Label: cancel, defaultButtonNum: 1, // Cancel buttonNumClicked: 1, // Cancel enableDelay: true, @@ -112,8 +124,13 @@ ExternalAppBlocker.prototype =
let propBag = PromptUtils.objectToPropBag(args); let uri = "chrome://global/content/commonDialog.xhtml"; - let promptWin = Services.ww.openWindow(parentWin, uri, "_blank", - "centerscreen,chrome,titlebar", propBag); + let promptWin = Services.ww.openWindow( + parentWin, + uri, + "_blank", + "centerscreen,chrome,titlebar", + propBag + ); promptWin.addEventListener("load", aEvent => { promptWin.addEventListener("unload", aEvent => { PromptUtils.propBagToObject(propBag, args); @@ -122,16 +139,18 @@ ExternalAppBlocker.prototype = // Save the checkbox value and tell the browser's external helper app // module about the user's choice. if (args.checked) { - Services.prefs.setBoolPref("extensions.torbutton.launch_warning", - false); + Services.prefs.setBoolPref( + "extensions.torbutton.launch_warning", + false + ); }
this._helperAppLauncher.continueRequest(); } else { this._helperAppLauncher.cancelRequest(Cr.NS_BINDING_ABORTED); } - }, false); - }, false); + }); + }); }, };
diff --git a/components/startup-observer.js b/components/startup-observer.js index 164c9219..77df172a 100644 --- a/components/startup-observer.js +++ b/components/startup-observer.js @@ -13,7 +13,9 @@ *************************************************************************/
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +);
XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", @@ -47,155 +49,163 @@ function cleanupCookies() { }
function StartupObserver() { - this.logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; - this._prefs = Services.prefs; - this.logger.log(3, "Startup Observer created"); - - var env = Cc["@mozilla.org/process/environment;1"] - .getService(Ci.nsIEnvironment); - var prefName = "browser.startup.homepage"; - if (env.exists("TOR_DEFAULT_HOMEPAGE")) { - // if the user has set this value in a previous installation, don't override it - if (!this._prefs.prefHasUserValue(prefName)) { - this._prefs.setCharPref(prefName, env.get("TOR_DEFAULT_HOMEPAGE")); - } - } - - try { - var test = this._prefs.getCharPref("torbrowser.version"); - this.is_tbb = true; - this.logger.log(3, "This is a Tor Browser's XPCOM"); - } catch(e) { - this.logger.log(3, "This is not a Tor Browser's XPCOM"); - } - - try { - // XXX: We're in a race with HTTPS-Everywhere to update our proxy settings - // before the initial SSL-Observatory test... If we lose the race, Firefox - // caches the old proxy settings for check.tp.o somehwere, and it never loads :( - this.setProxySettings(); - } catch(e) { - this.logger.log(4, "Early proxy change failed. Will try again at profile load. Error: "+e); + this.logger = Cc["@torproject.org/torbutton-logger;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + this._prefs = Services.prefs; + this.logger.log(3, "Startup Observer created"); + + var env = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment + ); + var prefName = "browser.startup.homepage"; + if (env.exists("TOR_DEFAULT_HOMEPAGE")) { + // if the user has set this value in a previous installation, don't override it + if (!this._prefs.prefHasUserValue(prefName)) { + this._prefs.setCharPref(prefName, env.get("TOR_DEFAULT_HOMEPAGE")); } + }
- cleanupCookies(); - - // Using all possible locales so that we do not have to change this list every time we support - // a new one. - const allLocales = [ - "en-US", "ach", "af", "an", "ar", "ast", "az", "be", "bg", "bn", "br", "bs", "ca", "cak", - "crh", "cs", "cy", "da", "de", "dsb", "el", "en-CA", "en-GB", "eo", "es-AR", "es-CL", - "es-ES", "es-MX", "et", "eu", "fa", "ff", "fi", "fr", "fy-NL", "ga-IE", "gd", "gl", "gn", - "gu-IN", "he", "hi-IN", "hr", "hsb", "hu", "hy-AM", "ia", "id", "is", "it", "ja", - "ja-JP-mac", "ka", "kab", "kk", "km", "kn", "ko", "lij", "lo", "lt", "ltg", "lv", "mk", "mr", - "ms", "my", "nb-NO", "ne-NP", "nl", "nn-NO", "oc", "pa-IN", "pl", "pt-BR", "pt-PT", "rm", - "ro", "ru", "si", "sk", "sl", "son", "sq", "sr", "sv-SE", "ta", "te", "th", "tl", "tr", - "trs", "uk", "ur", "uz", "vi", "wo", "xh", "zh-CN", "zh-TW" - ]; - let torSource = new FileSource( - "torbutton", - allLocales, - "resource://torbutton/locale/{locale}/", - true, // skip this FileSource locales when computing Services.locale.availableLocales + this.is_tbb = true; + + try { + // XXX: We're in a race with HTTPS-Everywhere to update our proxy settings + // before the initial SSL-Observatory test... If we lose the race, Firefox + // caches the old proxy settings for check.tp.o somehwere, and it never loads :( + this.setProxySettings(); + } catch (e) { + this.logger.log( + 4, + "Early proxy change failed. Will try again at profile load. Error: " + e ); - if (L10nRegistry.registerSources) { - L10nRegistry.registerSources([torSource]); - } else { - L10nRegistry.registerSource(torSource); - } + } + + cleanupCookies(); + + // Using all possible locales so that we do not have to change this list every time we support + // a new one. + /* eslint-disable */ + const allLocales = [ + "en-US", "ach", "af", "an", "ar", "ast", "az", "be", "bg", "bn", "br", "bs", "ca", "cak", + "crh", "cs", "cy", "da", "de", "dsb", "el", "en-CA", "en-GB", "eo", "es-AR", "es-CL", + "es-ES", "es-MX", "et", "eu", "fa", "ff", "fi", "fr", "fy-NL", "ga-IE", "gd", "gl", "gn", + "gu-IN", "he", "hi-IN", "hr", "hsb", "hu", "hy-AM", "ia", "id", "is", "it", "ja", + "ja-JP-mac", "ka", "kab", "kk", "km", "kn", "ko", "lij", "lo", "lt", "ltg", "lv", "mk", "mr", + "ms", "my", "nb-NO", "ne-NP", "nl", "nn-NO", "oc", "pa-IN", "pl", "pt-BR", "pt-PT", "rm", + "ro", "ru", "si", "sk", "sl", "son", "sq", "sr", "sv-SE", "ta", "te", "th", "tl", "tr", + "trs", "uk", "ur", "uz", "vi", "wo", "xh", "zh-CN", "zh-TW" + ]; + /* eslint-enable */ + let torSource = new FileSource( + "torbutton", + allLocales, + "resource://torbutton/locale/{locale}/", + true // skip this FileSource locales when computing Services.locale.availableLocales + ); + if (L10nRegistry.registerSources) { + L10nRegistry.registerSources([torSource]); + } else { + L10nRegistry.registerSource(torSource); + } }
StartupObserver.prototype = { - // Bug 6803: We need to get the env vars early due to - // some weird proxy caching code that showed up in FF15. - // Otherwise, homepage domain loads fail forever. - setProxySettings: function() { - if (!this.is_tbb) - return; - - // Bug 1506: Still want to get these env vars - let environ = Cc["@mozilla.org/process/environment;1"] - .getService(Ci.nsIEnvironment); - if (environ.exists("TOR_TRANSPROXY")) { - this.logger.log(3, "Resetting Tor settings to transproxy"); - this._prefs.setBoolPref("network.proxy.socks_remote_dns", false); - this._prefs.setIntPref("network.proxy.type", 0); - this._prefs.setIntPref("network.proxy.socks_port", 0); - this._prefs.setCharPref("network.proxy.socks", ""); - } else { - // Try to retrieve SOCKS proxy settings from Tor Launcher. - let socksPortInfo; - try { - let tlps = Cc["@torproject.org/torlauncher-protocol-service;1"] - .getService(Ci.nsISupports).wrappedJSObject; - socksPortInfo = tlps.TorGetSOCKSPortInfo(); - } catch(e) { - this.logger.log(3, "tor launcher failed " + e); - } + // Bug 6803: We need to get the env vars early due to + // some weird proxy caching code that showed up in FF15. + // Otherwise, homepage domain loads fail forever. + setProxySettings() { + if (!this.is_tbb) { + return; + }
- // If Tor Launcher is not available, check environment variables. - if (!socksPortInfo) { - socksPortInfo = { ipcFile: undefined, host: undefined, port: 0 }; + // Bug 1506: Still want to get these env vars + let environ = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment + ); + if (environ.exists("TOR_TRANSPROXY")) { + this.logger.log(3, "Resetting Tor settings to transproxy"); + this._prefs.setBoolPref("network.proxy.socks_remote_dns", false); + this._prefs.setIntPref("network.proxy.type", 0); + this._prefs.setIntPref("network.proxy.socks_port", 0); + this._prefs.setCharPref("network.proxy.socks", ""); + } else { + // Try to retrieve SOCKS proxy settings from Tor Launcher. + let socksPortInfo; + try { + let tlps = Cc[ + "@torproject.org/torlauncher-protocol-service;1" + ].getService(Ci.nsISupports).wrappedJSObject; + socksPortInfo = tlps.TorGetSOCKSPortInfo(); + } catch (e) { + this.logger.log(3, "tor launcher failed " + e); + }
- let isWindows = Services.appinfo.OS === "WINNT"; - if (!isWindows && environ.exists("TOR_SOCKS_IPC_PATH")) { - socksPortInfo.ipcFile = new FileUtils.File( - environ.get("TOR_SOCKS_IPC_PATH")); - } - else - { - if (environ.exists("TOR_SOCKS_HOST")) - socksPortInfo.host = environ.get("TOR_SOCKS_HOST"); - if (environ.exists("TOR_SOCKS_PORT")) - socksPortInfo.port = parseInt(environ.get("TOR_SOCKS_PORT")); - } - } + // If Tor Launcher is not available, check environment variables. + if (!socksPortInfo) { + socksPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
- // Adjust network.proxy prefs. - if (socksPortInfo.ipcFile) { - let fph = Services.io.getProtocolHandler("file") - .QueryInterface(Ci.nsIFileProtocolHandler); - let fileURI = fph.newFileURI(socksPortInfo.ipcFile); - this.logger.log(3, "Reset socks to "+fileURI.spec); - this._prefs.setCharPref("network.proxy.socks", fileURI.spec); - this._prefs.setIntPref("network.proxy.socks_port", 0); + let isWindows = Services.appinfo.OS === "WINNT"; + if (!isWindows && environ.exists("TOR_SOCKS_IPC_PATH")) { + socksPortInfo.ipcFile = new FileUtils.File( + environ.get("TOR_SOCKS_IPC_PATH") + ); } else { - if (socksPortInfo.host) { - this._prefs.setCharPref("network.proxy.socks", socksPortInfo.host); - this.logger.log(3, "Reset socks host to "+socksPortInfo.host); + if (environ.exists("TOR_SOCKS_HOST")) { + socksPortInfo.host = environ.get("TOR_SOCKS_HOST"); } - if (socksPortInfo.port) { - this._prefs.setIntPref("network.proxy.socks_port", - socksPortInfo.port); - this.logger.log(3, "Reset socks port to "+socksPortInfo.port); + if (environ.exists("TOR_SOCKS_PORT")) { + socksPortInfo.port = parseInt(environ.get("TOR_SOCKS_PORT")); } } + }
- if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) { - this._prefs.setBoolPref("network.proxy.socks_remote_dns", true); - this._prefs.setIntPref("network.proxy.type", 1); + // Adjust network.proxy prefs. + if (socksPortInfo.ipcFile) { + let fph = Services.io + .getProtocolHandler("file") + .QueryInterface(Ci.nsIFileProtocolHandler); + let fileURI = fph.newFileURI(socksPortInfo.ipcFile); + this.logger.log(3, "Reset socks to " + fileURI.spec); + this._prefs.setCharPref("network.proxy.socks", fileURI.spec); + this._prefs.setIntPref("network.proxy.socks_port", 0); + } else { + if (socksPortInfo.host) { + this._prefs.setCharPref("network.proxy.socks", socksPortInfo.host); + this.logger.log(3, "Reset socks host to " + socksPortInfo.host); + } + if (socksPortInfo.port) { + this._prefs.setIntPref( + "network.proxy.socks_port", + socksPortInfo.port + ); + this.logger.log(3, "Reset socks port to " + socksPortInfo.port); } }
- // Force prefs to be synced to disk - Services.prefs.savePrefFile(null); + if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) { + this._prefs.setBoolPref("network.proxy.socks_remote_dns", true); + this._prefs.setIntPref("network.proxy.type", 1); + } + } + + // Force prefs to be synced to disk + Services.prefs.savePrefFile(null);
- this.logger.log(3, "Synced network settings to environment."); - }, + this.logger.log(3, "Synced network settings to environment."); + },
- observe: function(subject, topic, data) { - if(topic == "profile-after-change") { - // Bug 1506 P1: We listen to these prefs as signals for startup, - // but only for hackish reasons. - this._prefs.setBoolPref("extensions.torbutton.startup", true); + observe(subject, topic, data) { + if (topic == "profile-after-change") { + // Bug 1506 P1: We listen to these prefs as signals for startup, + // but only for hackish reasons. + this._prefs.setBoolPref("extensions.torbutton.startup", true);
- this.setProxySettings(); - } + this.setProxySettings(); + }
- // In all cases, force prefs to be synced to disk - Services.prefs.savePrefFile(null); - }, + // In all cases, force prefs to be synced to disk + Services.prefs.savePrefFile(null); + },
QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]),
@@ -205,7 +215,7 @@ StartupObserver.prototype = { contractID: kMODULE_CONTRACTID,
// Hack to get us registered early to observe recovery - _xpcom_categories: [{category:"profile-after-change"}], + _xpcom_categories: [{ category: "profile-after-change" }], };
// Assign factory to global object. diff --git a/components/torCheckService.js b/components/torCheckService.js index 07b1aa99..41d716ff 100644 --- a/components/torCheckService.js +++ b/components/torCheckService.js @@ -3,12 +3,14 @@ * See LICENSE for licensing information. * * vim: set sw=2 sts=2 ts=8 et syntax=javascript: - * + * * Tor check service *************************************************************************/
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +);
XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", @@ -20,16 +22,16 @@ const kMODULE_CONTRACTID = "@torproject.org/torbutton-torCheckService;1"; const kMODULE_CID = Components.ID("5d57312b-5d8c-4169-b4af-e80d6a28a72e");
function TBTorCheckService() { - this._logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; + this._logger = Cc["@torproject.org/torbutton-logger;1"].getService( + Ci.nsISupports + ).wrappedJSObject; this._logger.log(3, "Torbutton Tor Check Service initialized");
this._statusOfTorCheck = this.kCheckNotInitiated; this.wrappedJSObject = this; }
-TBTorCheckService.prototype = -{ +TBTorCheckService.prototype = { QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]),
kCheckNotInitiated: 0, // Possible values for statusOfTorCheck. @@ -49,87 +51,84 @@ TBTorCheckService.prototype = contractID: kMODULE_CONTRACTID,
// method of nsIClassInfo - getInterfaces: function(count) { + getInterfaces(count) { var interfaceList = [Ci.nsIClassInfo]; count.value = interfaceList.length; return interfaceList; },
// method of nsIClassInfo - getHelperForLanguage: function(count) { return null; }, + getHelperForLanguage(count) { + return null; + },
// Public methods. - get statusOfTorCheck() - { + get statusOfTorCheck() { return this._statusOfTorCheck; },
- set statusOfTorCheck(aStatus) - { + set statusOfTorCheck(aStatus) { this._statusOfTorCheck = aStatus; },
- createCheckRequest: function(aAsync) - { - Cu.importGlobalProperties(["XMLHttpRequest"]); + createCheckRequest(aAsync) { let req = new XMLHttpRequest(); let url = Services.prefs.getCharPref("extensions.torbutton.test_url"); req.open("GET", url, aAsync); req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; req.overrideMimeType("text/xml"); - req.timeout = 120000; // Wait at most two minutes for a response. + req.timeout = 120000; // Wait at most two minutes for a response. return req; },
- parseCheckResponse: function(aReq) - { + parseCheckResponse(aReq) { let ret = 0; - if(aReq.status == 200) { - if(!aReq.responseXML) { - this._logger.log(5, "Check failed! Not text/xml!"); - ret = 1; - } else { - let result = aReq.responseXML.getElementById('TorCheckResult'); - - if(result===null) { - this._logger.log(5, "Test failed! No TorCheckResult element"); - ret = 2; - } else if(typeof(result.target) == 'undefined' - || result.target === null) { - this._logger.log(5, "Test failed! No target"); - ret = 3; - } else if(result.target === "success") { - this._logger.log(3, "Test Successful"); - ret = 4; - } else if(result.target === "failure") { - this._logger.log(5, "Tor test failed!"); - ret = 5; - } else if(result.target === "unknown") { - this._logger.log(5, "Tor test failed. TorDNSEL Failure?"); - ret = 6; - } else { - this._logger.log(5, "Tor test failed. Strange target."); - ret = 7; - } - } + if (aReq.status == 200) { + if (!aReq.responseXML) { + this._logger.log(5, "Check failed! Not text/xml!"); + ret = 1; } else { - if (0 == aReq.status) { - try { - var req = aReq.channel.QueryInterface(Ci.nsIRequest); - if (req.status == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED) - { - this._logger.log(5, "Tor test failed. Proxy connection refused"); - ret = 8; - } - } catch (e) {} + let result = aReq.responseXML.getElementById("TorCheckResult"); + + if (result === null) { + this._logger.log(5, "Test failed! No TorCheckResult element"); + ret = 2; + } else if ( + typeof result.target == "undefined" || + result.target === null + ) { + this._logger.log(5, "Test failed! No target"); + ret = 3; + } else if (result.target === "success") { + this._logger.log(3, "Test Successful"); + ret = 4; + } else if (result.target === "failure") { + this._logger.log(5, "Tor test failed!"); + ret = 5; + } else if (result.target === "unknown") { + this._logger.log(5, "Tor test failed. TorDNSEL Failure?"); + ret = 6; + } else { + this._logger.log(5, "Tor test failed. Strange target."); + ret = 7; } + } + } else { + if (0 == aReq.status) { + try { + var req = aReq.channel.QueryInterface(Ci.nsIRequest); + if (req.status == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED) { + this._logger.log(5, "Tor test failed. Proxy connection refused"); + ret = 8; + } + } catch (e) {} + }
- if (ret == 0) - { - this._logger.log(5, "Tor test failed. HTTP Error: "+aReq.status); - ret = -aReq.status; - } + if (ret == 0) { + this._logger.log(5, "Tor test failed. HTTP Error: " + aReq.status); + ret = -aReq.status; } + }
return ret; }, diff --git a/components/torbutton-logger.js b/components/torbutton-logger.js index d80d13c4..2fdcd7e6 100644 --- a/components/torbutton-logger.js +++ b/components/torbutton-logger.js @@ -14,31 +14,34 @@ const kMODULE_CONTRACTID = "@torproject.org/torbutton-logger;1"; const kMODULE_CID = Components.ID("f36d72c9-9718-4134-b550-e109638331d7");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +);
XPCOMUtils.defineLazyModuleGetters(this, { ComponentUtils: "resource://gre/modules/ComponentUtils.jsm", });
function TorbuttonLogger() { - // Register observer - Services.prefs.addObserver("extensions.torbutton", this); - - this.loglevel = Services.prefs.getIntPref("extensions.torbutton.loglevel"); - this.logmethod = Services.prefs.getIntPref("extensions.torbutton.logmethod"); - - try { - var logMngr = Cc["@mozmonkey.com/debuglogger/manager;1"] - .getService(Ci.nsIDebugLoggerManager); - this._debuglog = logMngr.registerLogger("torbutton"); - } catch (exErr) { - this._debuglog = false; - } - this._console = Services.console; + // Register observer + Services.prefs.addObserver("extensions.torbutton", this); + + this.loglevel = Services.prefs.getIntPref("extensions.torbutton.loglevel"); + this.logmethod = Services.prefs.getIntPref("extensions.torbutton.logmethod"); + + try { + var logMngr = Cc["@mozmonkey.com/debuglogger/manager;1"].getService( + Ci.nsIDebugLoggerManager + ); + this._debuglog = logMngr.registerLogger("torbutton"); + } catch (exErr) { + this._debuglog = false; + } + this._console = Services.console;
- // This JSObject is exported directly to chrome - this.wrappedJSObject = this; - this.log(3, "Torbutton debug output ready"); + // This JSObject is exported directly to chrome + this.wrappedJSObject = this; + this.log(3, "Torbutton debug output ready"); }
/** @@ -49,18 +52,16 @@ function TorbuttonLogger() {
const nsIClassInfo = Ci.nsIClassInfo;
-const logString = { 1:"VERB", 2:"DBUG", 3: "INFO", 4:"NOTE", 5:"WARN" }; +const logString = { 1: "VERB", 2: "DBUG", 3: "INFO", 4: "NOTE", 5: "WARN" };
-function padInt(i) -{ - return (i < 10) ? '0' + i : i; +function padInt(i) { + return i < 10 ? "0" + i : i; }
-TorbuttonLogger.prototype = -{ +TorbuttonLogger.prototype = { QueryInterface: ChromeUtils.generateQI([Ci.nsIClassInfo]),
- wrappedJSObject: null, // Initialized by constructor + wrappedJSObject: null, // Initialized by constructor
// make this an nsIClassInfo object flags: nsIClassInfo.DOM_OBJECT, @@ -71,62 +72,78 @@ TorbuttonLogger.prototype = contractID: kMODULE_CONTRACTID,
// method of nsIClassInfo - getInterfaces: function(count) { + getInterfaces(count) { var interfaceList = [nsIClassInfo]; count.value = interfaceList.length; return interfaceList; },
// method of nsIClassInfo - getHelperForLanguage: function(count) { return null; }, + getHelperForLanguage(count) { + return null; + },
- formatLog: function(str, level) { - var d = new Date(); - var now = padInt(d.getUTCMonth()+1)+"-"+padInt(d.getUTCDate())+" "+padInt(d.getUTCHours())+":"+padInt(d.getUTCMinutes())+":"+padInt(d.getUTCSeconds()); - return "["+now+"] Torbutton "+logString[level]+": "+str; + formatLog(str, level) { + var d = new Date(); + var now = + padInt(d.getUTCMonth() + 1) + + "-" + + padInt(d.getUTCDate()) + + " " + + padInt(d.getUTCHours()) + + ":" + + padInt(d.getUTCMinutes()) + + ":" + + padInt(d.getUTCSeconds()); + return "[" + now + "] Torbutton " + logString[level] + ": " + str; },
// error console log - eclog: function(level, str) { - switch(this.logmethod) { - case 0: // stderr - if(this.loglevel <= level) - dump(this.formatLog(str, level)+"\n"); - break; - default: // errorconsole - if(this.loglevel <= level) - this._console.logStringMessage(this.formatLog(str,level)); - break; - } + eclog(level, str) { + switch (this.logmethod) { + case 0: // stderr + if (this.loglevel <= level) { + dump(this.formatLog(str, level) + "\n"); + } + break; + default: + // errorconsole + if (this.loglevel <= level) { + this._console.logStringMessage(this.formatLog(str, level)); + } + break; + } },
- safe_log: function(level, str, scrub) { - if (this.loglevel < 4) { - this.eclog(level, str+scrub); - } else { - this.eclog(level, str+" [scrubbed]"); - } + safe_log(level, str, scrub) { + if (this.loglevel < 4) { + this.eclog(level, str + scrub); + } else { + this.eclog(level, str + " [scrubbed]"); + } },
- log: function(level, str) { - switch(this.logmethod) { - case 2: // debuglogger - if(this._debuglog) { - this._debuglog.log((6-level), this.formatLog(str,level)); - break; - } - // fallthrough - case 0: // stderr - if(this.loglevel <= level) - dump(this.formatLog(str,level)+"\n"); - break; - default: - dump("Bad log method: "+this.logmethod); - case 1: // errorconsole - if(this.loglevel <= level) - this._console.logStringMessage(this.formatLog(str,level)); - break; - } + log(level, str) { + switch (this.logmethod) { + case 2: // debuglogger + if (this._debuglog) { + this._debuglog.log(6 - level, this.formatLog(str, level)); + break; + } + // fallthrough + case 0: // stderr + if (this.loglevel <= level) { + dump(this.formatLog(str, level) + "\n"); + } + break; + case 1: // errorconsole + if (this.loglevel <= level) { + this._console.logStringMessage(this.formatLog(str, level)); + } + break; + default: + dump("Bad log method: " + this.logmethod); + } },
// Pref observer interface implementation @@ -134,29 +151,33 @@ TorbuttonLogger.prototype = // topic: what event occurred // subject: what nsIPrefBranch we're observing // data: which pref has been changed (relative to subject) - observe: function(subject, topic, data) - { - if (topic != "nsPref:changed") return; - switch (data) { - case "extensions.torbutton.logmethod": - this.logmethod = Services.prefs.getIntPref("extensions.torbutton.logmethod"); - if (this.logmethod === 0) { - Services.prefs.setBoolPref("browser.dom.window.dump.enabled", - true); - } else if (Services.prefs. - getIntPref("extensions.torlauncher.logmethod", 3) !== 0) { - // If Tor Launcher is not available or its log method is not 0 - // then let's reset the dump pref. - Services.prefs.setBoolPref("browser.dom.window.dump.enabled", - false); - } - break; - case "extensions.torbutton.loglevel": - this.loglevel = Services.prefs.getIntPref("extensions.torbutton.loglevel"); - break; - } - } -} + observe(subject, topic, data) { + if (topic != "nsPref:changed") { + return; + } + switch (data) { + case "extensions.torbutton.logmethod": + this.logmethod = Services.prefs.getIntPref( + "extensions.torbutton.logmethod" + ); + if (this.logmethod === 0) { + Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true); + } else if ( + Services.prefs.getIntPref("extensions.torlauncher.logmethod", 3) !== 0 + ) { + // If Tor Launcher is not available or its log method is not 0 + // then let's reset the dump pref. + Services.prefs.setBoolPref("browser.dom.window.dump.enabled", false); + } + break; + case "extensions.torbutton.loglevel": + this.loglevel = Services.prefs.getIntPref( + "extensions.torbutton.loglevel" + ); + break; + } + }, +};
// Assign factory to global object. const NSGetFactory = XPCOMUtils.generateNSGetFactory diff --git a/modules/tor-control-port.js b/modules/tor-control-port.js index 51ac8ac0..dc59c8da 100644 --- a/modules/tor-control-port.js +++ b/modules/tor-control-port.js @@ -20,27 +20,24 @@
/* jshint esnext: true */ /* jshint -W097 */ -/* global Components, console, Services */ +/* global console */ "use strict";
-// ### Mozilla Abbreviations -let { Constructor: CC } = Components; - // ### Import Mozilla Services const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorProtocolService, TorProcessStatus } = ChromeUtils.import( - "resource:///modules/TorProtocolService.jsm" + "resource:///modules/TorProtocolService.jsm" ); // tor-launcher observer topics const TorTopics = Object.freeze({ - ProcessIsReady: "TorProcessIsReady", + ProcessIsReady: "TorProcessIsReady", });
// __log__. // Logging function -let logger = Cc["@torproject.org/torbutton-logger;1"] - .getService(Ci.nsISupports).wrappedJSObject; +let logger = Cc["@torproject.org/torbutton-logger;1"].getService(Ci.nsISupports) + .wrappedJSObject; let log = x => logger.eclog(3, x.trimRight().replace(/\r\n/g, "\n"));
// ### announce this file @@ -48,19 +45,26 @@ log("Loading tor-control-port.js\n");
class AsyncSocket { constructor(ipcFile, host, port) { - let sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci.nsISocketTransportService); + let sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService( + Ci.nsISocketTransportService + ); const OPEN_UNBUFFERED = Ci.nsITransport.OPEN_UNBUFFERED;
- let socketTransport = ipcFile ? - sts.createUnixDomainTransport(ipcFile) : - sts.createTransport([], host, port, null, null); - + let socketTransport = ipcFile + ? sts.createUnixDomainTransport(ipcFile) + : sts.createTransport([], host, port, null, null);
- this.outputStream = socketTransport.openOutputStream(OPEN_UNBUFFERED, 1, 1).QueryInterface(Ci.nsIAsyncOutputStream); + this.outputStream = socketTransport + .openOutputStream(OPEN_UNBUFFERED, 1, 1) + .QueryInterface(Ci.nsIAsyncOutputStream); this.outputQueue = [];
- this.inputStream = socketTransport.openInputStream(OPEN_UNBUFFERED, 1, 1).QueryInterface(Ci.nsIAsyncInputStream); - this.scriptableInputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream); + this.inputStream = socketTransport + .openInputStream(OPEN_UNBUFFERED, 1, 1) + .QueryInterface(Ci.nsIAsyncInputStream); + this.scriptableInputStream = Cc[ + "@mozilla.org/scriptableinputstream;1" + ].createInstance(Ci.nsIScriptableInputStream); this.scriptableInputStream.init(this.inputStream); this.inputQueue = []; } @@ -68,13 +72,15 @@ class AsyncSocket { // asynchronously write string to underlying socket and return number of bytes written async write(str) { return new Promise((resolve, reject) => { - // asyncWait next write request const tryAsyncWait = () => { - if (this.outputQueue.length > 0) { + if (this.outputQueue.length) { this.outputStream.asyncWait( this.outputQueue.at(0), // next request - 0, 0, Services.tm.currentThread); + 0, + 0, + Services.tm.currentThread + ); } };
@@ -98,7 +104,7 @@ class AsyncSocket { // reject promise on error reject(err); } - } + }, });
// length 1 imples that there is no in-flight asyncWait, so we may immediately @@ -112,20 +118,24 @@ class AsyncSocket { // asynchronously read string from underlying socket and return it async read() { return new Promise((resolve, reject) => { - const tryAsyncWait = () => { - if (this.inputQueue.length > 0) { + if (this.inputQueue.length) { this.inputStream.asyncWait( - this.inputQueue.at(0), // next input request - 0, 0, Services.tm.currentThread); + this.inputQueue.at(0), // next input request + 0, + 0, + Services.tm.currentThread + ); } };
this.inputQueue.push({ - onInputStreamReady: (stream) => { + onInputStreamReady: stream => { try { // read our string from input stream - let str = this.scriptableInputStream.read(this.scriptableInputStream.available()); + let str = this.scriptableInputStream.read( + this.scriptableInputStream.available() + );
// remove this callback object from queue now that we have read this.inputQueue.shift(); @@ -138,7 +148,7 @@ class AsyncSocket { } catch (err) { reject(err); } - } + }, });
// length 1 imples that there is no in-flight asyncWait, so we may immediately @@ -153,7 +163,7 @@ class AsyncSocket { this.outputStream.close(); this.inputStream.close(); } -}; +}
class ControlSocket { constructor(asyncSocket) { @@ -165,9 +175,15 @@ class ControlSocket { this.mainDispatcher = io.callbackDispatcher(); this.notificationDispatcher = io.callbackDispatcher(); // mainDispatcher pushes only async notifications (650) to notificationDispatcher - this.mainDispatcher.addCallback(/^650/, this._handleNotification.bind(this)); + this.mainDispatcher.addCallback( + /^650/, + this._handleNotification.bind(this) + ); // callback for handling responses and errors - this.mainDispatcher.addCallback(/^[245]\d\d/, this._handleCommandReply.bind(this) ); + this.mainDispatcher.addCallback( + /^[245]\d\d/, + this._handleCommandReply.bind(this) + );
this.commandQueue = [];
@@ -178,7 +194,7 @@ class ControlSocket { // immediately returns next line in queue (pendingLines) if present async _readLine() { // keep reading from socket until we have a full line to return - while(this.pendingLines.length == 0) { + while (!this.pendingLines.length) { // read data from our socket and spit on newline tokens this.pendingData += await this.socket.read(); let lines = this.pendingData.split("\r\n"); @@ -189,7 +205,6 @@ class ControlSocket {
// copy remaining full lines to our pendingLines list this.pendingLines = this.pendingLines.concat(lines); - } return this.pendingLines.shift(); } @@ -216,23 +231,24 @@ class ControlSocket { // and waiting for a terminating "." on its own line. // (See control-spec section 3.9 and https://trac.torproject.org/16990#comment:28 // Ensure this is the first line of a new message + // eslint-disable-next-line no-lonely-if if (message.length === 1 && line.match(/^\d\d\d+.+?=$/)) { handlingMultlineValue = true; } // look for end of message (note the space character at end of the regex) - else if(line.match(/^\d\d\d /)) { + else if (line.match(/^\d\d\d /)) { if (message.length == 1) { endOfMessageFound = true; } else { - let firstReplyCode = message[0].substring(0,3); - let lastReplyCode = line.substring(0,3); + let firstReplyCode = message[0].substring(0, 3); + let lastReplyCode = line.substring(0, 3); if (firstReplyCode == lastReplyCode) { endOfMessageFound = true; } } } } - } while(!endOfMessageFound); + } while (!endOfMessageFound);
// join our lines back together to form one message return message.join("\r\n"); @@ -240,14 +256,14 @@ class ControlSocket {
async _startMessagePump() { try { - while(true) { + while (true) { let message = await this._readMessage(); log("controlPort >> " + message); this.mainDispatcher.pushMessage(message); } } catch (err) { this._isOpen = false; - for(const cmd of this.commandQueue) { + for (const cmd of this.commandQueue) { cmd.reject(err); } this.commandQueue = []; @@ -269,9 +285,9 @@ class ControlSocket { // in _startMessagePump (on stream error) return new Promise((resolve, reject) => { let command = { - commandString: commandString, - resolve: resolve, - reject: reject, + commandString, + resolve, + reject, };
this.commandQueue.push(command); @@ -288,7 +304,7 @@ class ControlSocket { } else if (message.match(/^[45]/)) { let myErr = new Error(cmd.commandString + " -> " + message); // Add Tor-specific information to the Error object. - let idx = message.indexOf(' '); + let idx = message.indexOf(" "); if (idx > 0) { myErr.torStatusCode = message.substring(0, idx); myErr.torMessage = message.substring(idx); @@ -297,11 +313,15 @@ class ControlSocket { } cmd.reject(myErr); } else { - cmd.reject(new Error(`ControlSocket::_handleCommandReply received unexpected message:\n----\n${message}\n----`)); + cmd.reject( + new Error( + `ControlSocket::_handleCommandReply received unexpected message:\n----\n${message}\n----` + ) + ); }
// send next command if one is available - if (this.commandQueue.length > 0) { + if (this.commandQueue.length) { this._writeNextCommand(); } } @@ -322,7 +342,7 @@ class ControlSocket { isOpen() { return this._isOpen; } -}; +}
// ## io // I/O utilities namespace @@ -336,28 +356,33 @@ let io = {}; // Pass pushMessage to another function that needs a callback with a single string // argument. Whenever dispatcher.pushMessage receives a string, the dispatcher will // check for any regex matches and pass the string on to the corresponding callback(s). -io.callbackDispatcher = function () { +io.callbackDispatcher = function() { let callbackPairs = [], - removeCallback = function (aCallback) { - callbackPairs = callbackPairs.filter(function ([regex, callback]) { - return callback !== aCallback; - }); - }, - addCallback = function (regex, callback) { - if (callback) { - callbackPairs.push([regex, callback]); - } - return function () { removeCallback(callback); }; - }, - pushMessage = function (message) { - for (let [regex, callback] of callbackPairs) { - if (message.match(regex)) { - callback(message); - } - } + removeCallback = function(aCallback) { + callbackPairs = callbackPairs.filter(function([regex, callback]) { + return callback !== aCallback; + }); + }, + addCallback = function(regex, callback) { + if (callback) { + callbackPairs.push([regex, callback]); + } + return function() { + removeCallback(callback); }; - return { pushMessage : pushMessage, removeCallback : removeCallback, - addCallback : addCallback }; + }, + pushMessage = function(message) { + for (let [regex, callback] of callbackPairs) { + if (message.match(regex)) { + callback(message); + } + } + }; + return { + pushMessage, + removeCallback, + addCallback, + }; };
// __io.controlSocket(ipcFile, host, port, password)__. @@ -374,7 +399,7 @@ io.callbackDispatcher = function () { // socket.removeNotificationCallback(callback); // // Close the socket permanently // socket.close(); -io.controlSocket = async function (ipcFile, host, port, password) { +io.controlSocket = async function(ipcFile, host, port, password) { let socket = new AsyncSocket(ipcFile, host, port); let controlSocket = new ControlSocket(socket);
@@ -392,21 +417,23 @@ let utils = {};
// __utils.identity(x)__. // Returns its argument unchanged. -utils.identity = function (x) { return x; }; +utils.identity = function(x) { + return x; +};
// __utils.isString(x)__. // Returns true iff x is a string. -utils.isString = function (x) { - return typeof(x) === 'string' || x instanceof String; +utils.isString = function(x) { + return typeof x === "string" || x instanceof String; };
// __utils.capture(string, regex)__. // Takes a string and returns an array of capture items, where regex must have a single // capturing group and use the suffix /.../g to specify a global search. -utils.capture = function (string, regex) { +utils.capture = function(string, regex) { let matches = []; // Special trick to use string.replace for capturing multiple matches. - string.replace(regex, function (a, captured) { + string.replace(regex, function(a, captured) { matches.push(captured); }); return matches; @@ -415,15 +442,17 @@ utils.capture = function (string, regex) { // __utils.extractor(regex)__. // Returns a function that takes a string and returns an array of regex matches. The // regex must use the suffix /.../g to specify a global search. -utils.extractor = function (regex) { - return function (text) { +utils.extractor = function(regex) { + return function(text) { return utils.capture(text, regex); }; };
// __utils.splitLines(string)__. // Splits a string into an array of strings, each corresponding to a line. -utils.splitLines = function (string) { return string.split(/\r?\n/); }; +utils.splitLines = function(string) { + return string.split(/\r?\n/); +};
// __utils.splitAtSpaces(string)__. // Splits a string into chunks between spaces. Does not split at spaces @@ -433,11 +462,14 @@ utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g); // __utils.splitAtFirst(string, regex)__. // Splits a string at the first instance of regex match. If no match is // found, returns the whole string. -utils.splitAtFirst = function (string, regex) { +utils.splitAtFirst = function(string, regex) { let match = string.match(regex); - return match ? [ string.substring(0, match.index), - string.substring(match.index + match[0].length) ] - : string; + return match + ? [ + string.substring(0, match.index), + string.substring(match.index + match[0].length), + ] + : string; };
// __utils.splitAtEquals(string)__. @@ -448,7 +480,7 @@ utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g); // __utils.mergeObjects(arrayOfObjects)__. // Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object. // Pure function. -utils.mergeObjects = function (arrayOfObjects) { +utils.mergeObjects = function(arrayOfObjects) { let result = {}; for (let obj of arrayOfObjects) { for (let key in obj) { @@ -468,10 +500,10 @@ utils.mergeObjects = function (arrayOfObjects) { // ["streamID", "event", "circuitID", "IP"]) // // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0", // // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}" -utils.listMapData = function (parameterString, listNames) { +utils.listMapData = function(parameterString, listNames) { // Split out the space-delimited parameters. let parameters = utils.splitAtSpaces(parameterString), - dataMap = {}; + dataMap = {}; // Assign listNames to the first n = listNames.length parameters. for (let i = 0; i < listNames.length; ++i) { dataMap[listNames[i]] = parameters[i]; @@ -506,13 +538,15 @@ let info = {}; // or single-line (with a `250-` or `250 ` prefix): // // 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943) -info.keyValueStringsFromMessage = utils.extractor(/^(250+[\s\S]+?^.|250[- ].+?)$/gmi); +info.keyValueStringsFromMessage = utils.extractor( + /^(250+[\s\S]+?^.|250[- ].+?)$/gim +);
// __info.applyPerLine(transformFunction)__. // Returns a function that splits text into lines, // and applies transformFunction to each line. -info.applyPerLine = function (transformFunction) { - return function (text) { +info.applyPerLine = function(transformFunction) { + return function(text) { return utils.splitLines(text.trim()).map(transformFunction); }; }; @@ -521,23 +555,31 @@ info.applyPerLine = function (transformFunction) { // Parses a router status entry as, described in // https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt // (search for "router status entry") -info.routerStatusParser = function (valueString) { +info.routerStatusParser = function(valueString) { let lines = utils.splitLines(valueString), - objects = []; + objects = []; for (let line of lines) { // Drop first character and grab data following it. let myData = line.substring(2), - // Accumulate more maps with data, depending on the first character in the line. - dataFun = { - "r" : data => utils.listMapData(data, ["nickname", "identity", "digest", - "publicationDate", "publicationTime", - "IP", "ORPort", "DirPort"]), - "a" : data => ({ "IPv6" : data }), - "s" : data => ({ "statusFlags" : utils.splitAtSpaces(data) }), - "v" : data => ({ "version" : data }), - "w" : data => utils.listMapData(data, []), - "p" : data => ({ "portList" : data.split(",") }), - }[line.charAt(0)]; + // Accumulate more maps with data, depending on the first character in the line. + dataFun = { + r: data => + utils.listMapData(data, [ + "nickname", + "identity", + "digest", + "publicationDate", + "publicationTime", + "IP", + "ORPort", + "DirPort", + ]), + a: data => ({ IPv6: data }), + s: data => ({ statusFlags: utils.splitAtSpaces(data) }), + v: data => ({ version: data }), + w: data => utils.listMapData(data, []), + p: data => ({ portList: data.split(",") }), + }[line.charAt(0)]; if (dataFun !== undefined) { objects.push(dataFun(myData)); } @@ -547,12 +589,12 @@ info.routerStatusParser = function (valueString) {
// __info.circuitStatusParser(line)__. // Parse the output of a circuit status line. -info.circuitStatusParser = function (line) { - let data = utils.listMapData(line, ["id","status","circuit"]), - circuit = data.circuit; +info.circuitStatusParser = function(line) { + let data = utils.listMapData(line, ["id", "status", "circuit"]), + circuit = data.circuit; // Parse out the individual circuit IDs and names. if (circuit) { - data.circuit = circuit.split(",").map(function (x) { + data.circuit = circuit.split(",").map(function(x) { return x.split(/~|=/); }); } @@ -561,12 +603,15 @@ info.circuitStatusParser = function (line) {
// __info.streamStatusParser(line)__. // Parse the output of a stream status line. -info.streamStatusParser = function (text) { - return utils.listMapData(text, ["StreamID", "StreamStatus", - "CircuitID", "Target"]); +info.streamStatusParser = function(text) { + return utils.listMapData(text, [ + "StreamID", + "StreamStatus", + "CircuitID", + "Target", + ]); };
- // TODO: fix this parsing logic to handle bridgeLine correctly // fingerprint/id is an optional parameter // __info.bridgeParser(bridgeLine)__. @@ -574,16 +619,26 @@ info.streamStatusParser = function (text) { // a map containing the bridge's type, address, and ID. info.bridgeParser = function(bridgeLine) { let result = {}, - tokens = bridgeLine.split(/\s+/); + tokens = bridgeLine.split(/\s+/); // First check if we have a "vanilla" bridge: if (tokens[0].match(/^\d+.\d+.\d+.\d+/)) { result.type = "vanilla"; [result.address, result.ID] = tokens; - // Several bridge types have a similar format: + // Several bridge types have a similar format: } else { result.type = tokens[0]; - if (["flashproxy", "fte", "meek", "meek_lite", "obfs3", "obfs4", "scramblesuit", - "snowflake"].indexOf(result.type) >= 0) { + if ( + [ + "flashproxy", + "fte", + "meek", + "meek_lite", + "obfs3", + "obfs4", + "scramblesuit", + "snowflake", + ].includes(result.type) + ) { [result.address, result.ID] = tokens.slice(1); } } @@ -594,10 +649,10 @@ info.bridgeParser = function(bridgeLine) { // A map of GETINFO and GETCONF keys to parsing function, which convert // result strings to JavaScript data. info.parsers = { - "ns/id/" : info.routerStatusParser, - "ip-to-country/" : utils.identity, - "circuit-status" : info.applyPerLine(info.circuitStatusParser), - "bridge" : info.bridgeParser, + "ns/id/": info.routerStatusParser, + "ip-to-country/": utils.identity, + "circuit-status": info.applyPerLine(info.circuitStatusParser), + bridge: info.bridgeParser, // Currently unused parsers: // "ns/name/" : info.routerStatusParser, // "stream-status" : info.applyPerLine(info.streamStatusParser), @@ -609,26 +664,31 @@ info.parsers = { // Takes a key and determines the parser function that should be used to // convert its corresponding valueString to JavaScript data. info.getParser = function(key) { - return info.parsers[key] || - info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]; + return ( + info.parsers[key] || + info.parsers[key.substring(0, key.lastIndexOf("/") + 1)] + ); };
// __info.stringToValue(string)__. // Converts a key-value string as from GETINFO or GETCONF to a value. -info.stringToValue = function (string) { +info.stringToValue = function(string) { // key should look something like `250+circuit-status=` or `250-circuit-status=...` // or `250 circuit-status=...` let matchForKey = string.match(/^250[ +-](.+?)=/), - key = matchForKey ? matchForKey[1] : null; - if (key === null) return null; + key = matchForKey ? matchForKey[1] : null; + if (key === null) { + return null; + } // matchResult finds a single-line result for `250-` or `250 `, // or a multi-line one for `250+`. - let matchResult = string.match(/^250[ -].+?=(.*)$/) || - string.match(/^250+.+?=([\s\S]*?)^.$/m), - // Retrieve the captured group (the text of the value in the key-value pair) - valueString = matchResult ? matchResult[1] : null, - // Get the parser function for the key found. - parse = info.getParser(key.toLowerCase()); + let matchResult = + string.match(/^250[ -].+?=(.*)$/) || + string.match(/^250+.+?=([\s\S]*?)^.$/m), + // Retrieve the captured group (the text of the value in the key-value pair) + valueString = matchResult ? matchResult[1] : null, + // Get the parser function for the key found. + parse = info.getParser(key.toLowerCase()); if (parse === undefined) { throw new Error("No parser found for '" + key + "'"); } @@ -638,15 +698,16 @@ info.stringToValue = function (string) {
// __info.getMultipleResponseValues(message)__. // Process multiple responses to a GETINFO or GETCONF request. -info.getMultipleResponseValues = function (message) { - return info.keyValueStringsFromMessage(message) - .map(info.stringToValue) - .filter(utils.identity); +info.getMultipleResponseValues = function(message) { + return info + .keyValueStringsFromMessage(message) + .map(info.stringToValue) + .filter(utils.identity); };
// __info.getInfo(controlSocket, key)__. // Sends GETINFO for a single key. Returns a promise with the result. -info.getInfo = function (aControlSocket, key) { +info.getInfo = function(aControlSocket, key) { if (!utils.isString(key)) { return utils.rejectPromise("key argument should be a string"); } @@ -657,7 +718,7 @@ info.getInfo = function (aControlSocket, key) {
// __info.getConf(aControlSocket, key)__. // Sends GETCONF for a single key. Returns a promise with the result. -info.getConf = function (aControlSocket, key) { +info.getConf = function(aControlSocket, key) { // GETCONF with a single argument returns results with // one or more lines that look like `250[- ]key=value`. // Any GETCONF lines that contain a single keyword only are currently dropped. @@ -665,21 +726,23 @@ info.getConf = function (aControlSocket, key) { if (!utils.isString(key)) { return utils.rejectPromise("key argument should be a string"); } - return aControlSocket.sendCommand("getconf " + key) - .then(info.getMultipleResponseValues); + return aControlSocket + .sendCommand("getconf " + key) + .then(info.getMultipleResponseValues); };
// ## onionAuth // A namespace for functions related to tor's ONION_CLIENT_AUTH_* commands. let onionAuth = {};
-onionAuth.keyInfoStringsFromMessage = utils.extractor(/^250-CLIENT\s+(.+)$/gmi); +onionAuth.keyInfoStringsFromMessage = utils.extractor(/^250-CLIENT\s+(.+)$/gim);
onionAuth.keyInfoObjectsFromMessage = function(message) { let keyInfoStrings = onionAuth.keyInfoStringsFromMessage(message); - return keyInfoStrings.map(infoStr => utils.listMapData(infoStr, - ["hsAddress", "typeAndKey"])); -} + return keyInfoStrings.map(infoStr => + utils.listMapData(infoStr, ["hsAddress", "typeAndKey"]) + ); +};
// __onionAuth.viewKeys()__. // Sends a ONION_CLIENT_AUTH_VIEW command to retrieve the list of private keys. @@ -688,16 +751,22 @@ onionAuth.keyInfoObjectsFromMessage = function(message) { // hsAddress // typeAndKey // Flags (e.g., "Permanent") -onionAuth.viewKeys = function (aControlSocket) { +onionAuth.viewKeys = function(aControlSocket) { let cmd = "onion_client_auth_view"; - return aControlSocket.sendCommand(cmd).then(onionAuth.keyInfoObjectsFromMessage); + return aControlSocket + .sendCommand(cmd) + .then(onionAuth.keyInfoObjectsFromMessage); };
// __onionAuth.add(controlSocket, hsAddress, b64PrivateKey, isPermanent)__. // Sends a ONION_CLIENT_AUTH_ADD command to add a private key to the // Tor configuration. -onionAuth.add = function (aControlSocket, hsAddress, b64PrivateKey, - isPermanent) { +onionAuth.add = function( + aControlSocket, + hsAddress, + b64PrivateKey, + isPermanent +) { if (!utils.isString(hsAddress)) { return utils.rejectPromise("hsAddress argument should be a string"); } @@ -708,15 +777,16 @@ onionAuth.add = function (aControlSocket, hsAddress, b64PrivateKey,
const keyType = "x25519"; let cmd = `onion_client_auth_add ${hsAddress} ${keyType}:${b64PrivateKey}`; - if (isPermanent) + if (isPermanent) { cmd += " Flags=Permanent"; + } return aControlSocket.sendCommand(cmd); };
// __onionAuth.remove(controlSocket, hsAddress)__. // Sends a ONION_CLIENT_AUTH_REMOVE command to remove a private key from the // Tor configuration. -onionAuth.remove = function (aControlSocket, hsAddress) { +onionAuth.remove = function(aControlSocket, hsAddress) { if (!utils.isString(hsAddress)) { return utils.rejectPromise("hsAddress argument should be a string"); } @@ -725,7 +795,6 @@ onionAuth.remove = function (aControlSocket, hsAddress) { return aControlSocket.sendCommand(cmd); };
- // ## event // Handlers for events
@@ -735,7 +804,7 @@ let event = {}; // A map of EVENT keys to parsing functions, which convert result strings to JavaScript // data. event.parsers = { - "stream" : info.streamStatusParser, + stream: info.streamStatusParser, // Currently unused: // "circ" : info.circuitStatusParser, }; @@ -743,9 +812,11 @@ event.parsers = { // __event.messageToData(type, message)__. // Extract the data from an event. Note, at present // we only extract streams that look like `"650" SP...` -event.messageToData = function (type, message) { +event.messageToData = function(type, message) { let dataText = message.match(/^650 \S+?\s(.*)/m)[1]; - return (dataText && type.toLowerCase() in event.parsers) ? event.parsers[type.toLowerCase()](dataText) : null; + return dataText && type.toLowerCase() in event.parsers + ? event.parsers[type.toLowerCase()](dataText) + : null; };
// __event.watchEvent(controlSocket, type, filter, onData)__. @@ -753,17 +824,20 @@ event.messageToData = function (type, message) { // data is passed to the onData callback. Returns a zero arg function that // stops watching the event. Note: we only observe `"650" SP...` events // currently (no `650+...` or `650-...` events). -event.watchEvent = function (controlSocket, type, filter, onData, raw=false) { - return controlSocket.addNotificationCallback(new RegExp("^650 " + type), - function (message) { +event.watchEvent = function(controlSocket, type, filter, onData, raw = false) { + return controlSocket.addNotificationCallback( + new RegExp("^650 " + type), + function(message) { let data = event.messageToData(type, message); if (filter === null || filter(data)) { if (raw || !data) { - return onData(message); + onData(message); + return; } onData(data); } - }); + } + ); };
// ## tor @@ -778,22 +852,23 @@ tor.controllerCache = new Map(); // __tor.controller(ipcFile, host, port, password)__. // Creates a tor controller at the given ipcFile or host and port, with the // given password. -tor.controller = async function (ipcFile, host, port, password) { +tor.controller = async function(ipcFile, host, port, password) { let socket = await io.controlSocket(ipcFile, host, port, password); - return { getInfo : key => info.getInfo(socket, key), - getConf : key => info.getConf(socket, key), - onionAuthViewKeys : () => onionAuth.viewKeys(socket), - onionAuthAdd : (hsAddress, b64PrivateKey, isPermanent) => - onionAuth.add(socket, hsAddress, b64PrivateKey, - isPermanent), - onionAuthRemove : (hsAddress) => - onionAuth.remove(socket, hsAddress), - watchEvent : (type, filter, onData, raw=false) => - event.watchEvent(socket, type, filter, onData, raw), - isOpen : () => socket.isOpen(), - close : () => { socket.close(); }, - sendCommand: cmd => socket.sendCommand(cmd), - }; + return { + getInfo: key => info.getInfo(socket, key), + getConf: key => info.getConf(socket, key), + onionAuthViewKeys: () => onionAuth.viewKeys(socket), + onionAuthAdd: (hsAddress, b64PrivateKey, isPermanent) => + onionAuth.add(socket, hsAddress, b64PrivateKey, isPermanent), + onionAuthRemove: hsAddress => onionAuth.remove(socket, hsAddress), + watchEvent: (type, filter, onData, raw = false) => + event.watchEvent(socket, type, filter, onData, raw), + isOpen: () => socket.isOpen(), + close: () => { + socket.close(); + }, + sendCommand: cmd => socket.sendCommand(cmd), + }; };
// ## Export @@ -804,7 +879,7 @@ let controlPortInfo = {}; // Sets Tor control port connection parameters to be used in future calls to // the controller() function. Example: // configureControlPortModule(undefined, "127.0.0.1", 9151, "MyPassw0rd"); -var configureControlPortModule = function (ipcFile, host, port, password) { +var configureControlPortModule = function(ipcFile, host, port, password) { controlPortInfo.ipcFile = ipcFile; controlPortInfo.host = host; controlPortInfo.port = port || 9151; @@ -827,28 +902,28 @@ var configureControlPortModule = function (ipcFile, host, port, password) { // let replyPromise = c.getInfo("ip-to-country/16.16.16.16"); // // Close the controller permanently // c.close(); -var controller = async function (avoidCache) { - - if (!controlPortInfo.ipcFile && !controlPortInfo.host) +var controller = async function(avoidCache) { + if (!controlPortInfo.ipcFile && !controlPortInfo.host) { throw new Error("Please call configureControlPortModule first"); + }
- const dest = (controlPortInfo.ipcFile) - ? `unix:${controlPortInfo.ipcFile.path}` - : `${controlPortInfo.host}:${controlPortInfo.port}`; + const dest = controlPortInfo.ipcFile + ? `unix:${controlPortInfo.ipcFile.path}` + : `${controlPortInfo.host}:${controlPortInfo.port}`;
// constructor shorthand - const newTorController = - async () => { - return await tor.controller( - controlPortInfo.ipcFile, - controlPortInfo.host, - controlPortInfo.port, - controlPortInfo.password); - }; + const newTorController = async () => { + return tor.controller( + controlPortInfo.ipcFile, + controlPortInfo.host, + controlPortInfo.port, + controlPortInfo.password + ); + };
// avoid cache so always return a new controller if (avoidCache) { - return await newTorController(); + return newTorController(); }
// first check our cache and see if we already have one @@ -872,17 +947,19 @@ var controller = async function (avoidCache) { // Same as controller() function, but explicitly waits until there is a tor daemon // to connect to (either launched by tor-launcher, or if we have an existing system // tor daemon) -var wait_for_controller = async function(avoidCache) { +var wait_for_controller = function(avoidCache) { // if tor process is running (either ours or system) immediately return controller - if (!TorProtocolService.ownsTorDaemon || - TorProtocolService.torProcessStatus == TorProcessStatus.Running) { - return await controller(avoidCache); + if ( + !TorProtocolService.ownsTorDaemon || + TorProtocolService.torProcessStatus == TorProcessStatus.Running + ) { + return controller(avoidCache); }
// otherwise we must wait for tor to finish launching before resolving return new Promise((resolve, reject) => { let observer = { - observe : async (subject, topic, data) => { + observe: async (subject, topic, data) => { if (topic === TorTopics.ProcessIsReady) { try { resolve(await controller(avoidCache)); @@ -898,4 +975,8 @@ var wait_for_controller = async function(avoidCache) { };
// Export functions for external use. -var EXPORTED_SYMBOLS = ["configureControlPortModule", "controller", "wait_for_controller"]; +var EXPORTED_SYMBOLS = [ + "configureControlPortModule", + "controller", + "wait_for_controller", +]; diff --git a/modules/utils.js b/modules/utils.js index b726342b..7ccd2da1 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -11,12 +11,16 @@ let prefs = Services.prefs;
// __getPrefValue(prefName)__ // Returns the current value of a preference, regardless of its type. -var getPrefValue = function (prefName) { - switch(prefs.getPrefType(prefName)) { - case prefs.PREF_BOOL: return prefs.getBoolPref(prefName); - case prefs.PREF_INT: return prefs.getIntPref(prefName); - case prefs.PREF_STRING: return prefs.getCharPref(prefName); - default: return null; +var getPrefValue = function(prefName) { + switch (prefs.getPrefType(prefName)) { + case prefs.PREF_BOOL: + return prefs.getBoolPref(prefName); + case prefs.PREF_INT: + return prefs.getIntPref(prefName); + case prefs.PREF_STRING: + return prefs.getCharPref(prefName); + default: + return null; } };
@@ -24,18 +28,24 @@ var getPrefValue = function (prefName) { // Applies prefHandler whenever the value of the pref changes. // If init is true, applies prefHandler to the current value. // Returns a zero-arg function that unbinds the pref. -var bindPref = function (prefName, prefHandler, init = false) { - let update = () => { prefHandler(getPrefValue(prefName)); }, - observer = { observe : function (subject, topic, data) { - if (data === prefName) { - update(); - } - } }; - prefs.addObserver(prefName, observer, false); +var bindPref = function(prefName, prefHandler, init = false) { + let update = () => { + prefHandler(getPrefValue(prefName)); + }, + observer = { + observe(subject, topic, data) { + if (data === prefName) { + update(); + } + }, + }; + prefs.addObserver(prefName, observer); if (init) { update(); } - return () => { prefs.removeObserver(prefName, observer); }; + return () => { + prefs.removeObserver(prefName, observer); + }; };
// __bindPrefAndInit(prefName, prefHandler)__ @@ -43,7 +53,7 @@ var bindPref = function (prefName, prefHandler, init = false) { // Re-applies prefHandler whenever the value of the pref changes. // Returns a zero-arg function that unbinds the pref. var bindPrefAndInit = (prefName, prefHandler) => - bindPref(prefName, prefHandler, true); + bindPref(prefName, prefHandler, true);
// ## Observers
@@ -51,15 +61,15 @@ var bindPrefAndInit = (prefName, prefHandler) => // Observe the given topic. When notification of that topic // occurs, calls callback(subject, data). Returns a zero-arg // function that stops observing. -var observe = function (topic, callback) { +var observe = function(topic, callback) { let observer = { - observe: function (aSubject, aTopic, aData) { + observe(aSubject, aTopic, aData) { if (topic === aTopic) { callback(aSubject, aData); } }, }; - Services.obs.addObserver(observer, topic, false); + Services.obs.addObserver(observer, topic); return () => Services.obs.removeObserver(observer, topic); };
@@ -67,12 +77,13 @@ var observe = function (topic, callback) {
// __env__. // Provides access to process environment variables. -let env = Cc["@mozilla.org/process/environment;1"] - .getService(Ci.nsIEnvironment); +let env = Cc["@mozilla.org/process/environment;1"].getService( + Ci.nsIEnvironment +);
// __getEnv(name)__. // Reads the environment variable of the given name. -var getEnv = function (name) { +var getEnv = function(name) { return env.exists(name) ? env.get(name) : undefined; };
@@ -91,17 +102,15 @@ let dialogsByName = {}; // __showDialog(parent, url, name, features, arg1, arg2, ...)__. // Like window.openDialog, but if the window is already // open, just focuses it instead of opening a new one. -var showDialog = function (parent, url, name, features) { +var showDialog = function(parent, url, name, features) { let existingDialog = dialogsByName[name]; if (existingDialog && !existingDialog.closed) { existingDialog.focus(); return existingDialog; - } else { - let newDialog = parent.openDialog.apply(parent, - Array.slice(arguments, 1)); - dialogsByName[name] = newDialog; - return newDialog; } + let newDialog = parent.openDialog.apply(parent, Array.slice(arguments, 1)); + dialogsByName[name] = newDialog; + return newDialog; };
// ## Tor control protocol utility functions @@ -112,71 +121,69 @@ let _torControl = { // Returns the unescaped string. Throws upon failure. // Within Tor Launcher, the file components/tl-protocol.js also contains a // copy of _strUnescape(). - _strUnescape: function(aStr) - { - if (!aStr) + _strUnescape(aStr) { + if (!aStr) { return aStr; + }
var len = aStr.length; - if ((len < 2) || ('"' != aStr.charAt(0)) || ('"' != aStr.charAt(len - 1))) + if (len < 2 || '"' != aStr.charAt(0) || '"' != aStr.charAt(len - 1)) { return aStr; + }
const kHexRE = /[0-9A-Fa-f]{2}/; const kOctalRE = /[0-7]{3}/; var rv = ""; var i = 1; var lastCharIndex = len - 2; - while (i <= lastCharIndex) - { + while (i <= lastCharIndex) { var c = aStr.charAt(i); - if ('\' == c) - { - if (++i > lastCharIndex) + if ("\" == c) { + if (++i > lastCharIndex) { throw new Error("missing character after \"); + }
c = aStr.charAt(i); - if ('n' == c) - rv += '\n'; - else if ('r' == c) - rv += '\r'; - else if ('t' == c) - rv += '\t'; - else if ('x' == c) - { - if ((i + 2) > lastCharIndex) + if ("n" == c) { + rv += "\n"; + } else if ("r" == c) { + rv += "\r"; + } else if ("t" == c) { + rv += "\t"; + } else if ("x" == c) { + if (i + 2 > lastCharIndex) { throw new Error("not enough hex characters"); + }
let s = aStr.substr(i + 1, 2); - if (!kHexRE.test(s)) + if (!kHexRE.test(s)) { throw new Error("invalid hex characters"); + }
let val = parseInt(s, 16); rv += String.fromCharCode(val); i += 3; - } - else if (this._isDigit(c)) - { + } else if (this._isDigit(c)) { let s = aStr.substr(i, 3); - if ((i + 2) > lastCharIndex) + if (i + 2 > lastCharIndex) { throw new Error("not enough octal characters"); + }
- if (!kOctalRE.test(s)) + if (!kOctalRE.test(s)) { throw new Error("invalid octal characters"); + }
let val = parseInt(s, 8); rv += String.fromCharCode(val); i += 3; - } - else // "\" and others - { + } // "\" and others + else { rv += c; ++i; } - } - else if ('"' == c) - throw new Error("unescaped " within string"); - else - { + } else if ('"' == c) { + throw new Error('unescaped " within string'); + } else { rv += c; ++i; } @@ -188,8 +195,7 @@ let _torControl = {
// Within Tor Launcher, the file components/tl-protocol.js also contains a // copy of _isDigit(). - _isDigit: function(aChar) - { + _isDigit(aChar) { const kRE = /^\d$/; return aChar && kRE.test(aChar); }, @@ -206,30 +212,35 @@ var unescapeTorString = function(str) { var show_torbrowser_manual = () => { let availableLocales = ["de", "en", "es", "fr", "nl", "pt", "tr", "vi", "zh"]; let shortLocale = getLocale().substring(0, 2); - return availableLocales.indexOf(shortLocale) >= 0; -} + return availableLocales.includes(shortLocale); +};
-var getFPDFromHost = (hostname) => { +var getFPDFromHost = hostname => { try { return Services.eTLD.getBaseDomainFromHost(hostname); } catch (e) { - if (e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS || - e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { + if ( + e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS || + e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS + ) { return hostname; } } return null; -} +};
// Assuming this is called with gBrowser.selectedBrowser -var getDomainForBrowser = (browser) => { +var getDomainForBrowser = browser => { let fpd = browser.contentPrincipal.originAttributes.firstPartyDomain; // Bug 31562: For neterror or certerror, get the original URL from // browser.currentURI and use it to calculate the firstPartyDomain. let knownErrors = ["about:neterror", "about:certerror"]; let documentURI = browser.documentURI; - if (documentURI && documentURI.schemeIs('about') && - knownErrors.some(x => documentURI.spec.startsWith(x))) { + if ( + documentURI && + documentURI.schemeIs("about") && + knownErrors.some(x => documentURI.spec.startsWith(x)) + ) { let knownSchemes = ["http", "https", "ftp"]; let currentURI = browser.currentURI; if (currentURI && knownSchemes.some(x => currentURI.schemeIs(x))) { @@ -239,58 +250,71 @@ var getDomainForBrowser = (browser) => { return fpd; };
-var m_tb_torlog = Cc["@torproject.org/torbutton-logger;1"] -.getService(Ci.nsISupports).wrappedJSObject; +var m_tb_torlog = Cc["@torproject.org/torbutton-logger;1"].getService( + Ci.nsISupports +).wrappedJSObject;
var m_tb_string_bundle = torbutton_get_stringbundle();
function torbutton_safelog(nLevel, sMsg, scrub) { - m_tb_torlog.safe_log(nLevel, sMsg, scrub); - return true; + m_tb_torlog.safe_log(nLevel, sMsg, scrub); + return true; }
function torbutton_log(nLevel, sMsg) { - m_tb_torlog.log(nLevel, sMsg); + m_tb_torlog.log(nLevel, sMsg);
- // So we can use it in boolean expressions to determine where the - // short-circuit is.. - return true; + // So we can use it in boolean expressions to determine where the + // short-circuit is.. + return true; }
// load localization strings -function torbutton_get_stringbundle() -{ - var o_stringbundle = false; - - try { - var oBundle = Services.strings; - o_stringbundle = oBundle.createBundle("chrome://torbutton/locale/torbutton.properties"); - } catch(err) { - o_stringbundle = false; - } - if (!o_stringbundle) { - torbutton_log(5, 'ERROR (init): failed to find torbutton-bundle'); - } +function torbutton_get_stringbundle() { + var o_stringbundle = false;
- return o_stringbundle; -} + try { + var oBundle = Services.strings; + o_stringbundle = oBundle.createBundle( + "chrome://torbutton/locale/torbutton.properties" + ); + } catch (err) { + o_stringbundle = false; + } + if (!o_stringbundle) { + torbutton_log(5, "ERROR (init): failed to find torbutton-bundle"); + }
-function torbutton_get_property_string(propertyname) -{ - try { - if (!m_tb_string_bundle) { - m_tb_string_bundle = torbutton_get_stringbundle(); - } + return o_stringbundle; +}
- return m_tb_string_bundle.GetStringFromName(propertyname); - } catch(e) { - torbutton_log(4, "Unlocalized string "+propertyname); +function torbutton_get_property_string(propertyname) { + try { + if (!m_tb_string_bundle) { + m_tb_string_bundle = torbutton_get_stringbundle(); }
- return propertyname; + return m_tb_string_bundle.GetStringFromName(propertyname); + } catch (e) { + torbutton_log(4, "Unlocalized string " + propertyname); + } + + return propertyname; }
// Export utility functions for external use. -let EXPORTED_SYMBOLS = ["bindPref", "bindPrefAndInit", "getEnv", "getLocale", "getDomainForBrowser", - "getPrefValue", "observe", "showDialog", "show_torbrowser_manual", "unescapeTorString", - "torbutton_safelog", "torbutton_log", "torbutton_get_property_string"]; +let EXPORTED_SYMBOLS = [ + "bindPref", + "bindPrefAndInit", + "getEnv", + "getLocale", + "getDomainForBrowser", + "getPrefValue", + "observe", + "showDialog", + "show_torbrowser_manual", + "unescapeTorString", + "torbutton_safelog", + "torbutton_log", + "torbutton_get_property_string", +];
tor-commits@lists.torproject.org