From 45147161282abe604afa83db5b42a0c20c20d8e4 Mon Sep 17 00:00:00 2001 From: Kathy Brade Date: Fri, 13 Jan 2017 11:40:24 -0500 Subject: [PATCH] Bug 4234: Use the Firefox Update Process for Tor Browser. New configure options: --with-tor-browser-version=VERSION # Pass TB version throughout build. --enable-tor-browser-update # Enable bundle update behavior. The following files are never updated: TorBrowser/Data/Browser/profiles.ini TorBrowser/Data/Browser/profile.default/bookmarks.html TorBrowser/Data/Tor/torrc Mac OS: Store update metadata under TorBrowser/UpdateInfo. Removed the %OS_VERSION% component from the update URL (13047) and added support for minSupportedOSVersion, an attribute of the element that may be used to trigger Firefox's "unsupported platform" behavior. Windows: disable "runas" code path in updater (15201). Windows: avoid writing to the registry (16236). Also includes fixes for tickets 13047, 13301, 13356, 13594, 15406, 16014, and 16909. --- .mozconfig | 3 +- .mozconfig-asan | 3 +- .mozconfig-mac | 2 + .mozconfig-mingw | 2 +- browser/app/Makefile.in | 3 + browser/app/profile/000-tor-browser.js | 1 - browser/app/profile/firefox.js | 3 +- browser/base/content/aboutDialog.js | 17 +- browser/base/jar.mn | 2 +- .../official/pref/firefox-branding.js | 8 +- browser/components/moz.build | 2 +- .../components/newtab/aboutNewTabService.js | 2 +- .../tests/xpcshell/test_AboutNewTabService.js | 2 +- browser/components/nsBrowserContentHandler.js | 50 ++- browser/confvars.sh | 20 +- browser/installer/Makefile.in | 4 + browser/installer/package-manifest.in | 2 + build/moz.configure/init.configure | 3 +- build/moz.configure/old.configure | 4 + config/createprecomplete.py | 17 +- old-configure.in | 27 +- toolkit/modules/UpdateUtils.jsm | 7 + toolkit/modules/debug.js | 2 + toolkit/modules/moz.build | 2 +- toolkit/mozapps/extensions/AddonManager.jsm | 2 +- .../mozapps/extensions/test/browser/head.js | 2 + .../extensions/test/xpcshell/head_addons.js | 3 +- toolkit/mozapps/update/moz.build | 5 +- toolkit/mozapps/update/nsUpdateService.js | 110 +++++- .../mozapps/update/updater/launchchild_osx.mm | 2 + toolkit/mozapps/update/updater/moz.build | 2 +- toolkit/mozapps/update/updater/updater.cpp | 351 ++++++++++++++++-- toolkit/xre/MacLaunchHelper.h | 2 + toolkit/xre/MacLaunchHelper.mm | 2 + toolkit/xre/nsAppRunner.cpp | 10 +- toolkit/xre/nsUpdateDriver.cpp | 170 +++++++++ toolkit/xre/nsXREDirProvider.cpp | 91 +++-- toolkit/xre/nsXREDirProvider.h | 1 + tools/update-packaging/common.sh | 106 ++++-- tools/update-packaging/make_full_update.sh | 53 ++- .../make_incremental_update.sh | 164 +++++++- 41 files changed, 1084 insertions(+), 180 deletions(-) diff --git a/.mozconfig b/.mozconfig index aa2467efaa51c..214ee2ee448d8 100755 --- a/.mozconfig +++ b/.mozconfig @@ -12,12 +12,13 @@ ac_add_options --enable-official-branding # Let's support GTK2 for ESR52 ac_add_options --enable-default-toolkit=cairo-gtk2 +ac_add_options --enable-tor-browser-update + ac_add_options --disable-strip ac_add_options --disable-install-strip ac_add_options --disable-tests ac_add_options --disable-debug ac_add_options --disable-maintenance-service -ac_add_options --disable-updater ac_add_options --disable-crashreporter ac_add_options --disable-webrtc # Let's make sure no preference is enabling either Adobe's or Google's CDM. diff --git a/.mozconfig-asan b/.mozconfig-asan index 8eda04c869626..0d6595cdc2f96 100644 --- a/.mozconfig-asan +++ b/.mozconfig-asan @@ -28,12 +28,13 @@ ac_add_options --enable-official-branding # Let's support GTK2 for ESR52 ac_add_options --enable-default-toolkit=cairo-gtk2 +ac_add_options --enable-tor-browser-update + ac_add_options --disable-strip ac_add_options --disable-install-strip ac_add_options --disable-tests ac_add_options --disable-debug ac_add_options --disable-maintenance-service -ac_add_options --disable-updater ac_add_options --disable-crashreporter ac_add_options --disable-webrtc #ac_add_options --disable-ctypes diff --git a/.mozconfig-mac b/.mozconfig-mac index c559edce7ee82..b595f0941309f 100644 --- a/.mozconfig-mac +++ b/.mozconfig-mac @@ -41,6 +41,8 @@ ac_add_options --enable-official-branding ac_add_options --enable-optimize ac_add_options --disable-debug +ac_add_options --enable-tor-browser-update + ac_add_options --disable-crashreporter ac_add_options --disable-maintenance-service ac_add_options --disable-webrtc diff --git a/.mozconfig-mingw b/.mozconfig-mingw index 410719064f5fb..211c2770712c6 100644 --- a/.mozconfig-mingw +++ b/.mozconfig-mingw @@ -14,7 +14,7 @@ ac_add_options --enable-optimize ac_add_options --enable-strip ac_add_options --enable-official-branding -ac_add_options --disable-updater +ac_add_options --enable-tor-browser-update # Let's make sure no preference is enabling either Adobe's or Google's CDM. ac_add_options --disable-eme diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in index 1fe767610f8fe..1fbdc1ed0c71e 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -86,9 +86,12 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) $(RM) $(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME) rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) $(dist_dest)/Contents/MacOS cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns + cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns +ifndef TOR_BROWSER_UPDATE $(MKDIR) -p $(dist_dest)/Contents/Library/LaunchServices mv -f $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater $(dist_dest)/Contents/Library/LaunchServices ln -s ../../../../Library/LaunchServices/org.mozilla.updater $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater +endif printf APPLTORB > $(dist_dest)/Contents/PkgInfo endif diff --git a/browser/app/profile/000-tor-browser.js b/browser/app/profile/000-tor-browser.js index 7c608ae618d3c..8ffdab034feb0 100644 --- a/browser/app/profile/000-tor-browser.js +++ b/browser/app/profile/000-tor-browser.js @@ -7,7 +7,6 @@ // Disable initial homepage notifications pref("browser.search.update", false); pref("browser.rights.3.shown", true); -pref("browser.startup.homepage_override.mstone", "ignore"); pref("startup.homepage_welcome_url", ""); pref("startup.homepage_welcome_url.additional", ""); // Not set Windows 10 users would get a special introduction on first start. diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 24de9bc8faa07..d4b2d9bf9b98d 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -134,7 +134,8 @@ pref("app.update.badge", false); pref("app.update.staging.enabled", true); // Update service URL: -pref("app.update.url", "https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml"); +pref("app.update.url", "https://aus1.torproject.org/torbrowser/update_3/%CHANNEL%/%BUILD_TARGET%/%VERSION%/%LOCALE%"); + // app.update.url.manual is in branding section // app.update.url.details is in branding section diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js index 569a65adba070..97c7b92e634fe 100644 --- a/browser/base/content/aboutDialog.js +++ b/browser/base/content/aboutDialog.js @@ -8,6 +8,11 @@ Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/AppConstants.jsm"); +#ifdef TOR_BROWSER_VERSION +# Add double-quotes back on (stripped by JarMaker.py). +#expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__"; +#endif + function init(aEvent) { if (aEvent.target != document) @@ -44,16 +49,16 @@ function init(aEvent) let versionField = document.getElementById("version"); let version = Services.appinfo.version; if (/a\d+$/.test(version)) { - let buildID = Services.appinfo.appBuildID; - let year = buildID.slice(0, 4); - let month = buildID.slice(4, 6); - let day = buildID.slice(6, 8); - versionField.textContent += ` (${year}-${month}-${day})`; - document.getElementById("experimental").hidden = false; document.getElementById("communityDesc").hidden = true; } +#ifdef TOR_BROWSER_VERSION + versionField.textContent = TOR_BROWSER_VERSION + + " (based on Mozilla Firefox " + + versionField.textContent + ")"; +#endif + // Append "(32-bit)" or "(64-bit)" build architecture to the version number: let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); let archResource = Services.appinfo.is64Bit diff --git a/browser/base/jar.mn b/browser/base/jar.mn index 4dcd47c95694d..4c486292ac2b1 100644 --- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -15,7 +15,7 @@ browser.jar: content/browser/aboutDialog-appUpdater.js (content/aboutDialog-appUpdater.js) * content/browser/aboutDialog.xul (content/aboutDialog.xul) - content/browser/aboutDialog.js (content/aboutDialog.js) +* content/browser/aboutDialog.js (content/aboutDialog.js) content/browser/aboutDialog.css (content/aboutDialog.css) content/browser/aboutRobots.xhtml (content/aboutRobots.xhtml) * content/browser/abouthome/aboutHome.xhtml (content/abouthome/aboutHome.xhtml) diff --git a/browser/branding/official/pref/firefox-branding.js b/browser/branding/official/pref/firefox-branding.js index 52aaa4f508d07..f1db84e3b226e 100644 --- a/browser/branding/official/pref/firefox-branding.js +++ b/browser/branding/official/pref/firefox-branding.js @@ -11,14 +11,14 @@ pref("app.update.interval", 43200); // 12 hours // background (in seconds) // 0 means "download everything at once" pref("app.update.download.backgroundInterval", 0); -// Give the user x seconds to react before showing the big UI. default=192 hours -pref("app.update.promptWaitTime", 691200); +// Give the user x seconds to react before showing the big UI. default=48 hours +pref("app.update.promptWaitTime", 172800); // URL user can browse to manually if for some reason all update installation // attempts fail. -pref("app.update.url.manual", "https://www.mozilla.org/firefox/"); +pref("app.update.url.manual", "https://www.torproject.org/download/download-easy.html"); // A default value for the "More information about this update" link // supplied in the "An update is available" page of the update wizard. -pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes"); +pref("app.update.url.details", "https://www.torproject.org/projects/torbrowser.html"); // The number of days a binary is permitted to be old // without checking for an update. This assumes that diff --git a/browser/components/moz.build b/browser/components/moz.build index 5ad29d08874f2..c2e7af5362c2c 100644 --- a/browser/components/moz.build +++ b/browser/components/moz.build @@ -38,10 +38,10 @@ XPIDL_MODULE = 'browsercompsbase' EXTRA_PP_COMPONENTS += [ 'BrowserComponents.manifest', + 'nsBrowserContentHandler.js', ] EXTRA_COMPONENTS += [ - 'nsBrowserContentHandler.js', 'nsBrowserGlue.js', ] diff --git a/browser/components/newtab/aboutNewTabService.js b/browser/components/newtab/aboutNewTabService.js index 54c3749e88e53..aff5127ec47bf 100644 --- a/browser/components/newtab/aboutNewTabService.js +++ b/browser/components/newtab/aboutNewTabService.js @@ -47,7 +47,7 @@ const PREF_REMOTE_MODE = "browser.newtabpage.remote.mode"; // The preference that tells which remote version is expected. const PREF_REMOTE_VERSION = "browser.newtabpage.remote.version"; -const VALID_CHANNELS = new Set(["esr", "release", "beta", "aurora", "nightly"]); +const VALID_CHANNELS = new Set(["esr", "release", "hardened", "beta", "alpha", "aurora", "nightly"]); function AboutNewTabService() { NewTabPrefsProvider.prefs.on(PREF_REMOTE_ENABLED, this._handleToggleEvent.bind(this)); diff --git a/browser/components/newtab/tests/xpcshell/test_AboutNewTabService.js b/browser/components/newtab/tests/xpcshell/test_AboutNewTabService.js index 21f68ab703374..fe5cf0948d1c4 100644 --- a/browser/components/newtab/tests/xpcshell/test_AboutNewTabService.js +++ b/browser/components/newtab/tests/xpcshell/test_AboutNewTabService.js @@ -185,7 +185,7 @@ add_task(function* test_updates() { * Returns the correct release names */ add_task(function* test_release_names() { - let valid_channels = ["esr", "release", "beta", "aurora", "nightly"]; + let valid_channels = ["esr", "release", "hardened", "beta", "alpha", "aurora", "nightly"]; let invalid_channels = new Set(["default", "invalid"]); for (let channel of valid_channels) { diff --git a/browser/components/nsBrowserContentHandler.js b/browser/components/nsBrowserContentHandler.js index e8fe0fe93949e..6e50ac871713c 100644 --- a/browser/components/nsBrowserContentHandler.js +++ b/browser/components/nsBrowserContentHandler.js @@ -44,6 +44,10 @@ const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED; const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT; +#ifdef TOR_BROWSER_VERSION +const kTBSavedVersionPref = "browser.startup.homepage_override.torbrowser.version"; +#endif + function shouldLoadURI(aURI) { if (aURI && !aURI.schemeIs("chrome")) return true; @@ -94,7 +98,8 @@ const OVERRIDE_NEW_BUILD_ID = 3; * Returns: * OVERRIDE_NEW_PROFILE if this is the first run with a new profile. * OVERRIDE_NEW_MSTONE if this is the first run with a build with a different - * Gecko milestone (i.e. right after an upgrade). + * Gecko milestone or Tor Browser version (i.e. right + * after an upgrade). * OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the * same Gecko milestone (i.e. after a nightly upgrade). * OVERRIDE_NONE otherwise. @@ -110,6 +115,15 @@ function needHomepageOverride(prefb) { var mstone = Services.appinfo.platformVersion; +#ifdef TOR_BROWSER_VERSION +#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__; + + var savedTBVersion = null; + try { + savedTBVersion = prefb.getCharPref(kTBSavedVersionPref); + } catch (e) {} +#endif + var savedBuildID = null; try { savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID"); @@ -127,9 +141,30 @@ function needHomepageOverride(prefb) { prefb.setCharPref("browser.startup.homepage_override.mstone", mstone); prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); +#ifdef TOR_BROWSER_VERSION + prefb.setCharPref(kTBSavedVersionPref, TOR_BROWSER_VERSION); + + // After an upgrade from an older release of Tor Browser (<= 5.5a1), the + // savedmstone will be undefined because those releases included the + // value "ignore" for the browser.startup.homepage_override.mstone pref. + // To correctly detect an upgrade vs. a new profile, we check for the + // presence of the "app.update.postupdate" pref. + var updated = prefb.prefHasUserValue("app.update.postupdate"); + return (savedmstone || updated) ? OVERRIDE_NEW_MSTONE + : OVERRIDE_NEW_PROFILE; +#else return (savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE); +#endif } +#ifdef TOR_BROWSER_VERSION + if (TOR_BROWSER_VERSION != savedTBVersion) { + prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); + prefb.setCharPref(kTBSavedVersionPref, TOR_BROWSER_VERSION); + return OVERRIDE_NEW_MSTONE; + } +#endif + if (buildID != savedBuildID) { prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); return OVERRIDE_NEW_BUILD_ID; @@ -493,6 +528,15 @@ nsBrowserContentHandler.prototype = { try { old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone"); } catch (ex) {} + +#ifdef TOR_BROWSER_VERSION + // We do the same for the Tor Browser version. + var old_tbversion = null; + try { + old_tbversion = prefb.getCharPref(kTBSavedVersionPref); + } catch (e) {} +#endif + override = needHomepageOverride(prefb); if (override != OVERRIDE_NONE) { switch (override) { @@ -518,6 +562,10 @@ nsBrowserContentHandler.prototype = { overridePage = getPostUpdateOverridePage(overridePage); overridePage = overridePage.replace("%OLD_VERSION%", old_mstone); +#ifdef TOR_BROWSER_VERSION + overridePage = overridePage.replace("%OLD_TOR_BROWSER_VERSION%", + old_tbversion); +#endif break; } } diff --git a/browser/confvars.sh b/browser/confvars.sh index dabd06ea797b6..ad632f410becd 100755 --- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -13,22 +13,6 @@ if test "$OS_ARCH" = "WINNT" -o \ MOZ_BUNDLED_FONTS=1 fi -if test "$OS_ARCH" = "WINNT"; then - MOZ_MAINTENANCE_SERVICE=1 - if ! test "$HAVE_64BIT_BUILD"; then - if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \ - "$MOZ_UPDATE_CHANNEL" = "aurora" -o \ - "$MOZ_UPDATE_CHANNEL" = "beta" -o \ - "$MOZ_UPDATE_CHANNEL" = "beta-dev" -o \ - "$MOZ_UPDATE_CHANNEL" = "release" -o \ - "$MOZ_UPDATE_CHANNEL" = "release-dev"; then - if ! test "$MOZ_DEBUG"; then - MOZ_STUB_INSTALLER=1 - fi - fi - fi -fi - # Enable building ./signmar and running libmar signature tests MOZ_ENABLE_SIGNMAR=1 @@ -47,9 +31,9 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} # This should usually be the same as the value MAR_CHANNEL_ID. # If more than one ID is needed, then you should use a comma separated list # of values. -ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-esr +ACCEPTED_MAR_CHANNEL_IDS=torbrowser-torproject-release # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t " -MAR_CHANNEL_ID=firefox-mozilla-esr +MAR_CHANNEL_ID=torbrowser-torproject-release MOZ_PROFILE_MIGRATOR=1 MOZ_APP_STATIC_INI=1 MOZ_WEBGL_CONFORMANT=1 diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index 55df797ef7aa2..38b50cf3f31f1 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -80,6 +80,10 @@ DEFINES += -DMOZ_PACKAGE_WIN_UCRT_DLLS=1 endif endif +ifdef TOR_BROWSER_UPDATE +DEFINES += -DTOR_BROWSER_UPDATE +endif + ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET))) DEFINES += -DMOZ_SHARED_MOZGLUE=1 endif diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 8192f93b20e97..43c89a9f9c4e0 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -35,7 +35,9 @@ #ifdef XP_MACOSX ; Mac bundle stuff @APPNAME@/Contents/Info.plist +#ifndef TOR_BROWSER_UPDATE @APPNAME@/Contents/Library/LaunchServices +#endif @APPNAME@/Contents/PkgInfo @RESPATH@/firefox.icns @RESPATH@/document.icns diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure index 2123bebc928b0..ad86e47fbe6dd 100644 --- a/build/moz.configure/init.configure +++ b/build/moz.configure/init.configure @@ -633,7 +633,6 @@ add_old_configure_assignment('MOZ_BUILD_APP', build_project) # set RELEASE_OR_BETA and NIGHTLY_BUILD variables depending on the cycle we're in # The logic works like this: # - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD) -# - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora # - otherwise, we're building Release/Beta (define RELEASE_OR_BETA) @depends(check_build_environment, '--help') @imports(_from='__builtin__', _import='open') @@ -648,7 +647,7 @@ def milestone(build_env, _): if 'a1' in milestone: is_nightly = True - elif 'a' not in milestone: + else: is_release_or_beta = True return namespace(version=milestone, diff --git a/build/moz.configure/old.configure b/build/moz.configure/old.configure index b32c3f7b79e8e..2f76cd7a96805 100644 --- a/build/moz.configure/old.configure +++ b/build/moz.configure/old.configure @@ -301,6 +301,10 @@ def old_configure_options(*options): '--enable-mapi', '--enable-calendar', '--enable-incomplete-external-linkage', + + # Tor additions. + '--with-tor-browser-version', + '--enable-tor-browser-update', ) @imports(_from='__builtin__', _import='compile') @imports(_from='__builtin__', _import='open') diff --git a/config/createprecomplete.py b/config/createprecomplete.py index 3241d526b2819..be571be820096 100644 --- a/config/createprecomplete.py +++ b/config/createprecomplete.py @@ -5,13 +5,22 @@ # update instructions which is used to remove files and directories that are no # longer present in a complete update. The current working directory is used for # the location to enumerate and to create the precomplete file. +# For symlinks, remove instructions are always generated. import sys import os +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove all lines in this file that contain: +# TorBrowser/Data + def get_build_entries(root_path): """ Iterates through the root_path, creating a list for each file and directory. Excludes any file paths ending with channel-prefs.js. + To support Tor Browser updates, excludes: + TorBrowser/Data/Browser/profiles.ini + TorBrowser/Data/Browser/profile.default/bookmarks.html + TorBrowser/Data/Tor/torrc """ rel_file_path_set = set() rel_dir_path_set = set() @@ -22,6 +31,9 @@ def get_build_entries(root_path): rel_path_file = rel_path_file.replace("\\", "/") if not (rel_path_file.endswith("channel-prefs.js") or rel_path_file.endswith("update-settings.ini") or + rel_path_file == "TorBrowser/Data/Browser/profiles.ini" or + rel_path_file == "TorBrowser/Data/Browser/profile.default/bookmarks.html" or + rel_path_file == "TorBrowser/Data/Tor/torrc" or rel_path_file.find("distribution/") != -1): rel_file_path_set.add(rel_path_file) @@ -30,7 +42,10 @@ def get_build_entries(root_path): rel_path_dir = os.path.join(parent_dir_rel_path, dir_name) rel_path_dir = rel_path_dir.replace("\\", "/")+"/" if rel_path_dir.find("distribution/") == -1: - rel_dir_path_set.add(rel_path_dir) + if (os.path.islink(rel_path_dir[:-1])): + rel_file_path_set.add(rel_path_dir[:-1]) + else: + rel_dir_path_set.add(rel_path_dir) rel_file_path_list = list(rel_file_path_set) rel_file_path_list.sort(reverse=True) diff --git a/old-configure.in b/old-configure.in index 85a4157e30f8f..56c06cf2828f4 100644 --- a/old-configure.in +++ b/old-configure.in @@ -3904,6 +3904,31 @@ if test -n "$MOZ_UPDATER"; then AC_DEFINE(MOZ_UPDATER) fi +dnl ======================================================== +dnl Tor additions +dnl ======================================================== +MOZ_ARG_WITH_STRING(tor-browser-version, +[ --with-tor-browser-version=VERSION + Set Tor Browser version, e.g., 7.0a1], + TOR_BROWSER_VERSION="$withval") + +MOZ_ARG_ENABLE_BOOL(tor-browser-update, +[ --enable-tor-browser-update + Enable Tor Browser update], + TOR_BROWSER_UPDATE=1, + TOR_BROWSER_UPDATE= ) + +if test -n "$TOR_BROWSER_UPDATE"; then + if test -z "$TOR_BROWSER_VERSION"; then + AC_MSG_ERROR([--enable-tor-browser-update requires --with-tor-browser-version.]) + fi + AC_DEFINE(TOR_BROWSER_UPDATE) +fi + +AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION,"$TOR_BROWSER_VERSION") +AC_SUBST(TOR_BROWSER_VERSION) +AC_SUBST(TOR_BROWSER_UPDATE) + dnl ======================================================== dnl parental controls (for Windows Vista) dnl ======================================================== @@ -5750,7 +5775,7 @@ if test "$MOZ_DEBUG"; then A11Y_LOG=1 fi case "$MOZ_UPDATE_CHANNEL" in -aurora|beta|release|esr) +aurora|alpha|beta|hardened|release|esr) ;; *) A11Y_LOG=1 diff --git a/toolkit/modules/UpdateUtils.jsm b/toolkit/modules/UpdateUtils.jsm index ef84753647039..bceb926f79f03 100644 --- a/toolkit/modules/UpdateUtils.jsm +++ b/toolkit/modules/UpdateUtils.jsm @@ -20,6 +20,9 @@ const PREF_APP_B2G_VERSION = "b2g.version"; const PREF_APP_UPDATE_CUSTOM = "app.update.custom"; const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash"; +#ifdef TOR_BROWSER_VERSION +#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__; +#endif this.UpdateUtils = { /** @@ -70,7 +73,11 @@ this.UpdateUtils = { */ formatUpdateURL(url) { url = url.replace(/%PRODUCT%/g, Services.appinfo.name); +#ifdef TOR_BROWSER_UPDATE + url = url.replace(/%VERSION%/g, TOR_BROWSER_VERSION); +#else url = url.replace(/%VERSION%/g, Services.appinfo.version); +#endif url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID); url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI); url = url.replace(/%OS_VERSION%/g, this.OSVersion); diff --git a/toolkit/modules/debug.js b/toolkit/modules/debug.js index de26ce6ff6f90..b77fff6769713 100644 --- a/toolkit/modules/debug.js +++ b/toolkit/modules/debug.js @@ -41,6 +41,8 @@ this.NS_ASSERT = function NS_ASSERT(condition, message) { switch (defB.getCharPref("app.update.channel")) { case "nightly": case "aurora": + case "alpha": + case "hardened": case "beta": case "default": releaseBuild = false; diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 7944dbcd1e917..7d4b0f3fa7130 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -94,7 +94,6 @@ EXTRA_JS_MODULES += [ 'Task.jsm', 'Timer.jsm', 'Troubleshoot.jsm', - 'UpdateUtils.jsm', 'WebChannel.jsm', 'WindowDraggingUtils.jsm', 'ZipUtils.jsm', @@ -121,6 +120,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'): EXTRA_PP_JS_MODULES += [ 'AppConstants.jsm', + 'UpdateUtils.jsm', ] if 'Android' != CONFIG['OS_TARGET']: diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index 11fbb87073a6e..3507e78066971 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -23,7 +23,7 @@ if ("@mozilla.org/xre/app-info;1" in Cc) { Cu.import("resource://gre/modules/AppConstants.jsm"); -const MOZ_COMPATIBILITY_NIGHTLY = !['aurora', 'beta', 'release', 'esr'].includes(AppConstants.MOZ_UPDATE_CHANNEL); +const MOZ_COMPATIBILITY_NIGHTLY = !['aurora', 'alpha', 'beta', 'hardened', 'release', 'esr'].includes(AppConstants.MOZ_UPDATE_CHANNEL); const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion"; const PREF_DEFAULT_PROVIDERS_ENABLED = "extensions.defaultProviders.enabled"; diff --git a/toolkit/mozapps/extensions/test/browser/head.js b/toolkit/mozapps/extensions/test/browser/head.js index 5a749099d58d5..7075d46274b37 100644 --- a/toolkit/mozapps/extensions/test/browser/head.js +++ b/toolkit/mozapps/extensions/test/browser/head.js @@ -51,7 +51,9 @@ var PREF_CHECK_COMPATIBILITY; channel = Services.prefs.getCharPref("app.update.channel"); } catch (e) { } if (channel != "aurora" && + channel != "alpha" && channel != "beta" && + channel != "hardened" && channel != "release" && channel != "esr") { var version = "nightly"; diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js index 960caceeb4852..ca3a7cf27969e 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js +++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -340,7 +340,8 @@ function isNightlyChannel() { } catch (e) { } - return channel != "aurora" && channel != "beta" && channel != "release" && channel != "esr"; + return channel != "aurora" && channel != "alpha" && channel != "beta" && + channel != "hardened" && channel != "release" && channel != "esr"; } /** diff --git a/toolkit/mozapps/update/moz.build b/toolkit/mozapps/update/moz.build index 78a6996b7d3d3..eeac6cc6a16c0 100644 --- a/toolkit/mozapps/update/moz.build +++ b/toolkit/mozapps/update/moz.build @@ -18,11 +18,14 @@ XPIDL_SOURCES += [ TEST_DIRS += ['tests'] EXTRA_COMPONENTS += [ - 'nsUpdateService.js', 'nsUpdateService.manifest', 'nsUpdateServiceStub.js', ] +EXTRA_PP_COMPONENTS += [ + 'nsUpdateService.js', +] + EXTRA_JS_MODULES += [ 'UpdateTelemetry.jsm', ] diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 43980aba95541..8a35101a4d6b8 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -11,7 +11,9 @@ const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); Cu.import("resource://gre/modules/FileUtils.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); +#ifdef XP_WIN Cu.import("resource://gre/modules/ctypes.jsm", this); +#endif Cu.import("resource://gre/modules/UpdateTelemetry.jsm", this); Cu.import("resource://gre/modules/AppConstants.jsm", this); Cu.importGlobalProperties(["XMLHttpRequest"]); @@ -58,6 +60,10 @@ const KEY_EXECUTABLE = "XREExeF"; // Gonk only const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD"; +#ifdef TOR_BROWSER_VERSION +#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__; +#endif + const DIR_UPDATES = "updates"; const FILE_ACTIVE_UPDATE_XML = "active-update.xml"; @@ -372,6 +378,11 @@ function areDirectoryEntriesWriteable(aDir) { * @return true if elevation is required, false otherwise */ function getElevationRequired() { +#if defined(TOR_BROWSER_UPDATE) + // To avoid potential security holes associated with running the updater + // process with elevated privileges, Tor Browser does not support elevation. + return false; +#else if (AppConstants.platform != "macosx") { return false; } @@ -395,6 +406,7 @@ function getElevationRequired() { LOG("getElevationRequired - able to write to application bundle, elevation " + "not required"); return false; +#endif } /** @@ -1197,6 +1209,9 @@ function handleUpdateFailure(update, errorCode) { cancelations++; Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, cancelations); if (AppConstants.platform == "macosx") { +#if defined(TOR_BROWSER_UPDATE) + cleanupActiveUpdate(); +#else let osxCancelations = getPref("getIntPref", PREF_APP_UPDATE_CANCELATIONS_OSX, 0); osxCancelations++; @@ -1213,6 +1228,7 @@ function handleUpdateFailure(update, errorCode) { writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING_ELEVATE); } +#endif update.statusText = gUpdateBundle.GetStringFromName("elevationFailure"); update.QueryInterface(Ci.nsIWritablePropertyBag); update.setProperty("patchingFailed", "elevationFailure"); @@ -1535,7 +1551,26 @@ function Update(update) { this._patches.push(patch); } - if (this._patches.length == 0 && !update.hasAttribute("unsupported")) { + if (update.hasAttribute("unsupported")) { + this.unsupported = ("true" == update.getAttribute("unsupported")); + } else if (update.hasAttribute("minSupportedOSVersion")) { + let minOSVersion = update.getAttribute("minSupportedOSVersion"); + try { + let osVersion = Services.sysinfo.getProperty("version"); + this.unsupported = (Services.vc.compare(osVersion, minOSVersion) < 0); + } catch (e) {} + } + if (!this.unsupported && update.hasAttribute("minSupportedInstructionSet")) { + let minInstructionSet = update.getAttribute("minSupportedInstructionSet"); + if (['MMX', 'SSE', 'SSE2', 'SSE3', + 'SSE4A', 'SSE4_1', 'SSE4_2'].indexOf(minInstructionSet) >= 0) { + try { + this.unsupported = !Services.sysinfo.getProperty("has" + minInstructionSet); + } catch (e) {} + } + } + + if (this._patches.length == 0 && !this.unsupported) { throw Cr.NS_ERROR_ILLEGAL_VALUE; } @@ -1574,9 +1609,7 @@ function Update(update) { if (!isNaN(attr.value)) { this.backgroundInterval = parseInt(attr.value); } - } else if (attr.name == "unsupported") { - this.unsupported = attr.value == "true"; - } else { + } else if (attr.name != "unsupported") { this[attr.name] = attr.value; switch (attr.name) { @@ -1935,6 +1968,10 @@ UpdateService.prototype = { * notify the user of install success. */ _postUpdateProcessing: function AUS__postUpdateProcessing() { +#if defined(TOR_BROWSER_UPDATE) && !defined(XP_MACOSX) + this._removeOrphanedTorBrowserFiles(); +#endif + if (!this.canCheckForUpdates) { LOG("UpdateService:_postUpdateProcessing - unable to check for " + "updates... returning early"); @@ -2123,6 +2160,42 @@ UpdateService.prototype = { } }, +#if defined(TOR_BROWSER_UPDATE) && !defined(XP_MACOSX) + /** + * When updating from an earlier version to Tor Browser 6.0 or later, old + * update info files are left behind on Linux and Windows. Remove them. + */ + _removeOrphanedTorBrowserFiles: function AUS__removeOrphanedTorBrowserFiles() { + try { + let oldUpdateInfoDir = getAppBaseDir(); // aka the Browser directory. + +#ifdef XP_WIN + // On Windows, the updater files were stored under + // Browser/TorBrowser/Data/Browser/Caches/firefox/ + oldUpdateInfoDir.appendRelativePath( + "TorBrowser\\Data\\Browser\\Caches\\firefox"); +#endif + + // Remove the updates directory. + let updatesDir = oldUpdateInfoDir.clone(); + updatesDir.append("updates"); + if (updatesDir.exists() && updatesDir.isDirectory()) { + updatesDir.remove(true); + } + + // Remove files: active-update.xml and updates.xml + let filesToRemove = [ "active-update.xml", "updates.xml" ]; + filesToRemove.forEach(function(aFileName) { + let f = oldUpdateInfoDir.clone(); + f.append(aFileName); + if (f.exists()) { + f.remove(false); + } + }); + } catch (e) {} + }, +#endif + /** * Register an observer when the network comes online, so we can short-circuit * the app.update.interval when there isn't connectivity @@ -2397,9 +2470,14 @@ UpdateService.prototype = { updates.forEach(function(aUpdate) { // Ignore updates for older versions of the application and updates for // the same version of the application with the same build ID. - if (vc.compare(aUpdate.appVersion, Services.appinfo.version) < 0 || - vc.compare(aUpdate.appVersion, Services.appinfo.version) == 0 && - aUpdate.buildID == Services.appinfo.appBuildID) { +#ifdef TOR_BROWSER_UPDATE + var compatVersion = TOR_BROWSER_VERSION; +#else + var compatVersion = Services.appinfo.version; +#endif + var rc = vc.compare(aUpdate.appVersion, compatVersion); + if (rc < 0 || ((rc == 0) && + (aUpdate.buildID == Services.appinfo.appBuildID))) { LOG("UpdateService:selectUpdate - skipping update because the " + "update's application version is less than the current " + "application version"); @@ -2688,14 +2766,24 @@ UpdateService.prototype = { // current application's version or the update's version is the same as the // application's version and the build ID is the same as the application's // build ID. +#ifdef TOR_BROWSER_UPDATE + var compatVersion = TOR_BROWSER_VERSION; +#else + var compatVersion = Services.appinfo.version; +#endif if (update.appVersion && - (Services.vc.compare(update.appVersion, Services.appinfo.version) < 0 || + (Services.vc.compare(update.appVersion, compatVersion) < 0 || update.buildID && update.buildID == Services.appinfo.appBuildID && - update.appVersion == Services.appinfo.version)) { + update.appVersion == compatVersion)) { LOG("UpdateService:downloadUpdate - canceling download of update since " + "it is for an earlier or same application version and build ID.\n" + - "current application version: " + Services.appinfo.version + "\n" + +#ifdef TOR_BROWSER_UPDATE + "current Tor Browser version: " + compatVersion + "\n" + + "update Tor Browser version : " + update.appVersion + "\n" + +#else + "current application version: " + compatVersion + "\n" + "update application version : " + update.appVersion + "\n" + +#endif "current build ID: " + Services.appinfo.appBuildID + "\n" + "update build ID : " + update.buildID); cleanupActiveUpdate(); @@ -2728,7 +2816,7 @@ UpdateService.prototype = { } } // Set the previous application version prior to downloading the update. - update.previousAppVersion = Services.appinfo.version; + update.previousAppVersion = compatVersion; this._downloader = new Downloader(background, this); return this._downloader.downloadUpdate(update); }, diff --git a/toolkit/mozapps/update/updater/launchchild_osx.mm b/toolkit/mozapps/update/updater/launchchild_osx.mm index 5a36ae6237ee5..000a3477024f0 100644 --- a/toolkit/mozapps/update/updater/launchchild_osx.mm +++ b/toolkit/mozapps/update/updater/launchchild_osx.mm @@ -291,6 +291,7 @@ bool ObtainUpdaterArguments(int* argc, char*** argv) @end +#ifndef TOR_BROWSER_UPDATE bool ServeElevatedUpdate(int argc, const char** argv) { MacAutoreleasePool pool; @@ -307,6 +308,7 @@ bool ServeElevatedUpdate(int argc, const char** argv) [updater release]; return didSucceed; } +#endif bool IsOwnedByGroupAdmin(const char* aAppBundle) { diff --git a/toolkit/mozapps/update/updater/moz.build b/toolkit/mozapps/update/updater/moz.build index 1cca83b5b99dc..c4323953536ac 100644 --- a/toolkit/mozapps/update/updater/moz.build +++ b/toolkit/mozapps/update/updater/moz.build @@ -44,7 +44,7 @@ primary_cert.script = 'gen_cert_header.py:create_header' secondary_cert.script = 'gen_cert_header.py:create_header' xpcshell_cert.script = 'gen_cert_header.py:create_header' -if CONFIG['MOZ_UPDATE_CHANNEL'] in ('beta', 'release', 'esr'): +if CONFIG['MOZ_UPDATE_CHANNEL'] in ('alpha', 'beta', 'hardened', 'release', 'esr'): primary_cert.inputs += ['release_primary.der'] secondary_cert.inputs += ['release_secondary.der'] elif CONFIG['MOZ_UPDATE_CHANNEL'] in ('nightly', 'aurora', 'nightly-elm', diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 63a92c0840c6f..19058842f1272 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -25,7 +25,7 @@ * updatev3.manifest * ----------------- * method = "add" | "add-if" | "add-if-not" | "patch" | "patch-if" | - * "remove" | "rmdir" | "rmrfdir" | type + * "remove" | "rmdir" | "rmrfdir" | "addsymlink" | type * * 'add-if-not' adds a file if it doesn't exist. * @@ -83,7 +83,9 @@ bool IsRecursivelyWritable(const char* aPath); void LaunchChild(int argc, const char** argv); void LaunchMacPostProcess(const char* aAppBundle); bool ObtainUpdaterArguments(int* argc, char*** argv); +#ifndef TOR_BROWSER_UPDATE bool ServeElevatedUpdate(int argc, const char** argv); +#endif void SetGroupOwnershipAndPermissions(const char* aAppBundle); struct UpdateServerThreadArgs { @@ -451,10 +453,12 @@ get_relative_path(const NS_tchar *fullpath) * The line from the manifest that contains the path. * @param isdir * Whether the path is a directory path. Defaults to false. + * @param islinktarget + * Whether the path is a symbolic link target. Defaults to false. * @return valid filesystem path or nullptr if the path checks fail. */ static NS_tchar* -get_valid_path(NS_tchar **line, bool isdir = false) +get_valid_path(NS_tchar **line, bool isdir = false, bool islinktarget = false) { NS_tchar *path = mstrtok(kQuote, line); if (!path) { @@ -489,10 +493,12 @@ get_valid_path(NS_tchar **line, bool isdir = false) path[NS_tstrlen(path) - 1] = NS_T('\0'); } - // Don't allow relative paths that resolve to a parent directory. - if (NS_tstrstr(path, NS_T("..")) != nullptr) { - LOG(("get_valid_path: paths must not contain '..': " LOG_S, path)); - return nullptr; + if (!islinktarget) { + // Don't allow relative paths that resolve to a parent directory. + if (NS_tstrstr(path, NS_T("..")) != nullptr) { + LOG(("get_valid_path: paths must not contain '..': " LOG_S, path)); + return nullptr; + } } return path; @@ -527,7 +533,8 @@ static void ensure_write_permissions(const NS_tchar *path) (void) _wchmod(path, _S_IREAD | _S_IWRITE); #else struct stat fs; - if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) { + if (!lstat(path, &fs) && !S_ISLNK(fs.st_mode) + && !(fs.st_mode & S_IWUSR)) { (void)chmod(path, fs.st_mode | S_IWUSR); } #endif @@ -728,11 +735,9 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest) return READ_ERROR; } -#ifdef XP_UNIX if (S_ISLNK(ss.st_mode)) { return ensure_copy_symlink(path, dest); } -#endif #if MAYBE_USE_HARD_LINKS if (sUseHardLinks) { @@ -828,12 +833,19 @@ static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest, return READ_ERROR; } -#ifdef XP_UNIX +#ifndef XP_WIN if (S_ISLNK(sInfo.st_mode)) { return ensure_copy_symlink(path, dest); } #endif +#ifdef XP_UNIX + // Ignore Unix domain sockets. See #20691. + if (S_ISSOCK(sInfo.st_mode)) { + return 0; + } +#endif + if (!S_ISDIR(sInfo.st_mode)) { return ensure_copy(path, dest); } @@ -887,14 +899,19 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, return rv; struct NS_tstat_t spathInfo; - rv = NS_tstat(spath, &spathInfo); + rv = NS_tlstat(spath, &spathInfo); // Get info about file or symlink. if (rv) { LOG(("rename_file: failed to read file status info: " LOG_S ", " \ "err: %d", spath, errno)); return READ_ERROR; } - if (!S_ISREG(spathInfo.st_mode)) { +#ifdef XP_WIN + if (!S_ISREG(spathInfo.st_mode)) +#else + if (!S_ISREG(spathInfo.st_mode) && !S_ISLNK(spathInfo.st_mode)) +#endif + { if (allowDirs && !S_ISDIR(spathInfo.st_mode)) { LOG(("rename_file: path present, but not a file: " LOG_S ", err: %d", spath, errno)); @@ -904,7 +921,12 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, } } - if (!NS_taccess(dpath, F_OK)) { +#ifdef XP_WIN + if (!NS_taccess(dpath, F_OK)) +#else + if (!S_ISLNK(spathInfo.st_mode) && !NS_taccess(dpath, F_OK)) +#endif + { if (ensure_remove(dpath)) { LOG(("rename_file: destination file exists and could not be " \ "removed: " LOG_S, dpath)); @@ -921,7 +943,7 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, return OK; } -#ifdef XP_WIN +#if defined(XP_WIN) && !defined(TOR_BROWSER_UPDATE) // Remove the directory pointed to by path and all of its files and // sub-directories. If a file is in use move it to the tobedeleted directory // and attempt to schedule removal of the file on reboot @@ -1012,7 +1034,19 @@ static int backup_restore(const NS_tchar *path, const NS_tchar *relPath) NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]), NS_T("%s") BACKUP_EXT, relPath); - if (NS_taccess(backup, F_OK)) { + bool isLink = false; +#ifndef XP_WIN + struct stat linkInfo; + int rv = lstat(backup, &linkInfo); + if (rv) { + LOG(("backup_restore: cannot get info for backup file: " LOG_S ", err: %d", + relBackup, errno)); + return OK; + } + isLink = S_ISLNK(linkInfo.st_mode); +#endif + + if (!isLink && NS_taccess(backup, F_OK)) { LOG(("backup_restore: backup file doesn't exist: " LOG_S, relBackup)); return OK; } @@ -1031,8 +1065,18 @@ static int backup_discard(const NS_tchar *path, const NS_tchar *relPath) NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]), NS_T("%s") BACKUP_EXT, relPath); + bool isLink = false; +#ifndef XP_WIN + struct stat linkInfo; + int rv2 = lstat(backup, &linkInfo); + if (rv2) { + return OK; // File does not exist; nothing to do. + } + isLink = S_ISLNK(linkInfo.st_mode); +#endif + // Nothing to discard - if (NS_taccess(backup, F_OK)) { + if (!isLink && NS_taccess(backup, F_OK)) { return OK; } @@ -1047,6 +1091,8 @@ static int backup_discard(const NS_tchar *path, const NS_tchar *relPath) relBackup, relPath)); return WRITE_ERROR_DELETE_BACKUP; } + +#if !defined(TOR_BROWSER_UPDATE) // The MoveFileEx call to remove the file on OS reboot will fail if the // process doesn't have write access to the HKEY_LOCAL_MACHINE registry key // but this is ok since the installer / uninstaller will delete the @@ -1059,6 +1105,7 @@ static int backup_discard(const NS_tchar *path, const NS_tchar *relPath) LOG(("backup_discard: failed to schedule OS reboot removal of " \ "file: " LOG_S, relPath)); } +#endif } #else if (rv) @@ -1113,7 +1160,7 @@ private: class RemoveFile : public Action { public: - RemoveFile() : mSkip(0) { } + RemoveFile() : mSkip(0), mIsLink(0) { } int Parse(NS_tchar *line); int Prepare(); @@ -1124,6 +1171,7 @@ private: mozilla::UniquePtr mFile; mozilla::UniquePtr mRelPath; int mSkip; + int mIsLink; }; int @@ -1150,28 +1198,39 @@ RemoveFile::Parse(NS_tchar *line) int RemoveFile::Prepare() { - // Skip the file if it already doesn't exist. - int rv = NS_taccess(mFile.get(), F_OK); - if (rv) { - mSkip = 1; - mProgressCost = 0; - return OK; + int rv; +#ifndef XP_WIN + struct stat linkInfo; + rv = lstat(mFile.get(), &linkInfo); + mIsLink = ((0 == rv) && S_ISLNK(linkInfo.st_mode)); +#endif + + if (!mIsLink) { + // Skip the file if it already doesn't exist. + rv = NS_taccess(mFile.get(), F_OK); + if (rv) { + mSkip = 1; + mProgressCost = 0; + return OK; + } } LOG(("PREPARE REMOVEFILE " LOG_S, mRelPath.get())); - // Make sure that we're actually a file... - struct NS_tstat_t fileInfo; - rv = NS_tstat(mFile.get(), &fileInfo); - if (rv) { - LOG(("failed to read file status info: " LOG_S ", err: %d", mFile.get(), - errno)); - return READ_ERROR; - } + if (!mIsLink) { + // Make sure that we're actually a file... + struct NS_tstat_t fileInfo; + rv = NS_tstat(mFile.get(), &fileInfo); + if (rv) { + LOG(("failed to read file status info: " LOG_S ", err: %d", mFile.get(), + errno)); + return READ_ERROR; + } - if (!S_ISREG(fileInfo.st_mode)) { - LOG(("path present, but not a file: " LOG_S, mFile.get())); - return DELETE_ERROR_EXPECTED_FILE; + if (!S_ISREG(fileInfo.st_mode)) { + LOG(("path present, but not a file: " LOG_S, mFile.get())); + return DELETE_ERROR_EXPECTED_FILE; + } } NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile.get(), NS_T('/')); @@ -1201,7 +1260,13 @@ RemoveFile::Execute() // The file is checked for existence here and in Prepare since it might have // been removed by a separate instruction: bug 311099. - int rv = NS_taccess(mFile.get(), F_OK); + int rv = 0; + if (mIsLink) { + struct NS_tstat_t linkInfo; + rv = NS_tlstat(mFile.get(), &linkInfo); + } else { + rv = NS_taccess(mFile.get(), F_OK); + } if (rv) { LOG(("file cannot be removed because it does not exist; skipping")); mSkip = 1; @@ -1967,6 +2032,106 @@ PatchIfFile::Finish(int status) PatchFile::Finish(status); } +#ifndef XP_WIN +class AddSymlink : public Action +{ +public: + AddSymlink() : mAdded(false) + { } + + virtual int Parse(NS_tchar *line); + virtual int Prepare(); + virtual int Execute(); + virtual void Finish(int status); + +private: + mozilla::UniquePtr mLinkPath; + mozilla::UniquePtr mRelPath; + mozilla::UniquePtr mTarget; + bool mAdded; +}; + +int +AddSymlink::Parse(NS_tchar *line) +{ + // format "" "target" + + NS_tchar * validPath = get_valid_path(&line); + if (!validPath) + return PARSE_ERROR; + + mRelPath = mozilla::MakeUnique(MAXPATHLEN); + NS_tstrcpy(mRelPath.get(), validPath); + mLinkPath.reset(get_full_path(validPath)); + if (!mLinkPath) { + return PARSE_ERROR; + } + + // consume whitespace between args + NS_tchar *q = mstrtok(kQuote, &line); + if (!q) + return PARSE_ERROR; + + validPath = get_valid_path(&line, false, true); + if (!validPath) + return PARSE_ERROR; + + mTarget = mozilla::MakeUnique(MAXPATHLEN); + NS_tstrcpy(mTarget.get(), validPath); + + return OK; +} + +int +AddSymlink::Prepare() +{ + LOG(("PREPARE ADDSYMLINK " LOG_S " -> " LOG_S, mRelPath.get(), mTarget.get())); + + return OK; +} + +int +AddSymlink::Execute() +{ + LOG(("EXECUTE ADDSYMLINK " LOG_S " -> " LOG_S, mRelPath.get(), mTarget.get())); + + // First make sure that we can actually get rid of any existing file or link. + struct stat linkInfo; + int rv = lstat(mLinkPath.get(), &linkInfo); + if ((0 == rv) && !S_ISLNK(linkInfo.st_mode)) { + rv = NS_taccess(mLinkPath.get(), F_OK); + } + if (rv == 0) { + rv = backup_create(mLinkPath.get()); + if (rv) + return rv; + } else { + rv = ensure_parent_dir(mLinkPath.get()); + if (rv) + return rv; + } + + // Create the link. + rv = symlink(mTarget.get(), mLinkPath.get()); + if (!rv) { + mAdded = true; + } + + return rv; +} + +void +AddSymlink::Finish(int status) +{ + LOG(("FINISH ADDSYMLINK " LOG_S " -> " LOG_S, mRelPath.get(), mTarget.get())); + // When there is an update failure and a link has been added it is removed + // here since there might not be a backup to replace it. + if (status && mAdded) + NS_tremove(mLinkPath.get()); + backup_finish(mLinkPath.get(), mRelPath.get(), status); +} +#endif + //----------------------------------------------------------------------------- #ifdef XP_WIN @@ -2286,14 +2451,29 @@ static int CopyInstallDirToDestDir() { // These files should not be copied over to the updated app -#ifdef XP_WIN -#define SKIPLIST_COUNT 3 -#elif XP_MACOSX -#define SKIPLIST_COUNT 0 +#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) + #ifdef XP_WIN + #define SKIPLIST_COUNT 6 + #else + #define SKIPLIST_COUNT 5 + #endif #else -#define SKIPLIST_COUNT 2 + #ifdef XP_WIN + #define SKIPLIST_COUNT 3 + #elif XP_MACOSX + #define SKIPLIST_COUNT 0 + #else + #define SKIPLIST_COUNT 2 + #endif #endif copy_recursive_skiplist skiplist; +#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) +#ifdef XP_MACOSX + skiplist.append(0, gInstallDirPath, NS_T("Updated.app")); + skiplist.append(1, gInstallDirPath, NS_T("TorBrowser/UpdateInfo/updates/0")); +#endif +#endif + #ifndef XP_MACOSX skiplist.append(0, gInstallDirPath, NS_T("updated")); skiplist.append(1, gInstallDirPath, NS_T("updates/0")); @@ -2302,6 +2482,23 @@ CopyInstallDirToDestDir() #endif #endif +#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) +#ifdef XP_WIN + skiplist.append(SKIPLIST_COUNT - 3, gInstallDirPath, + NS_T("TorBrowser/Data/Browser/profile.default/parent.lock")); + skiplist.append(SKIPLIST_COUNT - 2, gInstallDirPath, + NS_T("TorBrowser/Data/Browser/profile.meek-http-helper/parent.lock")); +#else + skiplist.append(SKIPLIST_COUNT - 3, gInstallDirPath, + NS_T("TorBrowser/Data/Browser/profile.default/.parentlock")); + skiplist.append(SKIPLIST_COUNT - 2, gInstallDirPath, + NS_T("TorBrowser/Data/Browser/profile.meek-http-helper/.parentlock")); +#endif + +skiplist.append(SKIPLIST_COUNT - 1, gInstallDirPath, + NS_T("TorBrowser/Data/Tor/lock")); +#endif + return ensure_copy_recursive(gInstallDirPath, gWorkingDirPath, skiplist); } @@ -2438,7 +2635,9 @@ ProcessReplaceRequest() if (NS_taccess(deleteDir, F_OK)) { NS_tmkdir(deleteDir, 0755); } +#if !defined(TOR_BROWSER_UPDATE) remove_recursive_on_reboot(tmpDir, deleteDir); +#endif #endif } @@ -2446,8 +2645,42 @@ ProcessReplaceRequest() // On OS X, we we need to remove the staging directory after its Contents // directory has been moved. NS_tchar updatedAppDir[MAXPATHLEN]; +#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) + NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]), + NS_T("%s/Updated.app"), gInstallDirPath); + // For Tor Browser on OS X, we also need to copy everything else that is inside Updated.app. + NS_tDIR *dir = NS_topendir(updatedAppDir); + if (dir) { + NS_tdirent *entry; + while ((entry = NS_treaddir(dir)) != 0) { + if (NS_tstrcmp(entry->d_name, NS_T(".")) && + NS_tstrcmp(entry->d_name, NS_T(".."))) { + NS_tchar childSrcPath[MAXPATHLEN]; + NS_tsnprintf(childSrcPath, sizeof(childSrcPath)/sizeof(childSrcPath[0]), + NS_T("%s/%s"), updatedAppDir, entry->d_name); + NS_tchar childDstPath[MAXPATHLEN]; + NS_tsnprintf(childDstPath, sizeof(childDstPath)/sizeof(childDstPath[0]), + NS_T("%s/%s"), gInstallDirPath, entry->d_name); + ensure_remove_recursive(childDstPath); + rv = rename_file(childSrcPath, childDstPath, true); + if (rv) { + LOG(("Moving " LOG_S " to " LOG_S " failed, err: %d", + childSrcPath, childDstPath, errno)); + } + } + } + + NS_tclosedir(dir); + } else { + LOG(("Updated.app dir can't be found: " LOG_S ", err: %d", + updatedAppDir, errno)); + } +#else NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]), NS_T("%s/Updated.app"), gPatchDirPath); +#endif + + // Remove the Updated.app directory. ensure_remove_recursive(updatedAppDir); #endif @@ -2693,11 +2926,15 @@ UpdateThreadFunc(void *param) static void ServeElevatedUpdateThreadFunc(void* param) { +#ifdef TOR_BROWSER_UPDATE + WriteStatusFile(ELEVATION_CANCELED); +#else UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param; gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv); if (!gSucceeded) { WriteStatusFile(ELEVATION_CANCELED); } +#endif QuitProgressUI(); } @@ -2721,7 +2958,7 @@ int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv, ) { if (argc > callbackIndex) { -#if defined(XP_WIN) +#if defined(XP_WIN) && !defined(TOR_BROWSER_UPDATE) if (gSucceeded) { if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) { fprintf(stderr, "The post update process was not launched"); @@ -2766,7 +3003,11 @@ int NS_main(int argc, NS_tchar **argv) #ifdef XP_MACOSX bool isElevated = +#ifdef TOR_BROWSER_UPDATE + false; +#else strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") != 0; +#endif if (isElevated) { if (!ObtainUpdaterArguments(&argc, &argv)) { // Won't actually get here because ObtainUpdaterArguments will terminate @@ -3382,6 +3623,26 @@ int NS_main(int argc, NS_tchar **argv) // using the service is because we are testing. if (!useService && !noServiceFallback && updateLockFileHandle == INVALID_HANDLE_VALUE) { +#ifdef TOR_BROWSER_UPDATE +#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR + // Because the TorBrowser-Data directory that contains the user's + // profile is a sibling of the Tor Browser installation directory, + // the user probably has permission to apply updates. Therefore, to + // avoid potential security issues such as CVE-2015-0833, do not + // attempt to elevate privileges. Instead, write a "failed" message + // to the update status file (this function will return immediately + // after the CloseHandle(elevatedFileHandle) call below). +#else + // Because the user profile is contained within the Tor Browser + // installation directory, the user almost certainly has permission to + // apply updates. Therefore, to avoid potential security issues such + // as CVE-2015-0833, do not attempt to elevate privileges. Instead, + // write a "failed" message to the update status file (this function + // will return immediately after the CloseHandle(elevatedFileHandle) + // call below). +#endif + WriteStatusFile(WRITE_ERROR_ACCESS_DENIED); +#else SHELLEXECUTEINFO sinfo; memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO)); sinfo.cbSize = sizeof(SHELLEXECUTEINFO); @@ -3403,6 +3664,7 @@ int NS_main(int argc, NS_tchar **argv) } else { WriteStatusFile(ELEVATION_CANCELED); } +#endif } if (argc > callbackIndex) { @@ -3740,6 +4002,7 @@ int NS_main(int argc, NS_tchar **argv) if (!sStagedUpdate && !sReplaceRequest && _wrmdir(gDeleteDirPath)) { LOG(("NS_main: unable to remove directory: " LOG_S ", err: %d", DELETE_DIR, errno)); +#if !defined(TOR_BROWSER_UPDATE) // The directory probably couldn't be removed due to it containing files // that are in use and will be removed on OS reboot. The call to remove the // directory on OS reboot is done after the calls to remove the files so the @@ -3756,6 +4019,7 @@ int NS_main(int argc, NS_tchar **argv) LOG(("NS_main: failed to schedule OS reboot removal of " \ "directory: " LOG_S, DELETE_DIR)); } +#endif } #endif /* XP_WIN */ @@ -4428,6 +4692,11 @@ int DoUpdate() else if (NS_tstrcmp(token, NS_T("patch-if")) == 0) { // Patch if exists action = new PatchIfFile(); } +#ifndef XP_WIN + else if (NS_tstrcmp(token, NS_T("addsymlink")) == 0) { + action = new AddSymlink(); + } +#endif else { LOG(("DoUpdate: unknown token: " LOG_S, token)); return PARSE_ERROR; diff --git a/toolkit/xre/MacLaunchHelper.h b/toolkit/xre/MacLaunchHelper.h index 08035c53b68cf..46e1570190554 100644 --- a/toolkit/xre/MacLaunchHelper.h +++ b/toolkit/xre/MacLaunchHelper.h @@ -17,7 +17,9 @@ extern "C" { * pid of the terminated process to confirm that it executed successfully. */ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid = 0); +#ifndef TOR_BROWSER_UPDATE bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid = 0); +#endif } #endif diff --git a/toolkit/xre/MacLaunchHelper.mm b/toolkit/xre/MacLaunchHelper.mm index 0dadb8de88953..84e28a4dd0a43 100644 --- a/toolkit/xre/MacLaunchHelper.mm +++ b/toolkit/xre/MacLaunchHelper.mm @@ -43,6 +43,7 @@ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) } } +#ifndef TOR_BROWSER_UPDATE BOOL InstallPrivilegedHelper() { AuthorizationRef authRef = NULL; @@ -135,3 +136,4 @@ bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid) } return didSucceed; } +#endif diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 46cfa89d52e3b..afb2eaf93d3fb 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -4160,12 +4160,20 @@ XREMain::XRE_mainStartup(bool* aExitFlag) NS_ENSURE_SUCCESS(rv, 1); rv = exeFile->GetParent(getter_AddRefs(exeDir)); NS_ENSURE_SUCCESS(rv, 1); +#ifdef TOR_BROWSER_UPDATE + nsAutoCString compatVersion(TOR_BROWSER_VERSION); +#endif ProcessUpdates(mDirProvider.GetGREDir(), exeDir, updRoot, gRestartArgc, gRestartArgv, - mAppData->version); +#ifdef TOR_BROWSER_UPDATE + compatVersion.get() +#else + mAppData->version +#endif + ); if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) { SaveToEnv("MOZ_TEST_PROCESS_UPDATES="); *aExitFlag = true; diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index 6f38a53d97632..789ffc16b5dd9 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -40,6 +40,7 @@ # include # include # include "nsWindowsHelpers.h" +# include "prprf.h" # define getcwd(path, size) _getcwd(path, size) # define getpid() GetCurrentProcessId() #elif defined(XP_UNIX) @@ -148,6 +149,46 @@ GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath) return NS_OK; } +#if defined(TOR_BROWSER_UPDATE) && defined(XP_WIN) +#define PATH_SEPARATOR ";" + +// In Tor Browser, updater.exe depends on some DLLs that are located in the +// app directory. To allow the updater to run when it has been copied into +// the update directory, we append the app directory to the PATH. +static nsresult +AdjustPathForUpdater(nsIFile *appDir) +{ + nsAutoCString appPath; + nsresult rv = appDir->GetNativePath(appPath); + NS_ENSURE_SUCCESS(rv, rv); + + char *s = nullptr; + char *pathValue = PR_GetEnv("PATH"); + if ((nullptr == pathValue) || ('\0' == *pathValue)) { + s = PR_smprintf("PATH=%s", appPath.get()); + } else { + s = PR_smprintf("PATH=%s" PATH_SEPARATOR "%s", pathValue, appPath.get()); + } + + // We intentionally leak the value that is passed into PR_SetEnv() because + // the environment will hold a pointer to it. + if ((nullptr == s) || (PR_SUCCESS != PR_SetEnv(s))) + return NS_ERROR_FAILURE; + + return NS_OK; +} +#endif + +#ifdef DEBUG +static void +dump_argv(const char *aPrefix, char **argv, int argc) +{ + printf("%s - %d args\n", aPrefix, argc); + for (int i = 0; i < argc; ++i) + printf(" %d: %s\n", i, argv[i]); +} +#endif + #if defined(XP_MACOSX) // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the // gBinaryPath check removed so that the updater can reload the stub executable @@ -235,6 +276,36 @@ typedef enum { eAppliedService, } UpdateStatus; +#ifdef DEBUG +static const char * +UpdateStatusToString(UpdateStatus aStatus) +{ + const char *rv = "unknown"; + switch (aStatus) { + case eNoUpdateAction: + rv = "NoUpdateAction"; + break; + case ePendingUpdate: + rv = "PendingUpdate"; + break; + case ePendingService: + rv = "PendingService"; + break; + case ePendingElevate: + rv = "PendingElevate"; + break; + case eAppliedUpdate: + rv = "AppliedUpdate"; + break; + case eAppliedService: + rv = "AppliedService"; + break; + } + + return rv; +} +#endif + /** * Returns a value indicating what needs to be done in order to handle an update. * @@ -307,12 +378,44 @@ IsOlderVersion(nsIFile *versionFile, const char *appVersion) if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0) return false; +#ifdef DEBUG + printf("IsOlderVersion checking appVersion %s against updateVersion %s\n", + appVersion, buf); +#endif + if (mozilla::Version(appVersion) > buf) return true; return false; } +#ifndef TOR_BROWSER_DATA_OUTSIDE_APP_DIR +#if defined(TOR_BROWSER_UPDATE) && defined(XP_MACOSX) +static nsresult +GetUpdateDirFromAppDir(nsIFile *aAppDir, nsIFile* *aResult) +{ + // On Mac OSX, we stage the update to an Updated.app directory that is + // directly below the main Tor Browser.app directory (two levels up from + // the appDir). + NS_ENSURE_ARG_POINTER(aAppDir); + NS_ENSURE_ARG_POINTER(aResult); + nsCOMPtr parentDir1, parentDir2; + nsresult rv = aAppDir->GetParent(getter_AddRefs(parentDir1)); + NS_ENSURE_SUCCESS(rv, rv); + rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr updatedDir; + if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { + return NS_ERROR_FAILURE; + } + + updatedDir.forget(aResult); + return NS_OK; +} +#endif +#endif + static bool CopyFileIntoUpdateDir(nsIFile *parentDir, const nsACString& leaf, nsIFile *updateDir) { @@ -529,7 +632,12 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, nsAutoCString applyToDir; nsCOMPtr updatedDir; #ifdef XP_MACOSX +#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) + rv = GetUpdateDirFromAppDir(appDir, getter_AddRefs(updatedDir)); + if (NS_FAILED(rv)) { +#else if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { +#endif #else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { #endif @@ -621,6 +729,13 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, AppendToLibPath(installDirPath.get()); #endif +#if defined(TOR_BROWSER_UPDATE) && defined(XP_WIN) + nsresult rv2 = AdjustPathForUpdater(appDir); + if (NS_FAILED(rv2)) { + LOG(("SwitchToUpdatedApp -- AdjustPathForUpdater failed (0x%x)\n", rv2)); + } +#endif + LOG(("spawning updater process for replacing [%s]\n", updaterPath.get())); #if defined(XP_UNIX) & !defined(XP_MACOSX) @@ -793,7 +908,12 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, applyToDir.Assign(installDirPath); } else { #ifdef XP_MACOSX +#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) + rv = GetUpdateDirFromAppDir(appDir, getter_AddRefs(updatedDir)); + if (NS_FAILED(rv)) { +#else if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { +#endif #else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { #endif @@ -907,6 +1027,14 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, if (isOSUpdate) { PR_SetEnv("MOZ_OS_UPDATE=1"); } + +#if defined(TOR_BROWSER_UPDATE) && defined(XP_WIN) + nsresult rv2 = AdjustPathForUpdater(appDir); + if (NS_FAILED(rv2)) { + LOG(("ApplyUpdate -- AdjustPathForUpdater failed (0x%x)\n", rv2)); + } +#endif + #if defined(MOZ_WIDGET_GONK) // We want the updater to be CPU friendly and not subject to being killed by // the low memory killer, so we pass in some preferences to allow it to @@ -929,6 +1057,9 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, #endif LOG(("spawning updater process [%s]\n", updaterPath.get())); +#ifdef DEBUG + dump_argv("ApplyUpdate updater", argv, argc); +#endif #if defined(XP_UNIX) && !defined(XP_MACOSX) // We use execv to spawn the updater process on all UNIX systems except Mac OSX @@ -957,6 +1088,10 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, } #elif defined(XP_MACOSX) CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart); +#ifdef DEBUG + dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc); +#endif +#ifndef TOR_BROWSER_UPDATE // We need to detect whether elevation is required for this update. This can // occur when an admin user installs the application, but another admin // user attempts to update (see bug 394984). @@ -967,12 +1102,15 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, } exit(0); } else { +#endif if (restart) { LaunchChildMac(argc, argv); exit(0); } LaunchChildMac(argc, argv, outpid); +#ifndef TOR_BROWSER_UPDATE } +#endif #else *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); if (restart) { @@ -1033,9 +1171,29 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, bool restart, bool isOSUpdate, nsIFile *osApplyToDir, ProcessType *pid) { +#if defined(XP_WIN) && defined(TOR_BROWSER_UPDATE) + // Try to remove the "tobedeleted" directory which, if present, contains + // files that could not be removed during a previous update (e.g., DLLs + // that were in use and therefore locked by Windows). + nsCOMPtr deleteDir; + nsresult winrv = appDir->Clone(getter_AddRefs(deleteDir)); + if (NS_SUCCEEDED(winrv)) { + winrv = deleteDir->AppendNative(NS_LITERAL_CSTRING("tobedeleted")); + if (NS_SUCCEEDED(winrv)) { + winrv = deleteDir->Remove(true); + } + } +#endif + nsresult rv; nsCOMPtr updatesDir; +#ifdef DEBUG + nsAutoCString path; + updRootDir->GetNativePath(path); + printf("ProcessUpdates updateRootDir: %s appVersion: %s\n", + path.get(), appVersion); +#endif rv = updRootDir->Clone(getter_AddRefs(updatesDir)); if (NS_FAILED(rv)) return rv; @@ -1060,6 +1218,12 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, nsCOMPtr statusFile; UpdateStatus status = GetUpdateStatus(updatesDir, statusFile); +#ifdef DEBUG + printf("ProcessUpdates status: %s (%d)\n", + UpdateStatusToString(status), status); + updatesDir->GetNativePath(path); + printf("ProcessUpdates updatesDir: %s\n", path.get()); +#endif switch (status) { case ePendingElevate: { if (NS_IsMainThread()) { @@ -1146,7 +1310,11 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) if (NS_FAILED(rv)) appDir = dirProvider->GetAppDir(); +#ifdef TOR_BROWSER_UPDATE + appVersion = TOR_BROWSER_VERSION; +#else appVersion = gAppData->version; +#endif argc = gRestartArgc; argv = gRestartArgv; } else { @@ -1176,6 +1344,8 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) getter_AddRefs(updRoot)); NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir"); + // To support Tor Browser updates from xpcshell, modify the following + // code to use TOR_BROWSER_VERSION from the configure process. nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1"); if (appInfo) { diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 41acf83c17a94..21f7c3ab5293d 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -1378,6 +1378,31 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult) NS_ENSURE_SUCCESS(rv, rv); #ifdef XP_MACOSX +#ifdef TOR_BROWSER_UPDATE +#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR + // For Tor Browser, we cannot store update history, etc. under the user's + // home directory. Instead, we place it under + // Tor Browser.app/../TorBrowser-Data/UpdateInfo/ + nsCOMPtr appRootDir; + rv = GetAppRootDir(getter_AddRefs(appRootDir)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr localDir; + rv = appRootDir->GetParent(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser-Data" + XPCOM_FILE_PATH_SEPARATOR "UpdateInfo")); +#else + // For Tor Browser, we cannot store update history, etc. under the user's home directory. + // Instead, we place it under Tor Browser.app/TorBrowser/UpdateInfo/ + nsCOMPtr localDir; + rv = GetAppRootDir(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = localDir->AppendNative(NS_LITERAL_CSTRING("TorBrowser")); + NS_ENSURE_SUCCESS(rv, rv); + rv = localDir->AppendNative(NS_LITERAL_CSTRING("UpdateInfo")); +#endif + NS_ENSURE_SUCCESS(rv, rv); +#else nsCOMPtr appRootDirFile; nsCOMPtr localDir; nsAutoString appDirPath; @@ -1408,6 +1433,7 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult) NS_FAILED(localDir->AppendRelativePath(appDirPath))) { return NS_ERROR_FAILURE; } +#endif localDir.forget(aResult); return NS_OK; @@ -1555,33 +1581,8 @@ nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal) NS_ENSURE_ARG_POINTER(aFile); nsCOMPtr localDir; - nsresult rv = GetAppDir()->Clone(getter_AddRefs(localDir)); + nsresult rv = GetAppRootDir(getter_AddRefs(localDir)); NS_ENSURE_SUCCESS(rv, rv); - - int levelsToRemove = 1; // In FF21+, appDir points to browser subdirectory. -#if defined(XP_MACOSX) - levelsToRemove += 2; -#endif - while (localDir && (levelsToRemove > 0)) { - // When crawling up the hierarchy, components named "." do not count. - nsAutoCString removedName; - rv = localDir->GetNativeLeafName(removedName); - NS_ENSURE_SUCCESS(rv, rv); - bool didRemove = !removedName.Equals("."); - - // Remove a directory component. - nsCOMPtr parentDir; - rv = localDir->GetParent(getter_AddRefs(parentDir)); - NS_ENSURE_SUCCESS(rv, rv); - localDir = parentDir; - - if (didRemove) - --levelsToRemove; - } - - if (!localDir) - return NS_ERROR_FAILURE; - rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser" XPCOM_FILE_PATH_SEPARATOR "Data" XPCOM_FILE_PATH_SEPARATOR "Browser")); @@ -1664,6 +1665,44 @@ nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal, return NS_OK; } +nsresult +nsXREDirProvider::GetAppRootDir(nsIFile* *aFile) +{ + NS_ENSURE_ARG_POINTER(aFile); + nsCOMPtr appRootDir; + + nsresult rv = GetAppDir()->Clone(getter_AddRefs(appRootDir)); + NS_ENSURE_SUCCESS(rv, rv); + + int levelsToRemove = 1; // In FF21+, appDir points to browser subdirectory. +#if defined(XP_MACOSX) + levelsToRemove += 2; +#endif + while (appRootDir && (levelsToRemove > 0)) { + // When crawling up the hierarchy, components named "." do not count. + nsAutoCString removedName; + rv = appRootDir->GetNativeLeafName(removedName); + NS_ENSURE_SUCCESS(rv, rv); + bool didRemove = !removedName.Equals("."); + + // Remove a directory component. + nsCOMPtr parentDir; + rv = appRootDir->GetParent(getter_AddRefs(parentDir)); + NS_ENSURE_SUCCESS(rv, rv); + appRootDir = parentDir; + + if (didRemove) + --levelsToRemove; + } + + if (!appRootDir) + return NS_ERROR_FAILURE; + + appRootDir.forget(aFile); + return NS_OK; +} + + nsresult nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory) { diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h index 17b8b9ff7e712..36bce9a256553 100644 --- a/toolkit/xre/nsXREDirProvider.h +++ b/toolkit/xre/nsXREDirProvider.h @@ -105,6 +105,7 @@ protected: #if defined(XP_UNIX) || defined(XP_MACOSX) static nsresult GetSystemExtensionsDirectory(nsIFile** aFile); #endif + nsresult GetAppRootDir(nsIFile* *aFile); static nsresult EnsureDirectoryExists(nsIFile* aDirectory); // Determine the profile path within the UAppData directory. This is different diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh index eb358806fd04a..96bfa4319e466 100755 --- a/tools/update-packaging/common.sh +++ b/tools/update-packaging/common.sh @@ -8,7 +8,13 @@ # Author: Darin Fisher # +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove all lines in this file that contain: +# TorBrowser/Data + # ----------------------------------------------------------------------------- +QUIET=0 + # By default just assume that these tools exist on our path MAR=${MAR:-mar} BZIP2=${BZIP2:-bzip2} @@ -21,6 +27,12 @@ notice() { echo "$*" 1>&2 } +verbose_notice() { + if [ $QUIET -eq 0 ]; then + notice "$*" + fi +} + get_file_size() { info=($(ls -ln "$1")) echo ${info[4]} @@ -54,22 +66,10 @@ make_add_instruction() { forced= fi - is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') - if [ $is_extension = "1" ]; then - # Use the subdirectory of the extensions folder as the file to test - # before performing this add instruction. - testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/') - notice " add-if \"$testdir\" \"$f\"" - echo "add-if \"$testdir\" \"$f\"" >> $filev2 - if [ ! $filev3 = "" ]; then - echo "add-if \"$testdir\" \"$f\"" >> $filev3 - fi - else - notice " add \"$f\"$forced" - echo "add \"$f\"" >> $filev2 - if [ ! $filev3 = "" ]; then - echo "add \"$f\"" >> $filev3 - fi + verbose_notice " add \"$f\"$forced" + echo "add \"$f\"" >> "$filev2" + if [ ! "$filev3" = "" ]; then + echo "add \"$f\"" >> "$filev3" fi } @@ -100,8 +100,19 @@ make_add_if_not_instruction() { f="$1" filev3="$2" - notice " add-if-not \"$f\" \"$f\"" - echo "add-if-not \"$f\" \"$f\"" >> $filev3 + verbose_notice " add-if-not \"$f\" \"$f\"" + echo "add-if-not \"$f\" \"$f\"" >> "$filev3" +} + +make_addsymlink_instruction() { + link="$1" + target="$2" + filev2="$3" + filev3="$4" + + verbose_notice " addsymlink: $link -> $target" + echo "addsymlink \"$link\" \"$target\"" >> "$filev2" + echo "addsymlink \"$link\" \"$target\"" >> "$filev3" } make_patch_instruction() { @@ -109,19 +120,9 @@ make_patch_instruction() { filev2="$2" filev3="$3" - is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') - if [ $is_extension = "1" ]; then - # Use the subdirectory of the extensions folder as the file to test - # before performing this add instruction. - testdir=$(echo "$f" | sed 's/\(.*distribution\/extensions\/[^\/]*\)\/.*/\1/') - notice " patch-if \"$testdir\" \"$f.patch\" \"$f\"" - echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> $filev2 - echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> $filev3 - else - notice " patch \"$f.patch\" \"$f\"" - echo "patch \"$f.patch\" \"$f\"" >> $filev2 - echo "patch \"$f.patch\" \"$f\"" >> $filev3 - fi + verbose_notice " patch \"$f.patch\" \"$f\"" + echo "patch \"$f.patch\" \"$f\"" >> "$filev2" + echo "patch \"$f.patch\" \"$f\"" >> "$filev3" } append_remove_instructions() { @@ -148,19 +149,19 @@ append_remove_instructions() { # Exclude comments if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then if [ $(echo "$f" | grep -c '\/$') = 1 ]; then - notice " rmdir \"$f\"" - echo "rmdir \"$f\"" >> $filev2 - echo "rmdir \"$f\"" >> $filev3 + verbose_notice " rmdir \"$f\"" + echo "rmdir \"$f\"" >> "$filev2" + echo "rmdir \"$f\"" >> "$filev3" elif [ $(echo "$f" | grep -c '\/\*$') = 1 ]; then # Remove the * f=$(echo "$f" | sed -e 's:\*$::') - notice " rmrfdir \"$f\"" - echo "rmrfdir \"$f\"" >> $filev2 - echo "rmrfdir \"$f\"" >> $filev3 + verbose_notice " rmrfdir \"$f\"" + echo "rmrfdir \"$f\"" >> "$filev2" + echo "rmrfdir \"$f\"" >> "$filev3" else - notice " remove \"$f\"" - echo "remove \"$f\"" >> $filev2 - echo "remove \"$f\"" >> $filev3 + verbose_notice " remove \"$f\"" + echo "remove \"$f\"" >> "$filev2" + echo "remove \"$f\"" >> "$filev3" fi fi fi @@ -170,6 +171,10 @@ append_remove_instructions() { # List all files in the current directory, stripping leading "./" # Pass a variable name and it will be filled as an array. +# To support Tor Browser updates, skip the following files: +# TorBrowser/Data/Browser/profiles.ini +# TorBrowser/Data/Browser/profile.default/bookmarks.html +# TorBrowser/Data/Tor/torrc list_files() { count=0 @@ -182,6 +187,11 @@ list_files() { | sed 's/\.\/\(.*\)/\1/' \ | sort -r > "temp-filelist" while read file; do + if [ "$file" = "TorBrowser/Data/Browser/profiles.ini" -o \ + "$file" = "TorBrowser/Data/Browser/profile.default/bookmarks.html" -o \ + "$file" = "TorBrowser/Data/Tor/torrc" ]; then + continue; + fi eval "${1}[$count]=\"$file\"" (( count++ )) done < "temp-filelist" @@ -203,3 +213,19 @@ list_dirs() { done < "temp-dirlist" rm "temp-dirlist" } + +# List all symbolic links in the current directory, stripping leading "./" +list_symlinks() { + count=0 + + find . -type l \ + | sed 's/\.\/\(.*\)/\1/' \ + | sort -r > "temp-symlinklist" + while read symlink; do + target=$(readlink "$symlink") + eval "${1}[$count]=\"$symlink\"" + eval "${2}[$count]=\"$target\"" + (( count++ )) + done < "temp-symlinklist" + rm "temp-symlinklist" +} diff --git a/tools/update-packaging/make_full_update.sh b/tools/update-packaging/make_full_update.sh index f0466144dfc6c..872221a65c1e5 100755 --- a/tools/update-packaging/make_full_update.sh +++ b/tools/update-packaging/make_full_update.sh @@ -28,10 +28,16 @@ if [ $1 = -h ]; then notice "" notice "Options:" notice " -h show this help text" + notice " -q be less verbose" notice "" exit 1 fi +if [ $1 = -q ]; then + QUIET=1 + shift +fi + # ----------------------------------------------------------------------------- archive="$1" @@ -63,17 +69,47 @@ if [ ! -f "precomplete" ]; then fi list_files files +list_symlinks symlinks symlink_targets + +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove the following lines (which remove entire directories +# which, if present, contain old, unpacked copies of HTTPS Everywhere): +# Make sure we delete the pre 5.1.0 HTTPS Everywhere as well in case it +# exists. The extension ID got changed with the version bump to 5.1.0. +ext_path='TorBrowser/Data/Browser/profile.default/extensions' +if [ -d "$ext_dir" ]; then + directories_to_remove="$ext_path/https-everywhere@eff.org $ext_path/https-everywhere-eff@eff.org" +else + directories_to_remove="" +fi +# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal popd # Add the type of update to the beginning of the update manifests. -> $updatemanifestv2 -> $updatemanifestv3 +> "$updatemanifestv2" +> "$updatemanifestv3" notice "" notice "Adding type instruction to update manifests" notice " type complete" -echo "type \"complete\"" >> $updatemanifestv2 -echo "type \"complete\"" >> $updatemanifestv3 +echo "type \"complete\"" >> "$updatemanifestv2" +echo "type \"complete\"" >> "$updatemanifestv3" + +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove the following lines: +# If removal of any old, existing directories is desired, emit the appropriate +# rmrfdir commands. +notice "" +notice "Adding directory removal instructions to update manifests" +for dir_to_remove in $directories_to_remove; do + # rmrfdir requires a trailing slash; if slash is missing, add one. + if ! [[ "$dir_to_remove" =~ /$ ]]; then + dir_to_remove="${dir_to_remove}/" + fi + echo "rmrfdir \"$dir_to_remove\"" >> "$updatemanifestv2" + echo "rmrfdir \"$dir_to_remove\"" >> "$updatemanifestv3" +done +# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal notice "" notice "Adding file add instructions to update manifests" @@ -99,6 +135,15 @@ for ((i=0; $i<$num_files; i=$i+1)); do targetfiles="$targetfiles \"$f\"" done +notice "" +notice "Adding symlink add instructions to update manifests" +num_symlinks=${#symlinks[*]} +for ((i=0; $i<$num_symlinks; i=$i+1)); do + link="${symlinks[$i]}" + target="${symlink_targets[$i]}" + make_addsymlink_instruction "$link" "$target" "$updatemanifestv2" "$updatemanifestv3" +done + # Append remove instructions for any dead files. notice "" notice "Adding file and directory remove instructions from file 'removed-files'" diff --git a/tools/update-packaging/make_incremental_update.sh b/tools/update-packaging/make_incremental_update.sh index 90372c017e9b1..622ae7f382c82 100755 --- a/tools/update-packaging/make_incremental_update.sh +++ b/tools/update-packaging/make_incremental_update.sh @@ -21,6 +21,7 @@ print_usage() { notice " -h show this help text" notice " -f clobber this file in the installation" notice " Must be a path to a file to clobber in the partial update." + notice " -q be less verbose" notice "" } @@ -71,6 +72,21 @@ check_for_forced_update() { ## "true" *giggle* return 0; fi + +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove the following lines: + # If the file in the skip list ends with /*, do a prefix match. + # This allows TorBrowser/Data/Browser/profile.default/extensions/https-everywhere-eff@eff.org/* + # to be used to force all HTTPS Everywhere files to be updated. + f_suffix=${f##*/} + if [[ $f_suffix = "*" ]]; then + f_prefix="${f%\/\*}"; + if [[ $forced_file_chk == $f_prefix* ]]; then + ## 0 means "true" + return 0; + fi + fi +# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal done ## 'false'... because this is bash. Oh yay! return 1; @@ -81,13 +97,19 @@ if [ $# = 0 ]; then exit 1 fi -requested_forced_updates='Contents/MacOS/firefox' +# Firefox uses requested_forced_updates='Contents/MacOS/firefox' due to +# 770996 but in Tor Browser we do not need that fix. +requested_forced_updates="" +directories_to_remove="" +extra_files_to_remove="" -while getopts "hf:" flag +while getopts "hqf:" flag do case "$flag" in h) print_usage; exit 0 ;; + q) QUIET=1 + ;; f) requested_forced_updates="$requested_forced_updates $OPTARG" ;; ?) print_usage; exit 1 @@ -114,6 +136,83 @@ updatemanifestv2="$workdir/updatev2.manifest" updatemanifestv3="$workdir/updatev3.manifest" archivefiles="updatev2.manifest updatev3.manifest" +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove the following lines: +# If the NoScript or HTTPS Everywhere extensions have changed between +# releases, add them to the "force updates" list. +ext_path='TorBrowser/Data/Browser/profile.default/extensions' +if [ -d "$newdir/$ext_path" ]; then + https_everywhere_dir='https-everywhere-eff@eff.org' + https_everywhere_xpi='https-everywhere-eff@eff.org.xpi' + noscript='{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi' + + # NoScript is a packed extension, so we simply compare the old and the new + # .xpi files. + noscript_path="$ext_path/$noscript" + diff -a "$olddir/$noscript_path" "$newdir/$noscript_path" > /dev/null + rc=$? + if [ $rc -gt 1 ]; then + notice "Unexpected exit $rc from $noscript_path diff command" + exit 2 + elif [ $rc -eq 1 ]; then + requested_forced_updates="$requested_forced_updates $noscript_path" + fi + + # As of HTTPS Everywhere 5.1.0, the extension ID gained "-eff". + # As of HTTPS Everywhere 5.2.2, the extension is packed (i.e., it remains + # an .xpi after it is installed in the browser profile). + force_https_update=0 + remove_unpacked_https_e_dirs=0 + unpacked_https_e_install_rdf="$ext_path/$https_everywhere_dir/install.rdf" + packed_https_e_path="$ext_path/$https_everywhere_xpi" + if [ -d "$newdir/$ext_path/$https_everywhere_dir" ]; then + # The new HTTPS-E extension is unpacked, and presumably the old one is + # too. We need to determine if any of the unpacked files have changed. + # Since that is messy, we simply compare the old install.rdf file to the + # new one. + diff "$olddir/$unpacked_https_e_install_rdf" \ + "$newdir/$unpacked_https_e_install_rdf" > /dev/null + rc=$? + if [ $rc -gt 1 -a -e "$olddir/$unpacked_https_e_install_rdf" ]; then + notice "Unexpected exit $rc from $unpacked_https_e_install_rdf diff command" + exit 2 + elif [ $rc -ge 1 ]; then + force_https_update=1 + remove_unpacked_https_e_dirs=1 + # In case we still ship an unpacked HTTPS-E extension but the user has + # updated to a packed one, arrange for the packed one to be removed. + extra_files_to_remove="$extra_files_to_remove $packed_https_e_path" + fi + elif [ -d "$olddir/$ext_path/$https_everywhere_dir" ]; then + # The old HTTPS-E extension is unpacked but the new one is packed. + force_https_update=1 + remove_unpacked_https_e_dirs=1 + else + # Both the old and new HTTPS-E extensions are packed. In this case we can + # simply compare the .xpi files to determine if the extension has changed. + diff -a "$olddir/$packed_https_e_path" "$newdir/$packed_https_e_path" > /dev/null + rc=$? + if [ $rc -gt 1 ]; then + notice "Unexpected exit $rc from $packed_https_e_path diff command" + exit 2 + elif [ $rc -eq 1 ]; then + force_https_update=1 + fi + fi + + if [ $force_https_update -ne 0 ]; then + requested_forced_updates="$requested_forced_updates $ext_path/$https_everywhere_dir/* $packed_https_e_path" + if [ "$remove_unpacked_https_e_dirs" -ne 0 ]; then + # The old version was unpacked, so remove the entire directory to ensure + # that the replace is "clean." Also, make sure we delete the pre 5.1.0 + # HTTPS Everywhere as well in case it exists (the extension ID got + # changed with the version bump to 5.1.0). + directories_to_remove="$directories_to_remove $ext_path/https-everywhere@eff.org $ext_path/$https_everywhere_dir" + fi + fi +fi +# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal + mkdir -p "$workdir" # Generate a list of all files in the target directory. @@ -123,6 +222,7 @@ if test $? -ne 0 ; then fi list_files oldfiles +list_symlinks oldsymlinks oldsymlink_targets list_dirs olddirs popd @@ -141,17 +241,34 @@ fi list_dirs newdirs list_files newfiles +list_symlinks newsymlinks newsymlink_targets popd # Add the type of update to the beginning of the update manifests. notice "" notice "Adding type instruction to update manifests" -> $updatemanifestv2 -> $updatemanifestv3 +> "$updatemanifestv2" +> "$updatemanifestv3" notice " type partial" -echo "type \"partial\"" >> $updatemanifestv2 -echo "type \"partial\"" >> $updatemanifestv3 +echo "type \"partial\"" >> "$updatemanifestv2" +echo "type \"partial\"" >> "$updatemanifestv3" + +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove the following lines: +# If removal of any old, existing directories is desired, emit the appropriate +# rmrfdir commands. +notice "" +notice "Adding directory removal instructions to update manifests" +for dir_to_remove in $directories_to_remove; do + # rmrfdir requires a trailing slash, so add one if missing. + if ! [[ "$dir_to_remove" =~ /$ ]]; then + dir_to_remove="${dir_to_remove}/" + fi + echo "rmrfdir \"$dir_to_remove\"" >> "$updatemanifestv2" + echo "rmrfdir \"$dir_to_remove\"" >> "$updatemanifestv3" +done +# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal notice "" notice "Adding file patch and add instructions to update manifests" @@ -244,6 +361,24 @@ for ((i=0; $i<$num_oldfiles; i=$i+1)); do fi done +# Remove and re-add symlinks +notice "" +notice "Adding symlink remove/add instructions to update manifests" +num_oldsymlinks=${#oldsymlinks[*]} +for ((i=0; $i<$num_oldsymlinks; i=$i+1)); do + link="${oldsymlinks[$i]}" + verbose_notice " remove: $link" + echo "remove \"$link\"" >> "$updatemanifestv2" + echo "remove \"$link\"" >> "$updatemanifestv3" +done + +num_newsymlinks=${#newsymlinks[*]} +for ((i=0; $i<$num_newsymlinks; i=$i+1)); do + link="${newsymlinks[$i]}" + target="${newsymlink_targets[$i]}" + make_addsymlink_instruction "$link" "$target" "$updatemanifestv2" "$updatemanifestv3" +done + # Newly added files notice "" notice "Adding file add instructions to update manifests" @@ -280,8 +415,8 @@ notice "Adding file remove instructions to update manifests" for ((i=0; $i<$num_removes; i=$i+1)); do f="${remove_array[$i]}" notice " remove \"$f\"" - echo "remove \"$f\"" >> $updatemanifestv2 - echo "remove \"$f\"" >> $updatemanifestv3 + echo "remove \"$f\"" >> "$updatemanifestv2" + echo "remove \"$f\"" >> "$updatemanifestv3" done # Add remove instructions for any dead files. @@ -289,6 +424,15 @@ notice "" notice "Adding file and directory remove instructions from file 'removed-files'" append_remove_instructions "$newdir" "$updatemanifestv2" "$updatemanifestv3" +# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, +# we should remove the following lines: +for f in $extra_files_to_remove; do + notice " remove \"$f\"" + echo "remove \"$f\"" >> "$updatemanifestv2" + echo "remove \"$f\"" >> "$updatemanifestv3" +done +# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal + notice "" notice "Adding directory remove instructions for directories that no longer exist" num_olddirs=${#olddirs[*]} @@ -298,8 +442,8 @@ for ((i=0; $i<$num_olddirs; i=$i+1)); do # If this dir doesn't exist in the new directory remove it. if [ ! -d "$newdir/$f" ]; then notice " rmdir $f/" - echo "rmdir \"$f/\"" >> $updatemanifestv2 - echo "rmdir \"$f/\"" >> $updatemanifestv3 + echo "rmdir \"$f/\"" >> "$updatemanifestv2" + echo "rmdir \"$f/\"" >> "$updatemanifestv3" fi done -- GitLab