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 83818 314cab6ecbe347e0e2149a8bc477646b85426735
parent 83817 34cbeb81ea8e86d6dc8dd3584bfd25e7d81cb83f
child 83819 1bd9f069576e63b5527ea9b4d99b7f3aca1bd5c4
push id4602
push usermak77@bonardo.net
push dateThu, 05 Jan 2012 10:31:59 +0000
treeherdermozilla-inbound@fcbdb7dc7fee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong
bugs481815
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 0000000000000000000000000000000000000000..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.