From b4e6ceb356107f0d18c2ca63aff8764cb4c9fcc1 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 (required). --enable-tor-browser-update # Enable bundle update behavior (optional). 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. Hide the "What's new" links (removed app.releaseNotesURL preference value). 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, 16909, 24476, and 25909. Also fix Bug 26049: reduce the delay before the update prompt is displayed. Instead of Firefox's 2 days, we use 1 hour (after which time the update doorhanger will be displayed). Also fix bug 27221: purge the startup cache if the Tor Browser version changed (even if the Firefox version and build ID did not change), e.g., after a minor Tor Browser update. Also fix 27828: "Check for Tor Browser update" doesn't seem to do anything. squash! Bug 4234: Use the Firefox Update Process for Tor Browser. Also fix 32616: Disable GetSecureOutputDirectoryPath() functionality. --- .mozconfig | 1 - .mozconfig-asan | 3 +- .mozconfig-mac | 1 + .mozconfig-mingw | 3 +- browser/app/Makefile.in | 2 + browser/app/profile/000-tor-browser.js | 9 +- browser/app/profile/firefox.js | 11 +- .../base/content/aboutDialog-appUpdater.js | 2 +- browser/base/content/aboutDialog.js | 12 +- browser/branding/branding-common.mozbuild | 11 +- .../official/pref/firefox-branding.js | 20 +- browser/components/BrowserContentHandler.jsm | 36 +- browser/confvars.sh | 33 +- browser/installer/Makefile.in | 4 + browser/installer/package-manifest.in | 2 + build/moz.configure/init.configure | 3 +- build/moz.configure/old.configure | 2 + config/createprecomplete.py | 17 +- .../src/actions/runtimes.js | 4 + old-configure.in | 22 ++ toolkit/modules/AppConstants.jsm | 2 + toolkit/modules/UpdateUtils.jsm | 18 +- toolkit/mozapps/extensions/AddonManager.jsm | 24 ++ .../mozapps/extensions/test/browser/head.js | 1 + .../extensions/test/xpcshell/head_addons.js | 1 + toolkit/mozapps/update/UpdateService.jsm | 137 ++++++- toolkit/mozapps/update/UpdateServiceStub.jsm | 4 + .../mozapps/update/common/updatehelper.cpp | 8 + toolkit/mozapps/update/moz.build | 5 +- .../mozapps/update/updater/launchchild_osx.mm | 2 + toolkit/mozapps/update/updater/moz.build | 2 +- toolkit/mozapps/update/updater/updater.cpp | 335 ++++++++++++++++-- toolkit/xre/MacLaunchHelper.h | 2 + toolkit/xre/MacLaunchHelper.mm | 2 + toolkit/xre/nsAppRunner.cpp | 22 +- toolkit/xre/nsUpdateDriver.cpp | 109 +++++- toolkit/xre/nsXREDirProvider.cpp | 36 ++ tools/update-packaging/common.sh | 92 +++-- tools/update-packaging/make_full_update.sh | 45 +++ .../make_incremental_update.sh | 149 +++++++- 40 files changed, 1029 insertions(+), 165 deletions(-) diff --git a/.mozconfig b/.mozconfig index f4ffd8cabce53..b4c5440304acc 100755 --- a/.mozconfig +++ b/.mozconfig @@ -28,7 +28,6 @@ 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-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 1f51e328d2518..bb9c1f2bf8f0a 100644 --- a/.mozconfig-asan +++ b/.mozconfig-asan @@ -27,11 +27,12 @@ ac_add_options --enable-official-branding # Let's support GTK3 for ESR60 ac_add_options --enable-default-toolkit=cairo-gtk3 +ac_add_options --enable-tor-browser-update + ac_add_options --disable-strip ac_add_options --disable-install-strip ac_add_options --enable-tests ac_add_options --enable-debug -ac_add_options --disable-updater ac_add_options --disable-crashreporter ac_add_options --disable-webrtc ac_add_options --disable-eme diff --git a/.mozconfig-mac b/.mozconfig-mac index 064e62ec7743d..25bf2e4824263 100644 --- a/.mozconfig-mac +++ b/.mozconfig-mac @@ -41,6 +41,7 @@ ac_add_options --enable-optimize ac_add_options --disable-debug ac_add_options --enable-tor-browser-data-outside-app-dir +ac_add_options --enable-tor-browser-update ac_add_options --disable-crashreporter ac_add_options --disable-webrtc diff --git a/.mozconfig-mingw b/.mozconfig-mingw index 4603e5b80dcc7..438ef3c228557 100644 --- a/.mozconfig-mingw +++ b/.mozconfig-mingw @@ -13,7 +13,8 @@ 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 +ac_add_options --disable-bits-download # 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 513bf9b5ed2ac..cce2d8a3806be 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -98,10 +98,12 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS' cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns' cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/document.icns '$(dist_dest)/Contents/Resources/document.icns' +ifndef TOR_BROWSER_UPDATE $(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices' ifdef MOZ_UPDATER 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 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 fda1d353a0533..952a691d90e6a 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", ""); @@ -22,8 +21,11 @@ pref("startup.homepage_override_url", "https://blog.torproject.org/category/tags pref("app.update.promptWaitTime", 3600); pref("app.update.badge", true); -#ifdef XP_WIN -// For now, disable staged updates on Windows (see #18292). +#ifndef XP_MACOSX +// Disable staged updates on platforms other than macOS. +// Staged updates do not work on Windows due to #18292. +// Also, on Windows and Linux any changes that are made to the browser profile +// or Tor data after an update is staged will be lost. pref("app.update.staging.enabled", false); #endif @@ -83,6 +85,7 @@ pref("datareporting.policy.dataSubmissionEnabled", false); // Make sure Unified Telemetry is really disabled, see: #18738. pref("toolkit.telemetry.unified", false); pref("toolkit.telemetry.enabled", false); +pref("toolkit.telemetry.updatePing.enabled", false); // Make sure updater telemetry is disabled; see #25909. // No experiments, use Tor Browser. See 21797. pref("experiments.enabled", false); pref("browser.syncPromoViewsLeftMap", "{\"addons\":0, \"passwords\":0, \"bookmarks\":0}"); // Don't promote sync diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index f4950e61d3929..f3efd5aa297fc 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -143,14 +143,8 @@ pref("app.update.download.promptMaxAttempts", 2); pref("app.update.elevation.promptMaxAttempts", 2); // If set to true, the Update Service will automatically download updates if the -// user can apply updates. This pref is no longer used on Windows, except as the -// default value to migrate to the new location that this data is now stored -// (which is in a file in the update directory). Because of this, this pref -// should no longer be used directly. Instead, getAppUpdateAutoEnabled and -// getAppUpdateAutoEnabled from UpdateUtils.jsm should be used. -#ifndef XP_WIN +// user can apply updates. pref("app.update.auto", true); -#endif // If set to true, the Update Service will present no UI for any event. pref("app.update.silent", false); @@ -162,7 +156,8 @@ pref("app.update.silent", 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-appUpdater.js b/browser/base/content/aboutDialog-appUpdater.js index 23e58c5bafeaa..7384e7c213340 100644 --- a/browser/base/content/aboutDialog-appUpdater.js +++ b/browser/base/content/aboutDialog-appUpdater.js @@ -221,7 +221,7 @@ appUpdater.prototype = { if (aChildID == "downloadAndInstall") { let updateVersion = gAppUpdater.update.displayVersion; // Include the build ID if this is an "a#" (nightly or aurora) build - if (/a\d+$/.test(updateVersion)) { + if (!AppConstants.TOR_BROWSER_UPDATE && /a\d+$/.test(updateVersion)) { let buildID = gAppUpdater.update.buildID; let year = buildID.slice(0, 4); let month = buildID.slice(4, 6); diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js index f1f5a37b68bc5..5f0f90cb3ca28 100644 --- a/browser/base/content/aboutDialog.js +++ b/browser/base/content/aboutDialog.js @@ -45,15 +45,13 @@ async function init(aEvent) { bits: Services.appinfo.is64Bit ? 64 : 32, }; + // Adjust version text to show the Tor Browser version + versionAttributes.version = AppConstants.TOR_BROWSER_VERSION + + " (based on Mozilla Firefox " + + AppConstants.MOZ_APP_VERSION_DISPLAY + ")"; + let version = Services.appinfo.version; if (/a\d+$/.test(version)) { - versionId = "aboutDialog-version-nightly"; - let buildID = Services.appinfo.appBuildID; - let year = buildID.slice(0, 4); - let month = buildID.slice(4, 6); - let day = buildID.slice(6, 8); - versionAttributes.isodate = `${year}-${month}-${day}`; - document.getElementById("experimental").hidden = false; document.getElementById("communityDesc").hidden = true; } diff --git a/browser/branding/branding-common.mozbuild b/browser/branding/branding-common.mozbuild index 78541ab5f528e..c810138754d7c 100644 --- a/browser/branding/branding-common.mozbuild +++ b/browser/branding/branding-common.mozbuild @@ -6,14 +6,9 @@ @template def FirefoxBranding(): - if CONFIG['MOZ_BRANDING_DIRECTORY'] == 'browser/branding/official': - JS_PREFERENCE_PP_FILES += [ - 'pref/firefox-branding.js', - ] - else: - JS_PREFERENCE_FILES += [ - 'pref/firefox-branding.js', - ] + JS_PREFERENCE_FILES += [ + 'pref/firefox-branding.js', + ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': FINAL_TARGET_FILES['..'] += [ diff --git a/browser/branding/official/pref/firefox-branding.js b/browser/branding/official/pref/firefox-branding.js index 59cf676fd713c..60b719a5eb31b 100644 --- a/browser/branding/official/pref/firefox-branding.js +++ b/browser/branding/official/pref/firefox-branding.js @@ -7,22 +7,16 @@ pref("startup.homepage_welcome_url", "about:welcome"); pref("startup.homepage_welcome_url.additional", ""); // Interval: Time between checks for a new version (in seconds) pref("app.update.interval", 43200); // 12 hours -// 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=1 hour +pref("app.update.promptWaitTime", 3600); // app.update.url.manual: URL user can browse to manually if for some reason // all update installation attempts fail. // app.update.url.details: a default value for the "More information about this // update" link supplied in the "An update is available" page of the update // wizard. -#if MOZ_UPDATE_CHANNEL == beta -pref("app.update.url.manual", "https://www.mozilla.org/firefox/beta"); -pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/beta/notes"); -pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew"); -#else -pref("app.update.url.manual", "https://www.mozilla.org/firefox/"); -pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes"); -pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew"); -#endif + +pref("app.update.url.manual", "https://www.torproject.org/download/languages/"); +pref("app.update.url.details", "https://www.torproject.org/download/"); // The number of days a binary is permitted to be old // without checking for an update. This assumes that @@ -30,8 +24,8 @@ pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/ pref("app.update.checkInstallTime.days", 63); // Give the user x seconds to reboot before showing a badge on the hamburger -// button. default=4 days -pref("app.update.badgeWaitTime", 345600); +// button. default=immediately +pref("app.update.badgeWaitTime", 0); // Number of usages of the web console or scratchpad. // If this is less than 5, then pasting code into the web console or scratchpad is disabled diff --git a/browser/components/BrowserContentHandler.jsm b/browser/components/BrowserContentHandler.jsm index 8d053135fd15b..3bfb9772f1dbf 100644 --- a/browser/components/BrowserContentHandler.jsm +++ b/browser/components/BrowserContentHandler.jsm @@ -43,6 +43,8 @@ XPCOMUtils.defineLazyGlobalGetters(this, [URL]); const NEWINSTALL_PAGE = "about:newinstall"; +const kTBSavedVersionPref = "browser.startup.homepage_override.torbrowser.version"; + function shouldLoadURI(aURI) { if (aURI && !aURI.schemeIs("chrome")) { return true; @@ -106,7 +108,8 @@ const OVERRIDE_ALTERNATE_PROFILE = 4; * 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. @@ -129,6 +132,11 @@ function needHomepageOverride(prefb) { var mstone = Services.appinfo.platformVersion; + var savedTBVersion = null; + try { + savedTBVersion = prefb.getCharPref(kTBSavedVersionPref); + } catch (e) {} + var savedBuildID = prefb.getCharPref( "browser.startup.homepage_override.buildID", "" @@ -147,7 +155,22 @@ function needHomepageOverride(prefb) { prefb.setCharPref("browser.startup.homepage_override.mstone", mstone); prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); - return savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE; + prefb.setCharPref(kTBSavedVersionPref, AppConstants.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. + let updated = prefb.prefHasUserValue("app.update.postupdate"); + return (savedmstone || updated) ? OVERRIDE_NEW_MSTONE + : OVERRIDE_NEW_PROFILE; + } + + if (AppConstants.TOR_BROWSER_VERSION != savedTBVersion) { + prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); + prefb.setCharPref(kTBSavedVersionPref, AppConstants.TOR_BROWSER_VERSION); + return OVERRIDE_NEW_MSTONE; } if (buildID != savedBuildID) { @@ -646,6 +669,13 @@ nsBrowserContentHandler.prototype = { "browser.startup.homepage_override.buildID", "unknown" ); + + // We do the same for the Tor Browser version. + let old_tbversion = null; + try { + old_tbversion = prefb.getCharPref(kTBSavedVersionPref); + } catch (e) {} + override = needHomepageOverride(prefb); if (override != OVERRIDE_NONE) { switch (override) { @@ -685,6 +715,8 @@ nsBrowserContentHandler.prototype = { } overridePage = overridePage.replace("%OLD_VERSION%", old_mstone); + overridePage = overridePage.replace("%OLD_TOR_BROWSER_VERSION%", + old_tbversion); break; } } diff --git a/browser/confvars.sh b/browser/confvars.sh index 5de0228fb2ca2..adf45362014a8 100755 --- a/browser/confvars.sh +++ b/browser/confvars.sh @@ -8,26 +8,6 @@ MOZ_APP_VENDOR=Mozilla MOZ_UPDATER=1 MOZ_PHOENIX=1 -if test "$OS_ARCH" = "WINNT"; then - if ! test "$HAVE_64BIT_BUILD"; then - if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \ - "$MOZ_UPDATE_CHANNEL" = "nightly-try" -o \ - "$MOZ_UPDATE_CHANNEL" = "aurora" -o \ - "$MOZ_UPDATE_CHANNEL" = "beta" -o \ - "$MOZ_UPDATE_CHANNEL" = "release"; then - if ! test "$MOZ_DEBUG"; then - if ! test "$USE_STUB_INSTALLER"; then - # Expect USE_STUB_INSTALLER from taskcluster for downstream task consistency - echo "ERROR: STUB installer expected to be enabled but" - echo "ERROR: USE_STUB_INSTALLER is not specified in the environment" - exit 1 - fi - MOZ_STUB_INSTALLER=1 - fi - fi - fi -fi - # Enable building ./signmar and running libmar signature tests MOZ_ENABLE_SIGNMAR=1 @@ -53,9 +33,18 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} if test "$MOZ_UPDATE_CHANNEL" = "aurora"; then ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-aurora MAR_CHANNEL_ID=firefox-mozilla-aurora +elif test "$MOZ_UPDATE_CHANNEL" = "alpha"; then + # alpha is currently using the -release channel id. However we accept + # both -alpha and -release channel ID for the alpha so that we can + # switch it to -alpha at some point. See bug 32498. + ACCEPTED_MAR_CHANNEL_IDS=torbrowser-torproject-alpha,torbrowser-torproject-release + MAR_CHANNEL_ID=torbrowser-torproject-release +elif test "$MOZ_UPDATE_CHANNEL" = "nightly"; then + ACCEPTED_MAR_CHANNEL_IDS=torbrowser-torproject-nightly + MAR_CHANNEL_ID=torbrowser-torproject-nightly else - ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-esr - MAR_CHANNEL_ID=firefox-mozilla-esr + ACCEPTED_MAR_CHANNEL_IDS=torbrowser-torproject-release + MAR_CHANNEL_ID=torbrowser-torproject-release fi # ASan reporter builds should have different channel ids if [ "${MOZ_ASAN_REPORTER}" = "1" ]; then diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in index d250a3d50355f..bf486ef48ae26 100644 --- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -93,6 +93,10 @@ ifdef TOR_BROWSER_DISABLE_TOR_LAUNCHER DEFINES += -DTOR_BROWSER_DISABLE_TOR_LAUNCHER 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 7cb9ee51790c8..a42dc801d8eb0 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -36,8 +36,10 @@ ; Mac bundle stuff @APPNAME@/Contents/Info.plist #ifdef MOZ_UPDATER +#ifndef TOR_BROWSER_UPDATE @APPNAME@/Contents/Library/LaunchServices #endif +#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 c772604035526..bb13599108622 100644 --- a/build/moz.configure/init.configure +++ b/build/moz.configure/init.configure @@ -1142,7 +1142,6 @@ def version_path(path): # 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, build_project, version_path, '--help') @imports(_from='__builtin__', _import='open') @@ -1189,7 +1188,7 @@ def milestone(build_env, build_project, version_path, _): if 'a1' in milestone: is_nightly = True - elif 'a' not in milestone: + else: is_release_or_beta = True major_version = milestone.split('.')[0] diff --git a/build/moz.configure/old.configure b/build/moz.configure/old.configure index 9271ab2629dc4..8acf540c13b5d 100644 --- a/build/moz.configure/old.configure +++ b/build/moz.configure/old.configure @@ -263,6 +263,8 @@ def old_configure_options(*options): '--x-libraries', # Tor additions. + '--with-tor-browser-version', + '--enable-tor-browser-update', '--enable-tor-browser-data-outside-app-dir', '--enable-tor-launcher', ) diff --git a/config/createprecomplete.py b/config/createprecomplete.py index 7a0a697d6a464..5bea7f9e1fbf7 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 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/devtools/client/aboutdebugging-new/src/actions/runtimes.js b/devtools/client/aboutdebugging-new/src/actions/runtimes.js index e43ba1a3d76b8..d9e696c408b6b 100644 --- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js +++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js @@ -68,6 +68,10 @@ async function getRuntimeIcon(runtime, channel) { } } + // Use the release build skin for devtools within Tor Browser alpha releases. + if (channel === "alpha") + return "chrome://devtools/skin/images/aboutdebugging-firefox-release.svg"; + return channel === "release" || channel === "beta" || channel === "aurora" ? `chrome://devtools/skin/images/aboutdebugging-firefox-${channel}.svg` : "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg"; diff --git a/old-configure.in b/old-configure.in index 8d0994cf49e8b..6a1bdec40cd3a 100644 --- a/old-configure.in +++ b/old-configure.in @@ -2193,6 +2193,25 @@ 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") + +if test -z "$TOR_BROWSER_VERSION"; then + AC_MSG_ERROR([--with-tor-browser-version is required for Tor Browser.]) +fi + +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 + AC_DEFINE(TOR_BROWSER_UPDATE) +fi + MOZ_ARG_ENABLE_BOOL(tor-browser-data-outside-app-dir, [ --enable-tor-browser-data-outside-app-dir Enable Tor Browser data outside of app directory], @@ -2203,6 +2222,9 @@ if test -n "$TOR_BROWSER_DATA_OUTSIDE_APP_DIR"; then AC_DEFINE(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) fi +AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION,$TOR_BROWSER_VERSION) +AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION_QUOTED,"$TOR_BROWSER_VERSION") +AC_SUBST(TOR_BROWSER_UPDATE) AC_SUBST(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) MOZ_ARG_DISABLE_BOOL(tor-launcher, diff --git a/toolkit/modules/AppConstants.jsm b/toolkit/modules/AppConstants.jsm index 004171775fbe4..109f1f94e3d3c 100644 --- a/toolkit/modules/AppConstants.jsm +++ b/toolkit/modules/AppConstants.jsm @@ -322,6 +322,8 @@ this.AppConstants = Object.freeze({ MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@", ANDROID_PACKAGE_NAME: "@ANDROID_PACKAGE_NAME@", + TOR_BROWSER_VERSION: "@TOR_BROWSER_VERSION@", + DEBUG_JS_MODULES: "@DEBUG_JS_MODULES@", MOZ_BING_API_CLIENTID: "@MOZ_BING_API_CLIENTID@", diff --git a/toolkit/modules/UpdateUtils.jsm b/toolkit/modules/UpdateUtils.jsm index 3ffb8a5e13764..e458dac76228d 100644 --- a/toolkit/modules/UpdateUtils.jsm +++ b/toolkit/modules/UpdateUtils.jsm @@ -98,7 +98,7 @@ var UpdateUtils = { case "PRODUCT": return Services.appinfo.name; case "VERSION": - return Services.appinfo.version; + return AppConstants.TOR_BROWSER_VERSION; case "BUILD_ID": return Services.appinfo.appBuildID; case "BUILD_TARGET": @@ -162,15 +162,17 @@ var UpdateUtils = { * downloads and installs updates. This corresponds to whether or not the user * has selected "Automatically install updates" in about:preferences. * - * On Windows, this setting is shared across all profiles for the installation + * On Windows (except in Tor Browser), this setting is shared across all profiles + * for the installation * and is read asynchrnously from the file. On other operating systems, this * setting is stored in a pref and is thus a per-profile setting. * * @return A Promise that resolves with a boolean. */ getAppUpdateAutoEnabled() { - if (AppConstants.platform != "win") { - // On platforms other than Windows the setting is stored in a preference. + if (AppConstants.TOR_BROWSER_UPDATE || (AppConstants.platform != "win")) { + // On platforms other than Windows and always in Tor Browser the setting + // is stored in a preference. let prefValue = Services.prefs.getBoolPref( PREF_APP_UPDATE_AUTO, DEFAULT_APP_UPDATE_AUTO @@ -241,7 +243,8 @@ var UpdateUtils = { * updates" and "Check for updates but let you choose to install them" options * in about:preferences. * - * On Windows, this setting is shared across all profiles for the installation + * On Windows (except in Tor Browser), this setting is shared across all profiles + * for the installation * and is written asynchrnously to the file. On other operating systems, this * setting is stored in a pref and is thus a per-profile setting. * @@ -257,8 +260,9 @@ var UpdateUtils = { * this operation simply sets a pref. */ setAppUpdateAutoEnabled(enabledValue) { - if (AppConstants.platform != "win") { - // Only in Windows do we store the update config in the update directory + if (AppConstants.TOR_BROWSER_UPDATE || (AppConstants.platform != "win")) { + // Only in Windows (but never for Tor Browser) do we store the update config + // in the update directory let prefValue = !!enabledValue; Services.prefs.setBoolPref(PREF_APP_UPDATE_AUTO, prefValue); maybeUpdateAutoConfigChanged(prefValue); diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index 7e38ade67dcbb..424cce9d636e2 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -23,6 +23,7 @@ const { AppConstants } = ChromeUtils.import( const MOZ_COMPATIBILITY_NIGHTLY = ![ "aurora", + "alpha", "beta", "release", "esr", @@ -36,6 +37,7 @@ const PREF_EM_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault"; const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility"; const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity"; const PREF_SYS_ADDON_UPDATE_ENABLED = "extensions.systemAddon.update.enabled"; +const PREF_EM_LAST_TORBROWSER_VERSION = "extensions.lastTorBrowserVersion"; const PREF_MIN_WEBEXT_PLATFORM_VERSION = "extensions.webExtensionsMinPlatformVersion"; @@ -748,6 +750,28 @@ var AddonManagerInternal = { ); } + // To ensure that extension and plugin code gets a chance to run + // after each browser update, set appChanged = true when the + // Tor Browser version has changed even if the Mozilla app + // version has not changed. + let tbChanged = undefined; + try { + tbChanged = AppConstants.TOR_BROWSER_VERSION != + Services.prefs.getCharPref(PREF_EM_LAST_TORBROWSER_VERSION); + } + catch (e) { } + if (tbChanged !== false) { + // Because PREF_EM_LAST_TORBROWSER_VERSION was not present in older + // versions of Tor Browser, an app change is indicated when tbChanged + // is undefined or true. + if (appChanged === false) { + appChanged = true; + } + + Services.prefs.setCharPref(PREF_EM_LAST_TORBROWSER_VERSION, + AppConstants.TOR_BROWSER_VERSION); + } + if (!MOZ_COMPATIBILITY_NIGHTLY) { PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + diff --git a/toolkit/mozapps/extensions/test/browser/head.js b/toolkit/mozapps/extensions/test/browser/head.js index b0f4e40cbdb22..46e03e1fec84c 100644 --- a/toolkit/mozapps/extensions/test/browser/head.js +++ b/toolkit/mozapps/extensions/test/browser/head.js @@ -44,6 +44,7 @@ var PREF_CHECK_COMPATIBILITY; var channel = Services.prefs.getCharPref("app.update.channel", "default"); if ( channel != "aurora" && + channel != "alpha" && channel != "beta" && channel != "release" && channel != "esr" diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js index 7d263d0cdd642..4eb80d55f0878 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js +++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -383,6 +383,7 @@ function isNightlyChannel() { return ( channel != "aurora" && + channel != "alpha" && channel != "beta" && channel != "release" && channel != "esr" diff --git a/toolkit/mozapps/update/UpdateService.jsm b/toolkit/mozapps/update/UpdateService.jsm index 442ef958739b2..db95e0e91c125 100644 --- a/toolkit/mozapps/update/UpdateService.jsm +++ b/toolkit/mozapps/update/UpdateService.jsm @@ -32,11 +32,15 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser", "XMLHttpRequest"]); XPCOMUtils.defineLazyModuleGetters(this, { AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm", CertUtils: "resource://gre/modules/CertUtils.jsm", +#ifdef XP_WIN ctypes: "resource://gre/modules/ctypes.jsm", +#endif DeferredTask: "resource://gre/modules/DeferredTask.jsm", OS: "resource://gre/modules/osfile.jsm", UpdateUtils: "resource://gre/modules/UpdateUtils.jsm", +#if !defined(TOR_BROWSER_UPDATE) WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm", +#endif }); const UPDATESERVICE_CID = Components.ID( @@ -307,6 +311,7 @@ function testWriteAccess(updateTestFile, createDirectory) { updateTestFile.remove(false); } +#ifdef XP_WIN /** * Windows only function that closes a Win32 handle. * @@ -397,6 +402,7 @@ function getPerInstallationMutexName(aGlobal = true) { (aGlobal ? "Global\\" : "") + "MozillaUpdateMutex-" + hasher.finish(true) ); } +#endif /** * Whether or not the current instance has the update mutex. The update mutex @@ -407,6 +413,7 @@ function getPerInstallationMutexName(aGlobal = true) { * @return true if this instance holds the update mutex */ function hasUpdateMutex() { +#ifdef XP_WIN if (AppConstants.platform != "win") { return true; } @@ -414,6 +421,9 @@ function hasUpdateMutex() { gUpdateMutexHandle = createMutex(getPerInstallationMutexName(true), false); } return !!gUpdateMutexHandle; +#else + return true; +#endif } /** @@ -444,6 +454,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; } @@ -478,6 +493,7 @@ function getElevationRequired() { "not required" ); return false; +#endif } /** @@ -510,6 +526,7 @@ function getCanApplyUpdates() { return false; } +#if !defined(TOR_BROWSER_UPDATE) if (AppConstants.platform == "macosx") { LOG( "getCanApplyUpdates - bypass the write since elevation can be used " + @@ -525,6 +542,7 @@ function getCanApplyUpdates() { ); return true; } +#endif try { if (AppConstants.platform == "win") { @@ -1190,6 +1208,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 = Services.prefs.getIntPref( PREF_APP_UPDATE_CANCELATIONS_OSX, 0 @@ -1213,6 +1234,7 @@ function handleUpdateFailure(update, errorCode) { (update.state = STATE_PENDING_ELEVATE) ); } +#endif update.statusText = gUpdateBundle.GetStringFromName("elevationFailure"); update.QueryInterface(Ci.nsIWritablePropertyBag); update.setProperty("patchingFailed", "elevationFailure"); @@ -1745,7 +1767,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; } @@ -1783,9 +1824,7 @@ function Update(update) { if (!isNaN(attr.value)) { this.promptWaitTime = parseInt(attr.value); } - } else if (attr.name == "unsupported") { - this.unsupported = attr.value == "true"; - } else { + } else if (attr.name != "unsupported") { switch (attr.name) { case "appVersion": case "buildID": @@ -1810,7 +1849,11 @@ function Update(update) { } if (!this.previousAppVersion) { +#ifdef TOR_BROWSER_UPDATE + this.previousAppVersion = AppConstants.TOR_BROWSER_VERSION; +#else this.previousAppVersion = Services.appinfo.version; +#endif } if (!this.elevationFailure) { @@ -2201,12 +2244,14 @@ UpdateService.prototype = { Services.obs.removeObserver(this, topic); Services.prefs.removeObserver(PREF_APP_UPDATE_LOG, this); +#ifdef XP_WIN if (AppConstants.platform == "win" && gUpdateMutexHandle) { // If we hold the update mutex, let it go! // The OS would clean this up sometime after shutdown, // but that would have no guarantee on timing. closeHandle(gUpdateMutexHandle); } +#endif if (this._retryTimer) { this._retryTimer.cancel(); } @@ -2250,6 +2295,7 @@ UpdateService.prototype = { } break; case "test-close-handle-update-mutex": +#ifdef XP_WIN if (Cu.isInAutomation) { if (AppConstants.platform == "win" && gUpdateMutexHandle) { LOG("UpdateService:observe - closing mutex handle for testing"); @@ -2257,6 +2303,7 @@ UpdateService.prototype = { gUpdateMutexHandle = null; } } +#endif break; } }, @@ -2278,6 +2325,9 @@ UpdateService.prototype = { */ _postUpdateProcessing: function AUS__postUpdateProcessing() { gUpdateFileWriteInfo = { phase: "startup", failure: false }; +#if defined(TOR_BROWSER_UPDATE) && !defined(XP_MACOSX) + this._removeOrphanedTorBrowserFiles(); +#endif if (!this.canCheckForUpdates) { LOG( "UpdateService:_postUpdateProcessing - unable to check for " + @@ -2517,6 +2567,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 @@ -2876,9 +2962,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 && +#ifdef TOR_BROWSER_UPDATE + let compatVersion = AppConstants.TOR_BROWSER_VERSION; +#else + let compatVersion = Services.appinfo.version; +#endif + let rc = vc.compare(aUpdate.appVersion, compatVersion); + if (rc < 0 || + (rc == 0 && aUpdate.buildID == Services.appinfo.appBuildID) ) { LOG( @@ -3252,20 +3343,32 @@ 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 + let compatVersion = AppConstants.TOR_BROWSER_VERSION; +#else + let 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" + +#ifdef TOR_BROWSER_UPDATE + "current Tor Browser version: " + + compatVersion + + "\n" + + "update Tor Browser version : " + +#else "current application version: " + - Services.appinfo.version + + compatVersion + "\n" + "update application version : " + +#endif update.appVersion + "\n" + "current build ID: " + @@ -3883,6 +3986,7 @@ Checker.prototype = { */ _callback: null, +#if !defined(TOR_BROWSER_UPDATE) _getCanMigrate: function UC__getCanMigrate() { if (AppConstants.platform != "win") { return false; @@ -3952,6 +4056,7 @@ Checker.prototype = { LOG("Checker:_getCanMigrate - no registry entries for this installation"); return false; }, +#endif // !defined(TOR_BROWSER_UPDATE) /** * The URL of the update service XML file to connect to that contains details @@ -3975,9 +4080,11 @@ Checker.prototype = { url += (url.includes("?") ? "&" : "?") + "force=1"; } +#if !defined(TOR_BROWSER_UPDATE) if (this._getCanMigrate()) { url += (url.includes("?") ? "&" : "?") + "mig64=1"; } +#endif LOG("Checker:getUpdateURL - update URL: " + url); return url; @@ -5645,7 +5752,15 @@ UpdatePrompt.prototype = { ); Services.obs.notifyObservers(update, "update-downloaded", update.state); - if (Services.prefs.getBoolPref(PREF_APP_UPDATE_DOORHANGER, false)) { + if (background && Services.prefs.getBoolPref(PREF_APP_UPDATE_DOORHANGER, false)) { + // To fix https://trac.torproject.org/projects/tor/ticket/27828 + // ("Check for Tor Browser update" doesn't seem to do anything), in + // Tor Browser we only return here when the background parameter is + // true. This causes the update wizard XUL window to (correctly) be + // opened in response to "Check for Tor Browser Update." + // This change does not alter the behavior of any existing + // update-related UI because all callers of showUpdateDownloaded() + // other than Torbutton pass true for the background parameter. return; } diff --git a/toolkit/mozapps/update/UpdateServiceStub.jsm b/toolkit/mozapps/update/UpdateServiceStub.jsm index 0318e52cd6c5a..f3fdc12f53e5e 100644 --- a/toolkit/mozapps/update/UpdateServiceStub.jsm +++ b/toolkit/mozapps/update/UpdateServiceStub.jsm @@ -45,8 +45,12 @@ function UpdateServiceStub() { // contains the status file's path // We may need to migrate update data + // In Tor Browser we skip this because we do not use an update agent and we + // do not want to store any data outside of the browser installation directory. + // For more info, see https://bugzilla.mozilla.org/show_bug.cgi?id=1458314 if ( AppConstants.platform == "win" && + !AppConstants.TOR_BROWSER_UPDATE && !Services.prefs.getBoolPref(prefUpdateDirMigrated, false) ) { migrateUpdateDirectory(); diff --git a/toolkit/mozapps/update/common/updatehelper.cpp b/toolkit/mozapps/update/common/updatehelper.cpp index 60bb6d5085772..7b66787cf252e 100644 --- a/toolkit/mozapps/update/common/updatehelper.cpp +++ b/toolkit/mozapps/update/common/updatehelper.cpp @@ -63,6 +63,13 @@ BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath, * @return TRUE if successful */ BOOL GetSecureOutputDirectoryPath(LPWSTR outBuf) { +#ifdef TOR_BROWSER_UPDATE + // This function is used to support the maintenance service and elevated + // updates and is therefore not called by Tor Browser's updater. We stub + // it out to avoid any chance that the Tor Browser updater will create + // files under C:\Program Files (x86)\ or a similar location. + return FALSE; +#else PWSTR progFilesX86; if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, KF_FLAG_CREATE, nullptr, &progFilesX86))) { @@ -96,6 +103,7 @@ BOOL GetSecureOutputDirectoryPath(LPWSTR outBuf) { } return TRUE; +#endif } /** diff --git a/toolkit/mozapps/update/moz.build b/toolkit/mozapps/update/moz.build index ddfda4571c314..d0c93895c1ecf 100644 --- a/toolkit/mozapps/update/moz.build +++ b/toolkit/mozapps/update/moz.build @@ -22,11 +22,14 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES += [ 'UpdateListener.jsm', - 'UpdateService.jsm', 'UpdateServiceStub.jsm', 'UpdateTelemetry.jsm', ] +EXTRA_PP_JS_MODULES += [ + 'UpdateService.jsm', +] + XPCOM_MANIFESTS += [ 'components.conf', ] diff --git a/toolkit/mozapps/update/updater/launchchild_osx.mm b/toolkit/mozapps/update/updater/launchchild_osx.mm index 5b80d2cc69581..a60fcb0cc4f37 100644 --- a/toolkit/mozapps/update/updater/launchchild_osx.mm +++ b/toolkit/mozapps/update/updater/launchchild_osx.mm @@ -254,6 +254,7 @@ bool ObtainUpdaterArguments(int* argc, char*** argv) { @end +#ifndef TOR_BROWSER_UPDATE bool ServeElevatedUpdate(int argc, const char** argv) { MacAutoreleasePool pool; @@ -269,6 +270,7 @@ bool ServeElevatedUpdate(int argc, const char** argv) { [updater release]; return didSucceed; } +#endif bool IsOwnedByGroupAdmin(const char* aAppBundle) { MacAutoreleasePool pool; diff --git a/toolkit/mozapps/update/updater/moz.build b/toolkit/mozapps/update/updater/moz.build index 4d67dc94ad582..6ab49fe4651b9 100644 --- a/toolkit/mozapps/update/updater/moz.build +++ b/toolkit/mozapps/update/updater/moz.build @@ -49,7 +49,7 @@ xpcshell_cert.script = 'gen_cert_header.py:create_header' dep1_cert.script = 'gen_cert_header.py:create_header' dep2_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', '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 1358fc76cf704..9d6e637fac97b 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -16,7 +16,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. * @@ -78,7 +78,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 { int argc; @@ -477,9 +479,12 @@ static const NS_tchar* 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) { +static NS_tchar* get_valid_path(NS_tchar** line, bool isdir = false, + bool islinktarget = false) { NS_tchar* path = mstrtok(kQuote, line); if (!path) { LOG(("get_valid_path: unable to determine path: " LOG_S, *line)); @@ -515,10 +520,12 @@ static NS_tchar* 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; @@ -558,7 +565,7 @@ 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 @@ -745,11 +752,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 AutoFile infile(ensure_open(path, NS_T("rb"), ss.st_mode)); if (!infile) { @@ -836,12 +841,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); } @@ -898,7 +910,7 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath, } 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", @@ -906,7 +918,12 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath, 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)); @@ -915,7 +932,12 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath, LOG(("rename_file: proceeding to rename the directory")); } - 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 " @@ -935,7 +957,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 @@ -1034,7 +1056,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; } @@ -1052,8 +1086,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; } @@ -1068,6 +1112,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 @@ -1084,6 +1130,7 @@ static int backup_discard(const NS_tchar* path, const NS_tchar* relPath) { "file: " LOG_S, relPath)); } +#endif } #else if (rv) { @@ -1138,7 +1185,7 @@ class Action { class RemoveFile : public Action { public: - RemoveFile() : mSkip(0) {} + RemoveFile() : mSkip(0), mIsLink(0) {} int Parse(NS_tchar* line) override; int Prepare() override; @@ -1149,6 +1196,7 @@ class RemoveFile : public Action { mozilla::UniquePtr mFile; mozilla::UniquePtr mRelPath; int mSkip; + int mIsLink; }; int RemoveFile::Parse(NS_tchar* line) { @@ -1171,28 +1219,39 @@ int 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('/')); @@ -1221,7 +1280,13 @@ int 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; @@ -1944,6 +2009,92 @@ void 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 @@ -2279,14 +2430,29 @@ static bool IsSecureUpdateStatusSucceeded(bool& isSucceeded) { */ static int CopyInstallDirToDestDir() { // These files should not be copied over to the updated app +#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 #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")); @@ -2295,6 +2461,25 @@ static int 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); } @@ -2432,7 +2617,9 @@ static int ProcessReplaceRequest() { if (NS_taccess(deleteDir, F_OK)) { NS_tmkdir(deleteDir, 0755); } +#if !defined(TOR_BROWSER_UPDATE) remove_recursive_on_reboot(tmpDir, deleteDir); +#endif #endif } @@ -2440,8 +2627,45 @@ static int 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 @@ -2620,11 +2844,15 @@ static void UpdateThreadFunc(void* param) { #ifdef XP_MACOSX 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(); } @@ -2648,7 +2876,7 @@ int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv, #endif ) { 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"); @@ -2701,8 +2929,12 @@ int NS_main(int argc, NS_tchar** argv) { UmaskContext umaskContext(0); 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 @@ -3337,6 +3569,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 // Get the secure ID before trying to update so it is possible to // determine if the updater has created a new one. char uuidStringBefore[UUID_LEN] = {'\0'}; @@ -3382,6 +3634,7 @@ int NS_main(int argc, NS_tchar** argv) { gCopyOutputFiles = false; WriteStatusFile(ELEVATION_CANCELED); } +#endif } // Note: The PostUpdate process is launched by the elevated updater which @@ -3741,6 +3994,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 @@ -3759,6 +4013,7 @@ int NS_main(int argc, NS_tchar** argv) { "directory: " LOG_S, DELETE_DIR)); } +#endif } #endif /* XP_WIN */ @@ -4400,7 +4655,13 @@ int DoUpdate() { action = new AddIfNotFile(); } else if (NS_tstrcmp(token, NS_T("patch-if")) == 0) { // Patch if exists action = new PatchIfFile(); - } else { + } +#ifndef XP_WIN + else if (NS_tstrcmp(token, NS_T("addsymlink")) == 0) { + action = new AddSymlink(); + } +#endif + else { LOG(("DoUpdate: unknown token: " LOG_S, token)); free(buf); return PARSE_ERROR; diff --git a/toolkit/xre/MacLaunchHelper.h b/toolkit/xre/MacLaunchHelper.h index f8dc75ee4d089..ce816acd83e24 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 60113be1d9f1d..87e248c7b0c09 100644 --- a/toolkit/xre/MacLaunchHelper.mm +++ b/toolkit/xre/MacLaunchHelper.mm @@ -41,6 +41,7 @@ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) { } } +#ifndef TOR_BROWSER_UPDATE BOOL InstallPrivilegedHelper() { AuthorizationRef authRef = NULL; OSStatus status = AuthorizationCreate( @@ -117,3 +118,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 f7d718e164163..de8d8761985bd 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -2643,6 +2643,11 @@ static bool CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion, } nsAutoCString buf; + + nsAutoCString tbVersion(TOR_BROWSER_VERSION_QUOTED); + rv = parser.GetString("Compatibility", "LastTorBrowserVersion", buf); + if (NS_FAILED(rv) || !tbVersion.Equals(buf)) return false; + rv = parser.GetString("Compatibility", "LastOSABI", buf); if (NS_FAILED(rv) || !aOSABI.Equals(buf)) return false; @@ -2728,6 +2733,12 @@ static void WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion, PR_Write(fd, kHeader, sizeof(kHeader) - 1); PR_Write(fd, aVersion.get(), aVersion.Length()); + nsAutoCString tbVersion(TOR_BROWSER_VERSION_QUOTED); + static const char kTorBrowserVersionHeader[] = + NS_LINEBREAK "LastTorBrowserVersion="; + PR_Write(fd, kTorBrowserVersionHeader, sizeof(kTorBrowserVersionHeader) - 1); + PR_Write(fd, tbVersion.get(), tbVersion.Length()); + static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI="; PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1); PR_Write(fd, aOSABI.get(), aOSABI.Length()); @@ -4214,8 +4225,17 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) { if (CheckArg("test-process-updates")) { SaveToEnv("MOZ_TEST_PROCESS_UPDATES=1"); } +#ifdef TOR_BROWSER_UPDATE + nsAutoCString compatVersion(TOR_BROWSER_VERSION_QUOTED); +#endif ProcessUpdates(mDirProvider.GetGREDir(), exeDir, updRoot, gRestartArgc, - gRestartArgv, mAppData->version); + gRestartArgv, +#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 a7c919439de3b..4f89a71e9adf6 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -173,6 +173,13 @@ static nsresult GetInstallDirPath(nsIFile* appDir, nsACString& installDirPath) { return NS_OK; } +#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 + static bool GetFile(nsIFile* dir, const nsACString& name, nsCOMPtr& result) { nsresult rv; @@ -228,6 +235,34 @@ 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. @@ -292,11 +327,41 @@ static bool IsOlderVersion(nsIFile* versionFile, const char* appVersion) { const char kNull[] = "null"; 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 + #if defined(XP_MACOSX) static bool CopyFileIntoUpdateDir(nsIFile* parentDir, const nsACString& leaf, nsIFile* updateDir) { @@ -537,7 +602,12 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir, } else { // Get the directory where the update is staged or will be staged. #if defined(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 @@ -632,6 +702,9 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir, } 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 @@ -669,6 +742,10 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir, } #elif defined(XP_MACOSX) UpdateDriverSetupMacCommandLine(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). @@ -681,6 +758,7 @@ if (restart && !IsRecursivelyWritable(installDirPath.get())) { } exit(0); } +#endif if (isStaged) { // Launch the updater to replace the installation with the staged updated. @@ -751,9 +829,27 @@ static bool ProcessHasTerminated(ProcessType pt) { nsresult ProcessUpdates(nsIFile* greDir, nsIFile* appDir, nsIFile* updRootDir, int argc, char** argv, const char* appVersion, bool restart, 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 + printf("ProcessUpdates updateRootDir: %s appVersion: %s\n", + updRootDir->HumanReadablePath().get(), appVersion); +#endif rv = updRootDir->Clone(getter_AddRefs(updatesDir)); if (NS_FAILED(rv)) { return rv; @@ -781,6 +877,12 @@ nsresult 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); + printf("ProcessUpdates updatesDir: %s\n", + updatesDir->HumanReadablePath().get()); +#endif switch (status) { case ePendingElevate: { if (NS_IsMainThread()) { @@ -854,13 +956,16 @@ nsUpdateProcessor::ProcessUpdate() { NS_ENSURE_SUCCESS(rv, rv); } + nsAutoCString appVersion; +#ifdef TOR_BROWSER_UPDATE + appVersion = TOR_BROWSER_VERSION_QUOTED; +#else nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1", &rv); NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString appVersion; rv = appInfo->GetVersion(appVersion); NS_ENSURE_SUCCESS(rv, rv); +#endif // Copy the parameters to the StagedUpdateInfo structure shared with the // watcher thread. diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 29a055ee7d95e..ec3c102e1c87b 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -1265,6 +1265,41 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult, } #endif nsCOMPtr updRoot; +#if defined(TOR_BROWSER_UPDATE) + // For Tor Browser, we store update history, etc. within the UpdateInfo + // directory under the user data directory. + nsresult rv = GetTorBrowserUserDataDir(getter_AddRefs(updRoot)); + NS_ENSURE_SUCCESS(rv, rv); + rv = updRoot->AppendNative(NS_LITERAL_CSTRING("UpdateInfo")); + NS_ENSURE_SUCCESS(rv, rv); +#if defined(XP_MACOSX) && defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) + // Since the TorBrowser-Data directory may be shared among different + // installations of the application, embed the app path in the update dir + // so that the update history is partitioned. This is much less likely to + // be an issue on Linux or Windows because the Tor Browser packages for + // those platforms include a "container" folder that provides partitioning + // by default, and we do not support use of a shared, OS-recommended area + // for user data on those platforms. + nsCOMPtr appFile; + bool per = false; + rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr appRootDirFile; + nsAutoString appDirPath; + if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) || + NS_FAILED(appRootDirFile->GetPath(appDirPath))) { + return NS_ERROR_FAILURE; + } + + int32_t dotIndex = appDirPath.RFind(".app"); + if (dotIndex == kNotFound) { + dotIndex = appDirPath.Length(); + } + appDirPath = Substring(appDirPath, 1, dotIndex - 1); + rv = updRoot->AppendRelativePath(appDirPath); + NS_ENSURE_SUCCESS(rv, rv); +#endif +#else // ! TOR_BROWSER_UPDATE nsCOMPtr appFile; bool per = false; nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile)); @@ -1336,6 +1371,7 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult, updatePathStr.Assign(updatePath.get()); updRoot->InitWithPath(updatePathStr); #endif // XP_WIN +#endif // ! TOR_BROWSER_UPDATE updRoot.forget(aResult); return NS_OK; } diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh index 42fbf52574d89..d3c5fc5efb2a0 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} MBSDIFF=${MBSDIFF:-mbsdiff} @@ -44,6 +50,12 @@ notice() { echo "$*" 1>&2 } +verbose_notice() { + if [ $QUIET -eq 0 ]; then + notice "$*" + fi +} + get_file_size() { info=($(ls -ln "$1")) echo ${info[4]} @@ -77,22 +89,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 } @@ -123,28 +123,29 @@ make_add_if_not_instruction() { f="$1" filev3="$2" - notice " add-if-not \"$f\" \"$f\"" + 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() { f="$1" 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() { @@ -171,17 +172,17 @@ append_remove_instructions() { # Exclude comments if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then if [ $(echo "$f" | grep -c '\/$') = 1 ]; then - notice " rmdir \"$f\"" + 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\"" + verbose_notice " rmrfdir \"$f\"" echo "rmrfdir \"$f\"" >> "$filev2" echo "rmrfdir \"$f\"" >> "$filev3" else - notice " remove \"$f\"" + verbose_notice " remove \"$f\"" echo "remove \"$f\"" >> "$filev2" echo "remove \"$f\"" >> "$filev3" fi @@ -193,6 +194,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 @@ -205,6 +210,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" @@ -226,3 +236,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 f024c20daf42a..e08d8808d6ea4 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,6 +69,20 @@ 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 @@ -75,6 +95,22 @@ notice " type complete" 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" num_files=${#files[*]} @@ -103,6 +139,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 e7f68c8caee0f..850a75306c829 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 "" } @@ -77,6 +78,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; @@ -87,13 +103,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 @@ -120,6 +142,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. @@ -130,6 +229,7 @@ fi list_files oldfiles list_dirs olddirs +list_symlinks oldsymlinks oldsymlink_targets popd @@ -147,6 +247,7 @@ fi list_dirs newdirs list_files newfiles +list_symlinks newsymlinks newsymlink_targets popd @@ -159,6 +260,23 @@ notice " type partial" 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" @@ -281,6 +399,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" @@ -330,6 +466,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[*]} -- GitLab