[tbb-commits] [tor-launcher/master] Bug 14272: Make Tor Launcher work with Unix Domain Socket option

gk at torproject.org gk at torproject.org
Thu Sep 1 07:57:22 UTC 2016


commit 8871259c966755233b134a5ddb2b4926539d25c6
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Wed Aug 31 15:26:08 2016 -0400

    Bug 14272: Make Tor Launcher work with Unix Domain Socket option
    
    On non-Windows platforms, use a Unix domain socket by default for the
    Tor control port. New preferences:
      extensions.torlauncher.control_port_use_socket (Boolean)
      extensions.torlauncher.control_socket_path (path string)
    New environment variable: TOR_CONTROL_SOCKET
    
    When starting tor, always include a ControlPort argument so that
    users can use environment variables to switch between the two control
    port types.
    
    Add a public TorGetControlSocketFile() function that Torbutton will
    use to retrieve the socket file (an nsIFile) when a Unix domain
    socket is in use.
    
    Moved the getTorFile() function to our utilities module.
---
 src/components/tl-process.js      | 253 +++----------------------------------
 src/components/tl-protocol.js     | 100 ++++++++++++---
 src/defaults/preferences/prefs.js |  14 ++-
 src/modules/tl-util.jsm           | 257 +++++++++++++++++++++++++++++++++++++-
 4 files changed, 370 insertions(+), 254 deletions(-)

diff --git a/src/components/tl-process.js b/src/components/tl-process.js
index 8e42feb..c1bbdda 100644
--- a/src/components/tl-process.js
+++ b/src/components/tl-process.js
@@ -31,8 +31,6 @@ TorProcessService.prototype =
 {
   kContractID : "@torproject.org/torlauncher-process-service;1",
   kServiceName : "Tor Launcher Process Service",
-  kThunderbirdID: "{3550f703-e582-4d05-9a08-453d09bdfdc6}",
-  kInstantbirdID: "{33cb9019-c295-46dd-be21-8c4936574bee}",
   kClassID: Components.ID("{FE7B4CAF-BCF4-4848-8BFF-EFA66C9AFDA1}"),
   kTorLauncherExtPath: "tor-launcher at torproject.org", // This could vary.
 
@@ -308,11 +306,6 @@ TorProcessService.prototype =
   mProtocolSvc: null,
   mTorProcess: null,    // nsIProcess
   mTorProcessStartTime: null, // JS Date.now()
-  // mIsUserDataOutsideOfAppDir is true when TorBrowser-Data is used.
-  // (cached; access via this._isUserDataOutsideOfAppDir)
-  mIsUserDataOutsideOfAppDir: undefined,
-  mAppDir: null,        // nsIFile (cached; access via this._appDir)
-  mDataDir: null,       // nsIFile (cached; access via this._dataDir)
   mControlConnTimer: null,
   mControlConnDelayMS: 0,
   mQuitSoon: false,     // Quit was requested by the user; do so soon.
@@ -333,11 +326,14 @@ TorProcessService.prototype =
 
       // Get the Tor data directory first so it is created before we try to
       // construct paths to files that will be inside it.
-      var dataDir = this._getTorFile("tordatadir", true);
-      var exeFile = this._getTorFile("tor", false);
-      var torrcFile = this._getTorFile("torrc", true);
-      var torrcDefaultsFile = this._getTorFile("torrc-defaults", false);
+      var dataDir = TorLauncherUtil.getTorFile("tordatadir", true);
+      var exeFile = TorLauncherUtil.getTorFile("tor", false);
+      var torrcFile = TorLauncherUtil.getTorFile("torrc", true);
+      var torrcDefaultsFile =
+                    TorLauncherUtil.getTorFile("torrc-defaults", false);
       var hashedPassword = this.mProtocolSvc.TorGetPassword(true);
+      var controlSocketFile = this.mProtocolSvc.TorGetControlSocketFile();
+      var controlPort = this.mProtocolSvc.TorGetControlPort();
 
       var detailsKey;
       if (!exeFile)
@@ -359,7 +355,6 @@ TorProcessService.prototype =
         return;
       }
 
-
       // The geoip and geoip6 files are in the same directory as torrc-defaults.
       var geoipFile = torrcDefaultsFile.clone();
       geoipFile.leafName = "geoip";
@@ -384,6 +379,19 @@ TorProcessService.prototype =
       args.push("HashedControlPassword");
       args.push(hashedPassword);
 
+      // Include a ControlPort argument to support switching between
+      // a TCP port and a Unix domain socket.
+      let controlPortArg;
+      if (controlSocketFile)
+        controlPortArg = "unix:" + controlSocketFile.path;
+      else if (controlPort)
+        controlPortArg = "" + controlPort;
+      if (controlPortArg)
+      {
+        args.push("ControlPort");
+        args.push(controlPortArg);
+      }
+
       var pid = this._getpid();
       if (0 != pid)
       {
@@ -692,227 +700,6 @@ TorProcessService.prototype =
     return argsArray;
   },
 
-  // Returns an nsIFile.
-  // If aCreate is true and the file doesn't exist, it is created.
-  _getTorFile: function(aTorFileType, aCreate)
-  {
-    if (!aTorFileType)
-      return null;
-
-    let isRelativePath = true;
-    let isUserData = (aTorFileType != "tor") &&
-                     (aTorFileType != "torrc-defaults");
-    let prefName = "extensions.torlauncher." + aTorFileType + "_path";
-    let path = TorLauncherUtil.getCharPref(prefName);
-    if (path)
-    {
-      let re = (TorLauncherUtil.isWindows) ?  /^[A-Za-z]:\\/ : /^\//;
-      isRelativePath = !re.test(path);
-    }
-    else
-    {
-      // Get default path.
-      if (this._isUserDataOutsideOfAppDir)
-      {
-        // This block is used for the TorBrowser-Data/ case.
-        if (TorLauncherUtil.isWindows)
-        {
-          if ("tor" == aTorFileType)
-            path = "TorBrowser\\Tor\\tor.exe";
-          else if ("torrc-defaults" == aTorFileType)
-            path = "TorBrowser\\Tor\\torrc-defaults";
-          else if ("torrc" == aTorFileType)
-            path = "Tor\\torrc";
-          else if ("tordatadir" == aTorFileType)
-            path = "Tor";
-        }
-        else if (TorLauncherUtil.isMac)
-        {
-          if ("tor" == aTorFileType)
-            path = "Contents/Resources/TorBrowser/Tor/tor";
-          else if ("torrc-defaults" == aTorFileType)
-            path = "Contents/Resources/TorBrowser/Tor/torrc-defaults";
-          else if ("torrc" == aTorFileType)
-            path = "Tor/torrc";
-          else if ("tordatadir" == aTorFileType)
-            path = "Tor";
-        }
-        else // Linux and others.
-        {
-          if ("tor" == aTorFileType)
-            path = "TorBrowser/Tor/tor";
-          else if ("torrc-defaults" == aTorFileType)
-            path = "TorBrowser/Tor/torrc-defaults";
-          else if ("torrc" == aTorFileType)
-            path = "Tor/torrc";
-          else if ("tordatadir" == aTorFileType)
-            path = "Tor";
-        }
-      }
-      else if (TorLauncherUtil.isWindows)
-      {
-        // This block is used for the non-TorBrowser-Data/ case.
-        if ("tor" == aTorFileType)
-          path = "Tor\\tor.exe";
-        else if ("torrc-defaults" == aTorFileType)
-          path = "Data\\Tor\\torrc-defaults";
-        else if ("torrc" == aTorFileType)
-          path = "Data\\Tor\\torrc";
-        else if ("tordatadir" == aTorFileType)
-          path = "Data\\Tor";
-      }
-      else // Linux, Mac OS and others.
-      {
-        // This block is also used for the non-TorBrowser-Data/ case.
-        if ("tor" == aTorFileType)
-          path = "Tor/tor";
-        else if ("torrc-defaults" == aTorFileType)
-          path = "Data/Tor/torrc-defaults";
-        else if ("torrc" == aTorFileType)
-          path = "Data/Tor/torrc";
-        else if ("tordatadir" == aTorFileType)
-          path = "Data/Tor";
-      }
-    }
-
-    if (!path)
-      return null;
-
-    try
-    {
-      let f;
-      if (isRelativePath)
-      {
-        // Turn 'path' into an absolute path.
-        if (this._isUserDataOutsideOfAppDir)
-        {
-          let baseDir = isUserData ? this._dataDir : this._appDir;
-          f = baseDir.clone();
-        }
-        else
-        {
-          f = this._appDir.clone();
-          f.append("TorBrowser");
-        }
-        f.appendRelativePath(path);
-      }
-      else
-      {
-        f = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
-        f.initWithPath(path);
-      }
-
-      if (!f.exists() && aCreate)
-      {
-        try
-        {
-          if ("tordatadir" == aTorFileType)
-            f.create(f.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
-          else
-            f.create(f.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
-        }
-        catch (e)
-        {
-          TorLauncherLogger.safelog(4, "unable to create " + f.path + ": ", e);
-          return null;
-        }
-      }
-
-      if (f.exists())
-      {
-        try { f.normalize(); } catch(e) {}
-
-        return f;
-      }
-
-      TorLauncherLogger.log(4, aTorFileType + " file not found: " + f.path);
-    }
-    catch(e)
-    {
-      TorLauncherLogger.safelog(4, "_getTorFile " + aTorFileType +
-                                     " failed for " + path + ": ", e);
-    }
-
-    return null;  // File not found or error (logged above).
-  }, // _getTorFile()
-
-  get _isUserDataOutsideOfAppDir()
-  {
-    if (this.mIsUserDataOutsideOfAppDir == undefined)
-    {
-      // Determine if we are using a "side-by-side" data model by checking
-      // whether the user profile is outside of the app directory.
-      try
-      {
-        let ds = Cc["@mozilla.org/file/directory_service;1"]
-                        .getService(Ci.nsIProperties);
-        let profDir = ds.get("ProfD", Ci.nsIFile);
-        this.mIsUserDataOutsideOfAppDir = !this._appDir.contains(profDir);
-      }
-      catch (e)
-      {
-        this.mIsUserDataOutsideOfAppDir = false;
-      }
-    }
-
-    return this.mIsUserDataOutsideOfAppDir;
-  }, // get _isUserDataOutsideOfAppDir
-
-  // Returns an nsIFile that points to the application directory.
-  // May throw.
-  get _appDir()
-  {
-    if (!this.mAppDir)
-    {
-      let topDir = Cc["@mozilla.org/file/directory_service;1"]
-                    .getService(Ci.nsIProperties).get("CurProcD", Ci.nsIFile);
-      let appInfo = Cc["@mozilla.org/xre/app-info;1"]
-                      .getService(Ci.nsIXULAppInfo);
-      // On Linux and Windows, we want to return the Browser/ directory.
-      // Because topDir ("CurProcD") points to Browser/browser on those
-      // platforms, we need to go up one level.
-      // On Mac OS, we want to return the TorBrowser.app/ directory.
-      // Because topDir points to Contents/Resources/browser on Mac OS,
-      // we need to go up 3 levels.
-      let tbbBrowserDepth = (TorLauncherUtil.isMac) ? 3 : 1;
-      if ((appInfo.ID == this.kThunderbirdID) ||
-          (appInfo.ID == this.kInstantbirdID))
-      {
-        // On Thunderbird/Instantbird, the topDir is the root dir and not
-        // browser/, so we need to iterate one level less than Firefox.
-        --tbbBrowserDepth;
-      }
-
-      while (tbbBrowserDepth > 0)
-      {
-        let didRemove = (topDir.leafName != ".");
-        topDir = topDir.parent;
-        if (didRemove)
-          tbbBrowserDepth--;
-      }
-
-      this.mAppDir = topDir;
-    }
-
-    return this.mAppDir;
-  }, // get _appDir
-
-  // Returns an nsIFile that points to the TorBrowser-Data/ directory.
-  // This function is only used when this._isUserDataOutsideOfAppDir == true.
-  // May throw.
-  get _dataDir()
-  {
-    if (!this.mDataDir)
-    {
-      let ds = Cc["@mozilla.org/file/directory_service;1"]
-                      .getService(Ci.nsIProperties);
-      let profDir = ds.get("ProfD", Ci.nsIFile);
-      this.mDataDir = profDir.parent.parent;
-    }
-
-    return this.mDataDir;
-  }, // get _dataDir
-
   _getpid: function()
   {
     // Use nsIXULRuntime.processID if it is available.
diff --git a/src/components/tl-protocol.js b/src/components/tl-protocol.js
index fdc2394..d394b53 100644
--- a/src/components/tl-protocol.js
+++ b/src/components/tl-protocol.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2015, The Tor Project, Inc.
+// Copyright (c) 2016, The Tor Project, Inc.
 // See LICENSE for licensing information.
 // TODO: Some code came from torbutton.js (pull in copyright and license?)
 //
@@ -17,6 +17,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherUtil",
                           "resource://torlauncher/modules/tl-util.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherLogger",
                           "resource://torlauncher/modules/tl-logger.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
 
 
 function TorProtocolService()
@@ -31,23 +33,44 @@ function TorProtocolService()
 
   try
   {
-    var env = Cc["@mozilla.org/process/environment;1"]
+    let isWindows = TorLauncherUtil.isWindows;
+    let env = Cc["@mozilla.org/process/environment;1"]
                 .getService(Ci.nsIEnvironment);
-
-    if (env.exists("TOR_CONTROL_HOST"))
-      this.mControlHost = env.get("TOR_CONTROL_HOST");
-    else
+    // Determine how Tor Launcher will connect to the Tor control port.
+    // Environment variables get top priority followed by preferences.
+    if (!isWindows && env.exists("TOR_CONTROL_SOCKET"))
     {
-      this.mControlHost = TorLauncherUtil.getCharPref(
-                        "extensions.torlauncher.control_host", "127.0.0.1");
+      let socketPath = env.get("TOR_CONTROL_SOCKET");
+      this.mControlSocketFile = new FileUtils.File(socketPath);
     }
-
-    if (env.exists("TOR_CONTROL_PORT"))
-      this.mControlPort = parseInt(env.get("TOR_CONTROL_PORT"), 10);
     else
     {
-      this.mControlPort = TorLauncherUtil.getIntPref(
+      // Check for TCP host and port environment variables.
+      if (env.exists("TOR_CONTROL_HOST"))
+        this.mControlHost = env.get("TOR_CONTROL_HOST");
+      if (env.exists("TOR_CONTROL_PORT"))
+        this.mControlPort = parseInt(env.get("TOR_CONTROL_PORT"), 10);
+
+      let useSocket = !isWindows && TorLauncherUtil.getBoolPref(
+                      "extensions.torlauncher.control_port_use_socket", true);
+      if (!this.mControlHost && !this.mControlPort && useSocket)
+      {
+        this.mControlSocketFile = TorLauncherUtil.getTorFile("control_socket",
+                                                             false);
+      }
+      else
+      {
+        if (!this.mControlHost)
+        {
+          this.mControlHost = TorLauncherUtil.getCharPref(
+                        "extensions.torlauncher.control_host", "127.0.0.1");
+        }
+        if (!this.mControlPort)
+        {
+          this.mControlPort = TorLauncherUtil.getIntPref(
                                "extensions.torlauncher.control_port", 9151);
+        }
+      }
     }
 
     // Populate mControlPassword so it is available when starting tor.
@@ -141,6 +164,19 @@ TorProtocolService.prototype =
   kCmdStatusOK: 250,
   kCmdStatusEventNotification: 650,
 
+  TorGetControlSocketFile: function()
+  {
+    if (!this.mControlSocketFile)
+      return undefined;
+
+    return this.mControlSocketFile.clone();
+  },
+
+  TorGetControlPort: function()
+  {
+    return this.mControlPort;
+  },
+
   // Returns Tor password string or null if an error occurs.
   TorGetPassword: function(aPleaseHash)
   {
@@ -512,6 +548,7 @@ TorProtocolService.prototype =
   mConsoleSvc: null,
   mControlPort: null,
   mControlHost: null,
+  mControlSocketFile: null,   // An nsIFile if using a UNIX domain socket.
   mControlPassword: null,     // JS string that contains hex-encoded password.
   mControlConnection: null,   // This is cached and reused.
   mEventMonitorConnection: null,
@@ -560,12 +597,43 @@ TorProtocolService.prototype =
     var conn;
     try
     {
-      var sts = Cc["@mozilla.org/network/socket-transport-service;1"]
+      let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
                   .getService(Ci.nsISocketTransportService);
-      TorLauncherLogger.log(2, "Opening control connection to " +
+      let socket;
+      if (this.mControlSocketFile)
+      {
+        let exists = this.mControlSocketFile.exists();
+        if (!exists)
+        {
+          TorLauncherLogger.log(5, "Control port socket does not exist: " +
+                                    this.mControlSocketFile.path);
+        }
+        else
+        {
+          let isSpecial = this.mControlSocketFile.isSpecial();
+          if (!isSpecial)
+          {
+            TorLauncherLogger.log(5, "Control port socket is not a socket: " +
+                                  this.mControlSocketFile.path);
+          }
+          else
+          {
+            TorLauncherLogger.log(2, "Opening control connection to socket " +
+                                  this.mControlSocketFile.path);
+            socket = sts.createUnixDomainTransport(this.mControlSocketFile);
+          }
+        }
+      }
+      else
+      {
+        TorLauncherLogger.log(2, "Opening control connection to " +
                                  this.mControlHost + ":" + this.mControlPort);
-      var socket = sts.createTransport(null, 0, this.mControlHost,
-                                       this.mControlPort, null);
+        socket = sts.createTransport(null, 0, this.mControlHost,
+                                     this.mControlPort, null);
+      }
+
+      if (!socket)
+        return null;
 
       // Our event monitor connection is non-blocking and unbuffered (an
       // asyncWait() call is used so we only read data when we know that
diff --git a/src/defaults/preferences/prefs.js b/src/defaults/preferences/prefs.js
index 2c0a95e..500fe2f 100644
--- a/src/defaults/preferences/prefs.js
+++ b/src/defaults/preferences/prefs.js
@@ -2,16 +2,24 @@
 pref("intl.locale.matchOS", true);
 pref("extensions.torlauncher.prompt_for_locale", true);
 
+pref("extensions.torlauncher.start_tor", true);
+pref("extensions.torlauncher.prompt_at_startup", true);
+
 pref("extensions.torlauncher.loglevel", 4);  // 1=verbose, 2=debug, 3=info, 4=note, 5=warn
 pref("extensions.torlauncher.logmethod", 1);  // 0=stdout, 1=errorconsole, 2=debuglog
 pref("extensions.torlauncher.max_tor_log_entries", 1000);
 
+// By default, a Unix domain socket at a default location is used for
+// the Tor control port.
+// Change control_port_use_socket to false to use a TCP connection
+// instead, as defined by control_host and control_port.
+// Modify control_socket_path to override the default socket location. If a
+// relative path is used, it is handled like torrc_path (see below).
+pref("extensions.torlauncher.control_port_use_socket", true);
+pref("extensions.torlauncher.control_socket_path", "");
 pref("extensions.torlauncher.control_host", "127.0.0.1");
 pref("extensions.torlauncher.control_port", 9151);
 
-pref("extensions.torlauncher.start_tor", true);
-pref("extensions.torlauncher.prompt_at_startup", true);
-
 // The tor_path is relative to the application directory. On Linux and
 // Windows this is the Browser/ directory that contains the firefox
 // executables, and on Mac OS it is the TorBrowser.app directory.
diff --git a/src/modules/tl-util.jsm b/src/modules/tl-util.jsm
index 7f93f39..992ef5b 100644
--- a/src/modules/tl-util.jsm
+++ b/src/modules/tl-util.jsm
@@ -1,4 +1,4 @@
-// Copyright (c) 2015, The Tor Project, Inc.
+// Copyright (c) 2016, The Tor Project, Inc.
 // See LICENSE for licensing information.
 //
 // vim: set sw=2 sts=2 ts=8 et syntax=javascript:
@@ -11,14 +11,19 @@ let EXPORTED_SYMBOLS = [ "TorLauncherUtil" ];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cu = Components.utils;
 const kPropBundleURI = "chrome://torlauncher/locale/torlauncher.properties";
 const kPropNamePrefix = "torlauncher.";
 
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherLogger",
+                          "resource://torlauncher/modules/tl-logger.jsm");
+
 let TorLauncherUtil =  // Public
 {
   get isMac()
   {
-    return ("Darwin" == TLUtilInternal._OS);
+    return TLUtilInternal._isMac;
   },
 
   get isWindows()
@@ -381,6 +386,163 @@ let TorLauncherUtil =  // Public
 
     return undefined;
   },
+
+  // Returns an nsIFile.
+  // If aTorFileType is "control_socket", aCreate is ignored and there is
+  // no requirement that the socket exist.
+  // For all other file types, null is returned if the file does not exist
+  // and it cannot be created (it will be created if aCreate is true).
+  getTorFile: function(aTorFileType, aCreate)
+  {
+    if (!aTorFileType)
+      return null;
+
+    let isRelativePath = true;
+    let isUserData = (aTorFileType != "tor") &&
+                     (aTorFileType != "torrc-defaults");
+    let isControlSocket = ("control_socket" == aTorFileType);
+    let prefName = "extensions.torlauncher." + aTorFileType + "_path";
+    let path = this.getCharPref(prefName);
+    if (path)
+    {
+      let re = (this.isWindows) ?  /^[A-Za-z]:\\/ : /^\//;
+      isRelativePath = !re.test(path);
+    }
+    else
+    {
+      // Get default path.
+      if (TLUtilInternal._isUserDataOutsideOfAppDir)
+      {
+        // This block is used for the TorBrowser-Data/ case.
+        if (this.isWindows)
+        {
+          if ("tor" == aTorFileType)
+            path = "TorBrowser\\Tor\\tor.exe";
+          else if ("torrc-defaults" == aTorFileType)
+            path = "TorBrowser\\Tor\\torrc-defaults";
+          else if ("torrc" == aTorFileType)
+            path = "Tor\\torrc";
+          else if ("tordatadir" == aTorFileType)
+            path = "Tor";
+        }
+        else if (this.isMac)
+        {
+          if ("tor" == aTorFileType)
+            path = "Contents/Resources/TorBrowser/Tor/tor";
+          else if ("torrc-defaults" == aTorFileType)
+            path = "Contents/Resources/TorBrowser/Tor/torrc-defaults";
+          else if ("torrc" == aTorFileType)
+            path = "Tor/torrc";
+          else if ("tordatadir" == aTorFileType)
+            path = "Tor";
+          else if (isControlSocket)
+            path = "Tor/control.socket";
+        }
+        else // Linux and others.
+        {
+          if ("tor" == aTorFileType)
+            path = "TorBrowser/Tor/tor";
+          else if ("torrc-defaults" == aTorFileType)
+            path = "TorBrowser/Tor/torrc-defaults";
+          else if ("torrc" == aTorFileType)
+            path = "Tor/torrc";
+          else if ("tordatadir" == aTorFileType)
+            path = "Tor";
+          else if (isControlSocket)
+            path = "Tor/control.socket";
+        }
+      }
+      else if (this.isWindows)
+      {
+        // This block is used for the non-TorBrowser-Data/ case.
+        if ("tor" == aTorFileType)
+          path = "Tor\\tor.exe";
+        else if ("torrc-defaults" == aTorFileType)
+          path = "Data\\Tor\\torrc-defaults";
+        else if ("torrc" == aTorFileType)
+          path = "Data\\Tor\\torrc";
+        else if ("tordatadir" == aTorFileType)
+          path = "Data\\Tor";
+      }
+      else // Linux, Mac OS and others.
+      {
+        // This block is also used for the non-TorBrowser-Data/ case.
+        if ("tor" == aTorFileType)
+          path = "Tor/tor";
+        else if ("torrc-defaults" == aTorFileType)
+          path = "Data/Tor/torrc-defaults";
+        else if ("torrc" == aTorFileType)
+          path = "Data/Tor/torrc";
+        else if ("tordatadir" == aTorFileType)
+          path = "Data/Tor";
+        else if (isControlSocket)
+          path = "Data/Tor/control.socket";
+      }
+    }
+
+    if (!path)
+      return null;
+
+    try
+    {
+      let f;
+      if (isRelativePath)
+      {
+        // Turn 'path' into an absolute path.
+        if (TLUtilInternal._isUserDataOutsideOfAppDir)
+        {
+          let baseDir = isUserData ? TLUtilInternal._dataDir
+                                   : TLUtilInternal._appDir;
+          f = baseDir.clone();
+        }
+        else
+        {
+          f = TLUtilInternal._appDir.clone();
+          f.append("TorBrowser");
+        }
+        f.appendRelativePath(path);
+      }
+      else
+      {
+        f = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
+        f.initWithPath(path);
+      }
+
+      if (!f.exists() && !isControlSocket && aCreate)
+      {
+        try
+        {
+          if ("tordatadir" == aTorFileType)
+            f.create(f.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+          else
+            f.create(f.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+        }
+        catch (e)
+        {
+          TorLauncherLogger.safelog(4, "unable to create " + f.path + ": ", e);
+          return null;
+        }
+      }
+
+      // If the file exists or the control socket was requested, normalize
+      // the path and return a file object. The control socket will be
+      // created by tor.
+      if (f.exists() || isControlSocket)
+      {
+        try { f.normalize(); } catch(e) {}
+        return f;
+      }
+
+      TorLauncherLogger.log(4, aTorFileType + " file not found: " + f.path);
+    }
+    catch(e)
+    {
+      TorLauncherLogger.safelog(4, "getTorFile " + aTorFileType +
+                                     " failed for " + path + ": ", e);
+    }
+
+    return null;  // File not found or error (logged above).
+  }, // getTorFile()
 };
 
 
@@ -389,9 +551,17 @@ Object.freeze(TorLauncherUtil);
 
 let TLUtilInternal =  // Private
 {
+  kThunderbirdID: "{3550f703-e582-4d05-9a08-453d09bdfdc6}",
+  kInstantbirdID: "{33cb9019-c295-46dd-be21-8c4936574bee}",
+
   mPrefsSvc : null,
   mStringBundle : null,
   mOS : "",
+  // mIsUserDataOutsideOfAppDir is true when TorBrowser-Data is used.
+  mIsUserDataOutsideOfAppDir: undefined, // Boolean (cached; access via
+                                         //   this._isUserDataOutsideOfAppDir)
+  mAppDir: null,        // nsIFile (cached; access via this._appDir)
+  mDataDir: null,       // nsIFile (cached; access via this._dataDir)
 
   _init: function()
   {
@@ -421,6 +591,89 @@ let TLUtilInternal =  // Private
 
     return this.mOS;
   },
+
+  get _isMac()
+  {
+    return ("Darwin" == this._OS);
+  },
+
+  get _isUserDataOutsideOfAppDir()
+  {
+    if (this.mIsUserDataOutsideOfAppDir == undefined)
+    {
+      // Determine if we are using a "side-by-side" data model by checking
+      // whether the user profile is outside of the app directory.
+      try
+      {
+        let ds = Cc["@mozilla.org/file/directory_service;1"]
+                        .getService(Ci.nsIProperties);
+        let profDir = ds.get("ProfD", Ci.nsIFile);
+        this.mIsUserDataOutsideOfAppDir = !this._appDir.contains(profDir);
+      }
+      catch (e)
+      {
+        this.mIsUserDataOutsideOfAppDir = false;
+      }
+    }
+
+    return this.mIsUserDataOutsideOfAppDir;
+  }, // get _isUserDataOutsideOfAppDir
+
+  // Returns an nsIFile that points to the application directory.
+  // May throw.
+  get _appDir()
+  {
+    if (!this.mAppDir)
+    {
+      let topDir = Cc["@mozilla.org/file/directory_service;1"]
+                    .getService(Ci.nsIProperties).get("CurProcD", Ci.nsIFile);
+      let appInfo = Cc["@mozilla.org/xre/app-info;1"]
+                      .getService(Ci.nsIXULAppInfo);
+      // On Linux and Windows, we want to return the Browser/ directory.
+      // Because topDir ("CurProcD") points to Browser/browser on those
+      // platforms, we need to go up one level.
+      // On Mac OS, we want to return the TorBrowser.app/ directory.
+      // Because topDir points to Contents/Resources/browser on Mac OS,
+      // we need to go up 3 levels.
+      let tbbBrowserDepth = (this._isMac) ? 3 : 1;
+      if ((appInfo.ID == this.kThunderbirdID) ||
+          (appInfo.ID == this.kInstantbirdID))
+      {
+        // On Thunderbird/Instantbird, the topDir is the root dir and not
+        // browser/, so we need to iterate one level less than Firefox.
+        --tbbBrowserDepth;
+      }
+
+      while (tbbBrowserDepth > 0)
+      {
+        let didRemove = (topDir.leafName != ".");
+        topDir = topDir.parent;
+        if (didRemove)
+          tbbBrowserDepth--;
+      }
+
+      this.mAppDir = topDir;
+    }
+
+    return this.mAppDir;
+  }, // get _appDir
+
+  // Returns an nsIFile that points to the TorBrowser-Data/ directory.
+  // This function is only used when this._isUserDataOutsideOfAppDir == true.
+  // May throw.
+  get _dataDir()
+  {
+    if (!this.mDataDir)
+    {
+      let ds = Cc["@mozilla.org/file/directory_service;1"]
+                      .getService(Ci.nsIProperties);
+      let profDir = ds.get("ProfD", Ci.nsIFile);
+      this.mDataDir = profDir.parent.parent;
+    }
+
+    return this.mDataDir;
+  }, // get _dataDir
+
 };
 
 





More information about the tbb-commits mailing list