commit e9ed8e28de7963d825a743257c0e00f89106137c
Author: Kathy Brade <brade(a)pearlcrescent.com>
Date: Mon Mar 14 11:15:43 2016 -0400
Bug 13252 - Do not store data in the app bundle
Add support for a "side-by-side" directory structure where
the writable data is stored in the TorBrowser-Data/ directory
next to the application directory.
---
src/chrome/locale/en/torlauncher.properties | 6 +-
src/components/tl-process.js | 232 +++++++++++++++++++++-------
src/defaults/preferences/prefs.js | 9 +-
3 files changed, 184 insertions(+), 63 deletions(-)
diff --git a/src/chrome/locale/en/torlauncher.properties b/src/chrome/locale/en/torlauncher.properties
index 02be756..0ef4437 100644
--- a/src/chrome/locale/en/torlauncher.properties
+++ b/src/chrome/locale/en/torlauncher.properties
@@ -1,4 +1,4 @@
-### Copyright (c) 2014, The Tor Project, Inc.
+### Copyright (c) 2016, The Tor Project, Inc.
### See LICENSE for licensing information.
torlauncher.error_title=Tor Launcher
@@ -13,8 +13,8 @@ torlauncher.tor_bootstrap_failed_details=%1$S failed (%2$S).
torlauncher.unable_to_start_tor=Unable to start Tor.\n\n%S
torlauncher.tor_missing=The Tor executable is missing.
-torlauncher.torrc_missing=The torrc file is missing.
-torlauncher.datadir_missing=The Tor data directory does not exist.
+torlauncher.torrc_missing=The torrc file is missing and could not be created.
+torlauncher.datadir_missing=The Tor data directory does not exist and could not be created.
torlauncher.password_hash_missing=Failed to get hashed password.
torlauncher.failed_to_get_settings=Unable to retrieve Tor settings.\n\n%S
diff --git a/src/components/tl-process.js b/src/components/tl-process.js
index 216c962..7adb5a7 100644
--- a/src/components/tl-process.js
+++ b/src/components/tl-process.js
@@ -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:
@@ -16,6 +16,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 TorProcessService()
{
@@ -290,7 +292,11 @@ TorProcessService.prototype =
mProtocolSvc: null,
mTorProcess: null, // nsIProcess
mTorProcessStartTime: null, // JS Date.now()
- mTorFileBaseDir: null, // nsIFile (cached)
+ // 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.
@@ -309,10 +315,13 @@ TorProcessService.prototype =
// Ideally, we would cd to the Firefox application directory before
// starting tor (but we don't know how to do that). Instead, we
// rely on the TBB launcher to start Firefox from the right place.
- var exeFile = this._getTorFile("tor");
- var torrcFile = this._getTorFile("torrc");
- var torrcDefaultsFile = this._getTorFile("torrc-defaults");
- var dataDir = this._getTorFile("tordatadir");
+
+ // 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 hashedPassword = this.mProtocolSvc.TorGetPassword(true);
var detailsKey;
@@ -335,11 +344,13 @@ TorProcessService.prototype =
return;
}
- var geoipFile = dataDir.clone();
- geoipFile.append("geoip");
- var geoip6File = dataDir.clone();
- geoip6File.append("geoip6");
+ // The geoip and geoip6 files are in the same directory as torrc-defaults.
+ var geoipFile = torrcDefaultsFile.clone();
+ geoipFile.leafName = "geoip";
+
+ var geoip6File = torrcDefaultsFile.clone();
+ geoip6File.leafName = "geoip6";
var args = [];
if (torrcDefaultsFile)
@@ -642,25 +653,65 @@ TorProcessService.prototype =
},
// Returns an nsIFile.
- // If file doesn't exist, null is returned.
- _getTorFile: function(aTorFileType)
+ // If aCreate is true and the file doesn't exist, it is created.
+ _getTorFile: function(aTorFileType, aCreate)
{
if (!aTorFileType)
return null;
- var isRelativePath = true;
- var prefName = "extensions.torlauncher." + aTorFileType + "_path";
- var path = TorLauncherUtil.getCharPref(prefName);
+ let isRelativePath = true;
+ let isUserData = (aTorFileType != "tor") &&
+ (aTorFileType != "torrc-defaults");
+ let prefName = "extensions.torlauncher." + aTorFileType + "_path";
+ let path = TorLauncherUtil.getCharPref(prefName);
if (path)
{
- var re = (TorLauncherUtil.isWindows) ? /^[A-Za-z]:\\/ : /^\//;
+ let re = (TorLauncherUtil.isWindows) ? /^[A-Za-z]:\\/ : /^\//;
isRelativePath = !re.test(path);
}
else
{
// Get default path.
- if (TorLauncherUtil.isWindows)
+ 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)
@@ -672,6 +723,7 @@ TorProcessService.prototype =
}
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)
@@ -679,7 +731,7 @@ TorProcessService.prototype =
else if ("torrc" == aTorFileType)
path = "Data/Tor/torrc";
else if ("tordatadir" == aTorFileType)
- path = "Data/Tor/";
+ path = "Data/Tor";
}
}
@@ -688,51 +740,20 @@ TorProcessService.prototype =
try
{
- var f;
+ let f;
if (isRelativePath)
{
- // Turn into an absolute path.
- if (!this.mTorFileBaseDir)
+ // Turn 'path' into an absolute path.
+ if (this._isUserDataOutsideOfAppDir)
{
- var topDir;
- var appInfo = Cc["@mozilla.org/xre/app-info;1"]
- .getService(Ci.nsIXULAppInfo);
- if (appInfo.ID == this.kThunderbirdID || appInfo.ID == this.kInstantbirdID)
- {
- topDir = Cc["@mozilla.org/file/directory_service;1"]
- .getService(Ci.nsIProperties).get("CurProcD", Ci.nsIFile);
- topDir.append("extensions");
- topDir.append(this.kTorLauncherExtPath);
- }
- else
- {
- // For Firefox, paths are relative to the top of the TBB install.
- var tbbBrowserDepth = 0; // Windows and Linux
- if (TorLauncherUtil.isAppVersionAtLeast("21.0"))
- {
- // In FF21+, CurProcD is the "browser" directory that is next to
- // the firefox binary, e.g., <TorFileBaseDir>/Browser/browser
- ++tbbBrowserDepth;
- }
- if (TorLauncherUtil.isMac)
- tbbBrowserDepth += 2;
-
- topDir = Cc["@mozilla.org/file/directory_service;1"]
- .getService(Ci.nsIProperties).get("CurProcD", Ci.nsIFile);
- while (tbbBrowserDepth > 0)
- {
- var didRemove = (topDir.leafName != ".");
- topDir = topDir.parent;
- if (didRemove)
- tbbBrowserDepth--;
- }
- }
-
- topDir.append("TorBrowser");
- this.mTorFileBaseDir = topDir;
+ let baseDir = isUserData ? this._dataDir : this._appDir;
+ f = baseDir.clone();
+ }
+ else
+ {
+ f = this._appDir.clone();
+ f.append("TorBrowser");
}
-
- f = this.mTorFileBaseDir.clone();
f.appendRelativePath(path);
}
else
@@ -741,6 +762,22 @@ TorProcessService.prototype =
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) {}
@@ -759,6 +796,83 @@ TorProcessService.prototype =
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
+ // for the existence of the TorBrowser-Data/ directory.
+ try
+ {
+ let f = this._appDir.parent;
+ f.append("TorBrowser-Data");
+ this.mIsUserDataOutsideOfAppDir = f.exists() && f.isDirectory();
+ }
+ 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);
+ if ((appInfo.ID == this.kThunderbirdID) ||
+ (appInfo.ID == this.kInstantbirdID))
+ {
+ // For TorBirdy and Tor Messenger the Tor Launcher extension
+ // directory is returned.
+ topDir.append("extensions");
+ topDir.append(this.kTorLauncherExtPath);
+ }
+ else // Tor Browser
+ {
+ // 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;
+ 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.
+ // May throw.
+ get _dataDir()
+ {
+ if (!this.mDataDir)
+ {
+ let f = this._appDir.parent.clone();
+ f.append("TorBrowser-Data");
+ this.mDataDir = f;
+ }
+
+ return this.mDataDir;
+ }, // get _dataDir
+
_getpid: function()
{
// Use nsIXULRuntime.processID if it is available.
diff --git a/src/defaults/preferences/prefs.js b/src/defaults/preferences/prefs.js
index 34bf1a7..ac1af21 100644
--- a/src/defaults/preferences/prefs.js
+++ b/src/defaults/preferences/prefs.js
@@ -8,8 +8,15 @@ pref("extensions.torlauncher.control_port", 9151);
pref("extensions.torlauncher.start_tor", true);
pref("extensions.torlauncher.prompt_at_startup", true);
-// All path prefs. are relative to the firefox executable's directory
+// 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.
pref("extensions.torlauncher.tor_path", "");
+
+// The torrc_path and tordatadir_path are relative to the data directory,
+// which is TorBrowser-Data/ if it exists as a sibling of the application
+// directory. If TorBrowser-Data/ does not exist, these paths are relative
+// to the TorBrowser/ directory within the application directory.
pref("extensions.torlauncher.torrc_path", "");
pref("extensions.torlauncher.tordatadir_path", "");