commit 8871259c966755233b134a5ddb2b4926539d25c6 Author: Kathy Brade brade@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@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 + };