Bug 481815 - Provide a Windows service for silent updates. r=rstrong.
authorBrian R. Bondy <netzen@gmail.com>
Wed, 04 Jan 2012 23:19:14 -0500
changeset 84992 314cab6ecbe347e0e2149a8bc477646b85426735
parent 84991 34cbeb81ea8e86d6dc8dd3584bfd25e7d81cb83f
child 84993 1bd9f069576e63b5527ea9b4d99b7f3aca1bd5c4
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong
bugs481815
milestone12.0a1
Bug 481815 - Provide a Windows service for silent updates. r=rstrong.
browser/Makefile.in
browser/app/profile/firefox.js
browser/base/content/aboutDialog.js
browser/components/preferences/advanced.js
browser/components/preferences/advanced.xul
browser/installer/package-manifest.in
browser/installer/removed-files.in
browser/installer/windows/Makefile.in
browser/installer/windows/nsis/defines.nsi.in
browser/installer/windows/nsis/installer.nsi
browser/installer/windows/nsis/maintenanceservice_installer.nsi
browser/installer/windows/nsis/shared.nsh
browser/installer/windows/nsis/uninstaller.nsi
other-licenses/nsis/Contrib/ServicesHelper/Services.cpp
other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.dsp
other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.dsw
other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.rc
other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.sln
other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.vcproj
other-licenses/nsis/Contrib/ServicesHelper/resource.h
other-licenses/nsis/Plugins/ServicesHelper.dll
toolkit/Makefile.in
toolkit/components/maintenanceservice/Makefile.in
toolkit/components/maintenanceservice/certificatecheck.cpp
toolkit/components/maintenanceservice/certificatecheck.h
toolkit/components/maintenanceservice/maintenanceservice.cpp
toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
toolkit/components/maintenanceservice/maintenanceservice.h
toolkit/components/maintenanceservice/maintenanceservice.rc
toolkit/components/maintenanceservice/pathhash.cpp
toolkit/components/maintenanceservice/pathhash.h
toolkit/components/maintenanceservice/registrycertificates.cpp
toolkit/components/maintenanceservice/registrycertificates.h
toolkit/components/maintenanceservice/resource.h
toolkit/components/maintenanceservice/servicebase.cpp
toolkit/components/maintenanceservice/servicebase.h
toolkit/components/maintenanceservice/serviceinstall.cpp
toolkit/components/maintenanceservice/serviceinstall.h
toolkit/components/maintenanceservice/workmonitor.cpp
toolkit/components/maintenanceservice/workmonitor.h
toolkit/mozapps/installer/windows/nsis/common.nsh
toolkit/mozapps/installer/windows/nsis/makensis.mk
toolkit/mozapps/readstrings/Makefile.in
toolkit/mozapps/update/Makefile.in
toolkit/mozapps/update/common/Makefile.in
toolkit/mozapps/update/common/launchwinprocess.cpp
toolkit/mozapps/update/common/launchwinprocess.h
toolkit/mozapps/update/common/uachelper.cpp
toolkit/mozapps/update/common/uachelper.h
toolkit/mozapps/update/common/updatedefines.h
toolkit/mozapps/update/common/updatelogging.cpp
toolkit/mozapps/update/common/updatelogging.h
toolkit/mozapps/update/content/updates.js
toolkit/mozapps/update/nsIUpdateService.idl
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/test/Makefile.in
toolkit/mozapps/update/test/sharedUpdateXML.js
toolkit/mozapps/update/updater/Makefile.in
toolkit/mozapps/update/updater/progressui.h
toolkit/mozapps/update/updater/updater.cpp
toolkit/toolkit-makefiles.sh
toolkit/xre/nsAppRunner.h
toolkit/xre/nsUpdateDriver.cpp
toolkit/xre/nsWindowsRestart.cpp
xpcom/base/Makefile.in
xpcom/base/nsWindowsHelpers.h
xpcom/ds/nsIWindowsRegKey.idl
--- a/browser/Makefile.in
+++ b/browser/Makefile.in
@@ -66,11 +66,13 @@ include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
 ifdef MOZ_INSTALLER
 
 # For Windows build the uninstaller during the application build since the
 # uninstaller is included with the application for mar file generation.
 libs::
 	$(MAKE) -C installer/windows uninstaller
-
+ifdef MOZ_MAINTENANCE_SERVICE
+	$(MAKE) -C installer/windows maintenanceservice_installer
 endif
 endif
+endif
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -195,16 +195,21 @@ pref("app.update.showInstalledUI", false
 
 // 0 = suppress prompting for incompatibilities if there are updates available
 //     to newer versions of installed addons that resolve them.
 // 1 = suppress prompting for incompatibilities only if there are VersionInfo
 //     updates available to installed addons that resolve them, not newer
 //     versions.
 pref("app.update.incompatible.mode", 0);
 
+// Whether or not to attempt using the service for updates.
+#ifdef MOZ_MAINTENANCE_SERVICE
+pref("app.update.service.enabled", true);
+#endif
+
 // Symmetric (can be overridden by individual extensions) update preferences.
 // e.g.
 //  extensions.{GUID}.update.enabled
 //  extensions.{GUID}.update.url
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
 pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -183,19 +183,23 @@ function appUpdater()
 
 appUpdater.prototype =
 {
   // true when there is an update check in progress.
   isChecking: false,
 
   // true when there is an update already staged / ready to be applied.
   get isPending() {
-    if (this.update)
-      return this.update.state == "pending";
-    return this.um.activeUpdate && this.um.activeUpdate.state == "pending";
+    if (this.update) {
+      return this.update.state == "pending" || 
+             this.update.state == "pending-service";
+    }
+    return this.um.activeUpdate &&
+           (this.um.activeUpdate.state == "pending" ||
+            this.um.activeUpdate.state == "pending-service");
   },
 
   // true when there is an update download in progress.
   get isDownloading() {
     if (this.update)
       return this.update.state == "downloading";
     return this.um.activeUpdate &&
            this.um.activeUpdate.state == "downloading";
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -526,16 +526,36 @@ var gAdvancedPane = {
     // A locked pref is sufficient to disable the radiogroup.
     radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked;
 
     var modePref = document.getElementById("app.update.mode");
     var warnIncompatible = document.getElementById("warnIncompatible");
     // the warnIncompatible checkbox value is set by readAddonWarn
     warnIncompatible.disabled = radiogroup.disabled || modePref.locked ||
                                 !enabledPref.value || !autoPref.value;
+
+#ifdef MOZ_MAINTENANCE_SERVICE
+    // Check to see if the maintenance service is installed.
+    // If it is don't show the preference at all.
+    var installed;
+    try {
+      Components.utils.reportError("0");
+      var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
+                .createInstance(Components.interfaces.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) {
+    }
+    if (installed != 1) {
+      document.getElementById("useService").hidden = true;
+    }
+#endif
   },
 
   /**
    * Sets the pref values based on the selected item of the radiogroup,
    * and sets the disabled state of the warnIncompatible checkbox accordingly.
    */
   updateWritePrefs: function ()
   {
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -103,16 +103,22 @@
 #ifdef MOZ_UPDATER
       <preference id="app.update.enabled"              name="app.update.enabled"              type="bool"/>
       <preference id="app.update.auto"                 name="app.update.auto"                 type="bool"/>
       <preference id="app.update.mode"                 name="app.update.mode"                 type="int"/>
 
       <preference id="app.update.disable_button.showUpdateHistory"
                   name="app.update.disable_button.showUpdateHistory"
                   type="bool"/>
+
+#ifdef MOZ_MAINTENANCE_SERVICE
+      <preference id="app.update.service.enabled"
+                  name="app.update.service.enabled"
+                  type="bool"/>
+#endif
 #endif
 
       <preference id="browser.search.update"           name="browser.search.update"           type="bool"/>
 
       <!-- Encryption tab -->
       <preference id="security.enable_ssl3"            name="security.enable_ssl3"            type="bool"/>
       <preference id="security.enable_tls"             name="security.enable_tls"             type="bool"/>
 
@@ -331,16 +337,23 @@
 
             <hbox>
               <button id="showUpdateHistory"
                       label="&updateHistory.label;"
                       accesskey="&updateHistory.accesskey;"
                       preference="app.update.disable_button.showUpdateHistory"
                       oncommand="gAdvancedPane.showUpdates();"/>
             </hbox>
+
+#ifdef MOZ_MAINTENANCE_SERVICE
+            <checkbox id="useService"
+                      label="&useService.label;"
+                      accesskey="&useService.accesskey;"
+                      preference="app.update.service.enabled"/>
+#endif
           </groupbox>
 #endif
           <groupbox id="updateOthers">
             <caption label="&updateOthers.label;"/>
             <checkbox id="enableSearchUpdate"
                       label="&enableSearchUpdate.label;"
                       accesskey="&enableSearchUpdate.accesskey;"
                       preference="browser.search.update"/>
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -539,16 +539,23 @@ bin/libfreebl_32int64_3.so
 ; [Updater]
 ;
 #ifdef XP_MACOSX
 @BINPATH@/updater.app/
 #else
 @BINPATH@/updater@BIN_SUFFIX@
 #endif
 
+; [MaintenanceService]
+;
+#ifdef MOZ_MAINTENANCE_SERVICE
+@BINPATH@/maintenanceservice.exe
+@BINPATH@/maintenanceservice_installer.exe
+#endif
+
 ; [Crash Reporter]
 ;
 #ifdef MOZ_CRASHREPORTER
 #ifdef XP_MACOSX
 @BINPATH@/crashreporter.app/
 #else
 @BINPATH@/crashreporter@BIN_SUFFIX@
 @BINPATH@/crashreporter.ini
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -1447,8 +1447,12 @@ extensions/inspector@mozilla.org/chrome/
 extensions/inspector@mozilla.org/defaults/preferences/inspector.js
 extensions/inspector@mozilla.org/platform/WINNT/chrome/icons/default/winInspectorMain.ico
 extensions/inspector@mozilla.org/components/inspector.xpt
 extensions/inspector@mozilla.org/components/@DLL_PREFIX@inspector@DLL_SUFFIX@
 extensions/inspector@mozilla.org/chrome/icons/default/winInspectorMain.ico
 components/nsPlacesTransactionsService.js
 components/browserplaces.xpt
 components/nsPlacesDBFlush.js
+#ifndef MOZ_MAINTENANCE_SERVICE
+maintenanceservice.exe
+maintenanceservice_installer.exe
+#endif
--- a/browser/installer/windows/Makefile.in
+++ b/browser/installer/windows/Makefile.in
@@ -56,16 +56,23 @@ DEFINES += -DPRE_RELEASE_SUFFIX="$(PRE_R
 
 INSTALLER_FILES = \
 	app.tag \
 	nsis/installer.nsi \
 	nsis/uninstaller.nsi \
 	nsis/shared.nsh \
 	$(NULL)
 
+ifdef MOZ_MAINTENANCE_SERVICE
+INSTALLER_FILES += \
+	nsis/maintenanceservice_installer.nsi \
+	$(NULL)
+endif
+
+
 BRANDING_FILES = \
 	branding.nsi \
 	wizHeader.bmp \
 	wizHeaderRTL.bmp \
 	wizWatermark.bmp \
 	$(NULL)
 
 DEFINES += \
@@ -99,16 +106,27 @@ uninstaller::
 	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(DIST)/branding/,$(BRANDING_FILES)) $(CONFIG_DIR)
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) \
 	  $(srcdir)/nsis/defines.nsi.in > $(CONFIG_DIR)/defines.nsi
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
 	  --preprocess-locale $(topsrcdir) \
 	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
 
+# For building the maintenanceservice installer
+ifdef MOZ_MAINTENANCE_SERVICE
+maintenanceservice_installer::
+	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) \
+	  $(srcdir)/nsis/defines.nsi.in > $(CONFIG_DIR)/defines.nsi
+	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+	  --preprocess-locale $(topsrcdir) \
+	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
+endif
+
 $(CONFIG_DIR)/setup.exe::
 	$(RM) -r $(CONFIG_DIR)
 	$(MKDIR) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(DIST)/branding/,$(BRANDING_FILES)) $(CONFIG_DIR)
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) \
 	  $(srcdir)/nsis/defines.nsi.in > $(CONFIG_DIR)/defines.nsi
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -12,16 +12,19 @@
 !define AppRegName            "Firefox"
 
 !define BrandShortName        "@MOZ_APP_DISPLAYNAME@"
 !define PreReleaseSuffix      "@PRE_RELEASE_SUFFIX@"
 !define BrandFullName         "${BrandFullNameInternal}${PreReleaseSuffix}"
 
 !define NO_UNINSTALL_SURVEY
 
+!define CERTIFICATE_NAME      "Mozilla Corporation"
+!define CERTIFICATE_ISSUER    "Thawte Code Signing CA - G2"
+
 # LSP_CATEGORIES is the permitted LSP categories for the application. Each LSP
 # category value is ANDed together to set multiple permitted categories.
 # See http://msdn.microsoft.com/en-us/library/ms742253%28VS.85%29.aspx
 # The value below removes all LSP categories previously set.
 !define LSP_CATEGORIES "0x00000000"
 
 # NO_INSTDIR_FROM_REG is defined for pre-releases which have a PreReleaseSuffix
 # (e.g. Alpha X, Beta X, etc.) to prevent finding a non-default installation
@@ -38,16 +41,20 @@
 !define HAVE_64BIT_OS
 !define ARCH "x64"
 !define MinSupportedVer "Microsoft Windows Vista x64"
 #else
 !define ARCH "x86"
 !define MinSupportedVer "Microsoft Windows 2000"
 #endif
 
+#ifdef MOZ_MAINTENANCE_SERVICE
+!define MOZ_MAINTENANCE_SERVICE
+#endif
+
 # File details shared by both the installer and uninstaller
 VIProductVersion "1.0.0.0"
 VIAddVersionKey "ProductName"     "${BrandShortName}"
 VIAddVersionKey "CompanyName"     "${CompanyName}"
 #ifdef MOZ_OFFICIAL_BRANDING
 VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of The Mozilla Foundation."
 #endif
 VIAddVersionKey "LegalCopyright"  "${CompanyName}"
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -14,37 +14,39 @@
 # The Original Code is the Mozilla Installer code.
 #
 # The Initial Developer of the Original Code is Mozilla Foundation
 # Portions created by the Initial Developer are Copyright (C) 2006
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  Robert Strong <robert.bugzilla@gmail.com>
+#  Brian R. Bondy <netzen@gmail.com>
 #
 # 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
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 # Required Plugins:
-# AppAssocReg   http://nsis.sourceforge.net/Application_Association_Registration_plug-in
-# ApplicationID http://nsis.sourceforge.net/ApplicationID_plug-in
-# CityHash      http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
-# ShellLink     http://nsis.sourceforge.net/ShellLink_plug-in
-# UAC           http://nsis.sourceforge.net/UAC_plug-in
+# AppAssocReg    http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# ApplicationID  http://nsis.sourceforge.net/ApplicationID_plug-in
+# CityHash       http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
+# ShellLink      http://nsis.sourceforge.net/ShellLink_plug-in
+# UAC            http://nsis.sourceforge.net/UAC_plug-in
+# ServicesHelper Mozilla specific plugin that is located in /other-licenses/nsis
 
 ; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
 !verbose 3
 
 ; 7-Zip provides better compression than the lzma from NSIS so we add the files
 ; uncompressed and use 7-Zip to create a SFX archive of it
 SetDatablockOptimize on
 SetCompress off
@@ -54,16 +56,17 @@ RequestExecutionLevel user
 
 !addplugindir ./
 
 Var TmpVal
 Var InstallType
 Var AddStartMenuSC
 Var AddQuickLaunchSC
 Var AddDesktopSC
+Var InstallMaintenanceService
 Var PageName
 
 ; By defining NO_STARTMENU_DIR an installer that doesn't provide an option for
 ; an application's Start Menu PROGRAMS directory and doesn't define the
 ; StartMenuDir variable can use the common InstallOnInitCommon macro.
 !define NO_STARTMENU_DIR
 
 ; On Vista and above attempt to elevate Standard Users in addition to users that
@@ -106,16 +109,17 @@ VIAddVersionKey "OriginalFilename" "setu
 !insertmacro CleanUpdatesDir
 !insertmacro CopyFilesFromDir
 !insertmacro CreateRegKey
 !insertmacro GetPathFromString
 !insertmacro GetParent
 !insertmacro InitHashAppModelId
 !insertmacro IsHandlerForInstallDir
 !insertmacro IsPinnedToTaskBar
+!insertmacro IsUserAdmin
 !insertmacro LogDesktopShortcut
 !insertmacro LogQuickLaunchShortcut
 !insertmacro LogStartMenuShortcut
 !insertmacro ManualCloseAppPrompt
 !insertmacro PinnedToStartMenuLnkCount
 !insertmacro RegCleanAppHandler
 !insertmacro RegCleanMain
 !insertmacro RegCleanUninstall
@@ -178,16 +182,21 @@ ShowInstDetails nevershow
 Page custom preOptions leaveOptions
 
 ; Select Install Directory Page
 !define MUI_PAGE_CUSTOMFUNCTION_PRE preDirectory
 !define MUI_PAGE_CUSTOMFUNCTION_LEAVE leaveDirectory
 !define MUI_DIRECTORYPAGE_VERIFYONLEAVE
 !insertmacro MUI_PAGE_DIRECTORY
 
+; Custom Components Page
+!ifdef MOZ_MAINTENANCE_SERVICE
+Page custom preComponents leaveComponents
+!endif
+
 ; Custom Shortcuts Page
 Page custom preShortcuts leaveShortcuts
 
 ; Custom Summary Page
 Page custom preSummary leaveSummary
 
 ; Install Files Page
 !insertmacro MUI_PAGE_INSTFILES
@@ -368,16 +377,47 @@ Section "-Application" APP_IDX
     ${If} $AddDesktopSC == 1
     ${OrIf} $AddStartMenuSC == 1
       WriteRegDWORD HKLM "$0" "IconsVisible" 1
     ${Else}
       WriteRegDWORD HKLM "$0" "IconsVisible" 0
     ${EndIf}
   ${EndIf}
 
+!ifdef MOZ_MAINTENANCE_SERVICE
+  ; If the maintenance service page was displayed then a value was already 
+  ; explicitly selected for installing the maintenance service and 
+  ; and so InstallMaintenanceService will already be 0 or 1.
+  ; If the maintenance service page was not displayed then 
+  ; InstallMaintenanceService will be equal to "".
+  ${If} $InstallMaintenanceService == ""
+    Call IsUserAdmin
+    Pop $R0
+    ${If} $R0 == "true"
+    ; Only proceed if we have HKLM write access
+    ${AndIf} $TmpVal == "HKLM"
+    ; On Windows 2000 we do not install the maintenance service.
+    ${AndIf} ${AtLeastWinXP}
+      ; The user is an admin so we should default to install service yes
+      StrCpy $InstallMaintenanceService "1"
+    ${Else}
+      ; The user is not admin so we should default to install service no
+      StrCpy $InstallMaintenanceService "0"
+    ${EndIf}
+  ${EndIf}
+
+  ${If} $InstallMaintenanceService == "1"
+    ; The user wants to install the maintenance service, so execute
+    ; the pre-packaged maintenance service installer. 
+    ; This option can only be turned on if the user is an admin so there
+    ; is no need to use ExecShell w/ verb runas to enforce elevated.
+    nsExec::Exec "$INSTDIR\maintenanceservice_installer.exe" 
+  ${EndIf}
+!endif
+
   ; These need special handling on uninstall since they may be overwritten by
   ; an install into a different location.
   StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
   ${WriteRegStr2} $TmpVal "$0" "" "$INSTDIR\${FileMainEXE}" 0
   ${WriteRegStr2} $TmpVal "$0" "Path" "$INSTDIR" 0
 
   StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\$R9"
   ${CreateRegKey} "$TmpVal" "$0" 0
@@ -421,17 +461,17 @@ Section "-Application" APP_IDX
   ; maintenance for the unelevated user context in case that is different than
   ; the current user.
   ClearErrors
   ${GetParameters} $0
   ${GetOptions} "$0" "/UAC:" $0
   ${Unless} ${Errors}
     GetFunctionAddress $0 FixShortcutAppModelIDs
     UAC::ExecCodeSegment $0
-  ${EndIf}
+  ${EndUnless}
 
   ; UAC only allows elevating to an Admin account so there is no need to add
   ; the Start Menu or Desktop shortcuts from the original unelevated process
   ; since this will either add it for the user if unelevated or All Users if
   ; elevated.
   ${If} $AddStartMenuSC == 1
     CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
     ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
@@ -477,16 +517,23 @@ Section "-Application" APP_IDX
         ; add the log entry without the path since there is no simple way to
         ; know the correct full path.
         ${LogMsg} "Added Quick Launch Shortcut: ${BrandFullName}.lnk"
         GetFunctionAddress $0 AddQuickLaunchShortcut
         UAC::ExecCodeSegment $0
       ${EndIf}
     ${EndUnless}
   ${EndIf}
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+  ${If} $TmpVal == "HKLM"
+    ; Add the registry keys for allowed certificates.
+    ${AddMaintCertKeys}
+  ${EndIf}
+!endif
 SectionEnd
 
 ; Cleanup operations to perform at the end of the installation.
 Section "-InstallEndCleanup"
   SetDetailsPrint both
   DetailPrint "$(STATUS_CLEANUP)"
   SetDetailsPrint none
 
@@ -794,16 +841,68 @@ Function leaveShortcuts
     ${MUI_INSTALLOPTIONS_READ} $AddQuickLaunchSC "shortcuts.ini" "Field 4" "State"
   ${EndUnless}
 
   ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
     Call CheckExistingInstall
   ${EndIf}
 FunctionEnd
 
+!ifdef MOZ_MAINTENANCE_SERVICE
+Function preComponents
+  ; If the service already exists, don't show this page
+  ServicesHelper::IsInstalled "MozillaMaintenance"
+  Pop $R9
+  ${If} $R9 == 1
+    ; The service already exists so don't show this page.
+    Abort
+  ${EndIf}
+
+  ; On Windows 2000 we do not install the maintenance service.
+  ${Unless} ${AtLeastWinXP}
+    Abort
+  ${EndUnless}
+
+  ; Don't show the custom components page if the
+  ; user is not an admin
+  Call IsUserAdmin
+  Pop $R9
+  ${If} $R9 != "true"
+    Abort
+  ${EndIf}
+
+  ; Only show the maintenance service page if we have write access to HKLM
+  ClearErrors
+  WriteRegStr HKLM "Software\Mozilla" \
+              "${BrandShortName}InstallerTest" "Write Test"
+  ${If} ${Errors}
+    ClearErrors
+    Abort
+  ${Else}
+    DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+  ${EndIf}
+
+  StrCpy $PageName "Components"
+  ${CheckCustomCommon}
+  !insertmacro MUI_HEADER_TEXT "$(COMPONENTS_PAGE_TITLE)" "$(COMPONENTS_PAGE_SUBTITLE)"
+  !insertmacro MUI_INSTALLOPTIONS_DISPLAY "components.ini"
+FunctionEnd
+
+Function leaveComponents
+  ${MUI_INSTALLOPTIONS_READ} $0 "components.ini" "Settings" "State"
+  ${If} $0 != 0
+    Abort
+  ${EndIf}
+  ${MUI_INSTALLOPTIONS_READ} $InstallMaintenanceService "components.ini" "Field 2" "State"
+  ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
+    Call CheckExistingInstall
+  ${EndIf}
+FunctionEnd
+!endif
+
 Function preSummary
   StrCpy $PageName "Summary"
   ; Setup the summary.ini file for the Custom Summary Page
   WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "3"
 
   WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Type   "label"
   WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Text   "$(SUMMARY_INSTALLED_TO)"
   WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Left   "0"
@@ -929,54 +1028,55 @@ Function .onInit
   StrCpy $PageName ""
   StrCpy $LANGUAGE 0
   ${SetBrandNameVars} "$EXEDIR\core\distribution\setup.ini"
 
   ${InstallOnInitCommon} "$(WARN_MIN_SUPPORTED_OS_MSG)"
 
   !insertmacro InitInstallOptionsFile "options.ini"
   !insertmacro InitInstallOptionsFile "shortcuts.ini"
+  !insertmacro InitInstallOptionsFile "components.ini"
   !insertmacro InitInstallOptionsFile "summary.ini"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Settings" NumFields "5"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Type   "label"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Text   "$(OPTIONS_SUMMARY)"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Left   "0"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Right  "-1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Top    "0"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Bottom "10"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Type   "RadioButton"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Text   "$(OPTION_STANDARD_RADIO)"
-  WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Left   "15"
+  WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Left   "0"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Right  "-1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Top    "25"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Bottom "35"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" State  "1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Flags  "GROUP"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Type   "RadioButton"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Text   "$(OPTION_CUSTOM_RADIO)"
-  WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Left   "15"
+  WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Left   "0"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Right  "-1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Top    "55"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Bottom "65"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" State  "0"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Type   "label"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Text   "$(OPTION_STANDARD_DESC)"
-  WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Left   "30"
+  WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Left   "15"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Right  "-1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Top    "37"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Bottom "57"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Type   "label"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Text   "$(OPTION_CUSTOM_DESC)"
-  WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Left   "30"
+  WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Left   "15"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Right  "-1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Top    "67"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Bottom "87"
 
   ; Setup the shortcuts.ini file for the Custom Shortcuts Page
   ; Don't offer to install the quick launch shortcut on Windows 7
   ${If} ${AtLeastWin7}
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Settings" NumFields "3"
@@ -988,42 +1088,61 @@ Function .onInit
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Text   "$(CREATE_ICONS_DESC)"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Left   "0"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Right  "-1"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Top    "5"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Bottom "15"
 
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Type   "checkbox"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Text   "$(ICONS_DESKTOP)"
-  WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Left   "15"
+  WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Left   "0"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Right  "-1"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Top    "20"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Bottom "30"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" State  "1"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Flags  "GROUP"
 
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Type   "checkbox"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Text   "$(ICONS_STARTMENU)"
-  WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Left   "15"
+  WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Left   "0"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Right  "-1"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Top    "40"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Bottom "50"
   WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" State  "1"
 
   ; Don't offer to install the quick launch shortcut on Windows 7
   ${Unless} ${AtLeastWin7}
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Type   "checkbox"
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Text   "$(ICONS_QUICKLAUNCH)"
-    WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Left   "15"
+    WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Left   "0"
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Right  "-1"
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Top    "60"
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Bottom "70"
     WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" State  "1"
   ${EndUnless}
 
+  ; Setup the components.ini file for the Components Page
+  WriteINIStr "$PLUGINSDIR\components.ini" "Settings" NumFields "2"
+
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Type   "label"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Text   "$(OPTIONAL_COMPONENTS_DESC)"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Left   "0"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Right  "-1"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Top    "5"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "15"
+
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Type   "checkbox"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Text   "$(MAINTENANCE_SERVICE_CHECKBOX_DESC)"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Left   "0"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Right  "-1"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Top    "20"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Bottom "30"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" State  "1"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Flags  "GROUP"
+
   ; There must always be a core directory.
   ${GetSize} "$EXEDIR\core\" "/S=0K" $R5 $R7 $R8
   SectionSetSize ${APP_IDX} $R5
 
   ; Initialize $hHeaderBitmap to prevent redundant changing of the bitmap if
   ; the user clicks the back button
   StrCpy $hHeaderBitmap ""
 FunctionEnd
new file mode 100644
--- /dev/null
+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
@@ -0,0 +1,283 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is an NSIS installer for the maintenance service
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Brian R. Bondy <netzen@gmail.com>
+#
+# 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
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+# Required Plugins:
+# ServicesHelper Mozilla specific plugin that is located in /other-licenses/nsis
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel admin
+!addplugindir ./
+
+; Variables
+Var TempMaintServiceName
+Var BrandFullNameDA
+Var BrandFullName
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include LogicLib.nsh
+!include MUI.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+
+!insertmacro GetOptions
+!insertmacro GetParameters
+!insertmacro GetSize
+
+; The test slaves use this fallback key to run tests.
+; And anyone that wants to run tests themselves should already have 
+; this installed.
+!define FallbackKey \
+  "SOFTWARE\Mozilla\MaintenanceService\3932ecacee736d366d6436db0f55bce4"
+
+!define CompanyName "Mozilla Corporation"
+!define BrandFullNameInternal ""
+
+; The following includes are custom.
+!include defines.nsi
+; We keep defines.nsi defined so that we get other things like 
+; the version number, but we redefine BrandFullName
+!define MaintFullName "Mozilla Maintenance Service"
+!undef BrandFullName
+!define BrandFullName "${MaintFullName}"
+
+!include common.nsh
+!include locales.nsi
+
+VIAddVersionKey "FileDescription" "${MaintFullName} Installer"
+VIAddVersionKey "OriginalFilename" "maintenanceservice_installer.exe"
+
+Name "${MaintFullName}"
+OutFile "maintenanceservice_installer.exe"
+
+; Get installation folder from registry if available
+InstallDirRegKey HKLM "Software\Mozilla\MaintenanceService" ""
+
+SetOverwrite on
+
+!define MaintUninstallKey \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\MozillaMaintenanceService"
+
+; The HAVE_64BIT_OS define also means that we have an x64 build,
+; not just an x64 OS.
+!ifdef HAVE_64BIT_OS
+  ; See below, we actually abort the install for x64 builds currently.
+  InstallDir "$PROGRAMFILES64\${MaintFullName}\"
+!else
+  InstallDir "$PROGRAMFILES32\${MaintFullName}\"
+!endif
+ShowUnInstDetails nevershow
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+
+;Interface Settings
+!define MUI_ABORTWARNING
+
+; Uninstaller Pages
+!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale
+; using " " for BrandingText will hide the "Nullsoft Install System..." branding
+BrandingText " "
+
+Function .onInit
+  SetSilent silent
+!ifdef HAVE_64BIT_OS
+  ; We plan to eventually enable 64bit native builds to use the maintenance
+  ; service, but for the initial release, to reduce testing and development,
+  ; 64-bit builds will not install the maintenanceservice.
+  Abort
+!endif
+
+  ; On Windows 2000 we do not install the maintenance service.
+  ; We won't run this installer from the parent installer, but just in case 
+  ; someone tries to execute it on Windows 2000...
+  ${Unless} ${AtLeastWinXP}
+    Abort
+  ${EndUnless}
+FunctionEnd
+
+Function un.onInit
+  StrCpy $BrandFullNameDA "${MaintFullName}"
+  StrCpy $BrandFullName "${MaintFullName}"
+FunctionEnd
+
+Section "MaintenanceService"
+  AllowSkipFiles off
+
+  CreateDirectory $INSTDIR
+  SetOutPath $INSTDIR
+
+  ; If the service already exists, then stop it if it is running.
+  ServicesHelper::IsInstalled "MozillaMaintenance"
+  Pop $R9
+  ${If} $R9 == 1
+    ; Stop the maintenance service so we can overwrite any
+    ; binaries that it uses.
+    ServicesHelper::Stop "MozillaMaintenance"
+  ${EndIf}
+
+  ; If we don't have maintenanceservice.exe already installed
+  ; then keep that name.  If we do use maintenanceservice_tmp.exe
+  ; which will auto install itself when you call it with the install parameter.
+  StrCpy $TempMaintServiceName "maintenanceservice.exe"
+  IfFileExists "$INSTDIR\maintenanceservice.exe" 0 skipAlreadyExists
+    StrCpy $TempMaintServiceName "maintenanceservice_tmp.exe"
+  skipAlreadyExists:
+
+  ; We always write out a copy and then decide whether to install it or 
+  ; not via calling its 'install' cmdline which works by version comparison.
+  CopyFiles "$EXEDIR\maintenanceservice.exe" "$INSTDIR\$TempMaintServiceName"
+
+  ; Install the application maintenance service.
+  ; If a service already exists, the command line parameter will stop the
+  ; service and only install itself if it is newer than the already installed
+  ; service.  If successful it will remove the old maintenanceservice.exe
+  ; and replace it with maintenanceservice_tmp.exe.
+  ClearErrors
+  ${GetParameters} $0
+  ${GetOptions} "$0" "/Upgrade" $0
+  ${If} ${Errors}
+    nsExec::Exec '"$INSTDIR\$TempMaintServiceName" install'
+  ${Else}
+    ; The upgrade cmdline is the same as install except
+    ; It will fail if the service isn't already installed.
+    nsExec::Exec '"$INSTDIR\$TempMaintServiceName" upgrade'
+  ${EndIf}
+
+  WriteUninstaller "$INSTDIR\Uninstall.exe"
+  WriteRegStr HKLM "${MaintUninstallKey}" "DisplayName" "${MaintFullName}"
+  WriteRegStr HKLM "${MaintUninstallKey}" "UninstallString" \
+                   '"$INSTDIR\uninstall.exe"'
+  WriteRegStr HKLM "${MaintUninstallKey}" "DisplayIcon" \
+                   "$INSTDIR\Uninstall.exe,0"
+  WriteRegStr HKLM "${MaintUninstallKey}" "DisplayVersion" "${AppVersion}"
+  WriteRegStr HKLM "${MaintUninstallKey}" "Publisher" "Mozilla"
+  WriteRegStr HKLM "${MaintUninstallKey}" "Comments" \
+                   "${BrandFullName} ${AppVersion} (${ARCH} ${AB_CD})"
+  ${GetSize} "$INSTDIR" "/S=0K" $R2 $R3 $R4
+  WriteRegDWORD HKLM "${MaintUninstallKey}" "EstimatedSize" $R2 
+
+  ; Write out that a maintenance service was attempted.
+  ; We do this because on upgrades we will check this value and we only
+  ; want to install once on the first upgrade to maintenance service.
+  ; Also write out that we are currently installed, preferences will check
+  ; this value to determine if we should show the service update pref.
+  ; Since the Maintenance service can be installed either x86 or x64,
+  ; always use the 64-bit registry for checking if an attempt was made.
+  SetRegView 64
+  WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Attempted" 1
+  WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Installed" 1
+
+  ; Included here for debug purposes only.  
+  ; These keys are used to bypass the installation dir is a valid installation
+  ; check from the service so that tests can be run.
+  ; WriteRegStr HKLM "${FallbackKey}\0" "name" "Mozilla Corporation"
+  ; WriteRegStr HKLM "${FallbackKey}\0" "issuer" "Thawte Code Signing CA - G2"
+  SetRegView lastused
+
+  # The Mozilla/updates directory will have an inherited permission
+  # which allows any user to write to it.  Work items are written there.
+  SetShellVarContext all
+  CreateDirectory "$APPDATA\Mozilla\updates"
+SectionEnd
+
+; By renaming before deleting we improve things slightly in case
+; there is a file in use error. In this case a new install can happen.
+Function un.RenameDelete
+  Pop $9
+  ; If the .moz-delete file already exists previously, delete it
+  ; If it doesn't exist, the call is ignored.
+  ; We don't need to pass /REBOOTOK here since it was already marked that way
+  ; if it exists.
+  Delete "$9.moz-delete"
+  Rename "$9" "$9.moz-delete"
+  ${If} ${Errors}
+    Delete /REBOOTOK "$9"
+  ${Else} 
+    Delete /REBOOTOK "$9.moz-delete"
+  ${EndIf}
+  ClearErrors
+FunctionEnd
+
+Section "Uninstall"
+  ; Delete the service so that no updates will be attempted
+  nsExec::Exec '"$INSTDIR\maintenanceservice.exe" uninstall'
+
+  Push "$INSTDIR\maintenanceservice.exe"
+  Call un.RenameDelete
+  Push "$INSTDIR\maintenanceservice_tmp.exe"
+  Call un.RenameDelete
+  Push "$INSTDIR\Uninstall.exe"
+  Call un.RenameDelete
+  RMDir /REBOOTOK "$INSTDIR"
+
+  DeleteRegKey HKLM "${MaintUninstallKey}"
+
+  SetRegView 64
+  DeleteRegValue HKLM "Software\Mozilla\MaintenanceService" "Installed"
+  DeleteRegKey HKLM "${FallbackKey}\"
+  SetRegView lastused
+SectionEnd
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -14,16 +14,17 @@
 # The Original Code is the Mozilla Installer code.
 #
 # The Initial Developer of the Original Code is Mozilla Foundation
 # Portions created by the Initial Developer are Copyright (C) 2006
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  Robert Strong <robert.bugzilla@gmail.com>
+#  Brian R. Bondy <netzen@gmail.com>
 #
 # 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
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -100,16 +101,56 @@
   ${FixClassKeys}
   ${SetUninstallKeys}
 
   ; Remove files that may be left behind by the application in the
   ; VirtualStore directory.
   ${CleanVirtualStore}
 
   ${RemoveDeprecatedFiles}
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+  Call IsUserAdmin
+  Pop $R0
+  ${If} $R0 == "true"
+  ; Only proceed if we have HKLM write access
+  ${AndIf} $TmpVal == "HKLM"
+  ; On Windows 2000 we do not install the maintenance service.
+  ${AndIf} ${AtLeastWinXP}
+    ; Add the registry keys for allowed certificates.
+    ${AddMaintCertKeys}
+
+    ; We check to see if the maintenance service install was already attempted.
+    ; Since the Maintenance service can be installed either x86 or x64,
+    ; always use the 64-bit registry for checking if an attempt was made.
+    SetRegView 64
+    ReadRegDWORD $5 HKLM "Software\Mozilla\MaintenanceService" "Attempted"
+    ClearErrors
+    SetRegView lastused
+
+    ; If the maintenance service is already installed, do nothing.
+    ; The maintenance service will launch:
+    ; maintenanceservice_installer.exe /Upgrade to upgrade the maintenance
+    ; service if necessary.   If the update was done from updater.exe without 
+    ; the service (i.e. service is failing), updater.exe will do the update of 
+    ; the service.  The reasons we do not do it here is because we don't want 
+    ; to have to prompt for limited user accounts when the service isn't used 
+    ; and we currently call the PostUpdate twice, once for the user and once
+    ; for the SYSTEM account.  Also, this would stop the maintenance service
+    ; and we need a return result back to the service when run that way.
+    ${If} $5 == ""
+      ; An install of maintenance service was never attempted.
+      ; We call ExecShell (which is ShellExecute) with the verb "runas"
+      ; to ask for elevation if the user isn't already elevated.  If the user 
+      ; is already elevated it will just launch the program.
+      ExecShell "runas" "$INSTDIR\maintenanceservice_installer.exe"
+    ${EndIf}
+  ${EndIf}
+!endif
+
 !macroend
 !define PostUpdate "!insertmacro PostUpdate"
 
 !macro SetAsDefaultAppGlobal
   ${RemoveDeprecatedKeys}
 
   SetShellVarContext all      ; Set SHCTX to all users (e.g. HKLM)
   ${SetHandlers}
@@ -432,48 +473,61 @@
 !macroend
 !define SetAppKeys "!insertmacro SetAppKeys"
 
 ; Add uninstall registry entries. This macro tests for write access to determine
 ; if the uninstall keys should be added to HKLM or HKCU.
 !macro SetUninstallKeys
   StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\Uninstall\${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})"
 
+  StrCpy $2 ""
+  ClearErrors
   WriteRegStr HKLM "$0" "${BrandShortName}InstallerTest" "Write Test"
   ${If} ${Errors}
-    StrCpy $1 "HKCU"
-    SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
+    ; If the uninstall keys already exist in HKLM don't create them in HKCU
+    ClearErrors
+    ReadRegStr $2 "HKLM" $0 "DisplayName"
+    ${If} $2 == ""
+      ; Otherwise we don't have any keys for this product in HKLM so proceeed
+      ; to create them in HKCU.  Better handling for this will be done in:
+      ; Bug 711044 - Better handling for 2 uninstall icons
+      StrCpy $1 "HKCU"
+      SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
+    ${EndIf}
+    ClearErrors
   ${Else}
     StrCpy $1 "HKLM"
     SetShellVarContext all     ; Set SHCTX to all users (e.g. HKLM)
     DeleteRegValue HKLM "$0" "${BrandShortName}InstallerTest"
   ${EndIf}
 
-  ${GetLongPath} "$INSTDIR" $8
+  ${If} $2 == ""
+    ${GetLongPath} "$INSTDIR" $8
 
-  ; Write the uninstall registry keys
-  ${WriteRegStr2} $1 "$0" "Comments" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
-  ${WriteRegStr2} $1 "$0" "DisplayIcon" "$8\${FileMainEXE},0" 0
-  ${WriteRegStr2} $1 "$0" "DisplayName" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
-  ${WriteRegStr2} $1 "$0" "DisplayVersion" "${AppVersion}" 0
-  ${WriteRegStr2} $1 "$0" "InstallLocation" "$8" 0
-  ${WriteRegStr2} $1 "$0" "Publisher" "Mozilla" 0
-  ${WriteRegStr2} $1 "$0" "UninstallString" "$8\uninstall\helper.exe" 0
-  ${WriteRegStr2} $1 "$0" "URLInfoAbout" "${URLInfoAbout}" 0
-  ${WriteRegStr2} $1 "$0" "URLUpdateInfo" "${URLUpdateInfo}" 0
-  ${WriteRegDWORD2} $1 "$0" "NoModify" 1 0
-  ${WriteRegDWORD2} $1 "$0" "NoRepair" 1 0
+    ; Write the uninstall registry keys
+    ${WriteRegStr2} $1 "$0" "Comments" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
+    ${WriteRegStr2} $1 "$0" "DisplayIcon" "$8\${FileMainEXE},0" 0
+    ${WriteRegStr2} $1 "$0" "DisplayName" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
+    ${WriteRegStr2} $1 "$0" "DisplayVersion" "${AppVersion}" 0
+    ${WriteRegStr2} $1 "$0" "InstallLocation" "$8" 0
+    ${WriteRegStr2} $1 "$0" "Publisher" "Mozilla" 0
+    ${WriteRegStr2} $1 "$0" "UninstallString" "$8\uninstall\helper.exe" 0
+    ${WriteRegStr2} $1 "$0" "URLInfoAbout" "${URLInfoAbout}" 0
+    ${WriteRegStr2} $1 "$0" "URLUpdateInfo" "${URLUpdateInfo}" 0
+    ${WriteRegDWORD2} $1 "$0" "NoModify" 1 0
+    ${WriteRegDWORD2} $1 "$0" "NoRepair" 1 0
 
-  ${GetSize} "$8" "/S=0K" $R2 $R3 $R4
-  ${WriteRegDWORD2} $1 "$0" "EstimatedSize" $R2 0
+    ${GetSize} "$8" "/S=0K" $R2 $R3 $R4
+    ${WriteRegDWORD2} $1 "$0" "EstimatedSize" $R2 0
 
-  ${If} "$TmpVal" == "HKLM"
-    SetShellVarContext all     ; Set SHCTX to all users (e.g. HKLM)
-  ${Else}
-    SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
+    ${If} "$TmpVal" == "HKLM"
+      SetShellVarContext all     ; Set SHCTX to all users (e.g. HKLM)
+    ${Else}
+      SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
+    ${EndIf}
   ${EndIf}
 !macroend
 !define SetUninstallKeys "!insertmacro SetUninstallKeys"
 
 ; Add app specific handler registry entries under Software\Classes if they
 ; don't exist (does not use SHCTX).
 !macro FixClassKeys
   StrCpy $1 "SOFTWARE\Classes"
@@ -542,16 +596,45 @@
   ${IsHandlerForInstallDir} "https" $R9
   ${If} "$R9" == "true"
     ${AddDDEHandlerValues} "https" "$2" "$8,1" "" "" \
                            "${DDEApplication}" "$3" "WWW_OpenURL"
   ${EndIf}
 !macroend
 !define UpdateProtocolHandlers "!insertmacro UpdateProtocolHandlers"
 
+!ifdef MOZ_MAINTENANCE_SERVICE
+; Adds maintenance service certificate keys for the install dir.
+; For the cert to work, it must also be signed by a trusted cert for the user.
+!macro AddMaintCertKeys
+  Push $R0
+  ; Allow main Mozilla cert information for updates
+  ; This call will push the needed key on the stack
+  ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
+  Pop $R0
+  ${If} $R0 != ""
+    ; More than one certificate can be specified in a different subfolder
+    ; for example: $R0\1, but each individual binary can be signed
+    ; with at most one certificate.  A fallback certificate can only be used
+    ; if the binary is replaced with a different certificate.
+    ; We always use the 64bit registry for certs.
+    ; This call is ignored on 32-bit systems.
+    SetRegView 64
+    DeleteRegKey HKLM "$R0"
+    WriteRegStr HKLM "$R0\0" "name" "${CERTIFICATE_NAME}"
+    WriteRegStr HKLM "$R0\0" "issuer" "${CERTIFICATE_ISSUER}"
+    SetRegView lastused
+    ClearErrors
+  ${EndIf} 
+  ; Restore the previously used value back
+  Pop $R0
+!macroend
+!define AddMaintCertKeys "!insertmacro AddMaintCertKeys"
+!endif
+
 ; Removes various registry entries for reasons noted below (does not use SHCTX).
 !macro RemoveDeprecatedKeys
   StrCpy $0 "SOFTWARE\Classes"
   ; Remove support for launching gopher urls from the shell during install or
   ; update if the DefaultIcon is from firefox.exe.
   ${RegCleanAppHandler} "gopher"
 
   ; Remove support for launching chrome urls from the shell during install or
@@ -1015,17 +1098,16 @@ Function SetAsDefaultAppUserHKCU
     ; as the default browser.
     ClearErrors
     ReadRegStr $0 HKLM "Software\RegisteredApplications" "${AppRegName}"
     ${Unless} ${Errors}
       AppAssocReg::SetAppAsDefaultAll "${AppRegName}"
     ${EndUnless}
   ${EndIf}
   ${RemoveDeprecatedKeys}
-
   ${PinToTaskBar}
 FunctionEnd
 
 ; Helper for updating the shortcut application model IDs.
 Function FixShortcutAppModelIDs
   ${If} ${AtLeastWin7}
   ${AndIf} "$AppUserModelID" != ""
     ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "$AppUserModelID" $0
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -56,16 +56,17 @@ RequestExecutionLevel user
 ; On Vista and above attempt to elevate Standard Users in addition to users that
 ; are a member of the Administrators group.
 !define NONADMIN_ELEVATE
 
 ; prevents compiling of the reg write logging.
 !define NO_LOG
 
 Var TmpVal
+Var MaintCertKey
 
 ; Other included files may depend upon these includes!
 ; The following includes are provided by NSIS.
 !include FileFunc.nsh
 !include LogicLib.nsh
 !include MUI.nsh
 !include WinMessages.nsh
 !include WinVer.nsh
@@ -91,16 +92,17 @@ VIAddVersionKey "OriginalFilename" "help
 !insertmacro AddDDEHandlerValues
 !insertmacro CleanVirtualStore
 !insertmacro ElevateUAC
 !insertmacro GetLongPath
 !insertmacro GetPathFromString
 !insertmacro InitHashAppModelId
 !insertmacro IsHandlerForInstallDir
 !insertmacro IsPinnedToTaskBar
+!insertmacro IsUserAdmin
 !insertmacro LogDesktopShortcut
 !insertmacro LogQuickLaunchShortcut
 !insertmacro LogStartMenuShortcut
 !insertmacro PinnedToStartMenuLnkCount
 !insertmacro RegCleanAppHandler
 !insertmacro RegCleanMain
 !insertmacro RegCleanUninstall
 !insertmacro SetAppLSPCategories
@@ -375,16 +377,31 @@ Section "Uninstall"
       FileClose $0
     ${EndUnless}
   ${EndIf}
 
   ; Refresh desktop icons otherwise the start menu internet item won't be
   ; removed and other ugly things will happen like recreation of the app's
   ; clients registry key by the OS under some conditions.
   System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)"
+
+!ifdef MOZ_MAINTENANCE_SERVICE
+  ; Get the path the allowed cert is at and remove it
+  ; Keep this block of code last since it modfies the reg view
+  ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
+  Pop $MaintCertKey
+  ${If} $MaintCertKey != ""
+    ; We always use the 64bit registry for certs
+    ; This call is ignored on 32-bit systems.
+    SetRegView 64
+    DeleteRegKey HKLM "$MaintCertKey\"
+    SetRegView lastused
+  ${EndIf}
+!endif
+
 SectionEnd
 
 ################################################################################
 # Helper Functions
 
 ; Don't setup the survey controls, functions, etc. when the application has
 ; defined NO_UNINSTALL_SURVEY
 !ifndef NO_UNINSTALL_SURVEY
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/Services.cpp
@@ -0,0 +1,274 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is an NSIS plugin for managing Windows NT services.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include "../../../../toolkit/components/maintenanceservice/pathhash.h"
+
+#pragma comment(lib, "advapi32.lib") 
+
+typedef struct _stack_t {
+  struct _stack_t *next;
+  TCHAR text[MAX_PATH];
+} stack_t;
+
+int popstring(stack_t **stacktop, LPTSTR str, int len);
+void pushstring(stack_t **stacktop, LPCTSTR str, int len);
+
+/**
+ * Determines if the specified service exists or not
+ *
+ * @param  serviceName The name of the service to check
+ * @param  exists      Whether or not the service exists 
+ * @return TRUE if there were no errors
+ */
+static BOOL
+IsServiceInstalled(LPCWSTR serviceName, BOOL &exists)
+{
+  exists = FALSE;
+
+  // Get a handle to the local computer SCM database with full access rights.
+  SC_HANDLE serviceManager = OpenSCManager(NULL, NULL, 
+                                           SC_MANAGER_ENUMERATE_SERVICE);
+  if (!serviceManager) {
+    return FALSE;
+  }
+
+  SC_HANDLE serviceHandle = OpenServiceW(serviceManager, 
+                                         serviceName, 
+                                         SERVICE_QUERY_CONFIG);
+  if (!serviceHandle && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST) {
+    CloseServiceHandle(serviceManager);
+    return FALSE;
+  }
+ 
+  if (serviceHandle) {
+    CloseServiceHandle(serviceHandle);
+    exists = TRUE;
+  } 
+
+  CloseServiceHandle(serviceManager);
+  return TRUE;
+}
+
+/**
+ * Determines if the specified service is installed or not
+ * 
+ * @param  stacktop  A pointer to the top of the stack
+ * @param  variables A pointer to the NSIS variables
+ * @return 0 if the service does not exist
+ *         1 if the service does exist
+ *         -1 if there was an error.
+ */
+extern "C" void __declspec(dllexport)
+IsInstalled(HWND hwndParent, int string_size, 
+            TCHAR *variables, stack_t **stacktop, void *extra)
+{
+  TCHAR tmp[MAX_PATH] = { L'\0' };
+  WCHAR serviceName[MAX_PATH] = { '\0' };
+  popstring(stacktop, tmp, MAX_PATH);
+
+#if !defined(UNICODE)
+    MultiByteToWideChar(CP_ACP, 0, tmp, -1, serviceName, MAX_PATH);
+#else
+    wcscpy(serviceName, tmp);
+#endif
+
+  BOOL serviceInstalled;
+  if (!IsServiceInstalled(serviceName, serviceInstalled)) {
+    pushstring(stacktop, TEXT("-1"), 3);
+  } else {
+    pushstring(stacktop, serviceInstalled ? TEXT("1") : TEXT("0"), 2);
+  }
+}
+
+/**
+ * Stops the specified service.
+ * 
+ * @param  serviceName The name of the service to stop
+ * @return TRUE if the operation was successful 
+ */
+static BOOL
+StopService(LPCWSTR serviceName)
+{
+  // Get a handle to the local computer SCM database with full access rights.
+  SC_HANDLE serviceManager = OpenSCManager(NULL, NULL, 
+                                           SC_MANAGER_ENUMERATE_SERVICE);
+  if (!serviceManager) {
+    return FALSE;
+  }
+
+  SC_HANDLE serviceHandle = OpenServiceW(serviceManager, 
+                                         serviceName, 
+                                         SERVICE_STOP);
+  if (!serviceHandle) {
+    CloseServiceHandle(serviceManager);
+    return FALSE;
+  }
+
+  //Stop the service so it deletes faster and so the uninstaller
+  // can actually delete its EXE.
+  DWORD totalWaitTime = 0;
+  SERVICE_STATUS status;
+  static const int maxWaitTime = 1000 * 60; // Never wait more than a minute
+  BOOL stopped = FALSE;
+  if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, &status)) {
+    do {
+      Sleep(status.dwWaitHint);
+      // + 10 milliseconds to make sure we always approach maxWaitTime
+      totalWaitTime += (status.dwWaitHint + 10);
+      if (status.dwCurrentState == SERVICE_STOPPED) {
+        stopped = true;
+        break;
+      } else if (totalWaitTime > maxWaitTime) {
+        break;
+      }
+    } while (QueryServiceStatus(serviceHandle, &status));
+  }
+
+  CloseServiceHandle(serviceHandle);
+  CloseServiceHandle(serviceManager);
+  return stopped;
+}
+
+/**
+ * Stops the specified service
+ * 
+ * @param  stacktop  A pointer to the top of the stack
+ * @param  variables A pointer to the NSIS variables 
+ * @return 1 if the service was stopped, 0 on error
+ */
+extern "C" void __declspec(dllexport)
+Stop(HWND hwndParent, int string_size, 
+     TCHAR *variables, stack_t **stacktop, void *extra)
+{
+  TCHAR tmp[MAX_PATH] = { L'\0' };
+  WCHAR serviceName[MAX_PATH] = { '\0' };
+
+  popstring(stacktop, tmp, MAX_PATH);
+
+#if !defined(UNICODE)
+    MultiByteToWideChar(CP_ACP, 0, tmp, -1, serviceName, MAX_PATH);
+#else
+    wcscpy(serviceName, tmp);
+#endif
+
+  if (StopService(serviceName)) {
+    pushstring(stacktop, TEXT("1"), 2);
+  } else {
+    pushstring(stacktop, TEXT("0"), 2);
+  }
+}
+
+/**
+ * Determines a unique registry path from a file or directory path
+ * 
+ * @param  stacktop  A pointer to the top of the stack
+ * @param  variables A pointer to the NSIS variables 
+ * @return The unique registry path or an empty string on error
+ */
+extern "C" void __declspec(dllexport)
+PathToUniqueRegistryPath(HWND hwndParent, int string_size, 
+                         TCHAR *variables, stack_t **stacktop, 
+                         void *extra)
+{
+  TCHAR tmp[MAX_PATH] = { L'\0' };
+  WCHAR installBasePath[MAX_PATH] = { '\0' };
+  popstring(stacktop, tmp, MAX_PATH);
+
+#if !defined(UNICODE)
+    MultiByteToWideChar(CP_ACP, 0, tmp, -1, installBasePath, MAX_PATH);
+#else
+    wcscpy(installBasePath, tmp);
+#endif
+
+  WCHAR registryPath[MAX_PATH + 1] = { '\0' };
+  if (CalculateRegistryPathFromFilePath(installBasePath, registryPath)) {
+    pushstring(stacktop, registryPath, wcslen(registryPath) + 1);
+  } else {
+    pushstring(stacktop, TEXT(""), 1);
+  }
+}
+
+BOOL WINAPI 
+DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
+{
+  return TRUE;
+}
+
+/**
+ * Removes an element from the top of the NSIS stack
+ *
+ * @param  stacktop A pointer to the top of the stack
+ * @param  str      The string to pop to
+ * @param  len      The max length
+ * @return 0 on success
+*/
+int popstring(stack_t **stacktop, TCHAR *str, int len)
+{
+  // Removes the element from the top of the stack and puts it in the buffer
+  stack_t *th;
+  if (!stacktop || !*stacktop) {
+    return 1;
+  }
+
+  th = (*stacktop);
+  lstrcpyn(str,th->text, len);
+  *stacktop = th->next;
+  GlobalFree((HGLOBAL)th);
+  return 0;
+}
+
+/**
+ * Adds an element to the top of the NSIS stack
+ *
+ * @param  stacktop A pointer to the top of the stack
+ * @param  str      The string to push on the stack
+ * @param  len      The length of the string to push on the stack
+ * @return 0 on success
+*/
+void pushstring(stack_t **stacktop, const TCHAR *str, int len)
+{
+  stack_t *th;
+  if (!stacktop) { 
+    return;
+  }
+
+  th = (stack_t*)GlobalAlloc(GPTR, sizeof(stack_t) + len);
+  lstrcpyn(th->text, str, len);
+  th->next = *stacktop;
+  *stacktop = th;
+}
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.dsp
@@ -0,0 +1,115 @@
+# Microsoft Developer Studio Project File - Name="ServicesHelper" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ServicesHelper - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "ServicesHelper.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "ServicesHelper.mak" CFG="ServicesHelper - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "ServicesHelper - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ServicesHelper - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "ServicesHelper - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXDLL_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /D "_USRDLL" /D "EXDLL_EXPORTS" /D _WIN32_WINNT=0x0400 /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  MSVCRT.LIB /nologo /entry:"DllMain" /dll /machine:I386 /nodefaultlib /out:"../../Plugins/ServicesHelper.dll" /opt:nowin98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "ServicesHelper - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXDLL_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXDLL_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "ServicesHelper - Win32 Release"
+# Name "ServicesHelper - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=\toolkit\components\maintenanceservice\pathhash.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=\toolkit\components\maintenanceservice\pathhash.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Services.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "ServicesHelper"=.\ServicesHelper.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.rc
@@ -0,0 +1,99 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "FileDescription", "NSIS Plug-in for managing Windows services"
+            VALUE "FileVersion", "1, 0, 0, 0"
+            VALUE "InternalName", "ServicesHelper"
+            VALUE "LegalCopyright", "MPL 1.1+/GPL 2.0+/LGPL 2.1+"
+            VALUE "OriginalFilename", "ServicesHelper.dll"
+            VALUE "ProductName", "ServicesHelper"
+            VALUE "ProductVersion", "1, 0, 0, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServicesHelper", "ServicesHelper.vcproj", "{A0D0AD52-1D8B-402E-92EF-81AE0C320BD7}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{A0D0AD52-1D8B-402E-92EF-81AE0C320BD7}.Debug|Win32.ActiveCfg = Debug|Win32
+		{A0D0AD52-1D8B-402E-92EF-81AE0C320BD7}.Debug|Win32.Build.0 = Debug|Win32
+		{A0D0AD52-1D8B-402E-92EF-81AE0C320BD7}.Release|Win32.ActiveCfg = Release|Win32
+		{A0D0AD52-1D8B-402E-92EF-81AE0C320BD7}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.vcproj
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="ServicesHelper"
+	ProjectGUID="{A0D0AD52-1D8B-402E-92EF-81AE0C320BD7}"
+	RootNamespace="ServicesHelper"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="../../Plugins"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../../../../toolkit/components/maintenanceservice/"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;APPLICATIONID_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="../../Plugins"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			ATLMinimizesCRunTimeLibraryUsage="true"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../../../../toolkit/components/maintenanceservice/"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;APPLICATIONID_EXPORTS"
+				RuntimeLibrary="0"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath="..\..\..\..\toolkit\components\maintenanceservice\pathhash.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Services.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\resource.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+			<File
+				RelativePath=".\ServicesHelper.rc"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
new file mode 100644
--- /dev/null
+++ b/other-licenses/nsis/Contrib/ServicesHelper/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ServicesHelper.rc
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1001
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..152bd357727a3e9754157a55e3414017cdaec797
GIT binary patch
literal 4608
zc%1E5Z){sv6+h2$T_^KWkJ+e0sdy=lHX&vbzc^WGOY_fZB9inaFY!yeH0i7JQrmj9
zz32DZroz%PV+r%HG)0JMd|+ROCec-VnNXS7Fl3_ZU{fWwGKnu8Op`|f3apAe7Wz2%
zzUMf!QnzpIhu-$wd(XMQb9{g29^YJ#9(xuV0Kkc2ngEx;YCZ7zt&GzA)mNM0mBzPQ
zFEJx;w@$_rSvI4lPpQ&rHYTM~X^lN8v#OqA6DfA!h`^ps&&$CFA8ZQPF(S^s-!k$y
zmh1BP@*_CL&L3KK;rQ|SN0%Gu_}H=o$B9HNPH?q7@DYF!#tGxWt7A3Wb!cvVfN=rf
ze{fs2*L>J{QCO;a>D*!Q2B?p~*A<5irP!^}8rJs$<Z;Yky!ksN2@^T_1#)>?!?>9{
zEC)6DoQC7<4tql`*hXS2#EGCfFKH4$w!tz`Ow(;!><L;hND@OkSmc{;!<B=otfXTA
zKeP=L=e-Tr_xS|xw|k*Y8EfN$aoimhg-RdRlCgfYu%R7UFb1+W7Y@#V=2|%TP0*RK
z(m<DkuRKB^RY9yY;Y6(5#A+%<+<Dmzd?~WYmz-U^STQhe{tdQJ7WJmedk8tol~)f{
z{(v(h=Xo8=%X`@wa54Z<vGNPFMk|*|Am?EW;^s|#v+^*8AmejR8Y;<5&&@u0WkoP%
z0^X=-d^h0ZHuiO&VU2-+ch@hSE<QejQ3cZ<XTh6)!;@ce4({((ucOxrQp~S;gs2hr
z;G)QxrASpwGNQ;AbJb#Q)4WzF>#e506ITy@85*D7RVd)cC*6o4P@VuZo$wRIc*E0V
zXI0bhML61zi>B@|bvN$T^!qH^u%Y{s&S_&$0QJ)74RJim7b7<a<WIQ#b+fB-6~B+h
zsJF<NT_Rt3hiDPpxL1st?Ma7?)SZ|(c3*B&Yoy^>47V>=)tW@QgBDb4nL(0EYd*}@
zLjGqr{);#Ht4qj1G~VLMrPnsl@W};SS^ErEjJFmp5bHlbqt*+h0&$3w^T6EuDlg+#
z+OqMb0`cH0Ee+(HH@EZ-dcrM*@|@n7Y>1!0+^;^I&uzk`8Jy}nE=}W*bkJ}=auBSC
zZV@ZZ6XiCN&S<pAnSLh{*#G-t#6yX8<DtwUGpqo=o30pPyt2IdL-yGOke=4anX5bS
zNQ(PO-me*#k^XUuzSp8(y_NodYV_B))BpDt{p$bGzsL~!Rg3;QrMrh_#F~GNQs0f#
zlMZcXobX-U5hr|!^Gi&fY<`UuqDf~G?^KJbv0m6H>YLiRB#SJg7E9y(!e8`8R0!48
z<E6-TKFMGq@3u<m+oEys084^b%|9EFYAMr{&wa>fZA&*#{2k!z9^!!T$1HgB7EW_D
zt-d#BWl%7$k@~qd%av=T(&D1I`tZ|Ra`n-tcP<s;P|p={v}{|Y@h_;?;IE!RrCxxy
z6x9;;RuO4??WM|Z(4mL7HS!5jnr`ovl_V37+ez*NJIPr5zw-dS4?G1^d(`O0yQ|gI
z0>09qlgDwl>2D`Fc14RjP5%=(#X4)C-<cKS<wma{xm0}7Qr?57%qM$He-Aw?f%wxn
zR*aoRSFwBc$+?w3nr%Eek+w<NPt$&g_A|6ULHiN**1wDQV%v-Fm?4xWP?}JxtpF92
z49<U!V;SXLlw*4>d=lpaC^IONC}&zN`zyHqKFYHwKOw#-ucN$U`z_lzXKVelzlU>7
zEgaZp`zg-x4u^Nyy6qVf1ND||xWAHpZ^PV8PG4aT%i4&P)gr2zRsnwO5ENO?!1tNM
zO8TUv463q>53~j)tEsWfVoHQ1d!<()kE!oOCdMKoTqroNC;%57XR{eKk<u1Km=Yor
zHG2a3)y0f9AuF<!mHX2v`U0G^tOGK}PA?8g**L&S%Z8gGIxpd492P7aF$|EG-ngar
ztFolY)a{66>5V<5C)8Se5ug+Lm2_4XWc5rUhS1c!f^3GEBN;hm8xb{V>HVWpN;)O0
zXqjThby;1sA%Z4pdKMrIB(j=TY&$^jK(v=1uCe^saW<AsVV3aAbLbG)bF_Dw@15>B
z2C&A=;gWJzg})IDfxs@rgb;&}VS@RHtSE8{;CaVs`E*vsC-SHe?Vp&mqFb%@TcHc@
za=gjt?;j$&Q4gTBqrCYLz$f?$x%?%7VbrHkQsnMO4+q$r&ko5-MpkL;;p}iKt4WF?
z&jT-M@yYa5D)E#qPspbdcs7f~27;Ep)1&?_Gy%_j5UWUpUYLLg%)lt5;XGEGg8w9{
zOQ@wl0~vb>C5AHrMTIlCLbMqgs||t>f^&3EenOB}!sE2R-|pGwwSJ#w+pn}=ZU0C6
z10CP$80(nrxY+UQj!1|PiJ@HRxzG!tmqI<<VJ^<)xF2xOb1!mlavyLncYe^h*69nk
ag}cHB!hPYP@K|^<d@OvoDcx^hy!{W$0+aXv
--- a/toolkit/Makefile.in
+++ b/toolkit/Makefile.in
@@ -50,22 +50,31 @@ PARALLEL_DIRS = \
   content \
   locales \
   mozapps/downloads \
   mozapps/extensions \
   mozapps/handling \
   mozapps/preferences \
   mozapps/plugins \
   mozapps/shared \
-  mozapps/update \
   obsolete \
   profile \
   themes \
   $(NULL)
 
+DIRS += \
+  mozapps/update \
+  $(NULL)
+
+ifdef MOZ_MAINTENANCE_SERVICE
+DIRS += \
+  components/maintenanceservice \
+  $(NULL)
+endif
+
 ifneq (,$(filter gtk2 qt,$(MOZ_WIDGET_TOOLKIT)))
 PARALLEL_DIRS += system/unixproxy
 endif
 
 ifneq (,$(filter cocoa,$(MOZ_WIDGET_TOOLKIT)))
 PARALLEL_DIRS += system/osxproxy
 endif
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/Makefile.in
@@ -0,0 +1,100 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla maintenance service build.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brian R. Bondy <netzen@gmail.com>
+#
+# 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
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+CPPSRCS = \
+  maintenanceservice.cpp \
+  serviceinstall.cpp \
+  workmonitor.cpp \
+  certificatecheck.cpp \
+  servicebase.cpp \
+  registrycertificates.cpp \
+  pathhash.cpp \
+  $(NULL)
+
+# For debugging purposes only
+#DEFINES += -DDISABLE_SERVICE_AUTHENTICODE_CHECK \
+#  -DDISABLE_CALLBACK_AUTHENTICODE_CHECK
+
+PROGRAM = maintenanceservice$(BIN_SUFFIX)
+DIST_PROGRAM = maintenanceservice$(BIN_SUFFIX)
+
+# Don't link the maintenanceservice against libmozutils. See bug 687139
+MOZ_UTILS_LDFLAGS =
+MOZ_UTILS_PROGRAM_LDFLAGS =
+
+LIBS += \
+  ../../mozapps/update/common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \
+  $(NULL)
+
+USE_STATIC_LIBS = 1
+HAVE_PROGRESSUI = 1
+RCINCLUDE = maintenanceservice.rc
+
+OS_LIBS += $(call EXPAND_LIBNAME,comctl32 ws2_32 shell32)
+DEFINES += -DUNICODE -D_UNICODE
+ifndef GNU_CC
+RCFLAGS += -I$(srcdir)
+else
+RCFLAGS += --include-dir $(srcdir)
+endif
+
+ifndef MOZ_WINCONSOLE
+ifdef MOZ_DEBUG
+MOZ_WINCONSOLE = 1
+else
+MOZ_WINCONSOLE = 0
+endif
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -DNS_NO_XPCOM
+
+ifdef _MSC_VER
+WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
+endif
+
+# Pick up nsWindowsRestart.cpp
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre \
+  -I$(topsrcdir)/toolkit/mozapps/update/common
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/certificatecheck.cpp
@@ -0,0 +1,301 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service certificate check code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <softpub.h>
+#include <wintrust.h>
+
+#include "certificatecheck.h"
+#include "servicebase.h"
+
+#pragma comment(lib, "wintrust.lib")
+#pragma comment(lib, "crypt32.lib")
+
+static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
+
+/**
+ * Checks to see if a file stored at filePath matches the specified info.
+ *
+ * @param  filePath    The PE file path to check
+ * @param  infoToMatch The acceptable information to match
+ * @return ERROR_SUCCESS if successful, ERROR_NOT_FOUND if the info 
+ *         does not match, or the last error otherwise.
+ */
+DWORD
+CheckCertificateForPEFile(LPCWSTR filePath, 
+                          CertificateCheckInfo &infoToMatch)
+{
+  HCERTSTORE certStore = NULL;
+  HCRYPTMSG cryptMsg = NULL; 
+  PCCERT_CONTEXT certContext = NULL;
+  PCMSG_SIGNER_INFO signerInfo = NULL;
+  DWORD lastError = ERROR_SUCCESS;
+
+  // Get the HCERTSTORE and HCRYPTMSG from the signed file.
+  DWORD encoding, contentType, formatType;
+  BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
+                                  filePath, 
+                                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+                                  CERT_QUERY_CONTENT_FLAG_ALL, 
+                                  0, &encoding, &contentType,
+                                  &formatType, &certStore, &cryptMsg, NULL);
+  if (!result) {
+    lastError = GetLastError();
+    LOG(("CryptQueryObject failed with %d\n", lastError));
+    goto cleanup;
+  }
+
+  // Pass in NULL to get the needed signer information size.
+  DWORD signerInfoSize;
+  result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, 
+                            NULL, &signerInfoSize);
+  if (!result) {
+    lastError = GetLastError();
+    LOG(("CryptMsgGetParam failed with %d\n", lastError));
+    goto cleanup;
+  }
+
+  // Allocate the needed size for the signer information.
+  signerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, signerInfoSize);
+  if (!signerInfo) {
+    lastError = GetLastError();
+    LOG(("Unable to allocate memory for Signer Info.\n"));
+    goto cleanup;
+  }
+
+  // Get the signer information (PCMSG_SIGNER_INFO).
+  // In particular we want the issuer and serial number.
+  result = CryptMsgGetParam(cryptMsg, CMSG_SIGNER_INFO_PARAM, 0, 
+                            (PVOID)signerInfo, &signerInfoSize);
+  if (!result) {
+    lastError = GetLastError();
+    LOG(("CryptMsgGetParam failed with %d\n", lastError));
+    goto cleanup;
+  }
+
+  // Search for the signer certificate in the certificate store.
+  CERT_INFO certInfo;     
+  certInfo.Issuer = signerInfo->Issuer;
+  certInfo.SerialNumber = signerInfo->SerialNumber;
+  certContext = CertFindCertificateInStore(certStore, ENCODING, 0, 
+                                           CERT_FIND_SUBJECT_CERT,
+                                           (PVOID)&certInfo, NULL);
+  if (!certContext) {
+    lastError = GetLastError();
+    LOG(("CertFindCertificateInStore failed with %d\n", lastError));
+    goto cleanup;
+  }
+
+  if (!DoCertificateAttributesMatch(certContext, infoToMatch)) {
+    lastError = ERROR_NOT_FOUND;
+    LOG(("Certificate did not match issuer or name\n"));
+    goto cleanup;
+  }
+
+cleanup:
+  if (signerInfo) {
+    LocalFree(signerInfo);
+  }
+  if (certContext) {
+    CertFreeCertificateContext(certContext);
+  }
+  if (certStore) { 
+    CertCloseStore(certStore, 0);
+  }
+  if (cryptMsg) { 
+    CryptMsgClose(cryptMsg);
+  }
+  return lastError;
+}
+
+/**
+ * Checks to see if a file stored at filePath matches the specified info.
+ *
+ * @param  certContext  The certificate context of the file
+ * @param  infoToMatch  The acceptable information to match
+ * @return FALSE if the info does not match or if any error occurs in the check
+ */
+BOOL 
+DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, 
+                             CertificateCheckInfo &infoToMatch)
+{
+  DWORD dwData;
+  LPTSTR szName = NULL;
+
+  if (infoToMatch.issuer) {
+    // Pass in NULL to get the needed size of the issuer buffer.
+    dwData = CertGetNameString(certContext, 
+                               CERT_NAME_SIMPLE_DISPLAY_TYPE,
+                               CERT_NAME_ISSUER_FLAG, NULL,
+                               NULL, 0);
+
+    if (!dwData) {
+      LOG(("CertGetNameString failed.\n"));
+      return FALSE;
+    }
+
+    // Allocate memory for Issuer name buffer.
+    LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
+    if (!szName) {
+      LOG(("Unable to allocate memory for issuer name.\n"));
+      return FALSE;
+    }
+
+    // Get Issuer name.
+    if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
+                           CERT_NAME_ISSUER_FLAG, NULL, szName, dwData)) {
+      LOG(("CertGetNameString failed.\n"));
+      LocalFree(szName);
+      return FALSE;
+    }
+
+    // If the issuer does not match, return a failure.
+    if (!infoToMatch.issuer ||
+        wcscmp(szName, infoToMatch.issuer)) {
+      LocalFree(szName);
+      return FALSE;
+    }
+
+    LocalFree(szName);
+    szName = NULL;
+  }
+
+  if (infoToMatch.name) {
+    // Pass in NULL to get the needed size of the name buffer.
+    dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
+                               0, NULL, NULL, 0);
+    if (!dwData) {
+      LOG(("CertGetNameString failed.\n"));
+      return FALSE;
+    }
+
+    // Allocate memory for the name buffer.
+    szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
+    if (!szName) {
+      LOG(("Unable to allocate memory for subject name.\n"));
+      return FALSE;
+    }
+
+    // Obtain the name.
+    if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+                            NULL, szName, dwData))) {
+      LOG(("CertGetNameString failed.\n"));
+      LocalFree(szName);
+      return FALSE;
+    }
+
+    // If the issuer does not match, return a failure.
+    if (!infoToMatch.name || 
+        wcscmp(szName, infoToMatch.name)) {
+      LocalFree(szName);
+      return FALSE;
+    }
+
+    // We have a match!
+    LocalFree(szName);
+  }
+
+  // If there were any errors we would have aborted by now.
+  return TRUE;
+}
+
+/**
+ * Duplicates the specified string
+ *
+ * @param  inputString The string to duplicate
+ * @return The duplicated string which should be freed by the caller.
+ */
+LPWSTR 
+AllocateAndCopyWideString(LPCWSTR inputString)
+{
+  LPWSTR outputString = 
+    (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR));
+  if (outputString) {
+    lstrcpyW(outputString, inputString);
+  }
+  return outputString;
+}
+
+/**
+ * Verifies the trust of the specified file path.
+ *
+ * @param  filePath  The file path to check.
+ * @return ERROR_SUCCESS if successful, or the last error code otherwise.
+ */
+DWORD
+VerifyCertificateTrustForFile(LPCWSTR filePath)
+{
+  // Setup the file to check.
+  WINTRUST_FILE_INFO fileToCheck;
+  ZeroMemory(&fileToCheck, sizeof(fileToCheck));
+  fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO);
+  fileToCheck.pcwszFilePath = filePath;
+
+  // Setup what to check, we want to check it is signed and trusted.
+  WINTRUST_DATA trustData;
+  ZeroMemory(&trustData, sizeof(trustData));
+  trustData.cbStruct = sizeof(trustData);
+  trustData.pPolicyCallbackData = NULL;
+  trustData.pSIPClientData = NULL;
+  trustData.dwUIChoice = WTD_UI_NONE;
+  trustData.fdwRevocationChecks = WTD_REVOKE_NONE; 
+  trustData.dwUnionChoice = WTD_CHOICE_FILE;
+  trustData.dwStateAction = 0;
+  trustData.hWVTStateData = NULL;
+  trustData.pwszURLReference = NULL;
+  // no UI
+  trustData.dwUIContext = 0;
+  trustData.pFile = &fileToCheck;
+
+  GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+  // Check if the file is signed by something that is trusted.
+  LONG ret = WinVerifyTrust(NULL, &policyGUID, &trustData);
+  if (ERROR_SUCCESS == ret) {
+    // The hash that represents the subject is trusted and there were no
+    // verification errors.  No publisher nor time stamp chain errors.
+    LOG(("The file \"%ls\" is signed and the signature was verified.\n",
+        filePath));
+      return ERROR_SUCCESS;
+  }
+
+  DWORD lastError = GetLastError();
+  LOG(("There was an error validating trust of the certificate for file"
+       " \"%ls\". Returned: %d, Last error: %d\n", filePath, ret, lastError));
+  return ret;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/certificatecheck.h
@@ -0,0 +1,55 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service certificate check code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions /PGM and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _CERTIFICATECHECK_H_
+#define _CERTIFICATECHECK_H_
+
+#include <wincrypt.h>
+
+struct CertificateCheckInfo
+{
+  LPCWSTR name;
+  LPCWSTR issuer;
+};
+
+BOOL DoCertificateAttributesMatch(PCCERT_CONTEXT pCertContext, 
+                                  CertificateCheckInfo &infoToMatch);
+DWORD VerifyCertificateTrustForFile(LPCWSTR filePath);
+DWORD CheckCertificateForPEFile(LPCWSTR filePath, 
+                                CertificateCheckInfo &infoToMatch);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/maintenanceservice.cpp
@@ -0,0 +1,379 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Maintenance service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include <shlwapi.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#include "serviceinstall.h"
+#include "maintenanceservice.h"
+#include "servicebase.h"
+#include "workmonitor.h"
+#include "shlobj.h"
+
+SERVICE_STATUS gSvcStatus = { 0 }; 
+SERVICE_STATUS_HANDLE gSvcStatusHandle = NULL; 
+HANDLE ghSvcStopEvent = NULL;
+BOOL gServiceStopping = FALSE;
+
+// logs are pretty small ~10 lines, so 5 seems reasonable.
+#define LOGS_TO_KEEP 5
+
+BOOL GetLogDirectoryPath(WCHAR *path);
+
+int 
+wmain(int argc, WCHAR **argv)
+{
+  // If command-line parameter is "install", install the service
+  // or upgrade if already installed
+  // If command line parameter is "forceinstall", install the service
+  // even if it is older than what is already installed.
+  // If command-line parameter is "upgrade", upgrade the service
+  // but do not install it if it is not already installed.
+  // If command line parameter is "uninstall", uninstall the service.
+  // Otherwise, the service is probably being started by the SCM.
+  bool forceInstall = !lstrcmpi(argv[1], L"forceinstall");
+  if (!lstrcmpi(argv[1], L"install") || forceInstall) {
+    WCHAR updatePath[MAX_PATH + 1];
+    if (GetLogDirectoryPath(updatePath)) {
+      LogInit(updatePath, L"maintenanceservice-install.log");
+    }
+
+    LOG(("Installing service"));
+    SvcInstallAction action = InstallSvc;
+    if (forceInstall) {
+      action = ForceInstallSvc;
+      LOG((" with force specified"));
+    }
+    LOG(("...\n"));
+
+    if (!SvcInstall(action)) {
+      LOG(("Could not install service (%d)\n", GetLastError()));
+      LogFinish();
+      return 1;
+    }
+
+    LOG(("The service was installed successfully\n"));
+    LogFinish();
+    return 0;
+  } 
+
+  if (!lstrcmpi(argv[1], L"upgrade")) {
+    WCHAR updatePath[MAX_PATH + 1];
+    if (GetLogDirectoryPath(updatePath)) {
+      LogInit(updatePath, L"maintenanceservice-install.log");
+    }
+    LOG(("Upgrading service if installed...\n"));
+    if (!SvcInstall(UpgradeSvc)) {
+      LOG(("Could not upgrade service (%d)\n", GetLastError()));
+      LogFinish();
+      return 1;
+    }
+
+    LOG(("The service was upgraded successfully\n"));
+    LogFinish();
+    return 0;
+  }
+
+  if (!lstrcmpi(argv[1], L"uninstall")) {
+    WCHAR updatePath[MAX_PATH + 1];
+    if (GetLogDirectoryPath(updatePath)) {
+      LogInit(updatePath, L"maintenanceservice-uninstall.log");
+    }
+    LOG(("Uninstalling service...\n"));
+    if (!SvcUninstall()) {
+      LOG(("Could not uninstall service (%d)\n", GetLastError()));
+      LogFinish();
+      return 1;
+    }
+    LOG(("The service was uninstalled successfully\n"));
+    LogFinish();
+    return 0;
+  }
+
+  SERVICE_TABLE_ENTRYW DispatchTable[] = { 
+    { SVC_NAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, 
+    { NULL, NULL } 
+  }; 
+
+  // This call returns when the service has stopped. 
+  // The process should simply terminate when the call returns.
+  if (!StartServiceCtrlDispatcher(DispatchTable)) {
+    LOG(("StartServiceCtrlDispatcher failed (%d)\n", GetLastError()));
+  }
+
+  return 0;
+}
+
+/**
+ * Wrapper callback for the monitoring thread.
+ *
+ * @param param Unused thread callback parameter
+ */
+DWORD
+WINAPI StartMonitoringThreadProc(LPVOID param) 
+{
+  StartDirectoryChangeMonitor();
+  return 0;
+}
+
+/**
+ * Obtains the base path where logs should be stored
+ *
+ * @param  path The out buffer for the backup log path of size MAX_PATH + 1
+ * @return TRUE if successful.
+ */
+BOOL
+GetLogDirectoryPath(WCHAR *path) 
+{
+  HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 
+    SHGFP_TYPE_CURRENT, path);
+  if (FAILED(hr)) {
+    return FALSE;
+  }
+
+  if (!PathAppendSafe(path, L"Mozilla")) {
+    return FALSE;
+  }
+  // The directory should already be created from the installer, but
+  // just to be safe in case someone deletes.
+  CreateDirectoryW(path, NULL);
+
+  if (!PathAppendSafe(path, L"logs")) {
+    return FALSE;
+  }
+  CreateDirectoryW(path, NULL);
+  return TRUE;
+}
+
+/**
+ * Calculated a backup path based on the log number.
+ *
+ * @param  path      The out buffer to store the log path of size MAX_PATH + 1
+ * @param  basePath  The base directory where the calculated path should go
+ * @param  logNumber The log number, 0 == updater.log
+ * @return TRUE if successful.
+ */
+BOOL
+GetBackupLogPath(LPWSTR path, LPCWSTR basePath, int logNumber)
+{
+  WCHAR logName[64];
+  wcscpy(path, basePath);
+  if (logNumber <= 0) {
+    swprintf(logName, L"maintenanceservice.log");
+  } else {
+    swprintf(logName, L"maintenanceservice-%d.log", logNumber);
+  }
+  return PathAppendSafe(path, logName);
+}
+
+/**
+ * Moves the old log files out of the way before a new one is written.
+ * If you for example keep 3 logs, then this function will do:
+ *   updater2.log -> updater3.log
+ *   updater1.log -> updater2.log
+ *   updater.log -> updater1.log
+ * Which clears room for a new updater.log in the basePath directory
+ * 
+ * @param basePath      The base directory path where log files are stored
+ * @param numLogsToKeep The number of logs to keep
+ */
+void
+BackupOldLogs(LPCWSTR basePath, int numLogsToKeep) 
+{
+  WCHAR oldPath[MAX_PATH + 1];
+  WCHAR newPath[MAX_PATH + 1];
+  for (int i = numLogsToKeep; i >= 1; i--) {
+    if (!GetBackupLogPath(oldPath, basePath, i -1)) {
+      continue;
+    }
+
+    if (!GetBackupLogPath(newPath, basePath, i)) {
+      continue;
+    }
+
+    if (!MoveFileEx(oldPath, newPath, MOVEFILE_REPLACE_EXISTING)) {
+      continue;
+    }
+  }
+}
+
+/**
+ * Main entry point when running as a service.
+ */
+void WINAPI 
+SvcMain(DWORD dwArgc, LPWSTR *lpszArgv)
+{
+  // Setup logging, and backup the old logs
+  WCHAR updatePath[MAX_PATH + 1];
+  if (GetLogDirectoryPath(updatePath)) {
+    BackupOldLogs(updatePath, LOGS_TO_KEEP);
+    LogInit(updatePath, L"maintenanceservice.log");
+  }
+
+  // Register the handler function for the service
+  gSvcStatusHandle = RegisterServiceCtrlHandlerW(SVC_NAME, SvcCtrlHandler);
+  if (!gSvcStatusHandle) {
+    LOG(("RegisterServiceCtrlHandler failed (%d)\n", GetLastError()));
+    return; 
+  } 
+
+  // These SERVICE_STATUS members remain as set here
+  gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+  gSvcStatus.dwServiceSpecificExitCode = 0;
+
+  // Report initial status to the SCM
+  ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
+
+  // Perform service-specific initialization and work.
+  SvcInit(dwArgc, lpszArgv);
+}
+
+/**
+ * Service initialization.
+ */
+void
+SvcInit(DWORD dwArgc, LPWSTR *lpszArgv)
+{
+  // Create an event. The control handler function, SvcCtrlHandler,
+  // signals this event when it receives the stop control code.
+  ghSvcStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (NULL == ghSvcStopEvent) {
+    ReportSvcStatus(SERVICE_STOPPED, 1, 0);
+    return;
+  }
+
+  DWORD threadID;
+  HANDLE thread = CreateThread(NULL, 0, StartMonitoringThreadProc, 0, 
+                               0, &threadID);
+
+  // Report running status when initialization is complete.
+  ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
+
+  // Perform work until service stops.
+  for(;;) {
+    // Check whether to stop the service.
+    WaitForSingleObject(ghSvcStopEvent, INFINITE);
+
+    WCHAR stopFilePath[MAX_PATH +1];
+    if (!GetUpdateDirectoryPath(stopFilePath)) {
+      LOG(("Could not obtain update directory path, terminating thread "
+           "forcefully.\n"));
+      TerminateThread(thread, 1);
+    }
+
+    // The stop file is to wake up the synchronous call for watching directory
+    // changes. Directory watching will only actually be stopped if 
+    // gServiceStopping is also set to TRUE.
+    gServiceStopping = TRUE;
+    if (!PathAppendSafe(stopFilePath, L"stop")) {
+      TerminateThread(thread, 2);
+    }
+    HANDLE stopFile = CreateFile(stopFilePath, GENERIC_READ, 0, 
+                                 NULL, CREATE_ALWAYS, 0, NULL);
+    if (stopFile == INVALID_HANDLE_VALUE) {
+      LOG(("Could not create stop file, terminating thread forcefully.\n"));
+      TerminateThread(thread, 3);
+    } else {
+      CloseHandle(stopFile);
+      DeleteFile(stopFilePath);
+    }
+
+    ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
+    return;
+  }
+}
+
+/**
+ * Sets the current service status and reports it to the SCM.
+ *  
+ * @param currentState  The current state (see SERVICE_STATUS)
+ * @param exitCode      The system error code
+ * @param waitHint      Estimated time for pending operation in milliseconds
+ */
+void
+ReportSvcStatus(DWORD currentState, 
+                DWORD exitCode, 
+                DWORD waitHint)
+{
+  static DWORD dwCheckPoint = 1;
+
+  // Fill in the SERVICE_STATUS structure.
+  gSvcStatus.dwCurrentState = currentState;
+  gSvcStatus.dwWin32ExitCode = exitCode;
+  gSvcStatus.dwWaitHint = waitHint;
+
+  if (SERVICE_START_PENDING == currentState) {
+    gSvcStatus.dwControlsAccepted = 0;
+  } else {
+    gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+  }
+
+  if ((SERVICE_RUNNING == currentState) ||
+      (SERVICE_STOPPED == currentState)) {
+    gSvcStatus.dwCheckPoint = 0;
+  } else {
+    gSvcStatus.dwCheckPoint = dwCheckPoint++;
+  }
+
+  // Report the status of the service to the SCM.
+  SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
+}
+
+/**
+ * Called by SCM whenever a control code is sent to the service
+ * using the ControlService function.
+ */
+void WINAPI
+SvcCtrlHandler(DWORD dwCtrl)
+{
+  // Handle the requested control code. 
+  switch(dwCtrl) {
+  case SERVICE_CONTROL_STOP: 
+    ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
+    // Signal the service to stop.
+    SetEvent(ghSvcStopEvent);
+    ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
+    LogFinish();
+    break;
+  case SERVICE_CONTROL_INTERROGATE: 
+    break; 
+  default: 
+    break;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+        version="1.0.0.0"
+        processorArchitecture="*"
+        name="MaintenanceService"
+        type="win32"
+/>
+<description>MaintenanceService</description>
+<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+  <ms_asmv3:security>
+    <ms_asmv3:requestedPrivileges>
+      <ms_asmv3:requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+    </ms_asmv3:requestedPrivileges>
+  </ms_asmv3:security>
+</ms_asmv3:trustInfo>
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!--The ID below indicates application support for Windows Vista --> 
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
+      <!--The ID below indicates application support for Windows 7 --> 
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> 
+    </application>
+  </compatibility>
+</assembly>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/maintenanceservice.h
@@ -0,0 +1,43 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Maintenance service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+void WINAPI SvcMain(DWORD dwArgc, LPWSTR *lpszArgv);
+void SvcInit(DWORD dwArgc, LPWSTR *lpszArgv);
+void WINAPI SvcCtrlHandler(DWORD dwCtrl);
+void ReportSvcStatus(DWORD dwCurrentState, 
+                     DWORD dwWin32ExitCode, 
+                     DWORD dwWaitHint);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/maintenanceservice.rc
@@ -0,0 +1,82 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+1                       RT_MANIFEST             "maintenanceservice.exe.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO 
+BEGIN
+END
+#endif    // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""winresrc.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/pathhash.cpp
@@ -0,0 +1,170 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is for Maintenance service path hashing 
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions /PGM and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include <wincrypt.h>
+#include "pathhash.h"
+
+
+/**
+ * Converts a binary sequence into a hex string
+ *
+ * @param hash      The binary data sequence
+ * @param hashSize  The size of the binary data sequence
+ * @param hexString A buffer to store the hex string, must be of 
+ *                  size 2 * @hashSize
+*/
+static void
+BinaryDataToHexString(const BYTE *hash, DWORD &hashSize, 
+                      LPWSTR hexString)
+{
+  WCHAR *p = hexString;
+  for (DWORD i = 0; i < hashSize; ++i) {
+    wsprintfW(p, L"%.2x", hash[i]);
+    p += 2;
+  }
+}
+
+/**
+ * Calculates an MD5 hash for the given input binary data
+ *
+ * @param  data     Any sequence of bytes
+ * @param  dataSize The number of bytes inside @data
+ * @param  hash     Output buffer to store hash, must be freed by the caller
+ * @param  hashSize The number of bytes in the output buffer
+ * @return TRUE on success
+*/
+static BOOL
+CalculateMD5(const char *data, DWORD dataSize, 
+             BYTE **hash, DWORD &hashSize)
+{
+  HCRYPTPROV hProv = 0;
+  HCRYPTHASH hHash = 0;
+
+  if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
+    if (NTE_BAD_KEYSET != GetLastError()) {
+      return FALSE;
+    }
+ 
+    // Maybe it doesn't exist, try to create it.
+    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 
+                            CRYPT_NEWKEYSET)) {
+      return FALSE;
+    }
+  }
+
+  if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
+    return FALSE;
+  }
+
+  if (!CryptHashData(hHash, reinterpret_cast<const BYTE*>(data), 
+                    dataSize, 0)) {
+    return FALSE;
+  }
+
+  DWORD dwCount = sizeof(DWORD);
+  if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&hashSize, 
+                        &dwCount, 0)) {
+    return FALSE;
+  }
+  
+  *hash = new BYTE[hashSize];
+  ZeroMemory(*hash, hashSize);
+  if (!CryptGetHashParam(hHash, HP_HASHVAL, *hash, &hashSize, 0)) {
+    return FALSE;
+  }
+
+  if (hHash) {
+    CryptDestroyHash(hHash);
+  }
+
+  if (hProv) {
+    CryptReleaseContext(hProv,0);
+  }
+
+  return TRUE;
+}
+
+/**
+ * Converts a file path into a unique registry location for cert storage
+ *
+ * @param  filePath     The input file path to get a registry path from
+ * @param  registryPath A buffer to write the registry path to, must 
+ *                      be of size in WCHARs MAX_PATH + 1
+ * @return TRUE if successful
+*/
+BOOL
+CalculateRegistryPathFromFilePath(const LPCWSTR filePath, 
+                                  LPWSTR registryPath)
+{
+  size_t filePathLen = wcslen(filePath); 
+  if (!filePathLen) {
+    return FALSE;
+  }
+
+  // If the file path ends in a slash, ignore that character
+  if (filePath[filePathLen -1] == L'\\' || 
+      filePath[filePathLen - 1] == L'/') {
+    filePathLen--;
+  }
+
+  // Copy in the full path into our own buffer.
+  // Copying in the extra slash is OK because we calculate the hash
+  // based on the filePathLen which excludes the slash.
+  // +2 to account for the possibly trailing slash and the null terminator.
+  WCHAR *lowercasePath = new WCHAR[filePathLen + 2];
+  wcscpy(lowercasePath, filePath);
+  _wcslwr(lowercasePath);
+
+  BYTE *hash;
+  DWORD hashSize = 0;
+  if (!CalculateMD5(reinterpret_cast<const char*>(lowercasePath), 
+                    filePathLen * 2, 
+                    &hash, hashSize)) {
+    delete[] lowercasePath;
+    return FALSE;
+  }
+  delete[] lowercasePath;
+
+  LPCWSTR baseRegPath = L"SOFTWARE\\Mozilla\\"
+    L"MaintenanceService\\";
+  wcsncpy(registryPath, baseRegPath, MAX_PATH);
+  BinaryDataToHexString(hash, hashSize, 
+                        registryPath + wcslen(baseRegPath));
+  delete[] hash;
+  return TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/pathhash.h
@@ -0,0 +1,52 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is for Maintenance service path hashing 
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions /PGM and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _PATHHASH_H_
+#define _PATHHASH_H_
+
+/**
+ * Converts a file path into a unique registry location for cert storage
+ *
+ * @param  filePath     The input file path to get a registry path from
+ * @param  registryPath A buffer to write the registry path to, must 
+ *                      be of size in WCHARs MAX_PATH + 1
+ * @return TRUE if successful
+*/
+BOOL CalculateRegistryPathFromFilePath(const LPCWSTR filePath, 
+                                       LPWSTR registryPath);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/registrycertificates.cpp
@@ -0,0 +1,168 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is for Maintenance service listing allowed certificates
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#include "registrycertificates.h"
+#include "pathhash.h"
+#include "nsWindowsHelpers.h"
+#include "servicebase.h"
+#define MAX_KEY_LENGTH 255
+
+/**
+ * Verifies if the file path matches any certificate stored in the registry.
+ *
+ * @param  filePath The file path of the application to check if allowed.
+ * @return TRUE if the binary matches any of the allowed certificates.
+ */
+BOOL
+DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate, LPCWSTR filePath)
+{ 
+  WCHAR maintenanceServiceKey[MAX_PATH + 1];
+  if (!CalculateRegistryPathFromFilePath(basePathForUpdate, 
+                                         maintenanceServiceKey)) {
+    return FALSE;
+  }
+
+  // We use KEY_WOW64_64KEY to always force 64-bit view.
+  // The user may have both x86 and x64 applications installed
+  // which each register information.  We need a consistent place
+  // to put those certificate attributes in and hence why we always
+  // force the non redirected registry under Wow6432Node.
+  // This flag is ignored on 32bit systems.
+  HKEY baseKeyRaw;
+  LSTATUS retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
+                                  maintenanceServiceKey, 0, 
+                                  KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
+  if (retCode != ERROR_SUCCESS) {
+    LOG(("Could not open key. (%d)\n", retCode));
+    // Our tests run with a different apply directory for each test.
+    // We use this registry key on our test slaves to store the 
+    // allowed name/issuers.
+    retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
+                            L"SOFTWARE\\Mozilla\\MaintenanceService"
+                            L"\\3932ecacee736d366d6436db0f55bce4", 0,
+                            KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not open fallback key. (%d)\n", retCode));
+      return FALSE;
+    }
+  }
+  nsAutoRegKey baseKey(baseKeyRaw);
+
+  // Get the number of subkeys.
+  DWORD subkeyCount = 0;
+  retCode = RegQueryInfoKeyW(baseKey, NULL, NULL, NULL, &subkeyCount, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+  if (retCode != ERROR_SUCCESS) {
+    LOG(("Could not query info key: %d\n", retCode));
+    return FALSE;
+  }
+
+  // Enumerate the subkeys, each subkey represents an allowed certificate.
+  for (DWORD i = 0; i < subkeyCount; i++) { 
+    WCHAR subkeyBuffer[MAX_KEY_LENGTH];
+    DWORD subkeyBufferCount = MAX_KEY_LENGTH;  
+    retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer, 
+                            &subkeyBufferCount, NULL, 
+                            NULL, NULL, NULL); 
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not enum Certs: %d\n", retCode));
+      return FALSE;
+    }
+
+    // Open the subkey for the current certificate
+    HKEY subKeyRaw;
+    retCode = RegOpenKeyExW(baseKey, 
+                            subkeyBuffer, 
+                            0, 
+                            KEY_READ | KEY_WOW64_64KEY, 
+                            &subKeyRaw);
+    nsAutoRegKey subKey(subKeyRaw);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not open subkey: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    const int MAX_CHAR_COUNT = 256;
+    DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
+    WCHAR name[MAX_CHAR_COUNT] = { L'\0' };
+    WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' };
+
+    // Get the name from the registry
+    retCode = RegQueryValueExW(subKey, L"name", 0, NULL, 
+                               (LPBYTE)name, &valueBufSize);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not obtain name from registry: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    // Get the issuer from the registry
+    valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
+    retCode = RegQueryValueExW(subKey, L"issuer", 0, NULL, 
+                               (LPBYTE)issuer, &valueBufSize);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Could not obtain issuer from registry: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    CertificateCheckInfo allowedCertificate = {
+      name, 
+      issuer, 
+    };
+
+    retCode = CheckCertificateForPEFile(filePath, allowedCertificate);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Error on certificate check: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    retCode = VerifyCertificateTrustForFile(filePath);
+    if (retCode != ERROR_SUCCESS) {
+      LOG(("Error on certificate trust check: %d\n", retCode));
+      continue; // Try the next subkey
+    }
+
+    // Raise the roof, we found a match!
+    return TRUE; 
+  }
+  
+  // No certificates match, :'(
+  return FALSE;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/registrycertificates.h
@@ -0,0 +1,46 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is for Maintenance service listing allowed certificates
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions /PGM and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _REGISTRYCERTIFICATES_H_
+#define _REGISTRYCERTIFICATES_H_
+
+#include "certificatecheck.h"
+
+BOOL DoesBinaryMatchAllowedCertificates(LPCWSTR basePathForUpdate,
+                                        LPCWSTR filePath);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by updater.rc
+//
+#define IDI_DIALOG                      1003
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        102
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1003
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/servicebase.cpp
@@ -0,0 +1,41 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Maintenance service base code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "servicebase.h"
+
+// Shared code between applications and updater.exe
+#include "nsWindowsRestart.cpp"
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/servicebase.h
@@ -0,0 +1,43 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Maintenance service base code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include "updatelogging.h"
+
+BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
+
+#define SERVICE_EVENT_NAME L"Global\\moz-5b780de9-065b-4341-a04f-ddd94b3723e5"
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/serviceinstall.cpp
@@ -0,0 +1,416 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service service installer code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include <aclapi.h>
+#include <stdlib.h>
+
+#include <nsAutoPtr.h>
+#include <nsWindowsHelpers.h>
+#include <nsMemory.h>
+
+#include "serviceinstall.h"
+#include "servicebase.h"
+#include "shellapi.h"
+
+#pragma comment(lib, "version.lib")
+
+/**
+ * Obtains the version number from the specified PE file's version information
+ * Version Format: A.B.C.D (Example 10.0.0.300)
+ *  
+ * @param  path The path of the file to check the version on
+ * @param  A    The first part of the version number
+ * @param  B    The second part of the version number
+ * @param  C    The third part of the version number
+ * @param  D    The fourth part of the version number
+ * @return TRUE if successful
+ */
+static BOOL
+GetVersionNumberFromPath(LPWSTR path, DWORD &A, DWORD &B, 
+                         DWORD &C, DWORD &D) 
+{
+  DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0);
+  nsAutoArrayPtr<char> fileVersionInfo = new char[fileVersionInfoSize];
+  if (!GetFileVersionInfoW(path, 0, fileVersionInfoSize,
+                           fileVersionInfo.get())) {
+      LOG(("Could not obtain file info of old service.  (%d)\n", 
+           GetLastError()));
+      return FALSE;
+  }
+
+  VS_FIXEDFILEINFO *fixedFileInfo = 
+    reinterpret_cast<VS_FIXEDFILEINFO *>(fileVersionInfo.get());
+  UINT size;
+  if (!VerQueryValueW(fileVersionInfo.get(), L"\\", 
+    reinterpret_cast<LPVOID*>(&fixedFileInfo), &size)) {
+      LOG(("Could not query file version info of old service.  (%d)\n", 
+           GetLastError()));
+      return FALSE;
+  }  
+
+  A = HIWORD(fixedFileInfo->dwFileVersionMS);
+  B = LOWORD(fixedFileInfo->dwFileVersionMS);
+  C = HIWORD(fixedFileInfo->dwFileVersionLS);
+  D = LOWORD(fixedFileInfo->dwFileVersionLS);
+  return TRUE;
+}
+
+/**
+ * Installs or upgrades the MozillaMaintenance service.
+ * If an existing service is already installed, we replace it with the
+ * currently running process.
+ *
+ * @param  action The action to perform.
+ * @return TRUE if the service was installed/upgraded
+ */
+BOOL
+SvcInstall(SvcInstallAction action)
+{
+  // Get a handle to the local computer SCM database with full access rights.
+  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, 
+                                                 SC_MANAGER_ALL_ACCESS));
+  if (!schSCManager) {
+    LOG(("Could not open service manager.  (%d)\n", GetLastError()));
+    return FALSE;
+  }
+
+  WCHAR newServiceBinaryPath[MAX_PATH + 1];
+  if (!GetModuleFileNameW(NULL, newServiceBinaryPath, 
+                          sizeof(newServiceBinaryPath) / 
+                          sizeof(newServiceBinaryPath[0]))) {
+    LOG(("Could not obtain module filename when attempting to "
+         "install service. (%d)\n",
+         GetLastError()));
+    return FALSE;
+  }
+
+  // Check if we already have an open service
+  nsAutoServiceHandle schService(OpenServiceW(schSCManager, 
+                                              SVC_NAME, 
+                                              SERVICE_ALL_ACCESS));
+  DWORD lastError = GetLastError();
+  if (!schService && ERROR_SERVICE_DOES_NOT_EXIST != lastError) {
+    // The service exists but we couldn't open it
+    LOG(("Could not open service.  (%d)\n", GetLastError()));
+    return FALSE;
+  }
+  
+  if (schService) {
+    // The service exists and we opened it
+    DWORD bytesNeeded;
+    if (!QueryServiceConfigW(schService, NULL, 0, &bytesNeeded) && 
+        GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      LOG(("Could not determine buffer size for query service config.  (%d)\n", 
+           GetLastError()));
+      return FALSE;
+    }
+
+    // Get the service config information, in particular we want the binary 
+    // path of the service.
+    nsAutoArrayPtr<char> serviceConfigBuffer = new char[bytesNeeded];
+    if (!QueryServiceConfigW(schService, 
+        reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), 
+        bytesNeeded, &bytesNeeded)) {
+      LOG(("Could open service but could not query service config.  (%d)\n", 
+           GetLastError()));
+      return FALSE;
+    }
+    QUERY_SERVICE_CONFIGW &serviceConfig = 
+      *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());
+
+    // Obtain the existing maintenanceservice file's version number and
+    // the new file's version number.  Versions are in the format of
+    // A.B.C.D.
+    DWORD existingA, existingB, existingC, existingD;
+    DWORD newA, newB, newC, newD; 
+    BOOL obtainedExistingVersionInfo = 
+      GetVersionNumberFromPath(serviceConfig.lpBinaryPathName, 
+                               existingA, existingB, 
+                               existingC, existingD);
+    if (!GetVersionNumberFromPath(newServiceBinaryPath, newA, 
+                                 newB, newC, newD)) {
+      LOG(("Could not obtain version number from new path\n"));
+      return FALSE;
+    }
+
+    schService.reset(); //Explicitly close the handle so we can delete it
+
+    // Check if we need to replace the old binary with the new one
+    // If we couldn't get the old version info then we assume we should 
+    // replace it.
+    if (ForceInstallSvc == action ||
+        !obtainedExistingVersionInfo || 
+        (existingA < newA) ||
+        (existingA == newA && existingB < newB) ||
+        (existingA == newA && existingB == newB && 
+         existingC < newC) ||
+        (existingA == newA && existingB == newB && 
+         existingC == newC && existingD < newD)) {
+      if (!SvcUninstall()) {
+        return FALSE;
+      }
+
+      if (!DeleteFile(serviceConfig.lpBinaryPathName)) {
+        LOG(("Could not delete old service binary file.  (%d)\n", GetLastError()));
+        return FALSE;
+      }
+
+      if (!CopyFile(newServiceBinaryPath, 
+                    serviceConfig.lpBinaryPathName, FALSE)) {
+        LOG(("Could not overwrite old service binary file. "
+             "This should never happen, but if it does the next upgrade will fix"
+             " it, the service is not a critical component that needs to be "
+             " installed for upgrades to work. (%d)\n", 
+             GetLastError()));
+        return FALSE;
+      }
+
+      // We made a copy of ourselves to the existing location.
+      // The tmp file (the process of which we are executing right now) will be
+      // left over.  Attempt to delete the file on the next reboot.
+      MoveFileEx(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
+      
+      // Setup the new module path
+      wcsncpy(newServiceBinaryPath, serviceConfig.lpBinaryPathName, MAX_PATH);
+      // Fall through so we replace the service
+    } else {
+      // We don't need to copy ourselves to the existing location.
+      // The tmp file (the process of which we are executing right now) will be
+      // left over.  Attempt to delete the file on the next reboot.
+      MoveFileEx(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
+      
+      return TRUE; // nothing to do, we already have a newer service installed
+    }
+  } else if (UpgradeSvc == action) {
+    // The service does not exist and we are upgrading, so don't install it
+    return TRUE;
+  }
+
+  // Create the service as on demand
+  schService.own(CreateServiceW(schSCManager, SVC_NAME, SVC_DISPLAY_NAME,
+                                SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+                                SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+                                newServiceBinaryPath, NULL, NULL, NULL, 
+                                NULL, NULL));
+  if (!schService) {
+    LOG(("Could not create Windows service. "
+         "This error should never happen since a service install "
+         "should only be called when elevated. (%d)\n", GetLastError()));
+    return FALSE;
+  } 
+
+  if (!SetUserAccessServiceDACL(schService)) {
+    LOG(("Could not set security ACE on service handle, the service will not be "
+         "able to be started from unelevated processes. "
+         "This error should never happen.  (%d)\n", 
+         GetLastError()));
+  }
+
+  return TRUE;
+}
+
+/**
+ * Stops the Maintenance service.
+ *
+ * @return TRUE if successful.
+ */
+BOOL
+StopService()
+{
+  // Get a handle to the local computer SCM database with full access rights.
+  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, 
+                                                 SC_MANAGER_ALL_ACCESS));
+  if (!schSCManager) {
+    LOG(("Could not open service manager.  (%d)\n", GetLastError()));
+    return FALSE;
+  }
+
+  // Open the service
+  nsAutoServiceHandle schService(OpenServiceW(schSCManager, SVC_NAME, 
+                                              SERVICE_ALL_ACCESS));
+  if (!schService) {
+    LOG(("Could not open service.  (%d)\n", GetLastError()));
+    return FALSE;
+  } 
+
+  SERVICE_STATUS status;
+  return ControlService(schService, SERVICE_CONTROL_STOP, &status);
+}
+
+/**
+ * Uninstalls the Maintenance service.
+ *
+ * @return TRUE if successful.
+ */
+BOOL
+SvcUninstall()
+{
+  // Get a handle to the local computer SCM database with full access rights.
+  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, 
+                                                 SC_MANAGER_ALL_ACCESS));
+  if (!schSCManager) {
+    LOG(("Could not open service manager.  (%d)\n", GetLastError()));
+    return FALSE;
+  }
+
+  // Open the service
+  nsAutoServiceHandle schService(OpenServiceW(schSCManager, SVC_NAME, 
+                                              SERVICE_ALL_ACCESS));
+  if (!schService) {
+    LOG(("Could not open service.  (%d)\n", GetLastError()));
+    return FALSE;
+  } 
+
+  //Stop the service so it deletes faster and so the uninstaller
+  // can actually delete its EXE.
+  DWORD totalWaitTime = 0;
+  SERVICE_STATUS status;
+  static const int maxWaitTime = 1000 * 60; // Never wait more than a minute
+  if (ControlService(schService, SERVICE_CONTROL_STOP, &status)) {
+    do {
+      Sleep(status.dwWaitHint);
+      totalWaitTime += (status.dwWaitHint + 10);
+      if (status.dwCurrentState == SERVICE_STOPPED) {
+        break;
+      } else if (totalWaitTime > maxWaitTime) {
+        break;
+      }
+    } while (QueryServiceStatus(schService, &status));
+  }
+
+  // Delete the service or mark it for deletion
+  BOOL deleted = DeleteService(schService);
+  if (!deleted) {
+    deleted = (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE);
+  }
+
+  return deleted;
+}
+
+/**
+ * Sets the access control list for user access for the specified service.
+ *
+ * @param  hService The service to set the access control list on
+ * @return TRUE if successful
+ */
+BOOL
+SetUserAccessServiceDACL(SC_HANDLE hService)
+{
+  PACL pNewAcl = NULL;
+  PSECURITY_DESCRIPTOR psd = NULL;
+  DWORD lastError = SetUserAccessServiceDACL(hService, pNewAcl, psd);
+  if (pNewAcl) {
+    LocalFree((HLOCAL)pNewAcl);
+  }
+  if (psd) {
+    LocalFree((LPVOID)psd);
+  }
+  return ERROR_SUCCESS == lastError;
+}
+
+/**
+ * Sets the access control list for user access for the specified service.
+ *
+ * @param  hService  The service to set the access control list on
+ * @param  pNewAcl   The out param ACL which should be freed by caller
+ * @param  psd       out param security descriptor, should be freed by caller
+ * @return TRUE if successful
+ */
+DWORD
+SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, 
+                         PSECURITY_DESCRIPTOR psd)
+{
+  // Get the current security descriptor needed size
+  DWORD needed = 0;
+  if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, 
+                                  &psd, 0, &needed)) {
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      return GetLastError();
+    }
+
+    DWORD size = needed;
+    psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, size);
+    if (!psd) {
+      return ERROR_INSUFFICIENT_BUFFER;
+    }
+
+    // Get the actual security descriptor now
+    if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, 
+                                    psd, size, &needed)) {
+      return GetLastError();
+    }
+  }
+
+  // Get the current DACL from the security descriptor.
+  PACL pacl = NULL;
+  BOOL bDaclPresent = FALSE;
+  BOOL bDaclDefaulted = FALSE;
+  if ( !GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, 
+                                  &bDaclDefaulted)) {
+    return GetLastError();
+  }
+
+  // Build the ACE.
+  EXPLICIT_ACCESS ea;
+  BuildExplicitAccessWithName(&ea, TEXT("Users"), 
+                              SERVICE_START | SERVICE_STOP | GENERIC_READ, 
+                              SET_ACCESS, NO_INHERITANCE);
+  DWORD lastError = SetEntriesInAcl(1, (PEXPLICIT_ACCESS)&ea, pacl, &pNewAcl);
+  if (ERROR_SUCCESS != lastError) {
+    return lastError;
+  }
+
+  // Initialize a new security descriptor.
+  SECURITY_DESCRIPTOR sd;
+  if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+    return GetLastError();
+  }
+
+  // Set the new DACL in the security descriptor.
+  if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE)) {
+    return GetLastError();
+  }
+
+  // Set the new security descriptor for the service object.
+  if (!SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, &sd)) {
+    return GetLastError();
+  }
+
+  // Woohoo, raise the roof
+  return ERROR_SUCCESS;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/serviceinstall.h
@@ -0,0 +1,47 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service service installer code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define SVC_NAME L"MozillaMaintenance"
+#define SVC_DISPLAY_NAME L"Mozilla Maintenance Service"
+
+enum SvcInstallAction { UpgradeSvc, InstallSvc, ForceInstallSvc };
+BOOL SvcInstall(SvcInstallAction action);
+BOOL SvcUninstall();
+BOOL StopService();
+BOOL SetUserAccessServiceDACL(SC_HANDLE hService);
+DWORD SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, 
+                               PSECURITY_DESCRIPTOR psd);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -0,0 +1,647 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service file system monitoring.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <wtsapi32.h>
+#include <userenv.h>
+#include <shellapi.h>
+
+#pragma comment(lib, "wtsapi32.lib")
+#pragma comment(lib, "userenv.lib")
+#pragma comment(lib, "shlwapi.lib")
+
+#include "nsWindowsHelpers.h"
+#include "nsAutoPtr.h"
+
+#include "workmonitor.h"
+#include "serviceinstall.h"
+#include "servicebase.h"
+#include "registrycertificates.h"
+#include "uachelper.h"
+#include "launchwinprocess.h"
+
+extern BOOL gServiceStopping;
+
+// Wait 15 minutes for an update operation to run at most.
+// Updates usually take less than a minute so this seems like a 
+// significantly large and safe amount of time to wait.
+static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
+PRUnichar* MakeCommandLine(int argc, PRUnichar **argv);
+BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
+BOOL WriteStatusPending(LPCWSTR updateDirPath);
+BOOL StartCallbackApp(int argcTmp, LPWSTR *argvTmp, DWORD callbackSessionID);
+BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,  LPCWSTR siblingFilePath, 
+                            LPCWSTR newFileName);
+
+// The error codes start from 16000 since Windows system error 
+// codes only go up to 15999
+const int SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000;
+const int SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
+const int SERVICE_UPDATER_SIGN_ERROR = 16002;
+const int SERVICE_CALLBACK_SIGN_ERROR = 16003;
+
+/**
+ * Runs an update process in the specified sessionID as an elevated process.
+ *
+ * @param  updaterPath   The path to the update process to start.
+ * @param  workingDir    The working directory to execute the update process
+ * @param  cmdLine       in. The command line parameters to pass to the update
+ *                       process. If they specify a callback application, it
+ *                       will be executed with an associated unelevated token 
+ *                       for the sessionID.
+ * @param processStarted Returns TRUE if the process was started.
+ * @param  callbackSessionID 
+ *                       If 0 and Windows Vista, the callback application will
+ *                       not be run.  If non zero the callback application will
+ *                       be injected into the user's session as a non-elevated 
+ *                       process.
+ * @return TRUE if the update process was run had a return code of 0.
+ */
+BOOL
+StartUpdateProcess(LPCWSTR updaterPath, 
+                   LPCWSTR workingDir, 
+                   int argcTmp,
+                   LPWSTR *argvTmp,
+                   BOOL &processStarted,
+                   DWORD callbackSessionID = 0)
+{
+  DWORD myProcessID = GetCurrentProcessId();
+  DWORD mySessionID = 0;
+  ProcessIdToSessionId(myProcessID, &mySessionID);
+
+  STARTUPINFO si = {0};
+  si.cb = sizeof(STARTUPINFO);
+  si.lpDesktop = L"winsta0\\Default";
+  PROCESS_INFORMATION pi = {0};
+
+  LOG(("Starting process in an elevated session.  Service "
+       "session ID: %d; Requested callback session ID: %d\n", 
+       mySessionID, callbackSessionID));
+
+  // The updater command line is of the form:
+  // updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
+  // So update callback-dir is the 4th, callback-path is the 5th and its args 
+  // are the 6th index.  So that we can execute the callback out of line we
+  // won't call updater.exe with those callback args and we will manage the
+  // callback ourselves.
+  LPWSTR cmdLine = MakeCommandLine(argcTmp, argvTmp);
+
+  // If we're about to start the update process from session 0,
+  // then we should not show a GUI.  This only really needs to be done
+  // on Vista and higher, but it's better to keep everything consistent
+  // across all OS if it's of no harm.
+  if (argcTmp >= 2 ) {
+    // Setting the desktop to blank will ensure no GUI is displayed
+    si.lpDesktop = L"";
+    si.dwFlags |= STARTF_USESHOWWINDOW;
+    si.wShowWindow = SW_HIDE;
+  }
+
+  // We move the updater.ini file out of the way because we will handle 
+  // executing PostUpdate through the service.  We handle PostUpdate from
+  // the service because there are some per user things that happen that
+  // can't run in session 0 which we run updater.exe in.
+  // Once we are done running updater.exe we rename updater.ini back so
+  // that if there were any errors the next updater.exe will run correctly.
+  WCHAR updaterINI[MAX_PATH + 1];
+  WCHAR updaterINITemp[MAX_PATH + 1];
+  BOOL selfHandlePostUpdate = FALSE;
+  // We use the updater.ini from the same directory as the updater.exe
+  // because of background updates.
+  if (PathGetSiblingFilePath(updaterINI, argvTmp[0], L"updater.ini") &&
+      PathGetSiblingFilePath(updaterINITemp, argvTmp[0], L"updater.tmp")) {
+    selfHandlePostUpdate = MoveFileEx(updaterINI, updaterINITemp, 
+                                      MOVEFILE_REPLACE_EXISTING);
+  }
+
+  // Create an environment block for the process we're about to start using
+  // the user's token.
+  WCHAR envVarString[32];
+  wsprintf(envVarString, L"MOZ_SESSION_ID=%d", callbackSessionID); 
+  _wputenv(envVarString);
+  LPVOID environmentBlock = NULL;
+  if (!CreateEnvironmentBlock(&environmentBlock, NULL, TRUE)) {
+    LOG(("Could not create an environment block, setting it to NULL.\n"));
+    environmentBlock = NULL;
+  }
+  // Empty value on _wputenv is how you remove an env variable in Windows
+  _wputenv(L"MOZ_SESSION_ID=");
+  processStarted = CreateProcessW(updaterPath, cmdLine, 
+                                  NULL, NULL, FALSE, 
+                                  CREATE_DEFAULT_ERROR_MODE | 
+                                  CREATE_UNICODE_ENVIRONMENT, 
+                                  environmentBlock, 
+                                  workingDir, &si, &pi);
+  if (environmentBlock) {
+    DestroyEnvironmentBlock(environmentBlock);
+  }
+  BOOL updateWasSuccessful = FALSE;
+  if (processStarted) {
+    // Wait for the updater process to finish
+    LOG(("Process was started... waiting on result.\n")); 
+    DWORD waitRes = WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
+    if (WAIT_TIMEOUT == waitRes) {
+      // We waited a long period of time for updater.exe and it never finished
+      // so kill it.
+      TerminateProcess(pi.hProcess, 1);
+    } else {
+      // Check the return code of updater.exe to make sure we get 0
+      DWORD returnCode;
+      if (GetExitCodeProcess(pi.hProcess, &returnCode)) {
+        LOG(("Process finished with return code %d.\n", returnCode)); 
+        // updater returns 0 if successful.
+        updateWasSuccessful = (returnCode == 0);
+      } else {
+        LOG(("Process finished but could not obtain return code.\n")); 
+      }
+    }
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+  } else {
+    DWORD lastError = GetLastError();
+    LOG(("Could not create process as current user, "
+         "updaterPath: %ls; cmdLine: %l.  (%d)\n", 
+         updaterPath, cmdLine, lastError));
+  }
+
+  // Now that we're done with the update, restore back the updater.ini file
+  // We use it ourselves, and also we want it back in case we had any type 
+  // of error so that the normal update process can use it.
+  if (selfHandlePostUpdate) {
+    MoveFileEx(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
+
+    // Only run the PostUpdate if the update was successful and if we have
+    // a callback application.  This is the same thing updater.exe does.
+    if (updateWasSuccessful && argcTmp > 5) {
+      LPCWSTR callbackApplication = argvTmp[5];
+      LPCWSTR updateInfoDir = argvTmp[1];
+      // Launch the PostUpdate process with SYSTEM in session 0. We force sync
+      // because we run this twice and we want to make sure the uninstaller
+      // keys get added to HKLM before the ones try to get added to HKCU.  If
+      // we did it async we'd have a race condition that would sometimes lead
+      // to 2 uninstall icons.
+      LaunchWinPostProcess(callbackApplication, updateInfoDir, true, NULL);
+      nsAutoHandle userToken(UACHelper::OpenUserToken(callbackSessionID));
+      LaunchWinPostProcess(callbackApplication, 
+                           updateInfoDir, false, userToken);
+    }
+  }
+
+  free(cmdLine);
+  return updateWasSuccessful;
+}
+
+/**
+ * Processes a work item (file by the name of .mz) and executes
+ * the command within.
+ * The only supported command at this time is running an update.
+ *
+ * @param  monitoringBasePath The base path that is being monitored.
+ * @param  notifyInfo         the notifyInfo struct for the changes.
+ * @return TRUE if we want the service to stop.
+ */
+BOOL
+ProcessWorkItem(LPCWSTR monitoringBasePath, 
+                FILE_NOTIFY_INFORMATION &notifyInfo)
+{
+  int filenameLength = notifyInfo.FileNameLength / 
+                       sizeof(notifyInfo.FileName[0]); 
+  notifyInfo.FileName[filenameLength] = L'\0';
+
+  // When the file is ready for processing it will be renamed 
+  // to have a .mz extension
+  BOOL haveWorkItem = notifyInfo.Action == FILE_ACTION_RENAMED_NEW_NAME && 
+                      notifyInfo.FileNameLength > 3 && 
+                      notifyInfo.FileName[filenameLength - 3] == L'.' &&
+                      notifyInfo.FileName[filenameLength - 2] == L'm' &&
+                      notifyInfo.FileName[filenameLength - 1] == L'z';
+  if (!haveWorkItem) {
+    // We don't have a work item, keep looking
+    return FALSE;
+  }
+
+  // Indicate that the service is busy and shouldn't be used by anyone else
+  // by creating a named event.  Programs should check if this event exists 
+  // before writing a work item out.
+  nsAutoHandle serviceRunning(CreateEventW(NULL, TRUE, 
+                                           FALSE, SERVICE_EVENT_NAME));
+
+  LOG(("Processing new command meta file: %ls\n", notifyInfo.FileName));
+  WCHAR fullMetaUpdateFilePath[MAX_PATH + 1];
+  wcscpy(fullMetaUpdateFilePath, monitoringBasePath);
+
+  // We only support file paths in monitoring directories that are MAX_PATH
+  // chars or less.
+  if (!PathAppendSafe(fullMetaUpdateFilePath, notifyInfo.FileName)) {
+    // Cannot process update, skipfileSize it.
+    return TRUE;
+  }
+
+  nsAutoHandle metaUpdateFile(CreateFile(fullMetaUpdateFilePath, 
+                                         GENERIC_READ, 
+                                         FILE_SHARE_READ | 
+                                         FILE_SHARE_WRITE | 
+                                         FILE_SHARE_DELETE, 
+                                         NULL, 
+                                         OPEN_EXISTING,
+                                         0, NULL));
+  if (metaUpdateFile == INVALID_HANDLE_VALUE) {
+    LOG(("Could not open command meta file: %ls\n", notifyInfo.FileName));
+    return TRUE;
+  }
+
+  DWORD fileSize = GetFileSize(metaUpdateFile, NULL);
+  DWORD sessionID = 0, commandID = 0;
+  // The file should be in wide characters so if it's of odd size it's
+  // an invalid file.
+  const int kSanityCheckFileSize = 1024 * 64;
+  if (fileSize == INVALID_FILE_SIZE || 
+      (fileSize %2) != 0 ||
+      fileSize > kSanityCheckFileSize ||
+      fileSize < sizeof(DWORD)) {
+    LOG(("Could not obtain file size or an improper file size was encountered "
+         "for command meta file: %ls\n", 
+        notifyInfo.FileName));
+    return TRUE;
+  }
+
+  // The first 4 bytes are for the command ID.
+  // Currently only command ID 1 which is for updates is supported.
+  DWORD commandIDCount;
+  BOOL result = ReadFile(metaUpdateFile, &commandID, 
+                         sizeof(DWORD), &commandIDCount, NULL);
+  fileSize -= sizeof(DWORD);
+
+  // The next 4 bytes are for the process ID
+  DWORD sessionIDCount;
+  result |= ReadFile(metaUpdateFile, &sessionID, 
+                     sizeof(DWORD), &sessionIDCount, NULL);
+  fileSize -= sizeof(DWORD);
+
+  // The next MAX_PATH wchar's are for the app to start
+  WCHAR updaterPath[MAX_PATH + 1];
+  ZeroMemory(updaterPath, sizeof(updaterPath));
+  DWORD updaterPathCount;
+  result |= ReadFile(metaUpdateFile, updaterPath, 
+                     MAX_PATH * sizeof(WCHAR), &updaterPathCount, NULL);
+  fileSize -= updaterPathCount;
+
+  // The next MAX_PATH wchar's are for the app to start
+  WCHAR workingDirectory[MAX_PATH + 1];
+  ZeroMemory(workingDirectory, sizeof(workingDirectory));
+  DWORD workingDirectoryCount;
+  result |= ReadFile(metaUpdateFile, workingDirectory, 
+                     MAX_PATH * sizeof(WCHAR), &workingDirectoryCount, NULL);
+  fileSize -= workingDirectoryCount;
+
+  // + 2 for wide char termination
+  nsAutoArrayPtr<char> cmdlineBuffer = new char[fileSize + 2];
+  DWORD cmdLineBufferRead;
+  result |= ReadFile(metaUpdateFile, cmdlineBuffer, 
+                     fileSize, &cmdLineBufferRead, NULL);
+  fileSize -= cmdLineBufferRead;
+
+  // We have all the info we need from the work item file, get rid of it.
+  metaUpdateFile.reset();
+  if (!DeleteFileW(fullMetaUpdateFilePath)) {
+    LOG(("Could not delete work item file: %ls. (%d)\n", 
+         fullMetaUpdateFilePath, GetLastError()));
+  }
+
+  if (!result ||
+      commandID != 1 ||
+      commandIDCount != sizeof(DWORD) ||
+      sessionIDCount != sizeof(DWORD) ||
+      updaterPathCount != MAX_PATH * sizeof(WCHAR) ||
+      workingDirectoryCount != MAX_PATH * sizeof(WCHAR) ||
+      fileSize != 0) {
+    LOG(("Could not read command data for command meta file: %ls\n", 
+         notifyInfo.FileName));
+    return TRUE;
+  }
+  cmdlineBuffer[cmdLineBufferRead] = '\0';
+  cmdlineBuffer[cmdLineBufferRead + 1] = '\0';
+  WCHAR *cmdlineBufferWide = reinterpret_cast<WCHAR*>(cmdlineBuffer.get());
+  LOG(("An update command was detected and is being processed for command meta "
+       "file: %ls\n", 
+      notifyInfo.FileName));
+
+  int argcTmp = 0;
+  LPWSTR* argvTmp = CommandLineToArgvW(cmdlineBufferWide, &argcTmp);
+
+  // Check for callback application sign problems
+  BOOL callbackSignProblem = FALSE;
+#ifndef DISABLE_CALLBACK_AUTHENTICODE_CHECK
+  // If we have less than 6 params, then there is no callback to check, so
+  // we have no callback sign problem.
+  if (argcTmp > 5) {
+    LPWSTR callbackApplication = argvTmp[5];
+    callbackSignProblem = 
+      !DoesBinaryMatchAllowedCertificates(argvTmp[2], callbackApplication);
+  }
+#endif
+
+    // Check for updater.exe sign problems
+  BOOL updaterSignProblem = FALSE;
+#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK
+  if (argcTmp > 2) {
+    updaterSignProblem = !DoesBinaryMatchAllowedCertificates(argvTmp[2], 
+                                                             updaterPath);
+  }
+#endif
+
+  // In order to proceed with the update we need at least 3 command line
+  // parameters and no sign problems.
+  if (argcTmp > 2 && !updaterSignProblem && !callbackSignProblem) {
+    BOOL updateProcessWasStarted = FALSE;
+    if (StartUpdateProcess(updaterPath, workingDirectory, 
+                           argcTmp, argvTmp,
+                           updateProcessWasStarted,
+                           sessionID)) {
+      LOG(("updater.exe was launched and run successfully!\n"));
+      StartServiceUpdate(argcTmp, argvTmp);
+    } else {
+      LOG(("Error running process in session %d.  "
+           "Updating update.status.  Last error: %d\n",
+           sessionID, GetLastError()));
+
+      // If the update process was started, then updater.exe is responsible for
+      // setting the failure code and running the callback.  If it could not 
+      // be started then we do the work.  We set an error instead of directly
+      // setting status pending so that the app.update.service.errors
+      // pref can be updated when the callback app restarts.
+      if (!updateProcessWasStarted) {
+        if (!WriteStatusFailure(argvTmp[1], 
+                                SERVICE_UPDATER_COULD_NOT_BE_STARTED)) {
+          LOG(("Could not write update.status service update failure."
+               "Last error: %d\n", GetLastError()));
+        }
+
+        // We only hit this condition when there is no callback sign error
+        // so we don't need to check it.
+        StartCallbackApp(argcTmp, argvTmp, sessionID);
+      }
+    }
+  } else if (argcTmp <= 2) {
+    LOG(("Not enough command line parameters specified. "
+         "Updating update.status.\n"));
+
+    // We can't start the callback in this case because there
+    // are not enough command line parameters. We set an error instead of
+    // directly setting status pending so that the app.update.service.errors
+    // pref can be updated when the callback app restarts.
+    if (argcTmp != 2 || 
+        !WriteStatusFailure(argvTmp[1], 
+                            SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) {
+      LOG(("Could not write update.status service update failure."
+           "Last error: %d\n", GetLastError()));
+    }
+  } else if (callbackSignProblem) {
+    LOG(("Will not run updater nor callback due to callback sign error "
+         "in session %d. Updating update.status.  Last error: %d\n",
+         sessionID, GetLastError()));
+
+    // When there is a certificate check error on the callback application, we
+    // want to write out the error.
+    if (!WriteStatusFailure(argvTmp[1], 
+                            SERVICE_CALLBACK_SIGN_ERROR)) {
+      LOG(("Could not write pending state to update.status.  (%d)\n", 
+           GetLastError()));
+    }
+  } else {
+    LOG(("Could not start process due to certificate check error on "
+         "updater.exe. Updating update.status.  Last error: %d\n", GetLastError()));
+
+    // When there is a certificate check error on the updater.exe application,
+    // we want to write out the error.
+    if (!WriteStatusFailure(argvTmp[1], 
+                            SERVICE_UPDATER_SIGN_ERROR)) {
+      LOG(("Could not write pending state to update.status.  (%d)\n", 
+           GetLastError()));
+    }
+
+    // On certificate check errors on updater.exe, updater.exe won't run at all
+    // so we need to manually start the callback application ourselves.
+    // This condition will only be hit when the callback app has no sign errors
+    StartCallbackApp(argcTmp, argvTmp, sessionID);
+  }
+  LocalFree(argvTmp);
+
+  // We processed a work item, whether or not it was successful.
+  return TRUE;
+}
+
+/**
+ * Starts monitoring the update directory for work items.
+ *
+ * @return FALSE if there was an error starting directory monitoring.
+ */
+BOOL
+StartDirectoryChangeMonitor() 
+{
+  LOG(("Starting directory change monitor...\n"));
+
+  // Init the update directory path and ensure it exists.
+  // Example: C:\programData\Mozilla\Firefox\updates\[channel]
+  // The channel is not included here as we want to monitor the base directory
+  WCHAR updateData[MAX_PATH + 1];
+  if (!GetUpdateDirectoryPath(updateData)) {
+    LOG(("Could not obtain update directory path\n"));
+    return FALSE;
+  }
+
+  // Obtain a directory handle, FILE_FLAG_BACKUP_SEMANTICS is needed for this.
+  nsAutoHandle directoryHandle(CreateFile(updateData, 
+                                          FILE_LIST_DIRECTORY, 
+                                          FILE_SHARE_READ | FILE_SHARE_WRITE, 
+                                          NULL, 
+                                          OPEN_EXISTING,
+                                          FILE_FLAG_BACKUP_SEMANTICS, 
+                                          NULL));
+  if (directoryHandle == INVALID_HANDLE_VALUE) {
+    LOG(("Could not obtain directory handle to monitor\n"));
+    return FALSE;
+  }
+
+  // Allocate a buffer that is big enough to hold 128 changes
+  // Note that FILE_NOTIFY_INFORMATION is a variable length struct
+  // so that's why we don't create a simple array directly.
+  char fileNotifyInfoBuffer[(sizeof(FILE_NOTIFY_INFORMATION) + 
+                            MAX_PATH) * 128];
+  ZeroMemory(&fileNotifyInfoBuffer, sizeof(fileNotifyInfoBuffer));
+  
+  DWORD bytesReturned;
+  // Listen for directory changes to the update directory
+  while (ReadDirectoryChangesW(directoryHandle, 
+                               fileNotifyInfoBuffer, 
+                               sizeof(fileNotifyInfoBuffer), 
+                               TRUE, FILE_NOTIFY_CHANGE_FILE_NAME, 
+                               &bytesReturned, NULL, NULL)) {
+    char *fileNotifyInfoBufferLocation = fileNotifyInfoBuffer;
+
+    // Make sure we have at least one entry to process
+    while (bytesReturned/sizeof(FILE_NOTIFY_INFORMATION) > 0) {
+      // Point to the current directory info which is changed
+      FILE_NOTIFY_INFORMATION &notifyInfo = 
+        *((FILE_NOTIFY_INFORMATION*)fileNotifyInfoBufferLocation);
+      fileNotifyInfoBufferLocation += notifyInfo .NextEntryOffset;
+      bytesReturned -= notifyInfo .NextEntryOffset;
+      if (!wcscmp(notifyInfo.FileName, L"stop") && gServiceStopping) {
+        LOG(("A stop command was issued.\n"));
+        return TRUE;
+      }
+
+      BOOL processedWorkItem = ProcessWorkItem(updateData, notifyInfo);
+      if (processedWorkItem) {
+        // We don't return here because during stop we will write out a stop 
+        // file which will stop monitoring.
+        StopService();
+        break;
+      }
+
+      // NextEntryOffset will be 0 if there are no more items to monitor,
+      // and we're ready to make another call to ReadDirectoryChangesW.
+      if (0 == notifyInfo.NextEntryOffset) {
+        break;
+      }
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+ * Starts the callback application from the updater.exe command line arguments.
+ *
+ * @param  argcTmp           The argc value normally sent to updater.exe
+ * @param  argvTmp           The argv value normally sent to updater.exe
+ * @param  callbackSessionID The session ID to start the callback with
+ * @return TRUE if successful
+ */
+BOOL
+StartCallbackApp(int argcTmp, LPWSTR *argvTmp, DWORD callbackSessionID) 
+{
+  if (0 == callbackSessionID  && UACHelper::IsVistaOrLater()) {
+    LOG(("Attempted to run callback application in session 0, not allowed.\n"));
+    LOG(("Session 0 is only for services on Vista and up.\n"));
+    return FALSE;
+  }
+
+  if (argcTmp < 5) {
+    LOG(("No callback application specified.\n"));
+    return FALSE;
+  }
+
+  LPWSTR callbackArgs = NULL;
+  LPWSTR callbackDirectory = argvTmp[4];
+  LPWSTR callbackApplication = argvTmp[5];
+  if (argcTmp > 6) {
+    // Make room for all of the ending args minus the first 6 
+    // + 1 for the app itself
+    const size_t callbackCommandLineLen = argcTmp - 5;
+    WCHAR **params = new WCHAR*[callbackCommandLineLen];
+    params[0] = callbackApplication;
+    for (size_t i = 1; i < callbackCommandLineLen; ++i) {
+      params[i] = argvTmp[5 + i];
+    }
+    callbackArgs = MakeCommandLine(callbackCommandLineLen, params);
+    delete[] params;
+  }
+
+  if (!callbackApplication) {
+    LOG(("Callback application is NULL.\n"));
+    if (callbackArgs) {
+      free(callbackArgs);
+    }
+    return FALSE;
+  }
+
+  nsAutoHandle unelevatedToken(UACHelper::OpenUserToken(callbackSessionID));
+  if (!unelevatedToken) {
+    LOG(("Could not obtain unelevated token for callback app.\n"));
+    if (callbackArgs) {
+      free(callbackArgs);
+    }
+    return FALSE;
+  }
+
+  // Create an environment block for the process we're about to start using
+  // the user's token.
+  LPVOID environmentBlock = NULL;
+  if (!CreateEnvironmentBlock(&environmentBlock, unelevatedToken, TRUE)) {
+    LOG(("Could not create an environment block, setting it to NULL.\n"));
+    environmentBlock = NULL;
+  }
+
+  STARTUPINFOW si = {0};
+  si.cb = sizeof(STARTUPINFOW);
+  si.lpDesktop = L"winsta0\\Default";
+  PROCESS_INFORMATION pi = {0};
+  if (CreateProcessAsUserW(unelevatedToken, 
+                            callbackApplication, 
+                            callbackArgs, 
+                            NULL, NULL, FALSE,
+                            CREATE_DEFAULT_ERROR_MODE |
+#ifdef DEBUG
+                            CREATE_NEW_CONSOLE |
+#endif
+                            CREATE_UNICODE_ENVIRONMENT,
+                            environmentBlock, 
+                            callbackDirectory, 
+                            &si, &pi)) {
+    LOG(("Callback app was run successfully.\n"));
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+    if (environmentBlock) {
+      DestroyEnvironmentBlock(environmentBlock);
+    }
+    if (callbackArgs) {
+      free(callbackArgs);
+    }
+    return TRUE;
+  } 
+  
+  LOG(("Could not run callback app, last error: %d", GetLastError()));
+  if (environmentBlock) {
+    DestroyEnvironmentBlock(environmentBlock);
+  }
+  if (callbackArgs) {
+    free(callbackArgs);
+  }
+  return FALSE;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/maintenanceservice/workmonitor.h
@@ -0,0 +1,41 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service file system monitoring.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define STOP_CMD L"stop"
+
+BOOL StartDirectoryChangeMonitor();
+BOOL GetUpdateDirectoryPath(LPWSTR);
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/common.nsh
@@ -16,16 +16,17 @@
 # The Initial Developer of the Original Code is Mozilla Foundation
 # Portions created by the Initial Developer are Copyright (C) 2006
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  Robert Strong <robert.bugzilla@gmail.com>
 #  Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #  Amir Szekely <kichik@gmail.com>
+#  Brian R. Bondy <netzen@gmail.com>
 #
 # 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
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -6675,16 +6676,50 @@
   !verbose ${_MOZFUNC_VERBOSE}
   Push "${_APP_ID}"
   Push "${_EXE_PATH}"
   Call UpdateShortcutAppModelIDs
   Pop ${_RESULT}
   !verbose pop
 !macroend
 
+!macro IsUserAdmin
+  ; Copied from: http://nsis.sourceforge.net/IsUserAdmin
+  Function IsUserAdmin
+    Push $R0
+    Push $R1
+    Push $R2
+ 
+    ClearErrors
+    UserInfo::GetName
+    IfErrors Win9x
+    Pop $R1
+    UserInfo::GetAccountType
+    Pop $R2
+ 
+    StrCmp $R2 "Admin" 0 Continue
+    StrCpy $R0 "true"
+    Goto Done
+ 
+    Continue:
+
+    StrCmp $R2 "" Win9x
+    StrCpy $R0 "false"
+    Goto Done
+ 
+    Win9x:
+    StrCpy $R0 "true"
+ 
+    Done:
+    Pop $R2
+    Pop $R1
+    Exch $R0
+  FunctionEnd
+!macroend
+
 /**
  * Retrieve if present or generate and store a 64 bit hash of an install path
  * using the City Hash algorithm.  On return the resulting id is saved in the
  * $AppUserModelID variable declared by inserting this macro. InitHashAppModelId
  * will attempt to load from HKLM/_REG_PATH first, then HKCU/_REG_PATH. If found
  * in either it will return the hash it finds. If not found it will generate a
  * new hash and attempt to store the hash in HKLM/_REG_PATH, then HKCU/_REG_PATH.
  * Subsequent calls will then retreive the stored hash value. On any failure,
--- a/toolkit/mozapps/installer/windows/nsis/makensis.mk
+++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk
@@ -58,16 +58,17 @@ TOOLKIT_NSIS_FILES = \
 CUSTOM_NSIS_PLUGINS = \
 	AccessControl.dll \
 	AppAssocReg.dll \
 	ApplicationID.dll \
 	CityHash.dll \
 	InvokeShellVerb.dll \
 	ShellLink.dll \
 	UAC.dll \
+	ServicesHelper.dll \
 	$(NULL)
 
 $(CONFIG_DIR)/setup.exe::
 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
 	cd $(CONFIG_DIR) && $(MAKENSISU) installer.nsi
 # Support for building the uninstaller when repackaging locales
 ifeq ($(CONFIG_DIR),l10ngen)
@@ -94,8 +95,15 @@ endif
 # For building the uninstaller during the application build so it can be
 # included for mar file generation.
 uninstaller::
 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
 	cd $(CONFIG_DIR) && $(MAKENSISU) uninstaller.nsi
 	$(NSINSTALL) -D $(DIST)/bin/uninstall
 	cp $(CONFIG_DIR)/helper.exe $(DIST)/bin/uninstall
+
+ifdef MOZ_MAINTENANCE_SERVICE
+maintenanceservice_installer::
+	cd $(CONFIG_DIR) && $(MAKENSISU) maintenanceservice_installer.nsi
+	$(NSINSTALL) -D $(DIST)/bin/
+	cp $(CONFIG_DIR)/maintenanceservice_installer.exe $(DIST)/bin
+endif
--- a/toolkit/mozapps/readstrings/Makefile.in
+++ b/toolkit/mozapps/readstrings/Makefile.in
@@ -44,10 +44,11 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE  = readstrings
 LIBRARY_NAME = readstrings
 FORCE_STATIC_LIB = 1
 export NO_SHUNT = 1
 USE_STATIC_LIBS = 1
 
 CPPSRCS = readstrings.cpp
+EXPORTS = readstrings.h
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/mozapps/update/Makefile.in
+++ b/toolkit/mozapps/update/Makefile.in
@@ -47,16 +47,17 @@ MODULE = update
 XPIDLSRCS = nsIUpdateTimerManager.idl
 
 EXTRA_PP_COMPONENTS = nsUpdateTimerManager.js nsUpdateTimerManager.manifest
 
 ifdef MOZ_UPDATER
 DIRS = ../readstrings
 
 ifneq ($(OS_TARGET),Android)
+DIRS += common
 DIRS += updater
 endif
 
 XPIDLSRCS += nsIUpdateService.idl
 
 EXTRA_PP_COMPONENTS += \
   nsUpdateService.js \
   nsUpdateServiceStub.js \
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/Makefile.in
@@ -0,0 +1,72 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla maintenance service build.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brian R. Bondy <netzen@gmail.com>
+#
+# 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
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = libupdatecommon
+LIBRARY_NAME = updatecommon
+FORCE_STATIC_LIB =1
+LIBXUL_LIBRARY = 1
+ifeq ($(OS_ARCH),WINNT)
+USE_STATIC_LIBS = 1
+endif
+
+CPPSRCS = \
+  updatelogging.cpp \
+  $(NULL)
+
+EXPORTS = updatelogging.h \
+  updatedefines.h \
+  $(NULL)
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
+CPPSRCS += launchwinprocess.cpp \
+  uachelper.cpp \
+  $(NULL)
+
+EXPORTS = launchwinprocess.h \
+  uachelper.h \
+  $(NULL)
+endif
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/launchwinprocess.cpp
@@ -0,0 +1,259 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is common code between maintenanceservice and updater
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include <shlwapi.h>
+
+BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
+
+
+/**
+ * Obtains the path of a file in the same directory as the specified file.
+ *
+ * @param  destinationBuffer A buffer of size MAX_PATH + 1 to store the result.
+ * @param  siblingFIlePath   The path of another file in the same directory
+ * @param  newFileName       The filename of another file in the same directory
+ * @return TRUE if successful
+ */
+BOOL
+PathGetSiblingFilePath(LPWSTR destinationBuffer, 
+                       LPCWSTR siblingFilePath, 
+                       LPCWSTR newFileName)
+{
+  if (wcslen(siblingFilePath) >= MAX_PATH) {
+    return FALSE;
+  }
+
+  wcscpy(destinationBuffer, siblingFilePath);
+  if (!PathRemoveFileSpecW(destinationBuffer)) {
+    return FALSE;
+  }
+
+  if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH) {
+    return FALSE;
+  }
+
+  return PathAppendSafe(destinationBuffer, newFileName);
+}
+
+/**
+ * Launch the post update application as the specified user (helper.exe).
+ * It takes in the path of the callback application to calculate the path
+ * of helper.exe.  For service updates this is called from both the system
+ * account and the current user account.
+ *
+ * @param  appExe        The path to the callback application binary.
+ * @param  updateInfoDir The directory where update info is stored.
+ * @param  forceSync     If true even if the ini file specifies async, the
+ *                       process will wait for termination of PostUpdate.
+ * @param  userToken     The user token to run as, if NULL the current user
+ *                       will be used.
+ */
+BOOL
+LaunchWinPostProcess(const WCHAR *appExe,
+                     const WCHAR *updateInfoDir,
+                     bool forceSync,
+                     HANDLE userToken)
+{
+  WCHAR workingDirectory[MAX_PATH + 1];
+  wcscpy(workingDirectory, appExe);
+  if (!PathRemoveFileSpecW(workingDirectory)) {
+    return FALSE;
+  }
+
+  // Launch helper.exe to perform post processing (e.g. registry and log file
+  // modifications) for the update.
+  WCHAR inifile[MAX_PATH + 1];
+  if (!PathGetSiblingFilePath(inifile, appExe, L"updater.ini")) {
+    return FALSE;
+  }
+
+  WCHAR exefile[MAX_PATH + 1];
+  WCHAR exearg[MAX_PATH + 1];
+  WCHAR exeasync[10];
+  bool async = true;
+  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", NULL, exefile,
+                                MAX_PATH + 1, inifile)) {
+    return FALSE;
+  }
+
+  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", NULL, exearg,
+                                MAX_PATH + 1, inifile))
+    return FALSE;
+
+  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE", 
+                                exeasync,
+                                sizeof(exeasync)/sizeof(exeasync[0]), inifile))
+    return FALSE;
+
+  WCHAR exefullpath[MAX_PATH + 1];
+  if (!PathGetSiblingFilePath(exefullpath, appExe, exefile)) {
+    return FALSE;
+  }
+
+  WCHAR dlogFile[MAX_PATH + 1];
+  if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) {
+    return FALSE;
+  }
+
+  WCHAR slogFile[MAX_PATH + 1];
+  wcscpy(slogFile, updateInfoDir);
+  if (!PathAppendSafe(slogFile, L"update.log")) {
+    return FALSE;
+  }
+
+  WCHAR dummyArg[14];
+  wcscpy(dummyArg, L"argv0ignored ");
+
+  size_t len = wcslen(exearg) + wcslen(dummyArg);
+  WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
+  if (!cmdline) {
+    return FALSE;
+  }
+
+  wcscpy(cmdline, dummyArg);
+  wcscat(cmdline, exearg);
+
+  if (forceSync ||
+      !_wcsnicmp(exeasync, L"false", 6) || 
+      !_wcsnicmp(exeasync, L"0", 2)) {
+    async = false;
+  }
+  
+  // We want to launch the post update helper app to update the Windows
+  // registry even if there is a failure with removing the uninstall.update
+  // file or copying the update.log file.
+  CopyFileW(slogFile, dlogFile, false);
+
+  STARTUPINFOW si = {sizeof(si), 0};
+  si.lpDesktop = L"";
+  PROCESS_INFORMATION pi = {0};
+
+  bool ok;
+  if (userToken) {
+    ok = CreateProcessAsUserW(userToken,
+                              exefullpath,
+                              cmdline,
+                              NULL,  // no special security attributes
+                              NULL,  // no special thread attributes
+                              false, // don't inherit filehandles
+                              0,     // No special process creation flags
+                              NULL,  // inherit my environment
+                              workingDirectory,
+                              &si,
+                              &pi);
+  } else {
+    ok = CreateProcessW(exefullpath,
+                        cmdline,
+                        NULL,  // no special security attributes
+                        NULL,  // no special thread attributes
+                        false, // don't inherit filehandles
+                        0,     // No special process creation flags
+                        NULL,  // inherit my environment
+                        workingDirectory,
+                        &si,
+                        &pi);
+  }
+  free(cmdline);
+  if (ok) {
+    if (!async)
+      WaitForSingleObject(pi.hProcess, INFINITE);
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+  }
+  return ok;
+}
+
+/**
+ * Starts the upgrade process for update of the service if it is
+ * already installed.
+ *
+ * @param  argc The argc value normally sent to updater.exe
+ * @param  argv The argv value normally sent to updater.exe
+ * @return TRUE if successful
+ */
+BOOL
+StartServiceUpdate(int argc, LPWSTR *argv)
+{
+  if (argc < 2) {
+    return FALSE;
+  }
+
+  // Get a handle to the local computer SCM database
+  SC_HANDLE manager = OpenSCManager(NULL, NULL, 
+                                    SC_MANAGER_ALL_ACCESS);
+  if (!manager) {
+    return FALSE;
+  }
+
+  // Open the service
+  SC_HANDLE svc = OpenServiceW(manager, L"MozillaMaintenance", 
+                               SERVICE_ALL_ACCESS);
+  if (!svc) {
+    CloseServiceHandle(manager);
+    return FALSE;
+  }
+  CloseServiceHandle(svc);
+  CloseServiceHandle(manager);
+
+  // If we reach here, then the service is installed, so
+  // proceed with upgrading it.
+
+  STARTUPINFOW si = {0};
+  si.cb = sizeof(STARTUPINFOW);
+  // No particular desktop because no UI
+  si.lpDesktop = L"";
+  PROCESS_INFORMATION pi = {0};
+
+  WCHAR maintserviceInstallerPath[MAX_PATH + 1];
+  wcscpy(maintserviceInstallerPath, argv[2]);
+  PathAppendSafe(maintserviceInstallerPath, 
+                 L"maintenanceservice_installer.exe");
+  WCHAR cmdLine[64];
+  wcscpy(cmdLine, L"dummyparam.exe /Upgrade");
+  BOOL svcUpdateProcessStarted = CreateProcessW(maintserviceInstallerPath, 
+                                                cmdLine, 
+                                                NULL, NULL, FALSE, 
+                                                CREATE_DEFAULT_ERROR_MODE | 
+                                                CREATE_UNICODE_ENVIRONMENT, 
+                                                NULL, argv[2], &si, &pi);
+  if (svcUpdateProcessStarted) {
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+  }
+  return svcUpdateProcessStarted;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/launchwinprocess.h
@@ -0,0 +1,42 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is common code between maintenanceservice and updater
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+BOOL LaunchWinPostProcess(const WCHAR *appExe, 
+                          const WCHAR *updateInfoDir, 
+                          bool forceSync,
+                          HANDLE userToken);
+BOOL StartServiceUpdate(int argc, LPWSTR *argv);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/uachelper.cpp
@@ -0,0 +1,101 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service UAC helper functions.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <windows.h>
+#include "uachelper.h"
+
+typedef BOOL (WINAPI *LPWTSQueryUserToken)(ULONG, PHANDLE);
+
+/**
+ * Determines if the OS is vista or later
+ *
+ * @return TRUE if the OS is vista or later.
+ */
+BOOL
+UACHelper::IsVistaOrLater() 
+{
+  // Check if we are running Vista or later.
+  OSVERSIONINFO osInfo;
+  osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  return GetVersionEx(&osInfo) && osInfo.dwMajorVersion >= 6;
+}
+
+/**
+ * Opens a user token for the given session ID
+ *
+ * @param  sessionID  The session ID for the token to obtain
+ * @return A handle to the token to obtain which will be primary if enough
+ *         permissions exist.  Caller should close the handle.
+ */
+HANDLE
+UACHelper::OpenUserToken(DWORD sessionID)
+{
+  HMODULE module = LoadLibraryW(L"wtsapi32.dll");
+  HANDLE token = NULL;
+  LPWTSQueryUserToken wtsQueryUserToken = 
+    (LPWTSQueryUserToken)GetProcAddress(module, "WTSQueryUserToken");
+  if (wtsQueryUserToken) {
+    wtsQueryUserToken(sessionID, &token);
+  }
+  FreeLibrary(module);
+  return token;
+}
+
+/**
+ * Opens a linked token for the specified token.
+ *
+ * @param  token The token to get the linked token from
+ * @return A linked token or NULL if one does not exist.
+ *         Caller should close the handle.
+ */
+HANDLE
+UACHelper::OpenLinkedToken(HANDLE token) 
+{
+  // Magic below...
+  // UAC creates 2 tokens.  One is the restricted token which we have.
+  // the other is the UAC elevated one. Since we are running as a service
+  // as the system account we have access to both.
+  TOKEN_LINKED_TOKEN tlt;
+  HANDLE hNewLinkedToken = NULL;
+  DWORD len;
+  if (GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, 
+                          &tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) {
+    token = tlt.LinkedToken;
+    hNewLinkedToken = token;
+  }
+  return hNewLinkedToken;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/uachelper.h
@@ -0,0 +1,49 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Maintenance service UAC helper functions.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _UACHELPER_H_
+#define _UACHELPER_H_
+
+class UACHelper
+{
+public:
+  static BOOL IsVistaOrLater();
+  static HANDLE OpenUserToken(DWORD sessionID);
+  static HANDLE OpenLinkedToken(HANDLE token);
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/updatedefines.h
@@ -0,0 +1,143 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is common code between maintenanceservice and updater
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef UPDATEDEFINES_H
+#define UPDATEDEFINES_H
+
+#include "prtypes.h"
+#include "readstrings.h"
+
+#ifndef MAXPATHLEN
+# ifdef PATH_MAX
+#  define MAXPATHLEN PATH_MAX
+# elif defined(MAX_PATH)
+#  define MAXPATHLEN MAX_PATH
+# elif defined(_MAX_PATH)
+#  define MAXPATHLEN _MAX_PATH
+# elif defined(CCHMAXPATH)
+#  define MAXPATHLEN CCHMAXPATH
+# else
+#  define MAXPATHLEN 1024
+# endif
+#endif
+
+#if defined(XP_WIN)
+# include <windows.h>
+# include <direct.h>
+# include <io.h>
+
+# define F_OK 00
+# define W_OK 02
+# define R_OK 04
+# define S_ISDIR(s) (((s) & _S_IFMT) == _S_IFDIR)
+# define S_ISREG(s) (((s) & _S_IFMT) == _S_IFREG)
+
+# define access _access
+
+# define putenv _putenv
+# define stat _stat
+# define DELETE_DIR L"tobedeleted"
+# define CALLBACK_BACKUP_EXT L".moz-callback"
+
+# define LOG_S "%S"
+# define NS_T(str) L ## str
+// On Windows, _snprintf and _snwprintf don't guarantee null termination. These
+// macros always leave room in the buffer for null termination and set the end
+// of the buffer to null in case the string is larger than the buffer. Having
+// multiple nulls in a string is fine and this approach is simpler (possibly
+// faster) than calculating the string length to place the null terminator and
+// truncates the string as _snprintf and _snwprintf do on other platforms.
+# define snprintf(dest, count, fmt, ...) \
+  PR_BEGIN_MACRO \
+  int _count = count - 1; \
+  _snprintf(dest, _count, fmt, ##__VA_ARGS__); \
+  dest[_count] = '\0'; \
+  PR_END_MACRO
+#define NS_tsnprintf(dest, count, fmt, ...) \
+  PR_BEGIN_MACRO \
+  int _count = count - 1; \
+  _snwprintf(dest, _count, fmt, ##__VA_ARGS__); \
+  dest[_count] = L'\0'; \
+  PR_END_MACRO
+# define NS_taccess _waccess
+# define NS_tchdir _wchdir
+# define NS_tchmod _wchmod
+# define NS_tfopen _wfopen
+# define NS_tmkdir(path, perms) _wmkdir(path)
+# define NS_tremove _wremove
+// _wrename is used to avoid the link tracking service.
+# define NS_trename _wrename
+# define NS_trmdir _wrmdir
+# define NS_tstat _wstat
+# define NS_tstrcat wcscat
+# define NS_tstrcmp wcscmp
+# define NS_tstrcpy wcscpy
+# define NS_tstrlen wcslen
+# define NS_tstrrchr wcsrchr
+# define NS_tstrstr wcsstr
+#else
+# include <sys/wait.h>
+# include <unistd.h>
+# include <fts.h>
+
+#ifdef XP_MACOSX
+# include <sys/time.h>
+#endif
+
+# define LOG_S "%s"
+# define NS_T(str) str
+# define NS_tsnprintf snprintf
+# define NS_taccess access
+# define NS_tchdir chdir
+# define NS_tchmod chmod
+# define NS_tfopen fopen
+# define NS_tmkdir mkdir
+# define NS_tremove remove
+# define NS_trename rename
+# define NS_trmdir rmdir
+# define NS_tstat stat
+# define NS_tstrcat strcat
+# define NS_tstrcmp strcmp
+# define NS_tstrcpy strcpy
+# define NS_tstrlen strlen
+# define NS_tstrrchr strrchr
+# define NS_tstrstr strstr
+#endif
+
+#define BACKUP_EXT NS_T(".moz-backup")
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/updatelogging.cpp
@@ -0,0 +1,87 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is common code between maintenanceservice and updater
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#if defined(XP_WIN)
+#include <windows.h>
+#endif
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "updatelogging.h"
+
+UpdateLog*  UpdateLog::primaryLog = NULL;
+
+UpdateLog::UpdateLog() : logFP(NULL)
+{
+}
+
+void UpdateLog::Init(NS_tchar* sourcePath, NS_tchar* fileName)
+{
+  if (logFP)
+    return;
+
+  this->sourcePath = sourcePath;
+  NS_tchar logFile[MAXPATHLEN];
+  NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]),
+    NS_T("%s/%s"), sourcePath, fileName);
+
+  logFP = NS_tfopen(logFile, NS_T("w"));
+}
+
+void UpdateLog::Finish()
+{
+  if (!logFP)
+    return;
+
+  fclose(logFP);
+  logFP = NULL;
+}
+
+void UpdateLog::Printf(const char *fmt, ... )
+{
+  if (!logFP)
+    return;
+
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(logFP, fmt, ap);
+  va_end(ap);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/update/common/updatelogging.h
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is common code between maintenanceservice and updater
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef UPDATELOGGING_H
+#define UPDATELOGGING_H
+
+#include "updatedefines.h"
+#include <stdio.h>
+
+class UpdateLog
+{
+public:
+  static UpdateLog & GetPrimaryLog() 
+  {
+    if (!primaryLog) {
+      primaryLog = new UpdateLog();
+    }
+    return *primaryLog;
+  }
+
+  void Init(NS_tchar* sourcePath, NS_tchar* fileName);
+  void Finish();
+  void Printf(const char *fmt, ... );
+
+  ~UpdateLog()
+  {
+    delete primaryLog;
+  }
+
+protected:
+  UpdateLog();
+  FILE *logFP;
+  NS_tchar* sourcePath;
+
+  static UpdateLog* primaryLog;
+};
+
+#define LOG(args) UpdateLog::GetPrimaryLog().Printf args
+#define LogInit(PATHNAME_, FILENAME_) \
+  UpdateLog::GetPrimaryLog().Init(PATHNAME_, FILENAME_)
+#define LogFinish() UpdateLog::GetPrimaryLog().Finish()
+
+#endif
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -63,16 +63,17 @@ const PREF_PLUGINS_UPDATEURL            
 const PREF_EM_HOTFIX_ID                  = "extensions.hotfix.id";
 
 const UPDATE_TEST_LOOP_INTERVAL     = 2000;
 
 const URI_UPDATES_PROPERTIES  = "chrome://mozapps/locale/update/updates.properties";
 
 const STATE_DOWNLOADING       = "downloading";
 const STATE_PENDING           = "pending";
+const STATE_PENDING_SVC       = "pending-service";
 const STATE_APPLYING          = "applying";
 const STATE_SUCCEEDED         = "succeeded";
 const STATE_DOWNLOAD_FAILED   = "download-failed";
 const STATE_FAILED            = "failed";
 
 const SRCEVT_FOREGROUND       = 1;
 const SRCEVT_BACKGROUND       = 2;
 
@@ -438,16 +439,17 @@ var gUpdates = {
               state = STATE_DOWNLOAD_FAILED;
             }
           }
 
           // Now select the best page to start with, given the current state of
           // the Update.
           switch (state) {
           case STATE_PENDING:
+          case STATE_PENDING_SVC:
             this.sourceEvent = SRCEVT_BACKGROUND;
             aCallback("finishedBackground");
             return;
           case STATE_DOWNLOADING:
             aCallback("downloading");
             return;
           case STATE_FAILED:
             window.getAttention();
@@ -1675,16 +1677,17 @@ var gErrorPatchingPage = {
    */
   onPageShow: function() {
     gUpdates.setButtons(null, null, "okButton", true);
   },
 
   onWizardNext: function() {
     switch (gUpdates.update.selectedPatch.state) {
     case STATE_PENDING:
+    case STATE_PENDING_SVC: 
       gUpdates.wiz.goTo("finished");
       break;
     case STATE_DOWNLOADING:
       gUpdates.wiz.goTo("downloading");
       break;
     case STATE_DOWNLOAD_FAILED:
       gUpdates.wiz.goTo("errors");
       break;
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -240,22 +240,23 @@ interface nsIUpdate : nsISupports
 
   /**
    * The currently selected patch for this update.
    */
   readonly attribute nsIUpdatePatch selectedPatch;
 
   /**
    * The state of the selected patch:
-   *   "downloading"       The update is being downloaded.
-   *   "pending"           The update is ready to be applied.
-   *   "applying"          The update is being applied.
-   *   "succeeded"         The update was successfully applied.
-   *   "download-failed"   The update failed to be downloaded.
-   *   "failed"            The update failed to be applied.
+   *   "downloading"        The update is being downloaded.
+   *   "pending"            The update is ready to be applied.
+   *   "pending-service"    The update is ready to be applied with the service.
+   *   "applying"           The update is being applied.
+   *   "succeeded"          The update was successfully applied.
+   *   "download-failed"    The update failed to be downloaded.
+   *   "failed"             The update failed to be applied.
    */
   attribute AString state;
 
   /**
    * A numeric error code that conveys additional information about the state
    * of a failed update or failed certificate attribute check during an update
    * check. If the update is not in the "failed" state or the certificate
    * attribute check has not failed the value is zero.
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -22,16 +22,17 @@
 # Contributor(s):
 #  Ben Goodger <ben@mozilla.org> (Original Author)
 #  Darin Fisher <darin@meer.net>
 #  Ben Turner <bent.mozilla@gmail.com>
 #  Jeff Walden <jwalden+code@mit.edu>
 #  Alexander J. Vincent <ajvincent@gmail.com>
 #  Dão Gottwald <dao@mozilla.com>
 #  Robert Strong <robert.bugzilla@gmail.com>
+#  Brian R. Bondy <netzen@gmail.com>
 #
 # 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
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -76,16 +77,19 @@ const PREF_APP_UPDATE_MODE              
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
 const PREF_APP_UPDATE_POSTUPDATE          = "app.update.postupdate";
 const PREF_APP_UPDATE_PROMPTWAITTIME      = "app.update.promptWaitTime";
 const PREF_APP_UPDATE_SHOW_INSTALLED_UI   = "app.update.showInstalledUI";
 const PREF_APP_UPDATE_SILENT              = "app.update.silent";
 const PREF_APP_UPDATE_URL                 = "app.update.url";
 const PREF_APP_UPDATE_URL_DETAILS         = "app.update.url.details";
 const PREF_APP_UPDATE_URL_OVERRIDE        = "app.update.url.override";
+const PREF_APP_UPDATE_SERVICE_ENABLED     = "app.update.service.enabled";
+const PREF_APP_UPDATE_SERVICE_ERRORS      = "app.update.service.errors";
+const PREF_APP_UPDATE_SERVICE_MAX_ERRORS  = "app.update.service.maxErrors";
 
 const PREF_PARTNER_BRANCH                 = "app.partner.";
 const PREF_APP_DISTRIBUTION               = "distribution.id";
 const PREF_APP_DISTRIBUTION_VERSION       = "distribution.version";
 
 const PREF_EM_HOTFIX_ID                   = "extensions.hotfix.id";
 
 const URI_UPDATE_PROMPT_DIALOG  = "chrome://mozapps/content/update/updates.xul";
@@ -124,36 +128,45 @@ const FILE_UPDATE_ACTIVE  = "active-upda
 const FILE_PERMS_TEST     = "update.test";
 const FILE_LAST_LOG       = "last-update.log";
 const FILE_BACKUP_LOG     = "backup-update.log";
 const FILE_UPDATE_LOCALE  = "update.locale";
 
 const STATE_NONE            = "null";
 const STATE_DOWNLOADING     = "downloading";
 const STATE_PENDING         = "pending";
+const STATE_PENDING_SVC     = "pending-service";
 const STATE_APPLYING        = "applying";
 const STATE_SUCCEEDED       = "succeeded";
 const STATE_DOWNLOAD_FAILED = "download-failed";
 const STATE_FAILED          = "failed";
 
 // From updater/errors.h:
 const WRITE_ERROR        = 7;
 const UNEXPECTED_ERROR   = 8;
 const ELEVATION_CANCELED = 9;
+const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000;
+const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
+const SERVICE_UPDATER_SIGN_ERROR           = 16002;
+const SERVICE_CALLBACK_SIGN_ERROR          = 16003;
 
 const CERT_ATTR_CHECK_FAILED_NO_UPDATE  = 100;
 const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 
 const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
 const DOWNLOAD_BACKGROUND_INTERVAL  = 600;    // seconds
 const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
 
 const UPDATE_WINDOW_NAME      = "Update:Wizard";
 
+// The number of consecutive failures when updating using the service before
+// setting the app.update.service.enabled preference to false.
+const DEFAULT_SERVICE_MAX_ERRORS = 10;
+
 var gLocale     = null;
 
 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
   return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
   return Services.strings.createBundle(URI_UPDATES_PROPERTIES);
@@ -456,17 +469,17 @@ function LOG(string) {
 #  Gets a preference value, handling the case where there is no default.
 #  @param   func
 #           The name of the preference function to call, on nsIPrefBranch
 #  @param   preference
 #           The name of the preference
 #  @param   defaultValue
 #           The default value to return in the event the preference has
 #           no setting
-#  @returns The value of the preference, or undefined if there was no
+#  @return  The value of the preference, or undefined if there was no
 #           user or default value.
  */
 function getPref(func, preference, defaultValue) {
   try {
     return Services.prefs[func](preference);
   }
   catch (e) {
   }
@@ -525,17 +538,17 @@ function getUpdateFile(pathArray) {
 /**
  * Returns human readable status text from the updates.properties bundle
  * based on an error code
  * @param   code
  *          The error code to look up human readable status text for
  * @param   defaultCode
  *          The default code to look up should human readable status text
  *          not exist for |code|
- * @returns A human readable status text string
+ * @return  A human readable status text string
  */
 function getStatusTextFromCode(code, defaultCode) {
   var reason;
   try {
     reason = gUpdateBundle.GetStringFromName("check_error-" + code);
     LOG("getStatusTextFromCode - transfer error: " + reason + ", code: " +
         code);
   }
@@ -545,30 +558,30 @@ function getStatusTextFromCode(code, def
     LOG("getStatusTextFromCode - transfer error: " + reason +
         ", default code: " + defaultCode);
   }
   return reason;
 }
 
 /**
  * Get the Active Updates directory
- * @returns The active updates directory, as a nsIFile object
+ * @return The active updates directory, as a nsIFile object
  */
 function getUpdatesDir() {
   // Right now, we only support downloading one patch at a time, so we always
   // use the same target directory.
   return getUpdateDirCreate([DIR_UPDATES, "0"]);
 }
 
 /**
  * Reads the update state from the update.status file in the specified
  * directory.
  * @param   dir
  *          The dir to look for an update.status file in
- * @returns The status value of the update.
+ * @return  The status value of the update.
  */
 function readStatusFile(dir) {
   var statusFile = dir.clone();
   statusFile.append(FILE_UPDATE_STATUS);
   var status = readStringFromFile(statusFile) || STATE_NONE;
   LOG("readStatusFile - status: " + status + ", path: " + statusFile.path);
   return status;
 }
@@ -585,16 +598,32 @@ function readStatusFile(dir) {
  */
 function writeStatusFile(dir, state) {
   var statusFile = dir.clone();
   statusFile.append(FILE_UPDATE_STATUS);
   writeStringToFile(statusFile, state);
 }
 
 /**
+ * Determines if the service should be used to attempt an update
+ * or not.  For now this is only when PREF_APP_UPDATE_SERVICE_ENABLED
+ * is true and we have Firefox.
+ *
+ * @return  true if the service should be used for updates.
+ */
+function shouldUseService() {
+#ifdef MOZ_MAINTENANCE_SERVICE
+  return getPref("getBoolPref", 
+                 PREF_APP_UPDATE_SERVICE_ENABLED, false);
+#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
 #  appVersion 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
@@ -1394,17 +1423,44 @@ UpdateService.prototype = {
       update.state = ary[0];
       if (update.state == STATE_FAILED && ary[1]) {
         update.errorCode = parseInt(ary[1]);
         if (update.errorCode == WRITE_ERROR) {
           prompter.showUpdateError(update);
           writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
           return;
         }
-        else if (update.errorCode == ELEVATION_CANCELED) {
+
+        if (update.errorCode == ELEVATION_CANCELED) {
+          writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
+          return;
+        }
+
+        if (update.errorCode == SERVICE_UPDATER_COULD_NOT_BE_STARTED ||
+            update.errorCode == SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS ||
+            update.errorCode == SERVICE_UPDATER_SIGN_ERROR || 
+            update.errorCode == SERVICE_CALLBACK_SIGN_ERROR) {
+          var failCount = getPref("getIntPref", 
+                                  PREF_APP_UPDATE_SERVICE_ERRORS, 0);
+          var maxFail = getPref("getIntPref", 
+                                PREF_APP_UPDATE_SERVICE_MAX_ERRORS, 
+                                DEFAULT_SERVICE_MAX_ERRORS);
+
+          // As a safety, when the service reaches maximum failures, it will
+          // disable itself and fallback to using the normal update mechanism
+          // without the service.
+          if (failCount >= maxFail) {
+            Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, false);
+            Services.prefs.clearUserPref(PREF_APP_UPDATE_SERVICE_ERRORS);
+          } else {
+            failCount++;
+            Services.prefs.setIntPref(PREF_APP_UPDATE_SERVICE_ERRORS, 
+                                      failCount);
+          }
+
           writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
           return;
         }
       }
 
       // Something went wrong with the patch application process.
       cleanupActiveUpdate();
 
@@ -1517,17 +1573,17 @@ UpdateService.prototype = {
   },
 
   /**
    * Determine the update from the specified updates that should be offered.
    * If both valid major and minor updates are available the minor update will
    * be offered.
    * @param   updates
    *          An array of available nsIUpdate items
-   * @returns The nsIUpdate to offer.
+   * @return  The nsIUpdate to offer.
    */
   selectUpdate: function AUS_selectUpdate(updates) {
     if (updates.length == 0)
       return null;
 
     if (getDesiredChannel()) {
       LOG("UpdateService:selectUpdate - skipping version checks for channel " +
           "change request");
@@ -2006,17 +2062,17 @@ UpdateManager.prototype = {
         this._activeUpdate = updates[0];
     }
   },
 
   /**
    * Loads an updates.xml formatted file into an array of nsIUpdate items.
    * @param   file
    *          A nsIFile for the updates.xml file
-   * @returns The array of nsIUpdate items held in the file.
+   * @return  The array of nsIUpdate items held in the file.
    */
   _loadXMLFileIntoArray: function UM__loadXMLFileIntoArray(file) {
     if (!file.exists()) {
       LOG("UpdateManager:_loadXMLFileIntoArray: XML file does not exist");
       return [];
     }
 
     var result = [];
@@ -2190,17 +2246,17 @@ UpdateManager.prototype = {
 
     this._ensureUpdates();
     // Don't write updates that have a temporary state to the updates.xml file.
     if (this._updates) {
       let updates = this._updates.slice();
       for (let i = updates.length - 1; i >= 0; --i) {
         let state = updates[i].state;
         if (state == STATE_NONE || state == STATE_DOWNLOADING ||
-            state == STATE_PENDING) {
+            state == STATE_PENDING || state == STATE_PENDING_SVC) {
           updates.splice(i, 1);
         }
       }
 
       this._writeUpdatesToXMLFile(updates.slice(0, 10),
                                   getUpdateFile([FILE_UPDATES_DB]));
     }
   },
@@ -2525,17 +2581,18 @@ Downloader.prototype = {
     if (this._request && this._request instanceof Ci.nsIRequest)
       this._request.cancel(Cr.NS_BINDING_ABORTED);
   },
 
   /**
    * Whether or not a patch has been downloaded and staged for installation.
    */
   get patchIsStaged() {
-    return readStatusFile(getUpdatesDir()) == STATE_PENDING;
+    var readState = readStatusFile(getUpdatesDir()); 
+    return readState == STATE_PENDING || readState == STATE_PENDING_SVC;
   },
 
   /**
    * Verify the downloaded file.  We assume that the download is complete at
    * this point.
    */
   _verifyDownload: function Downloader__verifyDownload() {
     if (!this._request)
@@ -2577,27 +2634,27 @@ Downloader.prototype = {
 
   /**
    * Select the patch to use given the current state of updateDir and the given
    * set of update patches.
    * @param   update
    *          A nsIUpdate object to select a patch from
    * @param   updateDir
    *          A nsIFile representing the update directory
-   * @returns A nsIUpdatePatch object to download
+   * @return  A nsIUpdatePatch object to download
    */
   _selectPatch: function Downloader__selectPatch(update, updateDir) {
     // Given an update to download, we will always try to download the patch
     // for a partial update over the patch for a full update.
 
     /**
      * Return the first UpdatePatch with the given type.
      * @param   type
      *          The type of the patch ("complete" or "partial")
-     * @returns A nsIUpdatePatch object matching the type specified
+     * @return  A nsIUpdatePatch object matching the type specified
      */
     function getPatchOfType(type) {
       for (var i = 0; i < update.patchCount; ++i) {
         var patch = update.getPatchAt(i);
         if (patch && patch.type == type)
           return patch;
       }
       return null;
@@ -2615,16 +2672,17 @@ Downloader.prototype = {
     var useComplete = getDesiredChannel() ? true : false;
     if (selectedPatch) {
       LOG("Downloader:_selectPatch - found existing patch with state: " +
           state);
       switch (state) {
       case STATE_DOWNLOADING:
         LOG("Downloader:_selectPatch - resuming download");
         return selectedPatch;
+      case STATE_PENDING_SVC:
       case STATE_PENDING:
         LOG("Downloader:_selectPatch - already downloaded and staged");
         return null;
       default:
         // Something went wrong when we tried to apply the previous patch.
         // Try the complete patch next time.
         if (update && selectedPatch.type == "partial") {
           useComplete = true;
@@ -2842,17 +2900,17 @@ Downloader.prototype = {
       LOG("Downloader:onStopRequest - original URI spec: " + request.URI.spec +
           ", final URI spec: " + request.finalURI.spec + ", status: " + status);
 
     var state = this._patch.state;
     var shouldShowPrompt = false;
     var deleteActiveUpdate = false;
     if (Components.isSuccessCode(status)) {
       if (this._verifyDownload()) {
-        state = STATE_PENDING;
+        state = shouldUseService() ? STATE_PENDING_SVC : STATE_PENDING
 
         // We only need to explicitly show the prompt if this is a background
         // download, since otherwise some kind of UI is already visible and
         // that UI will notify.
         if (this.background)
           shouldShowPrompt = true;
 
         // Tell the updater.exe we're ready to apply.
--- a/toolkit/mozapps/update/test/Makefile.in
+++ b/toolkit/mozapps/update/test/Makefile.in
@@ -73,16 +73,17 @@ INI_TEST_FILES = \
   TestAUSReadStrings1.ini \
   TestAUSReadStrings2.ini \
   TestAUSReadStrings3.ini \
   $(NULL)
 
 LOCAL_INCLUDES += \
   -I$(srcdir) \
   -I$(topsrcdir)/toolkit/mozapps/update \
+  -I$(topsrcdir)/toolkit/mozapps/update/common \
   $(NULL)
 
 MOZ_WINCONSOLE = 1
 
 LIBS += \
   ../../readstrings/$(LIB_PREFIX)readstrings.$(LIB_SUFFIX) \
   $(NULL)
 
--- a/toolkit/mozapps/update/test/sharedUpdateXML.js
+++ b/toolkit/mozapps/update/test/sharedUpdateXML.js
@@ -24,16 +24,17 @@ const SHA384_HASH_SIMPLE_MAR = "a5725055
                                "99c453";
 const SHA512_HASH_SIMPLE_MAR = "55d3e2a86acaeb0abb7a444c13bba748846fcbac7ff05" +
                                "8f8ee9c9260ba01e6aef86fa4a6c46a3016b675ef94e7" +
                                "7e63fbe912f64d155bed9b1c341dd56e575a26";
 
 const STATE_NONE            = "null";
 const STATE_DOWNLOADING     = "downloading";
 const STATE_PENDING         = "pending";
+const STATE_PENDING_SVC     = "pending-service";
 const STATE_APPLYING        = "applying";
 const STATE_SUCCEEDED       = "succeeded";
 const STATE_DOWNLOAD_FAILED = "download-failed";
 const STATE_FAILED          = "failed";
 
 /**
  * Constructs a string representing a remote update xml file.
  *
--- a/toolkit/mozapps/update/updater/Makefile.in
+++ b/toolkit/mozapps/update/updater/Makefile.in
@@ -50,19 +50,21 @@ CPPSRCS = \
   $(NULL)
 
 PROGRAM = updater$(BIN_SUFFIX)
 
 # Don't link the updater against libmozutils. See bug 687139
 MOZ_UTILS_LDFLAGS =
 MOZ_UTILS_PROGRAM_LDFLAGS =
 
-LOCAL_INCLUDES += -I$(srcdir)/../../readstrings
+LOCAL_INCLUDES += -I$(srcdir)/../../readstrings \
+  -I$(srcdir)/../common
 
 LIBS += \
+  ../common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \
   $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) \
   ../../readstrings/$(LIB_PREFIX)readstrings.$(LIB_SUFFIX) \
   $(BZ2_LIBS) \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 USE_STATIC_LIBS = 1
 HAVE_PROGRESSUI = 1
--- a/toolkit/mozapps/update/updater/progressui.h
+++ b/toolkit/mozapps/update/updater/progressui.h
@@ -34,16 +34,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef PROGRESSUI_H__
 #define PROGRESSUI_H__
 
+#include "updatedefines.h"
+
 #if defined(XP_WIN)
   typedef WCHAR NS_tchar;
   #define NS_main wmain
 #else
   typedef char NS_tchar;
   #define NS_main main
 #endif
 
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -64,102 +64,16 @@
  *  downgrades by causing the actions defined in precomplete to be performed.
  *
  *  precomplete
  *  -----------
  *  method   = "remove" | "rmdir" | "remove-cc"
  *
  * 'remove-cc' is a remove action to perform on channel change.
  */
-
-#if defined(XP_WIN)
-# include <windows.h>
-# include <direct.h>
-# include <io.h>
-
-# define F_OK 00
-# define W_OK 02
-# define R_OK 04
-# define S_ISDIR(s) (((s) & _S_IFMT) == _S_IFDIR)
-# define S_ISREG(s) (((s) & _S_IFMT) == _S_IFREG)
-
-# define access _access
-
-# define putenv _putenv
-# define stat _stat
-# define DELETE_DIR L"tobedeleted"
-# define CALLBACK_BACKUP_EXT L".moz-callback"
-
-# define LOG_S "%S"
-# define NS_T(str) L ## str
-// On Windows, _snprintf and _snwprintf don't guarantee null termination. These
-// macros always leave room in the buffer for null termination and set the end
-// of the buffer to null in case the string is larger than the buffer. Having
-// multiple nulls in a string is fine and this approach is simpler (possibly
-// faster) than calculating the string length to place the null terminator and
-// truncates the string as _snprintf and _snwprintf do on other platforms.
-# define snprintf(dest, count, fmt, ...) \
-  PR_BEGIN_MACRO \
-    int _count = count - 1; \
-    _snprintf(dest, _count, fmt, ##__VA_ARGS__); \
-    dest[_count] = '\0'; \
-  PR_END_MACRO
-# define NS_tsnprintf(dest, count, fmt, ...) \
-  PR_BEGIN_MACRO \
-    int _count = count - 1; \
-    _snwprintf(dest, _count, fmt, ##__VA_ARGS__); \
-    dest[_count] = L'\0'; \
-  PR_END_MACRO
-# define NS_taccess _waccess
-# define NS_tchdir _wchdir
-# define NS_tchmod _wchmod
-# define NS_tfopen _wfopen
-# define NS_tmkdir(path, perms) _wmkdir(path)
-# define NS_tremove _wremove
-// _wrename is used to avoid the link tracking service.
-# define NS_trename _wrename
-# define NS_trmdir _wrmdir
-# define NS_tstat _wstat
-# define NS_tstrcat wcscat
-# define NS_tstrcmp wcscmp
-# define NS_tstrcpy wcscpy
-# define NS_tstrlen wcslen
-# define NS_tstrrchr wcsrchr
-# define NS_tstrstr wcsstr
-#else
-# include <sys/wait.h>
-# include <unistd.h>
-# include <fts.h>
-
-#ifdef XP_MACOSX
-# include <sys/time.h>
-#endif
-
-# define LOG_S "%s"
-# define NS_T(str) str
-# define NS_tsnprintf snprintf
-# define NS_taccess access
-# define NS_tchdir chdir
-# define NS_tchmod chmod
-# define NS_tfopen fopen
-# define NS_tmkdir mkdir
-# define NS_tremove remove
-# define NS_trename rename
-# define NS_trmdir rmdir
-# define NS_tstat stat
-# define NS_tstrcat strcat
-# define NS_tstrcmp strcmp
-# define NS_tstrcpy strcpy
-# define NS_tstrlen strlen
-# define NS_tstrrchr strrchr
-# define NS_tstrstr strstr
-#endif
-
-#define BACKUP_EXT NS_T(".moz-backup")
-
 #include "bspatch.h"
 #include "progressui.h"
 #include "archivereader.h"
 #include "errors.h"
 #include "bzlib.h"
 
 #include <stdio.h>
 #include <string.h>
@@ -167,16 +81,18 @@
 #include <stdarg.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <errno.h>
 
+#include "updatelogging.h"
+
 // Amount of the progress bar to use in each of the 3 update stages,
 // should total 100.0.
 #define PROGRESS_PREPARE_SIZE 20.0f
 #define PROGRESS_EXECUTE_SIZE 75.0f
 #define PROGRESS_FINISH_SIZE   5.0f
 
 #if defined(XP_MACOSX)
 // These functions are defined in launchchild_osx.mm
@@ -191,37 +107,25 @@ void LaunchMacPostProcess(const char* aA
 #ifndef NULL
 # define NULL (0)
 #endif
 
 #ifndef SSIZE_MAX
 # define SSIZE_MAX LONG_MAX
 #endif
 
-#ifndef MAXPATHLEN
-# ifdef PATH_MAX
-#  define MAXPATHLEN PATH_MAX
-# elif defined(MAX_PATH)
-#  define MAXPATHLEN MAX_PATH
-# elif defined(_MAX_PATH)
-#  define MAXPATHLEN _MAX_PATH
-# elif defined(CCHMAXPATH)
-#  define MAXPATHLEN CCHMAXPATH
-# else
-#  define MAXPATHLEN 1024
-# endif
-#endif
-
 // We want to use execv to invoke the callback executable on platforms where
 // we were launched using execv.  See nsUpdateDriver.cpp.
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
 #define USE_EXECV
 #endif
 
 #ifdef XP_WIN
+#include "launchwinprocess.h"
+
 // Closes the handle if valid and if the updater is elevated returns with the
 // return code specified. This prevents multiple launches of the callback
 // application by preventing the elevated process from launching the callback.
 #define EXIT_WHEN_ELEVATED(path, handle, retCode) \
   { \
       if (handle != INVALID_HANDLE_VALUE) { \
         CloseHandle(handle); \
       } \
@@ -396,57 +300,16 @@ static NS_tchar* gDestPath;
 static NS_tchar gCallbackRelPath[MAXPATHLEN];
 static NS_tchar gCallbackBackupPath[MAXPATHLEN];
 #endif
 
 static const NS_tchar kWhitespace[] = NS_T(" \t");
 static const NS_tchar kNL[] = NS_T("\r\n");
 static const NS_tchar kQuote[] = NS_T("\"");
 
-//-----------------------------------------------------------------------------
-// LOGGING
-
-static FILE *gLogFP = NULL;
-
-static void LogInit()
-{
-  if (gLogFP)
-    return;
-
-  NS_tchar logFile[MAXPATHLEN];
-  NS_tsnprintf(logFile, sizeof(logFile)/sizeof(logFile[0]),
-               NS_T("%s/update.log"), gSourcePath);
-
-  gLogFP = NS_tfopen(logFile, NS_T("w"));
-}
-
-static void LogFinish()
-{
-  if (!gLogFP)
-    return;
-
-  fclose(gLogFP);
-  gLogFP = NULL;
-}
-
-static void LogPrintf(const char *fmt, ... )
-{
-  if (!gLogFP)
-    return;
-
-  va_list ap;
-  va_start(ap, fmt);
-  vfprintf(gLogFP, fmt, ap);
-  va_end(ap);
-}
-
-#define LOG(args) LogPrintf args
-
-//-----------------------------------------------------------------------------
-
 static inline size_t
 mmin(size_t a, size_t b)
 {
   return (a > b) ? b : a;
 }
 
 static NS_tchar*
 mstrtok(const NS_tchar *delims, NS_tchar **str)
@@ -1514,126 +1377,57 @@ PatchIfFile::Finish(int status)
 
   PatchFile::Finish(status);
 }
 
 //-----------------------------------------------------------------------------
 
 #ifdef XP_WIN
 #include "nsWindowsRestart.cpp"
-
-static void
-LaunchWinPostProcess(const WCHAR *appExe)
-{
-  // Launch helper.exe to perform post processing (e.g. registry and log file
-  // modifications) for the update.
-  WCHAR inifile[MAXPATHLEN];
-  wcscpy(inifile, appExe);
-
-  WCHAR *slash = wcsrchr(inifile, '\\');
-  if (!slash)
-    return;
-
-  wcscpy(slash + 1, L"updater.ini");
-
-  WCHAR exefile[MAXPATHLEN];
-  WCHAR exearg[MAXPATHLEN];
-  WCHAR exeasync[10];
-  bool async = true;
-  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", NULL, exefile,
-                                MAXPATHLEN, inifile))
-    return;
-
-  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", NULL, exearg,
-                                MAXPATHLEN, inifile))
-    return;
-
-  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE", exeasync,
-                                sizeof(exeasync)/sizeof(exeasync[0]), inifile))
-    return;
-
-  WCHAR exefullpath[MAXPATHLEN];
-  wcscpy(exefullpath, appExe);
-
-  slash = wcsrchr(exefullpath, '\\');
-  wcscpy(slash + 1, exefile);
-
-  WCHAR dlogFile[MAXPATHLEN];
-  wcscpy(dlogFile, exefullpath);
-
-  slash = wcsrchr(dlogFile, '\\');
-  wcscpy(slash + 1, L"uninstall.update");
-
-  WCHAR slogFile[MAXPATHLEN];
-  NS_tsnprintf(slogFile, sizeof(slogFile)/sizeof(slogFile[0]),
-               NS_T("%s/update.log"), gSourcePath);
-
-  WCHAR dummyArg[13];
-  wcscpy(dummyArg, L"argv0ignored ");
-
-  size_t len = wcslen(exearg) + wcslen(dummyArg);
-  WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
-  if (!cmdline)
-    return;
-
-  wcscpy(cmdline, dummyArg);
-  wcscat(cmdline, exearg);
-
-  if (!_wcsnicmp(exeasync, L"false", 6) || !_wcsnicmp(exeasync, L"0", 2))
-    async = false;
-
-  // We want to launch the post update helper app to update the Windows
-  // registry even if there is a failure with removing the uninstall.update
-  // file or copying the update.log file.
-  NS_tremove(dlogFile);
-  CopyFile(slogFile, dlogFile, false);
-
-  STARTUPINFOW si = {sizeof(si), 0};
-  PROCESS_INFORMATION pi = {0};
-
-  bool ok = CreateProcessW(exefullpath,
-                           cmdline,
-                           NULL,  // no special security attributes
-                           NULL,  // no special thread attributes
-                           false, // don't inherit filehandles
-                           0,     // No special process creation flags
-                           NULL,  // inherit my environment
-                           NULL,  // use my current directory
-                           &si,
-                           &pi);
-  free(cmdline);
-
-  if (ok) {
-    if (!async)
-      WaitForSingleObject(pi.hProcess, INFINITE);
-
-    CloseHandle(pi.hProcess);
-    CloseHandle(pi.hThread);
-  }
-}
+#include "uachelper.h"
 #endif
 
 static void
 LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv)
 {
   putenv(const_cast<char*>("NO_EM_RESTART="));
   putenv(const_cast<char*>("MOZ_LAUNCHED_CHILD=1"));
 
   // Run from the specified working directory (see bug 312360). This is not
   // necessary on Windows CE since the application that launches the updater
   // passes the working directory as an --environ: command line argument.
-  if(NS_tchdir(workingDir) != 0)
+  if (NS_tchdir(workingDir) != 0) {
     LOG(("Warning: chdir failed\n"));
+  }
 
 #if defined(USE_EXECV)
   execv(argv[0], argv);
 #elif defined(XP_MACOSX)
   LaunchChild(argc, argv);
+#elif defined(MOZ_MAINTENANCE_SERVICE)
+  // If updater.exe is run as session ID 0 and we have a MOZ_SESSION_ID 
+  // set, then get the unelevated token and use that to start the callback
+  // application.  Getting tokens will only work if the process is running
+  // as the system account.
+  DWORD myProcessID = GetCurrentProcessId();
+  DWORD mySessionID = 0;
+  ProcessIdToSessionId(myProcessID, &mySessionID);
+  nsAutoHandle unelevatedToken(NULL);
+  if (mySessionID == 0) {
+    WCHAR *sessionIDStr = _wgetenv(L"MOZ_SESSION_ID");
+    if (sessionIDStr) {
+      // Remove the env var now that we have its value.
+      int callbackSessionID = _wtoi(sessionIDStr);
+      _wputenv(L"MOZ_SESSION_ID=");
+      unelevatedToken.own(UACHelper::OpenUserToken(callbackSessionID));
+    }
+  }
+  WinLaunchChild(argv[0], argc, argv, unelevatedToken);
 #elif defined(XP_WIN)
-  WinLaunchChild(argv[0], argc, argv);
+  WinLaunchChild(argv[0], argc, argv, NULL);
 #else
 # warning "Need implementaton of LaunchCallbackApp"
 #endif
 }
 
 static void
 WriteStatusFile(int status)
 {
@@ -1867,17 +1661,17 @@ int NS_main(int argc, NS_tchar **argv)
       }
 
       CloseHandle(elevatedFileHandle);
       return 0;
     }
   }
 #endif
 
-  LogInit();
+  LogInit(gSourcePath, NS_T("update.log"));
   LOG(("SOURCE DIRECTORY " LOG_S "\n", argv[1]));
   LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[2]));
 
 #ifdef XP_WIN
   // Allocate enough space for the length of the path an optional additional
   // trailing slash and null termination.
   NS_tchar *destpath = (NS_tchar *) malloc((NS_tstrlen(argv[2]) + 2) * sizeof(NS_tchar));
   if (!destpath)
@@ -2030,17 +1824,30 @@ int NS_main(int argc, NS_tchar **argv)
   }
 #endif /* XP_WIN */
 
   LogFinish();
 
   if (argc > callbackIndex) {
 #if defined(XP_WIN)
     if (gSucceeded) {
-      LaunchWinPostProcess(argv[callbackIndex]);
+      // The service update will only be executed if it is already installed.
+      // For first time installs of the service, the install will happen from
+      // the PostUpdate process. We do the service update process here 
+      // because it's possible we are updating with updater.exe without the 
+      // service if the service failed to apply the update. We want to update
+      // the service to a newer version in that case. If we are not running
+      // through the service, then MOZ_SESSION_ID will not exist.
+      WCHAR *sessionIDStr = _wgetenv(L"MOZ_SESSION_ID");
+      if (!sessionIDStr) {
+        if (!LaunchWinPostProcess(argv[2], gSourcePath, false, NULL)) {
+          LOG(("NS_main: The post update process could not be launched.\n"));
+        }
+        StartServiceUpdate(argc, argv);
+      }
     }
     EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
 #endif /* XP_WIN */
 #ifdef XP_MACOSX
     if (gSucceeded) {
       LaunchMacPostProcess(argv[callbackIndex]);
     }
 #endif /* XP_MACOSX */
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -468,16 +468,17 @@ MAKEFILES_xulapp="
   toolkit/components/console/Makefile
   toolkit/components/contentprefs/Makefile
   toolkit/components/cookie/Makefile
   toolkit/components/downloads/Makefile
   toolkit/components/exthelper/Makefile
   toolkit/components/filepicker/Makefile
   toolkit/components/find/Makefile
   toolkit/components/intl/Makefile
+  toolkit/components/maintenanceservice/Makefile
   toolkit/components/microformats/Makefile
   toolkit/components/parentalcontrols/Makefile
   toolkit/components/passwordmgr/Makefile
   toolkit/components/perf/Makefile
   toolkit/components/places/Makefile
   toolkit/components/prompts/Makefile
   toolkit/components/prompts/src/Makefile
   toolkit/components/startup/Makefile
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -132,17 +132,22 @@ NS_HIDDEN_(nsresult)
 NS_LockProfilePath(nsILocalFile* aPath, nsILocalFile* aTempPath,
                    nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult);
 
 NS_HIDDEN_(void)
 WriteConsoleLog();
 
 #ifdef XP_WIN
 BOOL
-WinLaunchChild(const PRUnichar *exePath, int argc, char **argv);
+WinLaunchChild(const PRUnichar *exePath, int argc, 
+               char **argv, HANDLE userToken = NULL);
+BOOL
+WinLaunchServiceCommand(const PRUnichar *exePath, int argc, char **argv);
+BOOL
+WriteStatusPending(LPCWSTR updateDirPath);
 #endif
 
 #define NS_NATIVEAPPSUPPORT_CONTRACTID "@mozilla.org/toolkit/native-app-support;1"
 
 // Like nsXREAppData, but releases all strong refs/allocated memory
 // in the destructor.
 class ScopedAppData : public nsXREAppData
 {
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -19,16 +19,17 @@
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
  *  Ben Turner <mozilla@songbirdnest.com>
  *  Robert Strong <robert.bugzilla@gmail.com>
  *  Josh Aas <josh@mozilla.com>
+ *  Brian R. Bondy <netzen@gmail.com>
  *
  * 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
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -185,39 +186,48 @@ GetFile(nsIFile *dir, const nsCSubstring
 
 static bool
 GetStatusFile(nsIFile *dir, nsCOMPtr<nsILocalFile> &result)
 {
   return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
 }
 
 static bool
-IsPending(nsILocalFile *statusFile)
+IsPending(nsILocalFile *statusFile, bool &isPendingService)
 {
   PRFileDesc *fd = nsnull;
   nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
   if (NS_FAILED(rv))
     return false;
 
   char buf[32];
   const PRInt32 n = PR_Read(fd, buf, sizeof(buf));
   PR_Close(fd);
 
   if (n < 0)
     return false;
   
   const char kPending[] = "pending";
-  return (strncmp(buf, kPending, sizeof(kPending) - 1) == 0);
+  bool isPending = (strncmp(buf, kPending, sizeof(kPending) - 1) == 0);
+
+  const char kPendingService[] = "pending-service";
+  isPendingService = (strncmp(buf, kPendingService, 
+                      sizeof(kPendingService) - 1) == 0);
+
+  return isPending || isPendingService;
 }
 
 static bool
 SetStatusApplying(nsILocalFile *statusFile)
 {
   PRFileDesc *fd = nsnull;
-  nsresult rv = statusFile->OpenNSPRFileDesc(PR_WRONLY, 0660, &fd);
+  nsresult rv = statusFile->OpenNSPRFileDesc(PR_WRONLY | 
+                                             PR_TRUNCATE | 
+                                             PR_CREATE_FILE, 
+                                             0660, &fd);
   if (NS_FAILED(rv))
     return false;
 
   static const char kApplying[] = "Applying\n";
   PR_Write(fd, kApplying, sizeof(kApplying) - 1);
   PR_Close(fd);
 
   return true;
@@ -329,17 +339,17 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir
     return false;
 #endif
   rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin));
   return NS_SUCCEEDED(rv); 
 }
 
 static void
 ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
-            nsIFile *appDir, int appArgc, char **appArgv)
+            nsIFile *appDir, int appArgc, char **appArgv, bool isPendingService)
 {
   nsresult rv;
 
   // Steps:
   //  - mark update as 'applying'
   //  - copy updater into update dir
   //  - run updater w/ appDir as the current working dir
 
@@ -475,18 +485,52 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
     PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   }
 
   LOG(("spawning updater process [%s]\n", updaterPath.get()));
 
 #if defined(USE_EXECV)
   execv(updaterPath.get(), argv);
 #elif defined(XP_WIN)
-  if (!WinLaunchChild(updaterPathW.get(), argc, argv))
-    return;
+
+#ifndef MOZ_MAINTENANCE_SERVICE
+  // We never want the service to be used unless we have Firefox
+  isPendingService = false;
+#endif
+
+  if (isPendingService) {
+    // Make sure the service isn't already busy processing another work item.
+    SetLastError(ERROR_SUCCESS);
+    HANDLE serviceRunningEvent = 
+      OpenEvent(EVENT_ALL_ACCESS, 
+                FALSE, 
+                L"Global\\moz-5b780de9-065b-4341-a04f-ddd94b3723e5");
+    // Only use the service if we know the event exists.
+    // If we have a non NULL handle, or if ERROR_ACCESS_DENIED is returned,
+    // then the event exists.
+    isPendingService = !serviceRunningEvent && 
+                       GetLastError() != ERROR_ACCESS_DENIED;
+    if (serviceRunningEvent) {
+      CloseHandle(serviceRunningEvent);
+    }
+  }
+
+  // Launch the update operation using the service if the status file said so.
+  // We also set the status to pending to ensure we never attempt to use the 
+  // service more than once in a row for a single update.
+  if (!isPendingService || 
+      !WriteStatusPending(NS_ConvertUTF8toUTF16(updateDirPath).get()) ||
+      !WinLaunchServiceCommand(updaterPathW.get(), argc, argv)) {
+    // Launch the update using updater.exe
+    if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
+      return;
+    }
+  }
+
+  // We are going to process an update so we should exit now
   _exit(0);
 #elif defined(XP_MACOSX)
   CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
   // LaunchChildMac uses posix_spawnp and prefers the current
   // architecture when launching. It doesn't require a
   // null-terminated string but it doesn't matter if we pass one.
   LaunchChildMac(argc, argv);
   exit(0);
@@ -511,25 +555,28 @@ ProcessUpdates(nsIFile *greDir, nsIFile 
   if (NS_FAILED(rv))
     return rv;
 
   rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
   if (NS_FAILED(rv))
     return rv;
 
   nsCOMPtr<nsILocalFile> statusFile;
-  if (GetStatusFile(updatesDir, statusFile) && IsPending(statusFile)) {
+  bool isPendingService;
+  if (GetStatusFile(updatesDir, statusFile) && 
+      IsPending(statusFile, isPendingService)) {
     nsCOMPtr<nsILocalFile> versionFile;
     nsCOMPtr<nsILocalFile> channelChangeFile;
     // 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 (!GetChannelChangeFile(updatesDir, channelChangeFile) &&
         (!GetVersionFile(updatesDir, versionFile) ||
          IsOlderVersion(versionFile, appVersion))) {
       updatesDir->Remove(true);
     } else {
-      ApplyUpdate(greDir, updatesDir, statusFile, appDir, argc, argv);
+      ApplyUpdate(greDir, updatesDir, statusFile, appDir, 
+                  argc, argv, isPendingService);
     }
   }
 
   return NS_OK;
 }
--- a/toolkit/xre/nsWindowsRestart.cpp
+++ b/toolkit/xre/nsWindowsRestart.cpp
@@ -16,16 +16,17 @@
  * The Initial Developer of the Original Code is
  * Benjamin Smedberg <benjamin@smedbergs.us>.
  *
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Mozilla Foundation. All Rights Reserved.
  *
  * Contributor(s):
  *   Robert Strong <robert.bugzilla@gmail.com>
+ *   Brian R. Bondy <netzen@gmail.com>
  *
  * 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
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -41,18 +42,29 @@
 
 #ifdef nsWindowsRestart_cpp
 #error "nsWindowsRestart.cpp is not a header file, and must only be included once."
 #else
 #define nsWindowsRestart_cpp
 #endif
 
 #include "nsUTF8Utils.h"
+#include "nsWindowsHelpers.h"
 
 #include <shellapi.h>
+#include <shlwapi.h>
+#include <shlobj.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <rpc.h>
+#include <userenv.h>
+
+#pragma comment(lib, "shlwapi.lib")
+#pragma comment(lib, "rpcrt4.lib")
+#pragma comment(lib, "userenv.lib")
 
 #ifndef ERROR_ELEVATION_REQUIRED
 #define ERROR_ELEVATION_REQUIRED 740L
 #endif
 
 BOOL (WINAPI *pCreateProcessWithTokenW)(HANDLE,
                                         DWORD,
                                         LPCWSTR,
@@ -157,17 +169,17 @@ static PRUnichar* ArgToString(PRUnichar 
 }
 
 /**
  * Creates a command line from a list of arguments. The returned
  * string is allocated with "malloc" and should be "free"d.
  *
  * argv is UTF8
  */
-static PRUnichar*
+PRUnichar*
 MakeCommandLine(int argc, PRUnichar **argv)
 {
   int i;
   int len = 0;
 
   // The + 1 of the last argument handles the allocation for null termination
   for (i = 0; i < argc; ++i)
     len += ArgStrLen(argv[i]) + 1;
@@ -220,84 +232,429 @@ FreeAllocStrings(int argc, PRUnichar **a
     --argc;
     delete [] argv[argc];
   }
 
   delete [] argv;
 }
 
 /**
- * Launch a child process with the specified arguments.
- * @note argv[0] is ignored
- * @note The form of this function that takes char **argv expects UTF-8
+ * Determines if the maintenance service is running or not.
+ * 
+ * @return TRUE if the maintenance service is running.
+*/
+BOOL 
+EnsureWindowsServiceRunning() {
+  // Get a handle to the SCM database.
+  nsAutoServiceHandle serviceManager(OpenSCManager(NULL, NULL, 
+                                                   SC_MANAGER_CONNECT | 
+                                                   SC_MANAGER_ENUMERATE_SERVICE));
+  if (!serviceManager)  {
+    return FALSE;
+  }
+
+  // Get a handle to the service.
+  nsAutoServiceHandle service(OpenServiceW(serviceManager, 
+                                           L"MozillaMaintenance", 
+                                           SERVICE_QUERY_STATUS | SERVICE_START));
+  if (!service) { 
+    return FALSE;
+  }
+
+  // Make sure the service is not stopped.
+  SERVICE_STATUS_PROCESS ssp;
+  DWORD bytesNeeded;
+  if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
+                            sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
+    return FALSE;
+  }
+
+  if (ssp.dwCurrentState == SERVICE_STOPPED) {
+    if (!StartService(service, 0, NULL)) {
+      return FALSE;
+    }
+
+    // Make sure we can get into a started state without waiting too long.
+    // This usually starts instantly but the extra code is just in case it
+    // takes longer.
+    DWORD totalWaitTime = 0;
+    static const int maxWaitTime = 1000 * 5; // Never wait more than 5 seconds
+    while (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
+                                sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
+      if (ssp.dwCurrentState == SERVICE_RUNNING) {
+        break;
+      }
+      
+      if (ssp.dwCurrentState == SERVICE_START_PENDING &&
+          totalWaitTime > maxWaitTime) {
+        // We will probably eventually start, but we can't wait any longer.
+        break;
+      }
+      
+      if (ssp.dwCurrentState != SERVICE_START_PENDING) {
+        return FALSE;
+      }
+
+      Sleep(ssp.dwWaitHint);
+      // Increment by at least 10 milliseconds to ensure we always make 
+      // progress towards maxWaitTime in case dwWaitHint is 0.
+      totalWaitTime += (ssp.dwWaitHint + 10);
+    }
+  }
+
+  return ssp.dwCurrentState == SERVICE_RUNNING;
+}
+
+/**
+ * Joins a base directory path with a filename.
+ *
+ * @param  base  The base directory path of size MAX_PATH + 1
+ * @param  extra The filename to append
+ * @return TRUE if the file name was successful appended to base
  */
+BOOL
+PathAppendSafe(LPWSTR base, LPCWSTR extra)
+{
+  if (wcslen(base) + wcslen(extra) >= MAX_PATH) {
+    return FALSE;
+  }
+
+  return PathAppendW(base, extra);
+}
+
+/**
+ * Obtains the directory path to store work item files.
+ * 
+ * @return TRUE if the path was obtained successfully.
+*/
+BOOL
+GetUpdateDirectoryPath(PRUnichar *path) 
+{
+  HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 
+    SHGFP_TYPE_CURRENT, path);
+  if (FAILED(hr)) {
+    return FALSE;
+  }
+
+  if (!PathAppendSafe(path, L"Mozilla")) {
+    return FALSE;
+  }
+  // The directory should already be created from the installer, but
+  // just to be safe in case someone deletes.
+  CreateDirectoryW(path, NULL);
+
+  if (!PathAppendSafe(path, L"updates")) {
+    return FALSE;
+  }
+  CreateDirectoryW(path, NULL);
+  return TRUE;
+}
+
+/**
+ * Launch a service initiated action with the specified arguments.
+ *
+ * @param  exePath The path of the executable to run
+ * @param  argc    The total number of arguments in argv
+ * @param  argv    An array of null terminated strings to pass to the exePath,
+ *                 argv[0] is ignored
+ * @return TRUE if successful
+ */
+BOOL
+WinLaunchServiceCommand(const PRUnichar *exePath, int argc, PRUnichar **argv)
+{
+  // Ensure the service is running, if not we should try to start it, if it is
+  // not in a running state we cannot execute a service command.
+  if (!EnsureWindowsServiceRunning()) {
+    return FALSE;
+  }
+
+  PRUnichar updateData[MAX_PATH + 1];
+  if (!GetUpdateDirectoryPath(updateData)) {
+    return FALSE;
+  }
 
-BOOL
-WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv);
+  // Get a unique filename
+  PRUnichar tempFilePath[MAX_PATH + 1];
+  const int USE_SYSTEM_TIME = 0;
+  if (!GetTempFileNameW(updateData, L"moz", USE_SYSTEM_TIME, tempFilePath)) {
+    return FALSE;
+  }
+  
+  const int FILE_SHARE_NONE = 0;
+  nsAutoHandle updateMetaFile(CreateFileW(tempFilePath, GENERIC_WRITE, 
+                                          FILE_SHARE_NONE, NULL, CREATE_ALWAYS, 
+                                          0, NULL));
+  if (updateMetaFile == INVALID_HANDLE_VALUE) {
+    return FALSE;
+  }
+
+  // Write out the command ID.
+  // Command ID 1 is for an update work item file, which is the only supported
+  // command at this time.
+  DWORD commandID = 1, commandIDWrote;
+  BOOL result = WriteFile(updateMetaFile, &commandID, 
+                          sizeof(DWORD), 
+                          &commandIDWrote, NULL);
+
+  // Write out the command line arguments that are passed to updater.exe
+  PRUnichar *commandLineBuffer = MakeCommandLine(argc, argv);
+  DWORD sessionID, sessionIDWrote;
+  ProcessIdToSessionId(GetCurrentProcessId(), &sessionID);
+  result |= WriteFile(updateMetaFile, &sessionID, 
+                      sizeof(DWORD), 
+                      &sessionIDWrote, NULL);
+
+  PRUnichar appBuffer[MAX_PATH + 1];
+  ZeroMemory(appBuffer, sizeof(appBuffer));
+  wcscpy(appBuffer, exePath);
+  DWORD appBufferWrote;
+  result |= WriteFile(updateMetaFile, appBuffer, 
+                      MAX_PATH * sizeof(PRUnichar), 
+                      &appBufferWrote, NULL);
+
+  PRUnichar workingDirectory[MAX_PATH + 1];
+  ZeroMemory(workingDirectory, sizeof(appBuffer));
+  GetCurrentDirectoryW(sizeof(workingDirectory) / sizeof(workingDirectory[0]), 
+                       workingDirectory);
+  DWORD workingDirectoryWrote;
+  result |= WriteFile(updateMetaFile, workingDirectory, 
+                      MAX_PATH * sizeof(PRUnichar), 
+                      &workingDirectoryWrote, NULL);
+
+  DWORD commandLineLength = wcslen(commandLineBuffer) * sizeof(PRUnichar);
+  DWORD commandLineWrote;
+  result |= WriteFile(updateMetaFile, commandLineBuffer, 
+                      commandLineLength, 
+                      &commandLineWrote, NULL);
+  free(commandLineBuffer);
+  if (!result ||
+      sessionIDWrote != sizeof(DWORD) ||
+      commandIDWrote != sizeof(DWORD) ||
+      appBufferWrote != MAX_PATH * sizeof(PRUnichar) ||
+      workingDirectoryWrote != MAX_PATH * sizeof(PRUnichar) ||
+      commandLineWrote != commandLineLength) {
+    updateMetaFile.reset();
+    DeleteFileW(tempFilePath);
+    return FALSE;
+  }
+
+  // Note we construct the 'service work' meta object with a .tmp extension,
+  // When we want the service to start processing it we simply rename it to
+  // have a .mz extension.  This ensures that the service will never try to
+  // process a partial update work meta file. 
+  updateMetaFile.reset();
+  PRUnichar completedMetaFilePath[MAX_PATH + 1];
+  wcscpy(completedMetaFilePath, tempFilePath);
 
+  // Change the file extension of the temp file path from .tmp to .mz
+  LPWSTR extensionPart = 
+    &(completedMetaFilePath[wcslen(completedMetaFilePath) - 3]);
+  wcscpy(extensionPart, L"mz");
+  return MoveFileExW(tempFilePath, completedMetaFilePath, 
+                     MOVEFILE_REPLACE_EXISTING);
+}
+
+/**
+ * Sets update.status to pending so that the next startup will not use
+ * the service and instead will attempt an update the with a UAC prompt.
+ *
+ * @param  updateDirPath The path of the update directory
+ * @return TRUE if successful
+ */
 BOOL
-WinLaunchChild(const PRUnichar *exePath, int argc, char **argv)
+WriteStatusPending(LPCWSTR updateDirPath)
+{
+  PRUnichar updateStatusFilePath[MAX_PATH + 1];
+  wcscpy(updateStatusFilePath, updateDirPath);
+  if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
+    return FALSE;
+  }
+
+  const char pending[] = "pending";
+  nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, 
+                                      NULL, CREATE_ALWAYS, 0, NULL));
+  if (statusFile == INVALID_HANDLE_VALUE) {
+    return FALSE;
+  }
+
+  DWORD wrote;
+  BOOL ok = WriteFile(statusFile, pending, 
+                      sizeof(pending) - 1, &wrote, NULL); 
+  return ok && (wrote == sizeof(pending) - 1);
+}
+
+/**
+ * Sets update.status to a specific failure code
+ *
+ * @param  updateDirPath The path of the update directory
+ * @return TRUE if successful
+ */
+BOOL
+WriteStatusFailure(LPCWSTR updateDirPath, int errorCode) 
+{
+  PRUnichar updateStatusFilePath[MAX_PATH + 1];
+  wcscpy(updateStatusFilePath, updateDirPath);
+  if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
+    return FALSE;
+  }
+
+  nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, 
+                                      NULL, CREATE_ALWAYS, 0, NULL));
+  if (statusFile == INVALID_HANDLE_VALUE) {
+    return FALSE;
+  }
+  char failure[32];
+  sprintf(failure, "failed: %d", errorCode);
+
+  DWORD toWrite = strlen(failure);
+  DWORD wrote;
+  BOOL ok = WriteFile(statusFile, failure, 
+                      toWrite, &wrote, NULL); 
+  return ok && wrote == toWrite;
+}
+
+/**
+ * Launch a service initiated action with the specified arguments.
+ *
+ * @param  exePath The path of the executable to run
+ * @param  argc    The total number of arguments in argv
+ * @param  argv    An array of null terminated strings to pass to the exePath,
+ *                 argv[0] is ignored
+ * @return TRUE if successful
+ */
+BOOL
+WinLaunchServiceCommand(const PRUnichar *exePath, int argc, char **argv)
 {
   PRUnichar** argvConverted = new PRUnichar*[argc];
   if (!argvConverted)
     return FALSE;
 
   for (int i = 0; i < argc; ++i) {
     argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]);
     if (!argvConverted[i]) {
       FreeAllocStrings(i, argvConverted);
       return FALSE;
     }
   }
 
-  BOOL ok = WinLaunchChild(exePath, argc, argvConverted);
+  BOOL ok = WinLaunchServiceCommand(exePath, argc, argvConverted);
+  FreeAllocStrings(argc, argvConverted);
+  return ok;
+}
+
+
+
+/**
+ * Launch a child process with the specified arguments.
+ * @note argv[0] is ignored
+ * @note The form of this function that takes char **argv expects UTF-8
+ */
+
+BOOL
+WinLaunchChild(const PRUnichar *exePath, 
+               int argc, PRUnichar **argv, 
+               HANDLE userToken = NULL);
+
+BOOL
+WinLaunchChild(const PRUnichar *exePath, 
+               int argc, char **argv, 
+               HANDLE userToken)
+{
+  PRUnichar** argvConverted = new PRUnichar*[argc];
+  if (!argvConverted)
+    return FALSE;
+
+  for (int i = 0; i < argc; ++i) {
+    argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]);
+    if (!argvConverted[i]) {
+      FreeAllocStrings(i, argvConverted);
+      return FALSE;
+    }
+  }
+
+  BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken);
   FreeAllocStrings(argc, argvConverted);
   return ok;
 }
 
 BOOL
-WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv)
+WinLaunchChild(const PRUnichar *exePath, 
+               int argc, 
+               PRUnichar **argv, 
+               HANDLE userToken)
 {
   PRUnichar *cl;
   BOOL ok;
 
   cl = MakeCommandLine(argc, argv);
-  if (!cl)
+  if (!cl) {
     return FALSE;
+  }
 
-  STARTUPINFOW si = {sizeof(si), 0};
+  STARTUPINFOW si = {0};
+  si.cb = sizeof(STARTUPINFOW);
+  si.lpDesktop = L"winsta0\\Default";
   PROCESS_INFORMATION pi = {0};
 
-  ok = CreateProcessW(exePath,
-                      cl,
-                      NULL,  // no special security attributes
-                      NULL,  // no special thread attributes
-                      FALSE, // don't inherit filehandles
-                      0,     // No special process creation flags
-                      NULL,  // inherit my environment
-                      NULL,  // use my current directory
-                      &si,
-                      &pi);
+  if (userToken == NULL) {
+    ok = CreateProcessW(exePath,
+                        cl,
+                        NULL,  // no special security attributes
+                        NULL,  // no special thread attributes
+                        FALSE, // don't inherit filehandles
+                        0,     // No special process creation flags
+                        NULL,  // inherit my environment
+                        NULL,  // use my current directory
+                        &si,
+                        &pi);
+  } else {
+    // Create an environment block for the process we're about to start using
+    // the user's token.
+    LPVOID environmentBlock = NULL;
+    if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
+      environmentBlock = NULL;
+    }
+
+    ok = CreateProcessAsUserW(userToken, 
+                              exePath,
+                              cl,
+                              NULL,  // no special security attributes
+                              NULL,  // no special thread attributes
+                              FALSE, // don't inherit filehandles
+                              CREATE_DEFAULT_ERROR_MODE |
+#ifdef DEBUG
+                              CREATE_NEW_CONSOLE |
+#endif
+                              CREATE_UNICODE_ENVIRONMENT,                              
+                              environmentBlock,
+                              NULL,  // use my current directory
+                              &si,
+                              &pi);
+
+    if (environmentBlock) {
+      DestroyEnvironmentBlock(environmentBlock);
+    }
+  }
 
   if (ok) {
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
   } else {
     LPVOID lpMsgBuf = NULL;
     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-		  FORMAT_MESSAGE_FROM_SYSTEM |
-		  FORMAT_MESSAGE_IGNORE_INSERTS,
-		  NULL,
-		  GetLastError(),
-		  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		  (LPTSTR) &lpMsgBuf,
-		  0,
-		  NULL
-		  );
+                  FORMAT_MESSAGE_FROM_SYSTEM |
+                  FORMAT_MESSAGE_IGNORE_INSERTS,
+                  NULL,
+                  GetLastError(),
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (LPTSTR) &lpMsgBuf,
+                  0,
+                  NULL);
     wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
     if (lpMsgBuf)
       LocalFree(lpMsgBuf);
   }
 
   free(cl);
 
   return ok;
 }
+
--- a/xpcom/base/Makefile.in
+++ b/xpcom/base/Makefile.in
@@ -133,16 +133,22 @@ SDK_HEADERS     = \
 		nsAutoPtr.h \
 		nsError.h \
 		nsISupportsBase.h \
 		nscore.h \
 		nsAtomicRefcnt.h \
 		nsCycleCollector.h \
 		nsObjCExceptions.h \
 
+ifeq ($(OS_ARCH),WINNT)
+SDK_HEADERS += \
+		nsWindowsHelpers.h \
+		$(NULL)
+endif
+
 XPIDLSRCS	= \
 		nsIConsoleListener.idl \
 		nsIConsoleMessage.idl \
 		nsIConsoleService.idl \
 		nsICycleCollectorListener.idl \
 		nsIDebug2.idl \
 		nsIErrorService.idl \
 		nsIException.idl \
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsWindowsHelpers.h
@@ -0,0 +1,118 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is an RAII helper classes for Windows development.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian R. Bondy <netzen@gmail.com>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsWindowsHelpers_h
+#define nsWindowsHelpers_h
+
+#include "nsAutoRef.h"
+#include "nscore.h"
+
+template<>
+class nsAutoRefTraits<HKEY>
+{
+public:
+  typedef HKEY RawRef;
+  static HKEY Void() 
+  { 
+    return NULL; 
+  }
+
+  static void Release(RawRef aFD) 
+  { 
+    if (aFD != Void()) {
+      RegCloseKey(aFD);
+    }
+  }
+};
+
+template<>
+class nsAutoRefTraits<SC_HANDLE>
+{
+public:
+  typedef SC_HANDLE RawRef;
+  static SC_HANDLE Void() 
+  { 
+    return NULL; 
+  }
+
+  static void Release(RawRef aFD) 
+  { 
+    if (aFD != Void()) {
+      CloseServiceHandle(aFD);
+    }
+  }
+};
+
+template<>
+class nsSimpleRef<HANDLE>
+{
+protected:
+  typedef HANDLE RawRef;
+
+  nsSimpleRef() : mRawRef(NULL) 
+  {
+  }
+
+  nsSimpleRef(RawRef aRawRef) : mRawRef(aRawRef) 
+  {
+  }
+
+  bool HaveResource() const 
+  {
+    return mRawRef != NULL && mRawRef != INVALID_HANDLE_VALUE;
+  }
+
+public:
+  RawRef get() const 
+  {
+    return mRawRef;
+  }
+
+  static void Release(RawRef aRawRef) 
+  {
+    if (aRawRef != NULL && aRawRef != INVALID_HANDLE_VALUE) {
+      CloseHandle(aRawRef);
+    }
+  }
+  RawRef mRawRef;
+};
+
+typedef nsAutoRef<HKEY> nsAutoRegKey;
+typedef nsAutoRef<SC_HANDLE> nsAutoServiceHandle;
+typedef nsAutoRef<HANDLE> nsAutoHandle;
+
+#endif
--- a/xpcom/ds/nsIWindowsRegKey.idl
+++ b/xpcom/ds/nsIWindowsRegKey.idl
@@ -78,16 +78,19 @@ interface nsIWindowsRegKey : nsISupports
                                                   ACCESS_QUERY_VALUE |
                                                   ACCESS_ENUMERATE_SUB_KEYS |
                                                   ACCESS_NOTIFY;
   const unsigned long ACCESS_WRITE              = ACCESS_BASIC |
                                                   ACCESS_SET_VALUE |
                                                   ACCESS_CREATE_SUB_KEY;
   const unsigned long ACCESS_ALL                = ACCESS_READ |
                                                   ACCESS_WRITE;
+  const unsigned long WOW64_32                  = 0x00000200;
+  const unsigned long WOW64_64                  = 0x00000100;
+
 
   /**
    * Values for the type of a registry value.  The numeric values of these
    * constants are taken directly from WinNT.h in the MS Platform SDK.
    * The Microsoft documentation should be consulted for the exact meaning of
    * these value types.
    *
    * This interface is somewhat restricted to using only these value types.