From 8ed68047a0d320cbc3777222379813d76cdaed38 Mon Sep 17 00:00:00 2001 From: Steffen Wilberg Date: Wed, 7 Aug 2013 23:07:17 +0200 Subject: [PATCH] Bug 711475: Allow to perform updates using the About Firefox dialog on limited user accounts if the maintenance service is installed. r=bbondy --- toolkit/mozapps/update/nsIUpdateService.idl | 6 +- toolkit/mozapps/update/nsUpdateService.js | 165 ++++++++++++-------- 2 files changed, 105 insertions(+), 66 deletions(-) diff --git a/toolkit/mozapps/update/nsIUpdateService.idl b/toolkit/mozapps/update/nsIUpdateService.idl index 45e111a3cb8..9123c539f2a 100644 --- a/toolkit/mozapps/update/nsIUpdateService.idl +++ b/toolkit/mozapps/update/nsIUpdateService.idl @@ -424,8 +424,10 @@ interface nsIApplicationUpdateService : nsISupports /** * Whether or not the Update Service can download and install updates. - * This is a function of whether or not the current user has access - * privileges to the install directory. + * On Windows, this is a function of whether or not the maintenance service + * is installed and enabled. On other systems, and as a fallback on Windows, + * this depends on whether the current user has write access to the install + * directory. */ readonly attribute boolean canApplyUpdates; diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 6c97c238495..a6b2597d2a5 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -572,52 +572,61 @@ XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpda } } - try { - var updateTestFile = getUpdateFile([FILE_PERMS_TEST]); - LOG("gCanApplyUpdates - testing write access " + updateTestFile.path); - testWriteAccess(updateTestFile, false); + let useService = false; + if (shouldUseService() && isServiceInstalled()) { + // No need to perform directory write checks, the maintenance service will + // be able to write to all directories. + LOG("gCanApplyUpdates - bypass the write checks because we'll use the service"); + useService = true; + } + + if (!useService) { + try { + var updateTestFile = getUpdateFile([FILE_PERMS_TEST]); + LOG("gCanApplyUpdates - testing write access " + updateTestFile.path); + testWriteAccess(updateTestFile, false); #ifdef XP_WIN - var sysInfo = Cc["@mozilla.org/system-info;1"]. - getService(Ci.nsIPropertyBag2); + var sysInfo = Cc["@mozilla.org/system-info;1"]. + getService(Ci.nsIPropertyBag2); - // Example windowsVersion: Windows XP == 5.1 - var windowsVersion = sysInfo.getProperty("version"); - LOG("gCanApplyUpdates - windowsVersion = " + windowsVersion); - - /** -# For Vista, updates can be performed to a location requiring admin -# privileges by requesting elevation via the UAC prompt when launching -# updater.exe if the appDir is under the Program Files directory -# (e.g. C:\Program Files\) and UAC is turned on and we can elevate -# (e.g. user has a split token). -# -# Note: this does note attempt to handle the case where UAC is turned on -# and the installation directory is in a restricted location that -# requires admin privileges to update other than Program Files. - */ - var userCanElevate = false; - - if (parseFloat(windowsVersion) >= 6) { - try { - var fileLocator = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties); - // KEY_UPDROOT will fail and throw an exception if - // appDir is not under the Program Files, so we rely on that - var dir = fileLocator.get(KEY_UPDROOT, Ci.nsIFile); - // appDir is under Program Files, so check if the user can elevate - userCanElevate = Services.appinfo.QueryInterface(Ci.nsIWinAppHelper). - userCanElevate; - LOG("gCanApplyUpdates - on Vista, userCanElevate: " + userCanElevate); - } - catch (ex) { - // When the installation directory is not under Program Files, - // fall through to checking if write access to the - // installation directory is available. - LOG("gCanApplyUpdates - on Vista, appDir is not under Program Files"); - } - } + // Example windowsVersion: Windows XP == 5.1 + var windowsVersion = sysInfo.getProperty("version"); + LOG("gCanApplyUpdates - windowsVersion = " + windowsVersion); /** + # For Vista, updates can be performed to a location requiring admin + # privileges by requesting elevation via the UAC prompt when launching + # updater.exe if the appDir is under the Program Files directory + # (e.g. C:\Program Files\) and UAC is turned on and we can elevate + # (e.g. user has a split token). + # + # Note: this does note attempt to handle the case where UAC is turned on + # and the installation directory is in a restricted location that + # requires admin privileges to update other than Program Files. + */ + var userCanElevate = false; + + if (parseFloat(windowsVersion) >= 6) { + try { + var fileLocator = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties); + // KEY_UPDROOT will fail and throw an exception if + // appDir is not under the Program Files, so we rely on that + var dir = fileLocator.get(KEY_UPDROOT, Ci.nsIFile); + // appDir is under Program Files, so check if the user can elevate + userCanElevate = Services.appinfo.QueryInterface(Ci.nsIWinAppHelper). + userCanElevate; + LOG("gCanApplyUpdates - on Vista, userCanElevate: " + userCanElevate); + } + catch (ex) { + // When the installation directory is not under Program Files, + // fall through to checking if write access to the + // installation directory is available. + LOG("gCanApplyUpdates - on Vista, appDir is not under Program Files"); + } + } + + /** # On Windows, we no longer store the update under the app dir. # # If we are on Windows (including Vista, if we can't elevate) we need to @@ -636,25 +645,26 @@ XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpda # (e.g. the user does not have a split token) # 4) UAC is turned on and the user is already elevated, so they can't be # elevated again - */ - if (!userCanElevate) { - // if we're unable to create the test file this will throw an exception. - var appDirTestFile = getAppBaseDir(); - appDirTestFile.append(FILE_PERMS_TEST); - LOG("gCanApplyUpdates - testing write access " + appDirTestFile.path); - if (appDirTestFile.exists()) - appDirTestFile.remove(false) - appDirTestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); - appDirTestFile.remove(false); - } + */ + if (!userCanElevate) { + // if we're unable to create the test file this will throw an exception. + var appDirTestFile = getAppBaseDir(); + appDirTestFile.append(FILE_PERMS_TEST); + LOG("gCanApplyUpdates - testing write access " + appDirTestFile.path); + if (appDirTestFile.exists()) + appDirTestFile.remove(false) + appDirTestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); + appDirTestFile.remove(false); + } #endif //XP_WIN - } - catch (e) { - LOG("gCanApplyUpdates - unable to apply updates. Exception: " + e); - // No write privileges to install directory - submitHasPermissionsTelemetryPing(false); - return false; - } + } + catch (e) { + LOG("gCanApplyUpdates - unable to apply updates. Exception: " + e); + // No write privileges to install directory + submitHasPermissionsTelemetryPing(false); + return false; + } + } // if (!useService) if (!hasUpdateMutex()) { LOG("gCanApplyUpdates - unable to apply updates because another instance" + @@ -1116,6 +1126,33 @@ function shouldUseService() { #endif } +/** + * Determines if the service is is installed and enabled or not. + * + * @return true if the service should be used for updates, + * is installed and enabled. + */ +function isServiceInstalled() { +#ifdef XP_WIN + let installed = 0; + try { + let wrk = Cc["@mozilla.org/windows-registry-key;1"]. + createInstance(Ci.nsIWindowsRegKey); + wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, + "SOFTWARE\\Mozilla\\MaintenanceService", + wrk.ACCESS_READ | wrk.WOW64_64); + installed = wrk.readIntValue("Installed"); + wrk.close(); + } catch(e) { + } + installed = installed == 1; // convert to bool + LOG("isServiceInstalled = " + installed); + return installed; +#else + return false; +#endif +} + /** # Writes the update's application version to a file in the patch directory. If # the update doesn't provide application version information via the @@ -2243,7 +2280,7 @@ UpdateService.prototype = { * service was at some point installed, but is now uninstalled. */ _sendServiceInstalledTelemetryPing: function AUS__svcInstallTelemetryPing() { - let installed = 0; + let installed = isServiceInstalled(); // Is the service installed? let attempted = 0; try { let wrk = Cc["@mozilla.org/windows-registry-key;1"]. @@ -2251,14 +2288,14 @@ UpdateService.prototype = { wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, "SOFTWARE\\Mozilla\\MaintenanceService", wrk.ACCESS_READ | wrk.WOW64_64); + // Was the service at some point installed, but is now uninstalled? attempted = wrk.readIntValue("Attempted"); - installed = wrk.readIntValue("Installed"); wrk.close(); } catch(e) { } try { let h = Services.telemetry.getHistogramById("UPDATER_SERVICE_INSTALLED"); - h.add(installed); + h.add(Number(installed)); } catch(e) { // Don't allow any exception to be propagated. Cu.reportError(e); @@ -4270,7 +4307,7 @@ Downloader.prototype = { "max fail: " + maxFail + ", " + "retryTimeout: " + retryTimeout); if (Components.isSuccessCode(status)) { if (this._verifyDownload()) { - state = shouldUseService() ? STATE_PENDING_SVC : STATE_PENDING + state = shouldUseService() ? STATE_PENDING_SVC : STATE_PENDING; if (this.background) { shouldShowPrompt = !getCanStageUpdates(); }