[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [tor-launcher/master] Bug #10418: Provide "Use Default Bridges" option.



commit f3804aad9dd71fd4eefe33cf46ff2bab0355da4a
Author: Kathy Brade <brade@xxxxxxxxxxxxxxxxx>
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@xxxxxxxxxxxxxxxxx";
 
@@ -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.&#160; 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.&#160; 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.&#160; 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@xxxxxxxxxxxxxx", // 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;
+  },
 };
 
 



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits