From a342419c38777bec2b575e10ebb097fb994f3b05 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Fri, 14 Nov 2008 12:11:08 +0000 Subject: [PATCH] Bug 313057: Automatic updater downgrades/overwrites browser version when an older/identical version is in the queue. r=bsmedberg, a1.9.1b2=beltzner --- .../mozapps/update/src/nsUpdateService.js.in | 26 +++++++++ toolkit/xre/nsAppRunner.cpp | 3 +- toolkit/xre/nsUpdateDriver.cpp | 58 +++++++++++++++++-- toolkit/xre/nsUpdateDriver.h | 8 ++- 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/toolkit/mozapps/update/src/nsUpdateService.js.in b/toolkit/mozapps/update/src/nsUpdateService.js.in index 55e7ac1ffea..938847dc1db 100644 --- a/toolkit/mozapps/update/src/nsUpdateService.js.in +++ b/toolkit/mozapps/update/src/nsUpdateService.js.in @@ -84,6 +84,7 @@ const KEY_UAPPDATA = "UAppData"; const DIR_UPDATES = "updates"; const FILE_UPDATE_STATUS = "update.status"; +const FILE_UPDATE_VERSION = "update.version"; const FILE_UPDATE_ARCHIVE = "update.mar"; const FILE_UPDATE_LOG = "update.log" const FILE_UPDATES_DB = "updates.xml"; @@ -398,6 +399,28 @@ function writeStatusFile(dir, state) { writeStringToFile(statusFile, state); } +/** +# Writes the update's application version to a file in the patch directory. If +# the update doesn't provide application version information via the +# extensionVersion attribute the string "null" will be written to the file. +# This value is compared during startup (in nsUpdateDriver.cpp) to determine if +# the update should be applied. Note that this won't provide protection from +# downgrade of the application for the nightly user case where the application +# version doesn't change. +# @param dir +# The patch directory where the update.version file should be +# written. +# @param version +# The version value to write. Will be the string "null" when the +# update doesn't provide the extensionVersion attribute in the update +# xml. + */ +function writeVersionFile(dir, version) { + var versionFile = dir.clone(); + versionFile.append(FILE_UPDATE_VERSION); + writeStringToFile(versionFile, version); +} + /** * Removes the Updates Directory * @param key @@ -1228,6 +1251,7 @@ UpdateService.prototype = { if (update.errorCode == WRITE_ERROR) { prompter.showUpdateError(update); writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING); + writeVersionFile(getUpdatesDir(), update.extensionVersion); return; } } @@ -2410,6 +2434,7 @@ Downloader.prototype = { // This is a pretty fatal error. Just bail. LOG("Downloader", "_selectPatch - failed to apply complete patch!"); writeStatusFile(updateDir, STATE_NONE); + writeVersionFile(getUpdatesDir(), null); return null; } } @@ -2635,6 +2660,7 @@ Downloader.prototype = { // Tell the updater.exe we're ready to apply. writeStatusFile(getUpdatesDir(), state); + writeVersionFile(getUpdatesDir(), this._update.extensionVersion); this._update.installDate = (new Date()).getTime(); this._update.statusText = updateStrings.GetStringFromName("installPending"); } diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index d91b4ead815..8006fb28045 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -2976,7 +2976,8 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) dirProvider.GetAppDir(), updRoot, gRestartArgc, - gRestartArgv); + gRestartArgv, + appData.version); #endif nsCOMPtr profileLock; diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index 76caa8b3c5f..85c4a872671 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Darin Fisher * Ben Turner + * Robert Strong * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -51,6 +52,7 @@ #include "nsPrintfCString.h" #include "prproces.h" #include "prlog.h" +#include "nsVersionComparator.h" #ifdef XP_MACOSX #include "nsILocalFileMac.h" @@ -222,16 +224,16 @@ GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr &result) { nsresult rv; - nsCOMPtr statusFile; - rv = dir->Clone(getter_AddRefs(statusFile)); + nsCOMPtr file; + rv = dir->Clone(getter_AddRefs(file)); if (NS_FAILED(rv)) return PR_FALSE; - rv = statusFile->AppendNative(name); + rv = file->AppendNative(name); if (NS_FAILED(rv)) return PR_FALSE; - result = do_QueryInterface(statusFile, &rv); + result = do_QueryInterface(file, &rv); return NS_SUCCEEDED(rv); } @@ -274,6 +276,42 @@ SetStatus(nsILocalFile *statusFile, const char *status) return PR_TRUE; } +static PRBool +GetVersionFile(nsIFile *dir, nsCOMPtr &result) +{ + return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result); +} + +// Compares the current application version with the update's application +// version. +static PRBool +IsOlderVersion(nsILocalFile *versionFile, const char *&appVersion) +{ + nsresult rv; + + FILE *fp; + rv = versionFile->OpenANSIFileDesc("r", &fp); + if (NS_FAILED(rv)) + return PR_TRUE; + + char buf[32]; + char *result = fgets(buf, sizeof(buf), fp); + fclose(fp); + if (!result) + return PR_TRUE; + + // If the update xml doesn't provide the application version the file will + // contain the string "null" and it is assumed that the update is not older. + const char kNull[] = "null"; + if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0) + return PR_FALSE; + + if (NS_CompareVersions(appVersion, result) > 0) + return PR_TRUE; + + return PR_FALSE; +} + static PRBool CopyFileIntoUpdateDir(nsIFile *parentDir, const char *leafName, nsIFile *updateDir) { @@ -505,7 +543,7 @@ end: nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, - int argc, char **argv) + int argc, char **argv, const char *&appVersion) { nsresult rv; @@ -533,6 +571,16 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, for (int i = 0; i < dirEntries.Count(); ++i) { nsCOMPtr statusFile; if (GetStatusFile(dirEntries[i], statusFile) && IsPending(statusFile)) { + nsCOMPtr versionFile; + // Remove the update if the update application version file doesn't exist + // or if the update's application version is less than the current + // application version. + if (!GetVersionFile(dirEntries[i], versionFile) || + IsOlderVersion(versionFile, appVersion)) { + dirEntries[i]->Remove(PR_TRUE); + continue; + } + ApplyUpdate(greDir, dirEntries[i], statusFile, appDir, argc, argv); break; } diff --git a/toolkit/xre/nsUpdateDriver.h b/toolkit/xre/nsUpdateDriver.h index 2382ca86cd4..050c5ddff64 100644 --- a/toolkit/xre/nsUpdateDriver.h +++ b/toolkit/xre/nsUpdateDriver.h @@ -22,6 +22,7 @@ * Contributor(s): * Darin Fisher * Ben Turner + * Robert Strong * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -55,10 +56,15 @@ class nsIFile; * The argc and argv passed to this function should be what is needed to * relaunch the current process. * + * The appVersion param passed to this function is the current application's + * version and is used to determine if an update's version is older than the + * current application version. + * * This function does not modify appDir. */ NS_HIDDEN_(nsresult) ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, - int argc, char **argv); + int argc, char **argv, + const char *&appVersion); #endif // nsUpdateDriver_h__