commit f3804aad9dd71fd4eefe33cf46ff2bab0355da4a Author: Kathy Brade brade@pearlcrescent.com Date: Thu Feb 27 17:31:19 2014 -0500
Bug #10418: Provide "Use Default Bridges" option.
To enable this feature, add one or more preference values like: extensions.torlauncher.default_bridge.TYPE.1 extensions.torlauncher.default_bridge.TYPE.2 Other new preferences: extensions.torlauncher.default_bridge_type (stores chosen type) extensions.torlauncher.default_bridge_recommended_type The text "(recommended)" is displayed next to the recommended type, and it is selected by default. The default bridges are reconfigured each time the browser starts up; if no default bridges of the configured type are available, an error message is displayed and the user is placed on the bridge settings panel within the network configuration wizard. --- src/chrome/content/network-settings-overlay.xul | 39 ++-- src/chrome/content/network-settings-wizard.xul | 30 ++- src/chrome/content/network-settings.js | 261 +++++++++++++++++++---- src/chrome/locale/en/network-settings.dtd | 10 +- src/chrome/locale/en/torlauncher.properties | 4 + src/chrome/skin/network-settings.css | 8 +- src/components/tl-process.js | 101 ++++++++- src/components/tl-protocol.js | 26 +++ src/defaults/preferences/prefs.js | 7 + src/modules/tl-util.jsm | 76 +++++++ 10 files changed, 481 insertions(+), 81 deletions(-)
diff --git a/src/chrome/content/network-settings-overlay.xul b/src/chrome/content/network-settings-overlay.xul index b7141f0..29c3bb3 100644 --- a/src/chrome/content/network-settings-overlay.xul +++ b/src/chrome/content/network-settings-overlay.xul @@ -1,6 +1,6 @@ <?xml version="1.0"?> <!-- - - Copyright (c) 2013, The Tor Project, Inc. + - Copyright (c) 2014, The Tor Project, Inc. - See LICENSE for licensing information. - vim: set sw=2 sts=2 ts=8 et syntax=xml: --> @@ -74,24 +74,32 @@ </groupbox>
<groupbox id="bridgeSpecificSettings"> - <hbox> - <vbox flex="1"> + <hbox pack="end"> + <radiogroup id="bridgeTypeRadioGroup" flex="1" style="margin: 0px"> <hbox align="center"> - <button dlgtype="help" oncommand="onOpenHelp()" /> - <label value="&torsettings.useBridges.label;" control="bridgeList"/> + <radio id="bridgeRadioDefault" + label="&torsettings.useBridges.default;" selected="true" /> + <menulist id="defaultBridgeType" oncommand="onBridgeTypeChange()"> + <menupopup id="defaultBridgeType_menuPopup" /> + </menulist> + <spring/> </hbox> - <textbox id="bridgeList" multiline="true" rows="3" - placeholder="&torsettings.useBridges.placeholder;" /> - </vbox> -<!-- - <vbox> - <button id="copyBridgeList" label="Copy TODO" style="width: 20px" - oncommand="copyBridgeList();"/> - <button id="pasteBridgeList" label="Paste TODO" style="width: 20px" - oncommand="pasteBridgeList();"/> + <spacer style="height: 0.5em" /> + + <radio id="bridgeRadioCustom" label="&torsettings.useBridges.custom;" + oncommand="onCustomBridges()" /> + </radiogroup> + <vbox pack="start"> + <button dlgtype="help" oncommand="onOpenHelp()" /> </vbox> ---> </hbox> + <vbox id="bridgeCustomEntry"> + <label style="margin-top:0px;" + value="&torsettings.useBridges.label;" control="bridgeList"/> + <textbox id="bridgeList" multiline="true" rows="3" + oninput="onCustomBridgesTextInput();" + placeholder="&torsettings.useBridges.placeholder;" /> + </vbox> </groupbox>
<vbox id="bridgeHelpContent"> @@ -115,4 +123,3 @@ </vbox> </overlay>
- diff --git a/src/chrome/content/network-settings-wizard.xul b/src/chrome/content/network-settings-wizard.xul index b1c5c82..a1d820e 100644 --- a/src/chrome/content/network-settings-wizard.xul +++ b/src/chrome/content/network-settings-wizard.xul @@ -27,7 +27,7 @@ src="chrome://torlauncher/content/network-settings.js"/>
<wizardpage label=" " pageid="first" next="proxy" onextra2="onCopyLog();" - onpageshow="setTimeout(function() { showWizardNavButtons(false); }, 0);"> + onpageshow="setTimeout(function() { showWizardNavButtons(); }, 0);"> <hbox class="tbb-header"> <vbox class="tbb-logo-box" align="start"> <image class="tbb-logo" /> @@ -52,7 +52,7 @@ </wizardpage>
<wizardpage label=" " pageid="proxy" next="firewall" onextra2="onCopyLog();" - onpageshow="showWizardNavButtons(true);" + onpageshow="showWizardNavButtons();" onpageadvanced="return onWizardProxyNext(this);"> <vbox class="tbb-logo-box" align="start"> <image class="tbb-logo" /> @@ -113,14 +113,34 @@ </vbox> </wizardpage>
- <wizardpage label=" " pageid="bridges" onextra2="onCopyLog();" - onpageshow="showOrHideButton('finish', true, true)"> + <wizardpage pageid="bridges" onextra2="onCopyLog();" + onpageshow="onWizardUseBridgesRadioChange(this)"> + <vbox class="tbb-logo-box" align="start"> + <image class="tbb-logo" /> + </vbox> + <separator /> + <hbox> + <vbox flex="1"> + <label class="question">&torSettings.bridgeQuestion;</label> + <radiogroup id="useBridgesRadioGroup" + oncommand="onWizardUseBridgesRadioChange()"> + <radio id="bridgesRadioYes" label="&torSettings.yes;" /> + <radio id="bridgesRadioNo" label="&torSettings.no;" selected="true" /> + </radiogroup> + <description class="questionHelp">&torSettings.bridgeHelp; + </description> + </vbox> + </hbox> + </wizardpage> + + <wizardpage label=" " pageid="bridgeSettings" onextra2="onCopyLog();" + onpageshow="onWizardBridgeSettingsShow()"> <vbox class="tbb-logo-box" align="start"> <image class="tbb-logo" /> </vbox> <separator /> <vbox> - <label class="question">&torSettings.bridgeQuestion;</label> + <label class="question">&torSettings.bridgeSettingsPrompt;</label> <groupbox id="bridgeSpecificSettings" /> </vbox> </wizardpage> diff --git a/src/chrome/content/network-settings.js b/src/chrome/content/network-settings.js index 6446af2..de7259f 100644 --- a/src/chrome/content/network-settings.js +++ b/src/chrome/content/network-settings.js @@ -1,4 +1,4 @@ -// Copyright (c) 2013, The Tor Project, Inc. +// Copyright (c) 2014, The Tor Project, Inc. // See LICENSE for licensing information. // // vim: set sw=2 sts=2 ts=8 et syntax=javascript: @@ -15,6 +15,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherUtil", XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherLogger", "resource://torlauncher/modules/tl-logger.jsm");
+const kPrefDefaultBridgeRecommendedType = + "extensions.torlauncher.default_bridge_recommended_type"; +const kPrefDefaultBridgeType = "extensions.torlauncher.default_bridge_type";
const kSupportAddr = "help@rt.torproject.org";
@@ -26,6 +29,7 @@ const kTorLogHasWarnOrErrTopic = "TorLogHasWarnOrErr";
const kWizardProxyRadioGroup = "proxyRadioGroup"; const kWizardFirewallRadioGroup = "firewallRadioGroup"; +const kWizardUseBridgesRadioGroup = "useBridgesRadioGroup";
const kUseProxyCheckbox = "useProxy"; const kProxyTypeMenulist = "proxyType"; @@ -36,6 +40,8 @@ const kProxyPassword = "proxyPassword"; const kUseFirewallPortsCheckbox = "useFirewallPorts"; const kFirewallAllowedPorts = "firewallAllowedPorts"; const kUseBridgesCheckbox = "useBridges"; +const kDefaultBridgeTypeMenuList = "defaultBridgeType"; +const kCustomBridgesRadio = "bridgeRadioCustom"; const kBridgeList = "bridgeList";
const kTorConfKeyDisableNetwork = "DisableNetwork"; @@ -72,6 +78,11 @@ function initDialog()
var cancelBtn = document.documentElement.getButton("cancel"); gIsInitialBootstrap = window.arguments[0]; + + var startAtPanel; + if (window.arguments.length > 1) + startAtPanel = window.arguments[1]; + if (gIsInitialBootstrap) { if (cancelBtn) @@ -142,6 +153,8 @@ function initDialog() } }
+ initDefaultBridgeTypeMenu(); + gObsService.addObserver(gObserver, kTorBootstrapErrorTopic, false); gObsService.addObserver(gObserver, kTorLogHasWarnOrErrTopic, false); gObsService.addObserver(gObserver, kTorProcessExitedTopic, false); @@ -156,8 +169,12 @@ function initDialog() } else { - showPanel("settings"); readTorSettings(); + + if (startAtPanel) + advanceToWizardPanel(startAtPanel); + else + showPanel(); }
TorLauncherLogger.log(2, "initDialog done"); @@ -201,10 +218,55 @@ function onWizardFirewallNext(aWizPage) }
-function showWizardNavButtons(aShow) +function onWizardUseBridgesRadioChange(aWizPage) +{ + var wizard = getWizard(); + if (!aWizPage) + aWizPage = wizard.currentPage; + if (aWizPage) + { + var useBridges = getElemValue("bridgesRadioYes", false); + aWizPage.next = (useBridges) ? "bridgeSettings" : ""; + wizard.setAttribute("lastpage", !useBridges); + wizard._wizardButtons.onPageChange(); + } +} + + +function onWizardBridgeSettingsShow() +{ + var wizard = getWizard(); + wizard.setAttribute("lastpage", true); + wizard._wizardButtons.onPageChange(); + var btn = document.documentElement.getButton("finish"); + if (btn) + btn.focus(); +} + + +function onCustomBridgesTextInput() +{ + var customBridges = document.getElementById(kCustomBridgesRadio); + if (customBridges) + customBridges.control.selectedItem = customBridges; +} + + +function onCustomBridges() +{ + var bridgeList = document.getElementById(kBridgeList); + if (bridgeList) + bridgeList.focus(); +} + + +function showWizardNavButtons() { - showOrHideButton("back", aShow, false); - showOrHideButton("next", aShow, false); + var curPage = getWizard().currentPage; + var isFirstPage = ("first" == curPage.pageid); + + showOrHideButton("back", !isFirstPage, false); + showOrHideButton("next", !isFirstPage && curPage.next, false); }
@@ -222,7 +284,7 @@ var gObserver = { { gObsService.removeObserver(gObserver, kTorProcessReadyTopic); var haveWizard = (getWizard() != null); - showPanel(haveWizard ? "first" : "settings"); + showPanel(); if (haveWizard) { showOrHideButton("back", true, false); @@ -277,21 +339,47 @@ function readTorSettings() }
+// If aPanelID is undefined, the first panel is displayed. function showPanel(aPanelID) { + var wizard = getWizard(); + if (!aPanelID) + aPanelID = (wizard) ? "first" : "settings"; + var deckElem = document.getElementById("deck"); if (deckElem) { deckElem.selectedPanel = document.getElementById(aPanelID); showOrHideButton("extra2", (aPanelID != "bridgeHelp"), false); } - else - getWizard().goTo(aPanelID); + else if (wizard.currentPage.pageid != aPanelID) + wizard.goTo(aPanelID);
showOrHideButton("accept", (aPanelID == "settings"), true); }
+// This function assumes that you are starting on the first page. +function advanceToWizardPanel(aPanelID) +{ + var wizard = getWizard(); + if (!wizard) + return; + + onWizardConfigure(); // Equivalent to pressing "Configure" + + const kMaxTries = 10; + for (var count = 0; + ((count < kMaxTries) && + (wizard.currentPage.pageid != aPanelID) && + wizard.canAdvance); + ++count) + { + wizard.advance(); + } +} + + function showStartingTorPanel(aTorExited) { if (aTorExited) @@ -672,17 +760,50 @@ function initFirewallSettings() // Returns true if successful. function initBridgeSettings() { - var reply = gProtocolSvc.TorGetConfBool(kTorConfKeyUseBridges, false); - if (!gProtocolSvc.TorCommandSucceeded(reply)) - return false; + var typeList = TorLauncherUtil.defaultBridgeTypes; + var canUseDefaultBridges = (typeList && (typeList.length > 0)); + var defaultType = TorLauncherUtil.getCharPref(kPrefDefaultBridgeType); + var useDefault = canUseDefaultBridges && !!defaultType; + + // If not configured to use a default set of bridges, get UseBridges setting + // from tor. + var useBridges = useDefault; + if (!useDefault) + { + var reply = gProtocolSvc.TorGetConfBool(kTorConfKeyUseBridges, false); + if (!gProtocolSvc.TorCommandSucceeded(reply)) + return false;
- setElemValue(kUseBridgesCheckbox, reply.retVal); + useBridges = reply.retVal;
- var bridgeReply = gProtocolSvc.TorGetConf(kTorConfKeyBridgeList); - if (!gProtocolSvc.TorCommandSucceeded(bridgeReply)) - return false; + // Get bridge list from tor. + var bridgeReply = gProtocolSvc.TorGetConf(kTorConfKeyBridgeList); + if (!gProtocolSvc.TorCommandSucceeded(bridgeReply)) + return false;
- setBridgeListElemValue(bridgeReply.lineArray); + if (!setBridgeListElemValue(bridgeReply.lineArray)) + { + if (canUseDefaultBridges) + useDefault = true; // We have no custom values... back to default. + else + useBridges = false; // No custom or default bridges are available. + } + } + + setElemValue(kUseBridgesCheckbox, useBridges); + setYesNoRadioValue(kWizardUseBridgesRadioGroup, useBridges); + + if (!canUseDefaultBridges) + { + var radioGroup = document.getElementById("bridgeTypeRadioGroup"); + if (radioGroup) + radioGroup.setAttribute("hidden", true); + } + + var radioID = (useDefault) ? "bridgeRadioDefault" : "bridgeRadioCustom"; + var radio = document.getElementById(radioID); + if (radio) + radio.control.selectedItem = radio;
return true; } @@ -713,7 +834,7 @@ function applySettings() function useSettings() { var settings = {}; - settings[kTorConfKeyDisableNetwork] = "0"; + settings[kTorConfKeyDisableNetwork] = false; this.setConfAndReportErrors(settings, null);
gProtocolSvc.TorSendCommand("SAVECONF"); @@ -868,6 +989,42 @@ function getAndValidateFirewallSettings() }
+function initDefaultBridgeTypeMenu() +{ + var menu = document.getElementById(kDefaultBridgeTypeMenuList); + if (!menu) + return; + + menu.removeAllItems(); + + var typeArray = TorLauncherUtil.defaultBridgeTypes; + if (!typeArray || typeArray.length == 0) + return; + + var recommendedType = TorLauncherUtil.getCharPref( + kPrefDefaultBridgeRecommendedType, null); + var selectedType = TorLauncherUtil.getCharPref(kPrefDefaultBridgeType, null); + if (!selectedType) + selectedType = recommendedType; + + for (var i=0; i < typeArray.length; i++) + { + var bridgeType = typeArray[i]; + + var menuItemLabel = bridgeType; + if (bridgeType == recommendedType) + { + const key = "recommended_bridge"; + menuItemLabel += " " + TorLauncherUtil.getLocalizedString(key); + } + + var mi = menu.appendItem(menuItemLabel, bridgeType); + if (bridgeType == selectedType) + menu.selectedItem = mi; + } +} + + // Returns true if settings were successfully applied. function applyBridgeSettings() { @@ -875,7 +1032,7 @@ function applyBridgeSettings() if (!settings) return false;
- return this.setConfAndReportErrors(settings, "bridges"); + return this.setConfAndReportErrors(settings, "bridgeSettings"); }
@@ -886,18 +1043,43 @@ function getAndValidateBridgeSettings() settings[kTorConfKeyUseBridges] = null; settings[kTorConfKeyBridgeList] = null;
- var bridgeStr = getElemValue(kBridgeList, null); - var useBridges = (getWizard()) ? (bridgeStr && (0 != bridgeStr.length)) - : getElemValue(kUseBridgesCheckbox, false); + var useBridges = (getWizard()) ? getElemValue("bridgesRadioYes", false) + : getElemValue(kUseBridgesCheckbox, false);
- var bridgeList = parseAndValidateBridges(bridgeStr); - if (useBridges && !bridgeList) + var defaultBridgeType; + var bridgeList; + if (useBridges) { - reportValidationError("error_bridges_missing"); - return null; + var useCustom = getElemValue(kCustomBridgesRadio, false); + if (useCustom) + { + var bridgeStr = getElemValue(kBridgeList, null); + bridgeList = parseAndValidateBridges(bridgeStr); + if (!bridgeList) + { + reportValidationError("error_bridges_missing"); + return null; + } + + setBridgeListElemValue(bridgeList); + } + else + { + defaultBridgeType = getElemValue(kDefaultBridgeTypeMenuList, null); + if (!defaultBridgeType) + { + reportValidationError("error_default_bridges_type_missing"); + return null; + } + } }
- setBridgeListElemValue(bridgeList); + // Since it returns a filterd list of bridges, TorLauncherUtil.defaultBridges + // must be called after setting the kPrefDefaultBridgeType pref. + TorLauncherUtil.setCharPref(kPrefDefaultBridgeType, defaultBridgeType); + if (defaultBridgeType) + bridgeList = TorLauncherUtil.defaultBridges; + if (useBridges && bridgeList) { settings[kTorConfKeyUseBridges] = true; @@ -935,21 +1117,10 @@ function parseAndValidateBridges(aStr) // Returns true if successful. function setConfAndReportErrors(aSettingsObj, aShowOnErrorPanelID) { - var reply = gProtocolSvc.TorSetConf(aSettingsObj); - var didSucceed = gProtocolSvc.TorCommandSucceeded(reply); + var errObj = {}; + var didSucceed = gProtocolSvc.TorSetConfWithReply(aSettingsObj, errObj); if (!didSucceed) { - var details = ""; - if (reply && reply.lineArray) - { - for (var i = 0; i < reply.lineArray.length; ++i) - { - if (i > 0) - details += '\n'; - details += reply.lineArray[i]; - } - } - if (aShowOnErrorPanelID) { var wizardElem = getWizard(); @@ -967,7 +1138,7 @@ function setConfAndReportErrors(aSettingsObj, aShowOnErrorPanelID) } catch (e) {} }
- showSaveSettingsAlert(details); + showSaveSettingsAlert(errObj.details); }
return didSucceed; @@ -976,13 +1147,7 @@ function setConfAndReportErrors(aSettingsObj, aShowOnErrorPanelID)
function showSaveSettingsAlert(aDetails) { - if (!aDetails) - aDetails = TorLauncherUtil.getLocalizedString("ensure_tor_is_running"); - - var s = TorLauncherUtil.getFormattedLocalizedString( - "failed_to_save_settings", [aDetails], 1); - TorLauncherUtil.showAlert(window, s); - + TorLauncherUtil.showSaveSettingsAlert(window, aDetails); showOrHideButton("extra2", true, false); gWizIsCopyLogBtnShowing = true; } @@ -1020,6 +1185,7 @@ function setElemValue(aID, aValue) }
+// Returns true if one or more values were set. function setBridgeListElemValue(aBridgeArray) { // To be consistent with bridges.torproject.org, pre-pend "bridge" to @@ -1040,6 +1206,7 @@ function setBridgeListElemValue(aBridgeArray) }
setElemValue(kBridgeList, bridgeList); + return (bridgeList.length > 0); }
diff --git a/src/chrome/locale/en/network-settings.dtd b/src/chrome/locale/en/network-settings.dtd index 899a543..dca8d53 100644 --- a/src/chrome/locale/en/network-settings.dtd +++ b/src/chrome/locale/en/network-settings.dtd @@ -22,7 +22,9 @@ <!ENTITY torSettings.firewallQuestion "Does this computer's Internet connection go through a firewall that only allows connections to certain ports?"> <!ENTITY torSettings.firewallHelp "If you are not sure how to answer this question, choose No. If you encounter problems connecting to the Tor network, change this setting."> <!ENTITY torSettings.enterFirewall "Enter a comma-separated list of ports that are allowed by the firewall."> -<!ENTITY torSettings.bridgeQuestion "If this computer's Internet connection is censored, you will need to obtain and use bridge relays.  If not, just click Connect."> +<!ENTITY torSettings.bridgeQuestion "Does your Internet Service Provider (ISP) block or otherwise censor connections to the Tor Network?"> +<!ENTITY torSettings.bridgeHelp "If you are not sure how to answer this question, choose No.  If you choose Yes, you will be asked to configure Tor Bridges, which are unlisted relays that make it more difficult to block connections to the Tor Network."> +<!ENTITY torSettings.bridgeSettingsPrompt "You may use the default set of bridges or you may obtain and enter a custom set of bridges.">
<!-- Other: -->
@@ -44,13 +46,15 @@ <!ENTITY torsettings.firewall.checkbox "This computer goes through a firewall that only allows connections to certain ports"> <!ENTITY torsettings.firewall.allowedPorts "Allowed Ports:"> <!ENTITY torsettings.useBridges.checkbox "My Internet Service Provider (ISP) blocks connections to the Tor network"> +<!ENTITY torsettings.useBridges.default "Use Default Bridges of Type"> +<!ENTITY torsettings.useBridges.custom "Use Custom Bridges"> <!ENTITY torsettings.useBridges.label "Enter one or more bridge relays (one per line)."> -<!ENTITY torsettings.useBridges.placeholder "address:port OR transport address:port"> +<!ENTITY torsettings.useBridges.placeholder "type address:port">
<!ENTITY torsettings.copyLog "Copy Tor Log To Clipboard"> <!ENTITY torsettings.bridgeHelpTitle "Bridge Relay Help"> <!ENTITY torsettings.bridgeHelp1 "If you are unable to connect to the Tor network, it could be that your Internet Service Provider (ISP) or another agency is blocking Tor.  Often, you can work around this problem by using Tor Bridges, which are unlisted relays that are more difficult to block."> -<!ENTITY torsettings.bridgeHelp1B "Here are three ways to obtain bridge addresses:"> +<!ENTITY torsettings.bridgeHelp1B "You may use the preconfigured, default set of bridge addresses or you may obtain a custom set of addresses by using one of these three methods:"> <!ENTITY torsettings.bridgeHelp2Heading "Through the Web"> <!ENTITY torsettings.bridgeHelp2 "Use a web browser to visit https://bridges.torproject.org"> <!ENTITY torsettings.bridgeHelp3Heading "Through the Email Autoresponder"> diff --git a/src/chrome/locale/en/torlauncher.properties b/src/chrome/locale/en/torlauncher.properties index 3d5ee3f..38d314a 100644 --- a/src/chrome/locale/en/torlauncher.properties +++ b/src/chrome/locale/en/torlauncher.properties @@ -24,6 +24,10 @@ torlauncher.ensure_tor_is_running=Please ensure that Tor is running. torlauncher.error_proxy_addr_missing=You must specify both an IP address or hostname and a port number to configure Tor to use a proxy to access the Internet. torlauncher.error_proxy_type_missing=You must select the proxy type. torlauncher.error_bridges_missing=You must specify one or more bridges. +torlauncher.error_default_bridges_type_missing=You must select a transport type for the default bridges. +torlauncher.error_bridge_bad_default_type=No default bridges that have the transport type %S are available. Please adjust your settings. + +torlauncher.recommended_bridge=(recommended)
torlauncher.connect=Connect torlauncher.quit=Quit diff --git a/src/chrome/skin/network-settings.css b/src/chrome/skin/network-settings.css index 4f39e5a..d34adcf 100644 --- a/src/chrome/skin/network-settings.css +++ b/src/chrome/skin/network-settings.css @@ -7,7 +7,7 @@
dialog { width: 45em; - height: 36em; + height: 39em; font: -moz-dialog; }
@@ -29,7 +29,7 @@ wizard.os-windows {
.wizard-page-box { padding: 0px; - margin: 20px; + margin: 14px 20px 18px 20px; }
wizard .wizard-header { display: none; } @@ -92,6 +92,10 @@ button.firstAnswer { height: 70px; }
+#bridgeCustomEntry { + margin-left: 30px; +} + #startingTor { min-height: 300px; } diff --git a/src/components/tl-process.js b/src/components/tl-process.js index 48f0126..d927b06 100644 --- a/src/components/tl-process.js +++ b/src/components/tl-process.js @@ -35,6 +35,8 @@ TorProcessService.prototype = kTorLauncherExtPath: "tor-launcher@torproject.org", // This could vary.
kPrefPromptAtStartup: "extensions.torlauncher.prompt_at_startup", + kPrefDefaultBridgeType: "extensions.torlauncher.default_bridge_type", + kInitialControlConnDelayMS: 25, kMaxControlConnRetryMS: 500, kControlConnTimeoutMS: 30000, // Wait at most 30 seconds for tor to start. @@ -44,6 +46,10 @@ TorProcessService.prototype = kStatusRunning: 2, kStatusExited: 3, // Exited or failed to start.
+ kDefaultBridgesStatus_NotInUse: 0, + kDefaultBridgesStatus_InUse: 1, + kDefaultBridgesStatus_BadConfig: 2, + // nsISupports implementation. QueryInterface: function(aIID) { @@ -157,6 +163,14 @@ TorProcessService.prototype =
this.mProtocolSvc.TorRetrieveBootstrapStatus();
+ if (this._defaultBridgesStatus == this.kDefaultBridgesStatus_InUse) + { + // We configure default bridges each time we start tor in case + // new default bridge preference values are available (e.g., due + // to a TBB update). + this._configureDefaultBridges(); + } + this.mObsSvc.notifyObservers(null, "TorProcessIsReady", null); } else if ((Date.now() - this.mTorProcessStartTime) @@ -326,7 +340,23 @@ TorProcessService.prototype = args.push("" + pid); }
- if (TorLauncherUtil.shouldShowNetworkSettings) + // Start tor with networking disabled if first run or if the + // "Use Default Bridges of Type" option is turned on. Networking will + // be enabled after initial settings are chosen or after the default + // bridge settings have been configured. + var defaultBridgeType = + TorLauncherUtil.getCharPref(this.kPrefDefaultBridgeType); + var bridgeConfigIsBad = (this._defaultBridgesStatus == + this.kDefaultBridgesStatus_BadConfig); + if (bridgeConfigIsBad) + { + var key = "error_bridge_bad_default_type"; + var err = TorLauncherUtil.getFormattedLocalizedString(key, + [defaultBridgeType], 1); + TorLauncherUtil.showAlert(null, err); + } + + if (TorLauncherUtil.shouldShowNetworkSettings || defaultBridgeType) { args.push("DisableNetwork"); args.push("1"); @@ -361,10 +391,16 @@ TorProcessService.prototype = { this._monitorTorProcessStartup();
- if (TorLauncherUtil.shouldShowNetworkSettings) + var bridgeConfigIsBad = (this._defaultBridgesStatus == + this.kDefaultBridgesStatus_BadConfig); + if (TorLauncherUtil.shouldShowNetworkSettings || bridgeConfigIsBad) { if (this.mProtocolSvc) - this._openNetworkSettings(true); // Blocks until dialog is closed. + { + // Show network settings wizard. Blocks until dialog is closed. + var panelID = (bridgeConfigIsBad) ? "bridgeSettings" : undefined; + this._openNetworkSettings(true, panelID); + } } else { @@ -384,7 +420,7 @@ TorProcessService.prototype = var asSvc = Cc["@mozilla.org/toolkit/app-startup;1"] .getService(Ci.nsIAppStartup); var flags = asSvc.eAttemptQuit; - if (this.mRestartWithQuit) + if (this.mRestartWithQuit) flags |= asSvc.eRestart; asSvc.quit(flags); } @@ -454,8 +490,47 @@ TorProcessService.prototype = } }, // _processBootstrapStatus()
+ // Returns a kDefaultBridgesStatus value. + get _defaultBridgesStatus() + { + var defaultBridgeType = + TorLauncherUtil.getCharPref(this.kPrefDefaultBridgeType); + if (!defaultBridgeType) + return this.kDefaultBridgesStatus_NotInUse; + + var bridgeArray = TorLauncherUtil.defaultBridges; + if (!bridgeArray || (0 == bridgeArray.length)) + return this.kDefaultBridgesStatus_BadConfig; + + return this.kDefaultBridgesStatus_InUse; + }, + + _configureDefaultBridges: function() + { + var settings = {}; + var bridgeArray = TorLauncherUtil.defaultBridges; + var useBridges = (bridgeArray && (bridgeArray.length > 0)); + settings["UseBridges"] = useBridges; + settings["Bridge"] = bridgeArray; + var errObj = {}; + var didSucceed = this.mProtocolSvc.TorSetConfWithReply(settings, errObj); + + settings = {}; + settings["DisableNetwork"] = false; + if (!this.mProtocolSvc.TorSetConfWithReply(settings, + (didSucceed) ? errObj : null)) + { + didSucceed = false; + } + + if (didSucceed) + this.mProtocolSvc.TorSendCommand("SAVECONF"); + else + TorLauncherUtil.showSaveSettingsAlert(null, errObj.details); + }, + // Blocks until network settings dialog is closed. - _openNetworkSettings: function(aIsInitialBootstrap) + _openNetworkSettings: function(aIsInitialBootstrap, aStartAtWizardPanel) { const kSettingsURL = "chrome://torlauncher/content/network-settings.xul"; const kWizardURL = "chrome://torlauncher/content/network-settings-wizard.xul"; @@ -463,7 +538,8 @@ TorProcessService.prototype = var wwSvc = Cc["@mozilla.org/embedcomp/window-watcher;1"] .getService(Ci.nsIWindowWatcher); var winFeatures = "chrome,dialog=yes,modal,all"; - var argsArray = this._createOpenWindowArgsArray(aIsInitialBootstrap); + var argsArray = this._createOpenWindowArgsArray(aIsInitialBootstrap, + aStartAtWizardPanel); var url = (aIsInitialBootstrap) ? kWizardURL : kSettingsURL; wwSvc.openWindow(null, url, "_blank", winFeatures, argsArray); }, @@ -478,14 +554,23 @@ TorProcessService.prototype = wwSvc.openWindow(null, chromeURL, "_blank", winFeatures, argsArray); },
- _createOpenWindowArgsArray: function(aBool) + _createOpenWindowArgsArray: function(aArg1, aArg2) { var argsArray = Cc["@mozilla.org/array;1"] .createInstance(Ci.nsIMutableArray); var variant = Cc["@mozilla.org/variant;1"] .createInstance(Ci.nsIWritableVariant); - variant.setFromVariant(aBool); + variant.setFromVariant(aArg1); argsArray.appendElement(variant, false); + + if (aArg2) + { + variant = Cc["@mozilla.org/variant;1"] + .createInstance(Ci.nsIWritableVariant); + variant.setFromVariant(aArg2); + argsArray.appendElement(variant, false); + } + return argsArray; },
diff --git a/src/components/tl-protocol.js b/src/components/tl-protocol.js index e73f2e9..bcff6ab 100644 --- a/src/components/tl-protocol.js +++ b/src/components/tl-protocol.js @@ -249,6 +249,32 @@ TorProtocolService.prototype = return this.TorSendCommand("SETCONF", cmdArgs); }, // TorSetConf()
+ // Returns true if successful. + // Upon failure, aErrorObj.details will be set to a string. + TorSetConfWithReply: function(aSettingsObj, aErrorObj) + { + var reply = this.TorSetConf(aSettingsObj); + var didSucceed = this.TorCommandSucceeded(reply); + if (!didSucceed) + { + var details = ""; + if (reply && reply.lineArray) + { + for (var i = 0; i < reply.lineArray.length; ++i) + { + if (i > 0) + details += '\n'; + details += reply.lineArray[i]; + } + } + + if (aErrorObj) + aErrorObj.details = details; + } + + return didSucceed; + }, + // If successful, sends a "TorBootstrapStatus" notification. TorRetrieveBootstrapStatus: function() { diff --git a/src/defaults/preferences/prefs.js b/src/defaults/preferences/prefs.js index 2a4799d..34bf1a7 100644 --- a/src/defaults/preferences/prefs.js +++ b/src/defaults/preferences/prefs.js @@ -12,3 +12,10 @@ pref("extensions.torlauncher.prompt_at_startup", true); pref("extensions.torlauncher.tor_path", ""); pref("extensions.torlauncher.torrc_path", ""); pref("extensions.torlauncher.tordatadir_path", ""); + +// Recommended default bridge type (can be set per localized bundle). +// pref("extensions.torlauncher.default_bridge_recommended_type", "obfs3"); + +// Default bridges. +// pref("extensions.torlauncher.default_bridge.TYPE.1", "TYPE x.x.x.x:yy"); +// pref("extensions.torlauncher.default_bridge.TYPE.2", "TYPE x.x.x.x:yy"); diff --git a/src/modules/tl-util.jsm b/src/modules/tl-util.jsm index 0c31aa5..1754545 100644 --- a/src/modules/tl-util.jsm +++ b/src/modules/tl-util.jsm @@ -61,6 +61,16 @@ let TorLauncherUtil = // Public } },
+ showSaveSettingsAlert: function(aParentWindow, aDetails) + { + if (!aDetails) + aDetails = TorLauncherUtil.getLocalizedString("ensure_tor_is_running"); + + var s = TorLauncherUtil.getFormattedLocalizedString( + "failed_to_save_settings", [aDetails], 1); + this.showAlert(aParentWindow, s); + }, + // Localized Strings
// "torlauncher." is prepended to aStringName. @@ -181,6 +191,14 @@ let TorLauncherUtil = // Public return rv; },
+ setCharPref: function(aPrefName, aVal) + { + try + { + TLUtilInternal.mPrefsSvc.setCharPref(aPrefName, aVal ? aVal : ""); + } catch (e) {} + }, + get shouldStartAndOwnTor() { const kPrefStartTor = "extensions.torlauncher.start_tor"; @@ -228,6 +246,64 @@ let TorLauncherUtil = // Public
return this.getBoolPref(kPrefOnlyConfigureTor, false); }, + + // Returns an array of strings or undefined if none are available. + get defaultBridgeTypes() + { + try + { + var prefBranch = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .getBranch("extensions.torlauncher.default_bridge."); + var childPrefs = prefBranch.getChildList("", []); + var typeArray = []; + for (var i = 0; i < childPrefs.length; ++i) + { + var s = childPrefs[i].replace(/..*$/, ""); + if (-1 == typeArray.lastIndexOf(s)) + typeArray.push(s); + } + + return typeArray.sort(); + } catch(e) {}; + + return undefined; + }, + + // Returns an array of strings or undefined if none are available. + // The list is filtered by the default_bridge_type pref value. + get defaultBridges() + { + const kPrefName = "extensions.torlauncher.default_bridge_type"; + var filterType = this.getCharPref(kPrefName); + if (!filterType) + return undefined; + + try + { + var prefBranch = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefService) + .getBranch("extensions.torlauncher.default_bridge."); + var childPrefs = prefBranch.getChildList("", []); + var bridgeArray = []; + // The pref service seems to return the values in reverse order, so + // we compensate by traversing in reverse order. + for (var i = childPrefs.length - 1; i >= 0; --i) + { + var bridgeType = childPrefs[i].replace(/..*$/, ""); + if (bridgeType == filterType) + { + var s = prefBranch.getCharPref(childPrefs[i]); + if (s) + bridgeArray.push(s); + } + } + + return bridgeArray; + } catch(e) {}; + + return undefined; + }, };