Bug 481815 - Provide a Windows service for silent updates. r=rstrong.

This commit is contained in:
Brian R. Bondy 2012-01-04 23:19:14 -05:00
Родитель 3434d6d244
Коммит 8011a2a8d9
67 изменённых файлов: 5645 добавлений и 333 удалений

Просмотреть файл

@ -71,6 +71,8 @@ ifdef MOZ_INSTALLER
# 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

Просмотреть файл

@ -200,6 +200,11 @@ pref("app.update.showInstalledUI", false);
// 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

Просмотреть файл

@ -188,9 +188,13 @@ appUpdater.prototype =
// 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.

Просмотреть файл

@ -531,6 +531,26 @@ var gAdvancedPane = {
// 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
},
/**

Просмотреть файл

@ -108,6 +108,12 @@
<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"/>
@ -336,6 +342,13 @@
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">

Просмотреть файл

@ -544,6 +544,13 @@ bin/libfreebl_32int64_3.so
@BINPATH@/updater@BIN_SUFFIX@
#endif
; [MaintenanceService]
;
#ifdef MOZ_MAINTENANCE_SERVICE
@BINPATH@/maintenanceservice.exe
@BINPATH@/maintenanceservice_installer.exe
#endif
; [Crash Reporter]
;
#ifdef MOZ_CRASHREPORTER

Просмотреть файл

@ -1452,3 +1452,7 @@ 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

Просмотреть файл

@ -61,6 +61,13 @@ INSTALLER_FILES = \
nsis/shared.nsh \
$(NULL)
ifdef MOZ_MAINTENANCE_SERVICE
INSTALLER_FILES += \
nsis/maintenanceservice_installer.nsi \
$(NULL)
endif
BRANDING_FILES = \
branding.nsi \
wizHeader.bmp \
@ -104,6 +111,17 @@ uninstaller::
--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)

Просмотреть файл

@ -17,6 +17,9 @@
!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
@ -43,6 +46,10 @@
!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}"

Просмотреть файл

@ -19,6 +19,7 @@
#
# 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
@ -35,11 +36,12 @@
# ***** 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
@ -59,6 +61,7 @@ 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
@ -111,6 +114,7 @@ VIAddVersionKey "OriginalFilename" "setup.exe"
!insertmacro InitHashAppModelId
!insertmacro IsHandlerForInstallDir
!insertmacro IsPinnedToTaskBar
!insertmacro IsUserAdmin
!insertmacro LogDesktopShortcut
!insertmacro LogQuickLaunchShortcut
!insertmacro LogStartMenuShortcut
@ -183,6 +187,11 @@ Page custom preOptions leaveOptions
!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
@ -373,6 +382,37 @@ Section "-Application" APP_IDX
${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}"
@ -426,7 +466,7 @@ Section "-Application" APP_IDX
${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
@ -482,6 +522,13 @@ Section "-Application" APP_IDX
${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.
@ -799,6 +846,58 @@ Function leaveShortcuts
${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
@ -934,6 +1033,7 @@ Function .onInit
!insertmacro InitInstallOptionsFile "options.ini"
!insertmacro InitInstallOptionsFile "shortcuts.ini"
!insertmacro InitInstallOptionsFile "components.ini"
!insertmacro InitInstallOptionsFile "summary.ini"
WriteINIStr "$PLUGINSDIR\options.ini" "Settings" NumFields "5"
@ -947,7 +1047,7 @@ Function .onInit
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"
@ -956,7 +1056,7 @@ Function .onInit
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"
@ -964,14 +1064,14 @@ Function .onInit
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"
@ -993,7 +1093,7 @@ Function .onInit
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"
@ -1002,7 +1102,7 @@ Function .onInit
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"
@ -1012,13 +1112,32 @@ Function .onInit
${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

Просмотреть файл

@ -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

Просмотреть файл

@ -19,6 +19,7 @@
#
# 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
@ -105,6 +106,46 @@
${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"
@ -437,38 +478,51 @@
!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"
@ -547,6 +601,35 @@
!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"
@ -1020,7 +1103,6 @@ Function SetAsDefaultAppUserHKCU
${EndUnless}
${EndIf}
${RemoveDeprecatedKeys}
${PinToTaskBar}
FunctionEnd

Просмотреть файл

@ -61,6 +61,7 @@ RequestExecutionLevel user
!define NO_LOG
Var TmpVal
Var MaintCertKey
; Other included files may depend upon these includes!
; The following includes are provided by NSIS.
@ -96,6 +97,7 @@ VIAddVersionKey "OriginalFilename" "helper.exe"
!insertmacro InitHashAppModelId
!insertmacro IsHandlerForInstallDir
!insertmacro IsPinnedToTaskBar
!insertmacro IsUserAdmin
!insertmacro LogDesktopShortcut
!insertmacro LogQuickLaunchShortcut
!insertmacro LogStartMenuShortcut
@ -380,6 +382,21 @@ Section "Uninstall"
; 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
################################################################################

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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>
{{{
}}}
###############################################################################

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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>

Просмотреть файл

@ -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

Двоичные данные
other-licenses/nsis/Plugins/ServicesHelper.dll Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -55,12 +55,21 @@ PARALLEL_DIRS = \
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

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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>

Просмотреть файл

@ -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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -21,6 +21,7 @@
# 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
@ -6680,6 +6681,40 @@
!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

Просмотреть файл

@ -63,6 +63,7 @@ CUSTOM_NSIS_PLUGINS = \
InvokeShellVerb.dll \
ShellLink.dll \
UAC.dll \
ServicesHelper.dll \
$(NULL)
$(CONFIG_DIR)/setup.exe::
@ -99,3 +100,10 @@ uninstaller::
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

Просмотреть файл

@ -49,5 +49,6 @@ export NO_SHUNT = 1
USE_STATIC_LIBS = 1
CPPSRCS = readstrings.cpp
EXPORTS = readstrings.h
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -52,6 +52,7 @@ ifdef MOZ_UPDATER
DIRS = ../readstrings
ifneq ($(OS_TARGET),Android)
DIRS += common
DIRS += updater
endif

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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

Просмотреть файл

@ -68,6 +68,7 @@ const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properti
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";
@ -443,6 +444,7 @@ var gUpdates = {
// the Update.
switch (state) {
case STATE_PENDING:
case STATE_PENDING_SVC:
this.sourceEvent = SRCEVT_BACKGROUND;
aCallback("finishedBackground");
return;
@ -1680,6 +1682,7 @@ var gErrorPatchingPage = {
onWizardNext: function() {
switch (gUpdates.update.selectedPatch.state) {
case STATE_PENDING:
case STATE_PENDING_SVC:
gUpdates.wiz.goTo("finished");
break;
case STATE_DOWNLOADING:

Просмотреть файл

@ -245,12 +245,13 @@ interface nsIUpdate : nsISupports
/**
* 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;

Просмотреть файл

@ -27,6 +27,7 @@
# 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
@ -81,6 +82,9 @@ 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";
@ -129,6 +133,7 @@ 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";
@ -138,6 +143,10 @@ const STATE_FAILED = "failed";
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;
@ -149,6 +158,10 @@ 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() {
@ -461,7 +474,7 @@ function LOG(string) {
# @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) {
@ -530,7 +543,7 @@ function getUpdateFile(pathArray) {
* @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;
@ -550,7 +563,7 @@ function getStatusTextFromCode(code, defaultCode) {
/**
* 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
@ -563,7 +576,7 @@ function getUpdatesDir() {
* 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();
@ -589,6 +602,22 @@ function writeStatusFile(dir, state) {
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
@ -1399,7 +1428,34 @@ UpdateService.prototype = {
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;
}
@ -1522,7 +1578,7 @@ UpdateService.prototype = {
* 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)
@ -2011,7 +2067,7 @@ UpdateManager.prototype = {
* 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()) {
@ -2195,7 +2251,7 @@ UpdateManager.prototype = {
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);
}
}
@ -2530,7 +2586,8 @@ Downloader.prototype = {
* 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;
},
/**
@ -2582,7 +2639,7 @@ Downloader.prototype = {
* 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
@ -2592,7 +2649,7 @@ Downloader.prototype = {
* 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) {
@ -2620,6 +2677,7 @@ Downloader.prototype = {
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;
@ -2847,7 +2905,7 @@ Downloader.prototype = {
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

Просмотреть файл

@ -78,6 +78,7 @@ INI_TEST_FILES = \
LOCAL_INCLUDES += \
-I$(srcdir) \
-I$(topsrcdir)/toolkit/mozapps/update \
-I$(topsrcdir)/toolkit/mozapps/update/common \
$(NULL)
MOZ_WINCONSOLE = 1

Просмотреть файл

@ -29,6 +29,7 @@ const SHA512_HASH_SIMPLE_MAR = "55d3e2a86acaeb0abb7a444c13bba748846fcbac7ff05" +
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";

Просмотреть файл

@ -55,9 +55,11 @@ PROGRAM = updater$(BIN_SUFFIX)
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) \

Просмотреть файл

@ -39,6 +39,8 @@
#ifndef PROGRESSUI_H__
#define PROGRESSUI_H__
#include "updatedefines.h"
#if defined(XP_WIN)
typedef WCHAR NS_tchar;
#define NS_main wmain

Просмотреть файл

@ -69,92 +69,6 @@
*
* '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"
@ -172,6 +86,8 @@
#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
@ -196,20 +112,6 @@ void LaunchMacPostProcess(const char* aAppExe);
# 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)
@ -217,6 +119,8 @@ void LaunchMacPostProcess(const char* aAppExe);
#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.
@ -401,47 +305,6 @@ 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)
{
@ -1519,96 +1382,7 @@ PatchIfFile::Finish(int 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
@ -1620,15 +1394,35 @@ LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv)
// 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
@ -1872,7 +1666,7 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
LogInit();
LogInit(gSourcePath, NS_T("update.log"));
LOG(("SOURCE DIRECTORY " LOG_S "\n", argv[1]));
LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[2]));
@ -2035,7 +1829,20 @@ int NS_main(int argc, NS_tchar **argv)
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 */

Просмотреть файл

@ -473,6 +473,7 @@ MAKEFILES_xulapp="
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

Просмотреть файл

@ -137,7 +137,12 @@ 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"

Просмотреть файл

@ -24,6 +24,7 @@
* 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
@ -190,7 +191,7 @@ GetStatusFile(nsIFile *dir, nsCOMPtr<nsILocalFile> &result)
}
static bool
IsPending(nsILocalFile *statusFile)
IsPending(nsILocalFile *statusFile, bool &isPendingService)
{
PRFileDesc *fd = nsnull;
nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
@ -205,14 +206,23 @@ IsPending(nsILocalFile *statusFile)
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;
@ -334,7 +344,7 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
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;
@ -480,8 +490,42 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
#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);
@ -516,7 +560,9 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
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
@ -527,7 +573,8 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
IsOlderVersion(versionFile, appVersion))) {
updatesDir->Remove(true);
} else {
ApplyUpdate(greDir, updatesDir, statusFile, appDir, argc, argv);
ApplyUpdate(greDir, updatesDir, statusFile, appDir,
argc, argv, isPendingService);
}
}

Просмотреть файл

@ -21,6 +21,7 @@
*
* 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
@ -46,8 +47,19 @@
#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
@ -162,7 +174,7 @@ static PRUnichar* ArgToString(PRUnichar *d, const PRUnichar *s)
*
* argv is UTF8
*/
static PRUnichar*
PRUnichar*
MakeCommandLine(int argc, PRUnichar **argv)
{
int i;
@ -225,16 +237,291 @@ FreeAllocStrings(int argc, PRUnichar **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
WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv);
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
WinLaunchChild(const PRUnichar *exePath, int argc, char **argv)
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;
}
// 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
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)
@ -248,34 +535,104 @@ WinLaunchChild(const PRUnichar *exePath, int argc, char **argv)
}
}
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);
@ -283,15 +640,14 @@ WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv)
} 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);
@ -301,3 +657,4 @@ WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv)
return ok;
}

Просмотреть файл

@ -138,6 +138,12 @@ SDK_HEADERS = \
nsCycleCollector.h \
nsObjCExceptions.h \
ifeq ($(OS_ARCH),WINNT)
SDK_HEADERS += \
nsWindowsHelpers.h \
$(NULL)
endif
XPIDLSRCS = \
nsIConsoleListener.idl \
nsIConsoleMessage.idl \

Просмотреть файл

@ -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

Просмотреть файл

@ -83,6 +83,9 @@ interface nsIWindowsRegKey : nsISupports
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