Merge mozilla-central and mozilla-inbound

This commit is contained in:
Marco Bonardo 2012-01-05 11:31:12 +01:00
Родитель 813de7c62d 87e8274cc8
Коммит ce3b90daa1
109 изменённых файлов: 11229 добавлений и 812 удалений

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

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

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

@ -13,6 +13,7 @@ tabbrowser {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
}
#tabbrowser-tabs:not([overflow="true"]) ~ #alltabs-button,
#tabbrowser-tabs:not([overflow="true"]) + #new-tab-button,
#tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
#TabsToolbar[currentset]:not([currentset*="tabbrowser-tabs,new-tab-button"]) > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
@ -20,6 +21,10 @@ tabbrowser {
visibility: collapse;
}
#tabbrowser-tabs:not([overflow="true"])[using-closing-tabs-spacer] ~ #alltabs-button {
visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
}
.tabbrowser-tab {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
}

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

@ -2927,7 +2927,6 @@
<field name="_tabDefaultMaxWidth">NaN</field>
<field name="_lastTabClosedByMouse">false</field>
<field name="_hasTabTempMaxWidth">false</field>
<field name="_usingClosingTabsSpacer">false</field>
<!-- Try to keep the active tab's close button under the mouse cursor -->
<method name="_lockTabSizing">
@ -2995,7 +2994,7 @@
<body><![CDATA[
let spacer = this._closingTabsSpacer;
spacer.style.width = parseFloat(spacer.style.width) + pixels + "px";
this._usingClosingTabsSpacer = true;
this.setAttribute("using-closing-tabs-spacer", "true");
this.tabbrowser.addEventListener("mousemove", this, false);
window.addEventListener("mouseout", this, false);
]]></body>
@ -3005,14 +3004,16 @@
<body><![CDATA[
this.tabbrowser.removeEventListener("mousemove", this, false);
window.removeEventListener("mouseout", this, false);
if (this._hasTabTempMaxWidth) {
this._hasTabTempMaxWidth = false;
let tabs = this.tabbrowser.visibleTabs;
for (let i = 0; i < tabs.length; i++)
tabs[i].style.maxWidth = "";
}
if (this._usingClosingTabsSpacer) {
this._usingClosingTabsSpacer = false;
if (this.hasAttribute("using-closing-tabs-spacer")) {
this.removeAttribute("using-closing-tabs-spacer");
this._closingTabsSpacer.style.width = 0;
}
]]></body>

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

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

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

@ -41,6 +41,12 @@ MOZ_APP_VENDOR=Mozilla
MOZ_UPDATER=1
MOZ_PHOENIX=1
if test "$OS_ARCH" = "WINNT"; then
if ! test "$HAVE_64BIT_OS"; then
MOZ_MAINTENANCE_SERVICE=1
fi
fi
MOZ_CHROME_FILE_FORMAT=omni
MOZ_SAFE_BROWSING=1
MOZ_SERVICES_SYNC=1

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

@ -332,21 +332,7 @@ var Scratchpad = {
"Scratchpad", 1);
}
catch (ex) {
this.openWebConsole();
let contentWindow = this.gBrowser.selectedBrowser.contentWindow;
let scriptError = Cc["@mozilla.org/scripterror;1"].
createInstance(Ci.nsIScriptError);
scriptError.initWithWindowID(ex.message + "\n" + ex.stack, ex.fileName,
"", ex.lineNumber, 0, scriptError.errorFlag,
"content javascript",
this.getInnerWindowId(contentWindow));
Services.console.logMessage(scriptError);
error = true;
error = ex;
}
return [error, result];
@ -368,11 +354,7 @@ var Scratchpad = {
"Scratchpad", 1);
}
catch (ex) {
Cu.reportError(ex);
Cu.reportError(ex.stack);
this.openErrorConsole();
error = true;
error = ex;
}
return [error, result];
@ -397,12 +379,30 @@ var Scratchpad = {
/**
* Execute the selected text (if any) or the entire editor content in the
* current context.
* @return mixed
* The script evaluation result.
*/
run: function SP_run()
execute: function SP_execute()
{
let selection = this.selectedText || this.getText();
let [error, result] = this.evalForContext(selection);
this.deselect();
return [selection, error, result];
},
/**
* Execute the selected text (if any) or the entire editor content in the
* current context.
*/
run: function SP_run()
{
let [selection, error, result] = this.execute();
if (!error) {
this.deselect();
} else {
this.writeAsErrorComment(error);
}
return [selection, error, result];
},
@ -413,10 +413,13 @@ var Scratchpad = {
*/
inspect: function SP_inspect()
{
let [selection, error, result] = this.run();
let [selection, error, result] = this.execute();
if (!error) {
this.deselect();
this.openPropertyPanel(selection, result);
} else {
this.writeAsErrorComment(error);
}
},
@ -427,25 +430,48 @@ var Scratchpad = {
* selected text.
*/
display: function SP_display()
{
let [selectedText, error, result] = this.execute();
if (!error) {
this.writeAsComment(result);
} else {
this.writeAsErrorComment(error);
}
},
/**
* Write out a value at the current insertion point as a block comment
* @param object aValue
* The Object to write out as a string
*/
writeAsComment: function SP_writeAsComment(aValue)
{
let selection = this.getSelectionRange();
let insertionPoint = selection.start != selection.end ?
selection.end : // after selected text
this.editor.getCharCount(); // after text end
let [selectedText, error, result] = this.run();
if (error) {
return;
}
let newComment = "/*\n" + result + "\n*/";
let newComment = "/*\n" + aValue + "\n*/";
this.setText(newComment, insertionPoint, insertionPoint);
// Select the new comment.
this.selectRange(insertionPoint, insertionPoint + newComment.length);
},
/**
* Write out an error at the current insertion point as a block comment
* @param object aValue
* The Error object to write out the message and stack trace
*/
writeAsErrorComment: function SP_writeAsErrorComment(aError)
{
let newComment = "Exception: " + aError.message + "\n" + aError.stack.substring(0, aError.stack.length - 1);
this.writeAsComment(newComment);
},
/**
* Open the Property Panel to inspect the given object.
*

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

@ -61,6 +61,7 @@ _BROWSER_TEST_FILES = \
head.js \
browser_scratchpad_bug_653427_confirm_close.js \
browser_scratchpad_bug684546_reset_undo.js \
browser_scratchpad_bug690552_display_outputs_errors.js \
libs:: $(_BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

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

@ -0,0 +1,53 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function browserLoad() {
gBrowser.selectedBrowser.removeEventListener("load", browserLoad, true);
openScratchpad(runTests, {"state":{"text":""}});
}, true);
content.location = "data:text/html,<p>test that exceptions our output as " +
"comments for 'display' and not sent to the console in Scratchpad";
}
function runTests()
{
scratchpad = gScratchpadWindow.Scratchpad;
var message = "\"Hello World!\""
var openComment = "/*\n";
var closeComment = "\n*/";
var error = "throw new Error(\"Ouch!\")";
let messageArray = {};
let count = {};
scratchpad.setText(message);
scratchpad.display();
is(scratchpad.getText(),
message + openComment + "Hello World!" + closeComment,
"message display output");
scratchpad.setText(error);
scratchpad.display();
is(scratchpad.getText(),
error + openComment + "Exception: Ouch!\n@Scratchpad:1" + closeComment,
"error display output");
scratchpad.setText(message);
scratchpad.run();
is(scratchpad.getText(), message, "message run output");
scratchpad.setText(error);
scratchpad.run();
is(scratchpad.getText(),
error + openComment + "Exception: Ouch!\n@Scratchpad:1" + closeComment,
"error display output");
finish();
}

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

@ -1361,6 +1361,11 @@ function HUD_SERVICE()
HUD_SERVICE.prototype =
{
/**
* Last value entered
*/
lastInputValue: "",
/**
* L10N shortcut function
*

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

@ -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,269 @@
# ***** 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 *****
; 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 it will be stopped when upgrading it
; via the maintenanceservice_tmp.exe command executed below.
; The maintenanceservice_tmp.exe command will rename the file to
; maintenanceservice.exe if maintenanceservice_tmp.exe is newer.
; If the service does not exist yet, we install it and drop the file on
; disk as maintenanceservice.exe directly.
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
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
################################################################################

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

@ -136,6 +136,7 @@ ACCESSIBILITY = @ACCESSIBILITY@
MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
XPCOM_USE_LEA = @XPCOM_USE_LEA@
MOZ_INSTALLER = @MOZ_INSTALLER@
MOZ_MAINTENANCE_SERVICE = @MOZ_MAINTENANCE_SERVICE@
MOZ_UPDATER = @MOZ_UPDATER@
MOZ_UPDATE_CHANNEL = @MOZ_UPDATE_CHANNEL@
MOZ_UPDATE_PACKAGING = @MOZ_UPDATE_PACKAGING@

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

@ -6333,6 +6333,23 @@ AC_CHECK_PROGS(WGET, wget, "")
AC_MSG_RESULT([$WGET])
AC_SUBST(WGET)
dnl ========================================================
dnl Maintenance Service
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(maintenance-service,
[ --enable-maintenance-service Enable building of maintenanceservice],
MOZ_MAINTENANCE_SERVICE=1,
MOZ_MAINTENANCE_SERVICE= )
if test -n "$MOZ_MAINTENANCE_SERVICE"; then
if test "$OS_ARCH" = "WINNT"; then
AC_DEFINE(MOZ_MAINTENANCE_SERVICE)
else
AC_MSG_ERROR([Can only build with --enable-maintenance-service with a Windows target])
fi
fi
dnl ========================================================
dnl Updater
dnl ========================================================
@ -8424,6 +8441,7 @@ AC_SUBST(MOZ_SPELLCHECK)
AC_SUBST(MOZ_JAVA_COMPOSITOR)
AC_SUBST(MOZ_USER_DIR)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_MAINTENANCE_SERVICE)
AC_SUBST(MOZ_UPDATER)
AC_SUBST(MOZ_ANGLE)
AC_SUBST(MOZ_DIRECTX_SDK_PATH)

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

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

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

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

@ -38,6 +38,7 @@ skip-if = os == "android"
[include:toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini]
[include:toolkit/mozapps/extensions/test/xpcshell-unpack/xpcshell.ini]
[include:toolkit/mozapps/update/test_timermanager/unit/xpcshell.ini]
[include:toolkit/mozapps/update/test_svc/unit/xpcshell.ini]
[include:toolkit/mozapps/update/test/unit/xpcshell.ini]
[include:security/manager/ssl/tests/unit/xpcshell.ini]
[include:testing/xpcshell/example/unit/xpcshell.ini]

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

@ -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,98 @@
# ***** 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 \
$(NULL)
# For debugging purposes only
#DEFINES += -DDISABLE_UPDATER_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,415 @@
/* ***** 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 <shlobj.h>
#include "serviceinstall.h"
#include "maintenanceservice.h"
#include "servicebase.h"
#include "workmonitor.h"
#include "uachelper.h"
#include "updatehelper.h"
SERVICE_STATUS gSvcStatus = { 0 };
SERVICE_STATUS_HANDLE gSvcStatusHandle = NULL;
HANDLE gWorkDoneEvent = NULL;
HANDLE gThread = NULL;
bool gServiceControlStopping = 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_FUNCTIONW) SvcMain },
{ NULL, NULL }
};
// This call returns when the service has stopped.
// The process should simply terminate when the call returns.
if (!StartServiceCtrlDispatcherW(DispatchTable)) {
LOG(("StartServiceCtrlDispatcher failed (%d)\n", GetLastError()));
}
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 (!MoveFileExW(oldPath, newPath, MOVEFILE_REPLACE_EXISTING)) {
continue;
}
}
}
/**
* Ensures the service is shutdown once all work is complete.
* There is an issue on XP SP2 and below where the service can hang
* in a stop pending state even though the SCM is notified of a stopped
* state. Control *should* be returned to StartServiceCtrlDispatcher from the
* call to SetServiceStatus on a stopped state in the wmain thread.
* Sometimes this is not the case though. This thread will terminate the process
* if it has been 5 seconds after all work is done and the process is still not
* terminated. This thread is only started once a stopped state was sent to the
* SCM. The stop pending hang can be reproduced intermittently even if you set
* a stopped state dirctly and never set a stop pending state. It is safe to
* forcefully terminate the process ourselves since all work is done once we
* start this thread.
*/
DWORD WINAPI
EnsureProcessTerminatedThread(LPVOID)
{
Sleep(5000);
exit(0);
return 0;
}
void
StartTerminationThread()
{
// If the process does not self terminate like it should, this thread
// will terminate the process after 5 seconds.
HANDLE thread = CreateThread(NULL, 0, EnsureProcessTerminatedThread,
NULL, 0, NULL);
if (thread) {
CloseHandle(thread);
}
}
/**
* Main entry point when running as a service.
*/
void WINAPI
SvcMain(DWORD argc, LPWSTR *argv)
{
// 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");
}
// Disable every privilege we don't need. Processes started using
// CreateProcess will use the same token as this process.
UACHelper::DisablePrivileges(NULL);
// Register the handler function for the service
gSvcStatusHandle = RegisterServiceCtrlHandlerW(SVC_NAME, SvcCtrlHandler);
if (!gSvcStatusHandle) {
LOG(("RegisterServiceCtrlHandler failed (%d)\n", GetLastError()));
ExecuteServiceCommand(argc, argv);
LogFinish();
exit(1);
}
// These values will be re-used later in calls involving gSvcStatus
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gSvcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
// This event will be used to tell the SvcCtrlHandler when the work is
// done for when a stop comamnd is manually issued.
gWorkDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!gWorkDoneEvent) {
ReportSvcStatus(SERVICE_STOPPED, 1, 0);
StartTerminationThread();
return;
}
// Initialization complete and we're about to start working on
// the actual command. Report the service state as running to the SCM.
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
// The service command was executed, stop logging and set an event
// to indicate the work is done in case someone is waiting on a
// service stop operation.
ExecuteServiceCommand(argc, argv);
LogFinish();
SetEvent(gWorkDoneEvent);
// If we aren't already in a stopping state then tell the SCM we're stopped
// now. If we are already in a stopping state then the SERVICE_STOPPED state
// will be set by the SvcCtrlHandler.
if (!gServiceControlStopping) {
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
StartTerminationThread();
}
}
/**
* 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;
gSvcStatus.dwCurrentState = currentState;
gSvcStatus.dwWin32ExitCode = exitCode;
gSvcStatus.dwWaitHint = waitHint;
if (SERVICE_START_PENDING == currentState ||
SERVICE_STOP_PENDING == currentState) {
gSvcStatus.dwControlsAccepted = 0;
} else {
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
}
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);
}
/**
* Since the SvcCtrlHandler should only spend at most 30 seconds before
* returning, this function does the service stop work for the SvcCtrlHandler.
*/
DWORD WINAPI
StopServiceAndWaitForCommandThread(LPVOID)
{
do {
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000);
} while(WaitForSingleObject(gWorkDoneEvent, 100) == WAIT_TIMEOUT);
CloseHandle(gWorkDoneEvent);
gWorkDoneEvent = NULL;
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
StartTerminationThread();
return 0;
}
/**
* Called by SCM whenever a control code is sent to the service
* using the ControlService function.
*/
void WINAPI
SvcCtrlHandler(DWORD dwCtrl)
{
// After a SERVICE_CONTROL_STOP there should be no more commands sent to
// the SvcCtrlHandler.
if (gServiceControlStopping) {
return;
}
// Handle the requested control code.
switch(dwCtrl) {
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP: {
gServiceControlStopping = true;
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 1000);
// The SvcCtrlHandler thread should not spend more than 30 seconds in
// shutdown so we spawn a new thread for stopping the service
HANDLE thread = CreateThread(NULL, 0, StopServiceAndWaitForCommandThread,
NULL, 0, NULL);
if (thread) {
CloseHandle(thread);
} else {
// Couldn't start the thread so just call the stop ourselves.
// If it happens to take longer than 30 seconds the caller will
// get an error.
StopServiceAndWaitForCommandThread(NULL);
}
}
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,167 @@
/* ***** 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;
LONG 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,
TEST_ONLY_FALLBACK_KEY_PATH, 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,119 @@
/* ***** 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"
#include "nsWindowsHelpers.h"
// Shared code between applications and updater.exe
#include "nsWindowsRestart.cpp"
/**
* Verifies if 2 files are byte for byte equivalent.
*
* @param file1Path The first file to verify.
* @param file2Path The second file to verify.
* @param sameContent Out parameter, TRUE if the files are equal
* @return TRUE If there was no error checking the files.
*/
BOOL
VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent)
{
sameContent = FALSE;
nsAutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL));
if (!file1) {
return FALSE;
}
nsAutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL));
if (!file2) {
return FALSE;
}
DWORD fileSize1 = GetFileSize(file1, NULL);
DWORD fileSize2 = GetFileSize(file2, NULL);
if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2) {
return FALSE;
}
if (fileSize1 != fileSize2) {
// sameContent is already set to FALSE
return TRUE;
}
char buf1[COMPARE_BLOCKSIZE];
char buf2[COMPARE_BLOCKSIZE];
DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE;
DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE;
DWORD readAmount;
for (DWORD i = 0; i < numBlocks; i++) {
if (!ReadFile(file1, buf1, COMPARE_BLOCKSIZE, &readAmount, NULL) ||
readAmount != COMPARE_BLOCKSIZE) {
return FALSE;
}
if (!ReadFile(file2, buf2, COMPARE_BLOCKSIZE, &readAmount, NULL) ||
readAmount != COMPARE_BLOCKSIZE) {
return FALSE;
}
if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE)) {
// sameContent is already set to FALSE
return TRUE;
}
}
if (leftOver) {
if (!ReadFile(file1, buf1, leftOver, &readAmount, NULL) ||
readAmount != leftOver) {
return FALSE;
}
if (!ReadFile(file2, buf2, leftOver, &readAmount, NULL) ||
readAmount != leftOver) {
return FALSE;
}
if (memcmp(buf1, buf2, leftOver)) {
// sameContent is already set to FALSE
return TRUE;
}
}
sameContent = TRUE;
return TRUE;
}

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

@ -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 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);
BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent);
// 32KiB for comparing files at a time seems reasonable.
// The bigger the better for speed, but this will be used
// on the stack so I don't want it to be too big.
#define COMPARE_BLOCKSIZE 32768
// The following string resource value is used to uniquely identify the signed
// Mozilla application as an updater. Before the maintenance service will
// execute the updater it must have this updater identity string in its string
// table. No other signed Mozilla product will have this string table value.
#define UPDATER_IDENTITY_STRING \
"moz-updater.exe-4cdccec4-5ee0-4a06-9817-4cd899a9db49"
#define IDS_UPDATER_IDENTITY 1006

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

@ -0,0 +1,439 @@
/* ***** 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 "updatehelper.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 SVC_NAME 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
BOOL serviceAlreadyExists = FALSE;
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) {
serviceAlreadyExists = TRUE;
// 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 (!StopService()) {
return FALSE;
}
if (!DeleteFileW(serviceConfig.lpBinaryPathName)) {
LOG(("Could not delete old service binary file %ls. (%d)\n",
serviceConfig.lpBinaryPathName, GetLastError()));
return FALSE;
}
if (!CopyFileW(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.
MoveFileExW(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.
MoveFileExW(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;
}
if (!serviceAlreadyExists) {
// 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;
}
LOG(("Sending stop request...\n"));
SERVICE_STATUS status;
if (!ControlService(schService, SERVICE_CONTROL_STOP, &status)) {
LOG(("Error sending stop request: %d\n", GetLastError()));
}
schSCManager.reset();
schService.reset();
LOG(("Waiting for service stop...\n"));
DWORD lastState = WaitForServiceStop(SVC_NAME, 30);
// The service can be in a stopped state but the exe still in use
// so make sure the process is really gone before proceeding
WaitForProcessExit(L"maintenanceservice.exe", 30);
LOG(("Done waiting for service stop, last service state: %d\n", lastState));
return lastState == SERVICE_STOPPED;
}
/**
* 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,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 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_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,458 @@
/* ***** 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")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "rpcrt4.lib")
#include "nsWindowsHelpers.h"
#include "nsAutoPtr.h"
#include "workmonitor.h"
#include "serviceinstall.h"
#include "servicebase.h"
#include "registrycertificates.h"
#include "uachelper.h"
#include "updatehelper.h"
// 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 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_UPDATER_COMPARE_ERROR = 16003;
const int SERVICE_UPDATER_IDENTITY_ERROR = 16004;
const int SERVICE_STILL_APPLYING_ON_SUCCESS = 16005;
const int SERVICE_STILL_APPLYING_ON_FAILURE = 16006;
/*
* Read the update.status file and sets isApplying to true if
* the status is set to applying
*
* @param updateDirPath The directory where update.status is stored
* @param isApplying Out parameter for specifying if the status
* is set to applying or not.
* @return TRUE if the information was filled.
*/
static BOOL
IsStatusApplying(LPCWSTR updateDirPath, BOOL &isApplying)
{
isApplying = FALSE;
WCHAR updateStatusFilePath[MAX_PATH + 1];
wcscpy(updateStatusFilePath, updateDirPath);
if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
return FALSE;
}
nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, NULL));
char buf[32] = { 0 };
DWORD read;
if (!ReadFile(statusFile, buf, sizeof(buf), &read, NULL)) {
return FALSE;
}
const char kApplying[] = "applying";
isApplying = strncmp(buf, kApplying,
sizeof(kApplying) - 1) == 0;
return TRUE;
}
/**
* Runs an update process as the service using the SYSTEM account.
*
* @param argc The number of arguments in argv
* @param argv The arguments normally passed to updater.exe
* argv[0] must be the path to updater.exe
* @param processStarted Set to TRUE if the process was started.
* @return TRUE if the update process was run had a return code of 0.
*/
BOOL
StartUpdateProcess(int argc,
LPWSTR *argv,
BOOL &processStarted)
{
LOG(("Starting update process as the service in session 0.\n"));
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = L"winsta0\\Default";
PROCESS_INFORMATION pi = {0};
// The updater command line is of the form:
// updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
LPWSTR cmdLine = MakeCommandLine(argc, argv);
// 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 (argc >= 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, argv[0], L"updater.ini") &&
PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) {
selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp,
MOVEFILE_REPLACE_EXISTING);
}
// Add an env var for MOZ_USING_SERVICE so the updater.exe can
// do anything special that it needs to do for service updates.
// Search in updater.cpp for more info on MOZ_USING_SERVICE.
putenv(const_cast<char*>("MOZ_USING_SERVICE=1"));
LOG(("Starting service with cmdline: %ls\n", cmdLine));
processStarted = CreateProcessW(argv[0], cmdLine,
NULL, NULL, FALSE,
CREATE_DEFAULT_ERROR_MODE,
NULL,
NULL, &si, &pi);
// Empty value on putenv is how you remove an env variable in Windows
putenv(const_cast<char*>("MOZ_USING_SERVICE="));
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);
// Check just in case updater.exe didn't change the status from
// applying. If this is the case we report an error.
BOOL isApplying = FALSE;
if (IsStatusApplying(argv[1], isApplying) && isApplying) {
if (updateWasSuccessful) {
LOG(("update.status is still applying even know update "
" was successful.\n"));
if (!WriteStatusFailure(argv[1],
SERVICE_STILL_APPLYING_ON_SUCCESS)) {
LOG(("Could not write update.status still applying on"
" success error.\n"));
}
// Since we still had applying we know updater.exe didn't do its
// job correctly.
updateWasSuccessful = FALSE;
} else {
LOG(("update.status is still applying and update was not successful.\n"));
if (!WriteStatusFailure(argv[1],
SERVICE_STILL_APPLYING_ON_FAILURE)) {
LOG(("Could not write update.status still applying on"
" success error.\n"));
}
}
}
} else {
DWORD lastError = GetLastError();
LOG(("Could not create process as current user, "
"updaterPath: %ls; cmdLine: %l. (%d)\n",
argv[0], 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) {
MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
// Only run the PostUpdate if the update was successful
if (updateWasSuccessful && argc > 2) {
LPCWSTR installationDir = argv[2];
LPCWSTR updateInfoDir = argv[1];
// Launch the PostProcess with admin access in session 0. This is
// actually launching the post update process but it takes in the
// callback app path to figure out where to apply to.
// The PostUpdate process with user only access will be done inside
// the unelevated updater.exe after the update process is complete
// from the service. We don't know here which session to start
// the user PostUpdate process from.
LOG(("Launching post update process as the service in session 0.\n"));
if (!LaunchWinPostProcess(installationDir, updateInfoDir, true, NULL)) {
LOG(("The post update process could not be launched.\n"));
}
}
}
free(cmdLine);
return updateWasSuccessful;
}
/**
* Processes a software update command
*
* @param argc The number of arguments in argv
* @param argv The arguments normally passed to updater.exe
* argv[0] must be the path to updater.exe
* @return TRUE if the update was successful.
*/
BOOL
ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
{
BOOL result = TRUE;
if (argc < 3) {
LOG(("Not enough command line parameters specified. "
"Updating update.status.\n"));
// We can only update update.status if argv[1] exists. argv[1] is
// the directory where the update.status file exists.
if (argc > 1 ||
!WriteStatusFailure(argv[1],
SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) {
LOG(("Could not write update.status service update failure."
"Last error: %d\n", GetLastError()));
}
return FALSE;
}
// Verify that the updater.exe that we are executing is the same
// as the one in the installation directory which we are updating.
// The installation dir that we are installing to is argv[2].
WCHAR installDirUpdater[MAX_PATH + 1];
wcsncpy(installDirUpdater, argv[2], MAX_PATH);
if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
LOG(("Install directory updater could not be determined.\n"));
result = FALSE;
}
BOOL updaterIsCorrect;
if (result && !VerifySameFiles(argv[0], installDirUpdater,
updaterIsCorrect)) {
LOG(("Error checking if the updaters are the same.\n"
"Path 1: %ls\nPath 2: %ls\n", argv[0], installDirUpdater));
result = FALSE;
}
if (result && !updaterIsCorrect) {
LOG(("The updaters do not match, udpater will not run.\n"));
result = FALSE;
}
if (result) {
LOG(("updater.exe was compared successfully to the installation directory"
" updater.exe.\n"));
} else {
if (!WriteStatusFailure(argv[1],
SERVICE_UPDATER_COMPARE_ERROR)) {
LOG(("Could not write update.status updater compare failure.\n"));
}
return FALSE;
}
// Check to make sure the udpater.exe module has the unique updater identity.
// This is a security measure to make sure that the signed executable that
// we will run is actually an updater.
HMODULE updaterModule = LoadLibrary(argv[0]);
if (!updaterModule) {
LOG(("updater.exe module could not be loaded. (%d)\n", GetLastError()));
result = FALSE;
} else {
char updaterIdentity[64];
if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY,
updaterIdentity, sizeof(updaterIdentity))) {
LOG(("The updater.exe application does not contain the Mozilla"
" updater identity.\n"));
result = FALSE;
}
if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) {
LOG(("The updater.exe identity string is not valid.\n"));
result = FALSE;
}
FreeLibrary(updaterModule);
}
if (result) {
LOG(("The updater.exe application contains the Mozilla"
" updater identity.\n"));
} else {
if (!WriteStatusFailure(argv[1],
SERVICE_UPDATER_IDENTITY_ERROR)) {
LOG(("Could not write update.status no updater identity.\n"));
}
return TRUE;
}
// Check for updater.exe sign problems
BOOL updaterSignProblem = FALSE;
#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
updaterSignProblem = !DoesBinaryMatchAllowedCertificates(argv[2],
argv[0]);
#endif
// Only proceed with the update if we have no signing problems
if (!updaterSignProblem) {
BOOL updateProcessWasStarted = FALSE;
if (StartUpdateProcess(argc, argv,
updateProcessWasStarted)) {
LOG(("updater.exe was launched and run successfully!\n"));
LogFlush();
// We might not execute code after StartServiceUpdate because
// the service installer will stop the service if it is running.
StartServiceUpdate(argc, argv);
} else {
result = FALSE;
LOG(("Error running update process. Updating update.status"
" Last error: %d\n", GetLastError()));
LogFlush();
// If the update process was started, then updater.exe is responsible for
// setting the failure code. 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(argv[1],
SERVICE_UPDATER_COULD_NOT_BE_STARTED)) {
LOG(("Could not write update.status service update failure."
"Last error: %d\n", GetLastError()));
}
}
}
} else {
result = FALSE;
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(argv[1],
SERVICE_UPDATER_SIGN_ERROR)) {
LOG(("Could not write pending state to update.status. (%d)\n",
GetLastError()));
}
}
LocalFree(argv);
return result;
}
/**
* Executes a service command.
*
* @param argc The number of arguments in argv
* @param argv The service command line arguments, argv[0] and argv[1]
* and automatically included by Windows. argv[2] is the
* service command.
*
* @return FALSE if there was an error executing the service command.
*/
BOOL
ExecuteServiceCommand(int argc, LPWSTR *argv)
{
if (argc < 3) {
LOG(("Not enough command line arguments to execute a service command\n"));
return FALSE;
}
// The tests work by making sure the log has changed, so we put a
// unique ID in the log.
RPC_WSTR guidString = RPC_WSTR(L"");
GUID guid;
HRESULT hr = CoCreateGuid(&guid);
if (SUCCEEDED(hr)) {
UuidToString(&guid, &guidString);
}
LOG(("Executing service command %ls, ID: %ls\n",
argv[2], reinterpret_cast<LPCWSTR>(guidString)));
RpcStringFree(&guidString);
BOOL result = FALSE;
if (!lstrcmpi(argv[2], L"software-update")) {
result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3);
// We might not reach here if the service install succeeded
// because the service self updates itself and the service
// installer will stop the service.
LOG(("Service command %ls complete.\n", argv[2]));
} else {
LOG(("Service command not recognized: %ls.\n", argv[2]));
// result is already set to FALSE
}
LOG(("service command %ls complete with result: %ls.\n",
argv[1], (result ? L"Success" : L"Failure")));
return TRUE;
}

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

@ -0,0 +1,39 @@
/* ***** 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 ***** */
BOOL ExecuteServiceCommand(int argc, LPWSTR *argv);
BOOL GetUpdateDirectoryPath(LPWSTR);

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

@ -3291,8 +3291,8 @@ var gDragDrop = {
if (pos == urls.length) {
if (installs.length > 0) {
// Display the normal install confirmation for the installs
AddonManager.installAddonsFromWebpage("application/x-xpinstall", this,
null, installs);
AddonManager.installAddonsFromWebpage("application/x-xpinstall",
window, null, installs);
}
return;
}

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

@ -75,6 +75,17 @@ WindowOpenListener.prototype = {
}
};
var gSawInstallNotification = false;
var gInstallNotificationObserver = {
observe: function(aSubject, aTopic, aData) {
var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
isnot(installInfo.originatingWindow, null, "Notification should have non-null originatingWindow");
gSawInstallNotification = true;
Services.obs.removeObserver(this, "addon-install-started");
}
};
function test() {
waitForExplicitFinish();
@ -113,9 +124,15 @@ function test_confirmation(aWindow, aExpectedURLs) {
add_test(function() {
var url = TESTROOT + "addons/browser_dragdrop1.xpi";
Services.obs.addObserver(gInstallNotificationObserver,
"addon-install-started", false);
new WindowOpenListener(INSTALL_URI, function(aWindow) {
test_confirmation(aWindow, [url]);
}, run_next_test);
}, function() {
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
run_next_test();
});
var viewContainer = gManagerWindow.document.getElementById("view-port");
var effect = chromeUtils.synthesizeDrop(viewContainer, viewContainer,
@ -128,9 +145,15 @@ add_test(function() {
add_test(function() {
var fileurl = get_addon_file_url("browser_dragdrop1.xpi");
Services.obs.addObserver(gInstallNotificationObserver,
"addon-install-started", false);
new WindowOpenListener(INSTALL_URI, function(aWindow) {
test_confirmation(aWindow, [fileurl.spec]);
}, run_next_test);
}, function() {
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
run_next_test();
});
var viewContainer = gManagerWindow.document.getElementById("view-port");
var effect = chromeUtils.synthesizeDrop(viewContainer, viewContainer,
@ -144,9 +167,15 @@ add_test(function() {
var url1 = TESTROOT + "addons/browser_dragdrop1.xpi";
var url2 = TESTROOT2 + "addons/browser_dragdrop2.xpi";
Services.obs.addObserver(gInstallNotificationObserver,
"addon-install-started", false);
new WindowOpenListener(INSTALL_URI, function(aWindow) {
test_confirmation(aWindow, [url1, url2]);
}, run_next_test);
}, function() {
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
run_next_test();
});
var viewContainer = gManagerWindow.document.getElementById("view-port");
var effect = chromeUtils.synthesizeDrop(viewContainer, viewContainer,
@ -161,9 +190,15 @@ add_test(function() {
var fileurl1 = get_addon_file_url("browser_dragdrop1.xpi");
var fileurl2 = get_addon_file_url("browser_dragdrop2.xpi");
Services.obs.addObserver(gInstallNotificationObserver,
"addon-install-started", false);
new WindowOpenListener(INSTALL_URI, function(aWindow) {
test_confirmation(aWindow, [fileurl1.spec, fileurl2.spec]);
}, run_next_test);
}, function() {
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
run_next_test();
});
var viewContainer = gManagerWindow.document.getElementById("view-port");
var effect = chromeUtils.synthesizeDrop(viewContainer, viewContainer,
@ -178,9 +213,15 @@ add_test(function() {
var url = TESTROOT + "addons/browser_dragdrop1.xpi";
var fileurl = get_addon_file_url("browser_dragdrop2.xpi");
Services.obs.addObserver(gInstallNotificationObserver,
"addon-install-started", false);
new WindowOpenListener(INSTALL_URI, function(aWindow) {
test_confirmation(aWindow, [url, fileurl.spec]);
}, run_next_test);
}, function() {
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
run_next_test();
});
var viewContainer = gManagerWindow.document.getElementById("view-port");
var effect = chromeUtils.synthesizeDrop(viewContainer, viewContainer,

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

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

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

@ -50,4 +50,12 @@
#define UNEXPECTED_ERROR 8
#define ELEVATION_CANCELED 9
// The following error codes are only used by updater.exe
// when a fallback key exists and XPCShell tests are being run.
#define FALLBACKKEY_UNKNOWN_ERROR 100
#define FALLBACKKEY_REGPATH_ERROR 101
#define FALLBACKKEY_NOKEY_ERROR 102
#define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
#define FALLBACKKEY_LAUNCH_ERROR 104
#endif // Errors_h__

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

@ -52,6 +52,7 @@ ifdef MOZ_UPDATER
DIRS = ../readstrings
ifneq ($(OS_TARGET),Android)
DIRS += common
DIRS += updater
endif
@ -69,6 +70,9 @@ DIRS += test_timermanager
# Update tests require the updater binary
ifdef MOZ_UPDATER
DIRS += test
ifdef MOZ_MAINTENANCE_SERVICE
DIRS += test_svc
endif
endif
endif

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

@ -0,0 +1,74 @@
# ***** 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 += updatehelper.cpp \
uachelper.cpp \
pathhash.cpp \
$(NULL)
EXPORTS = updatehelper.h \
uachelper.h \
pathhash.h \
$(NULL)
endif
include $(topsrcdir)/config/rules.mk

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

@ -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,61 @@
/* ***** 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);
// The test only fallback key, as its name implies, is only present on machines
// that will use automated tests. Since automated tests always run from a
// different directory for each test, the presence of this key bypasses the
// "This is a valid installation directory" check. This key also stores
// the allowed name and issuer for cert checks so that the cert check
// code can still be run unchanged.
#define TEST_ONLY_FALLBACK_KEY_PATH \
L"SOFTWARE\\Mozilla\\MaintenanceService\\3932ecacee736d366d6436db0f55bce4"
#endif

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

@ -0,0 +1,241 @@
/* ***** 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"
#include "updatelogging.h"
typedef BOOL (WINAPI *LPWTSQueryUserToken)(ULONG, PHANDLE);
// See the MSDN documentation with title: Privilege Constants
// At the time of this writing, this documentation is located at:
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx
LPCTSTR UACHelper::PrivsToDisable[] = {
SE_ASSIGNPRIMARYTOKEN_NAME,
SE_AUDIT_NAME,
SE_BACKUP_NAME,
// From testing ReadDirectoryChanges still succeeds even with a low
// integrity process with the following privilege disabled.
SE_CHANGE_NOTIFY_NAME,
SE_CREATE_GLOBAL_NAME,
SE_CREATE_PAGEFILE_NAME,
SE_CREATE_PERMANENT_NAME,
SE_CREATE_SYMBOLIC_LINK_NAME,
SE_CREATE_TOKEN_NAME,
SE_DEBUG_NAME,
SE_ENABLE_DELEGATION_NAME,
SE_IMPERSONATE_NAME,
SE_INC_BASE_PRIORITY_NAME,
SE_INCREASE_QUOTA_NAME,
SE_INC_WORKING_SET_NAME,
SE_LOAD_DRIVER_NAME,
SE_LOCK_MEMORY_NAME,
SE_MACHINE_ACCOUNT_NAME,
SE_MANAGE_VOLUME_NAME,
SE_PROF_SINGLE_PROCESS_NAME,
SE_RELABEL_NAME,
SE_REMOTE_SHUTDOWN_NAME,
SE_RESTORE_NAME,
SE_SECURITY_NAME,
SE_SHUTDOWN_NAME,
SE_SYNC_AGENT_NAME,
SE_SYSTEM_ENVIRONMENT_NAME,
SE_SYSTEM_PROFILE_NAME,
SE_SYSTEMTIME_NAME,
SE_TAKE_OWNERSHIP_NAME,
SE_TCB_NAME,
SE_TIME_ZONE_NAME,
SE_TRUSTED_CREDMAN_ACCESS_NAME,
SE_UNDOCK_NAME,
SE_UNSOLICITED_INPUT_NAME
};
/**
* 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;
}
/**
* Enables or disables a privilege for the specified token.
*
* @param token The token to adjust the privilege on.
* @param priv The privilege to adjust.
* @param enable Whether to enable or disable it
* @return TRUE if the token was adjusted to the specified value.
*/
BOOL
UACHelper::SetPrivilege(HANDLE token, LPCTSTR priv, BOOL enable)
{
LUID luidOfPriv;
if (!LookupPrivilegeValue(NULL, priv, &luidOfPriv)) {
return FALSE;
}
TOKEN_PRIVILEGES tokenPriv;
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luidOfPriv;
tokenPriv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
SetLastError(ERROR_SUCCESS);
if (!AdjustTokenPrivileges(token, false, &tokenPriv,
sizeof(tokenPriv), NULL, NULL)) {
return FALSE;
}
return GetLastError() == ERROR_SUCCESS;
}
/**
* For each privilege that is specified, an attempt will be made to
* drop the privilege.
*
* @param token The token to adjust the privilege on.
* Pass NULL for current token.
* @param unneededPrivs An array of unneeded privileges.
* @param count The size of the array
* @return TRUE if there were no errors
*/
BOOL
UACHelper::DisableUnneededPrivileges(HANDLE token,
LPCTSTR *unneededPrivs,
size_t count)
{
HANDLE obtainedToken = NULL;
if (!token) {
// Note: This handle is a pseudo-handle and need not be closed
HANDLE process = GetCurrentProcess();
if (!OpenProcessToken(process, TOKEN_ALL_ACCESS_P, &obtainedToken)) {
LOG(("Could not obtain token for current process, no "
"privileges changed. (%d)\n", GetLastError()));
return FALSE;
}
token = obtainedToken;
}
BOOL result = TRUE;
for (size_t i = 0; i < count; i++) {
if (SetPrivilege(token, unneededPrivs[i], FALSE)) {
LOG(("Disabled unneeded token privilege: %s.\n",
unneededPrivs[i]));
} else {
LOG(("Could not disable token privilege value: %s. (%d)\n",
unneededPrivs[i], GetLastError()));
result = FALSE;
}
}
if (obtainedToken) {
CloseHandle(obtainedToken);
}
return result;
}
/**
* Disables privileges for the specified token.
* The privileges to disable are in PrivsToDisable.
* In the future there could be new privs and we are not sure if we should
* explicitly disable these or not.
*
* @param token The token to drop the privilege on.
* Pass NULL for current token.
* @return TRUE if there were no errors
*/
BOOL
UACHelper::DisablePrivileges(HANDLE token)
{
static const size_t PrivsToDisableSize =
sizeof(UACHelper::PrivsToDisable) / sizeof(UACHelper::PrivsToDisable[0]);
return DisableUnneededPrivileges(token, UACHelper::PrivsToDisable,
PrivsToDisableSize);
}

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

@ -0,0 +1,56 @@
/* ***** 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);
static BOOL DisablePrivileges(HANDLE token);
private:
static BOOL SetPrivilege(HANDLE token, LPCTSTR privs, BOOL enable);
static BOOL DisableUnneededPrivileges(HANDLE token,
LPCTSTR *unneededPrivs, size_t count);
static LPCTSTR PrivsToDisable[];
};
#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,633 @@
/* ***** 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 <stdio.h>
#include "shlobj.h"
#include "updatehelper.h"
// Needed for PathAppendW
#include <shlwapi.h>
// Needed for CreateToolhelp32Snapshot
#include <tlhelp32.h>
#pragma comment(lib, "shlwapi.lib")
WCHAR* MakeCommandLine(int argc, WCHAR **argv);
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 installationDir 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.
* @return TRUE if there was no error starting the process.
*/
BOOL
LaunchWinPostProcess(const WCHAR *installationDir,
const WCHAR *updateInfoDir,
bool forceSync,
HANDLE userToken)
{
WCHAR workingDirectory[MAX_PATH + 1];
wcscpy(workingDirectory, installationDir);
// Launch helper.exe to perform post processing (e.g. registry and log file
// modifications) for the update.
WCHAR inifile[MAX_PATH + 1];
wcscpy(inifile, installationDir);
if (!PathAppendSafe(inifile, 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];
wcscpy(exefullpath, installationDir);
if (!PathAppendSafe(exefullpath, 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, SVC_NAME,
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,
0,
NULL, argv[2], &si, &pi);
if (svcUpdateProcessStarted) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return svcUpdateProcessStarted;
}
/**
* Executes a maintenance service command
*
* @param argc The total number of arguments in argv
* @param argv An array of null terminated strings to pass to the service,
* @return ERROR_SUCCESS if the service command was started.
* Less than 16000, a windows system error code from StartServiceW
* More than 20000, 20000 + the last state of the service constant if
* the last state is something other than stopped.
* 17001 if the SCM could not be opened
* 17002 if the service could not be opened
*/
DWORD
StartServiceCommand(int argc, LPCWSTR* argv)
{
DWORD lastState = WaitForServiceStop(SVC_NAME, 5);
if (lastState != SERVICE_STOPPED) {
return 20000 + lastState;
}
// Get a handle to the SCM database.
SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
SC_MANAGER_CONNECT |
SC_MANAGER_ENUMERATE_SERVICE);
if (!serviceManager) {
return 17001;
}
// Get a handle to the service.
SC_HANDLE service = OpenServiceW(serviceManager,
SVC_NAME,
SERVICE_START);
if (!service) {
CloseServiceHandle(serviceManager);
return 17002;
}
// Wait at most 5 seconds trying to start the service in case of errors
// like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT.
const DWORD maxWaitMS = 5000;
DWORD currentWaitMS = 0;
DWORD lastError = ERROR_SUCCESS;
while (currentWaitMS < maxWaitMS) {
BOOL result = StartServiceW(service, argc, argv);
if (result) {
lastError = ERROR_SUCCESS;
break;
} else {
lastError = GetLastError();
}
Sleep(100);
currentWaitMS += 100;
}
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
return lastError;
}
/**
* Launch a service initiated action for a software update 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] must be the path to the updater.exe
* @return ERROR_SUCCESS if successful
*/
DWORD
LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR* argv)
{
// The service command is the same as the updater.exe command line except
// it has 2 extra args: 1) The Path to udpater.exe, and 2) the command
// being executed which is "software-update"
LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2];
updaterServiceArgv[0] = L"MozillaMaintenance";
updaterServiceArgv[1] = L"software-update";
for (int i = 0; i < argc; ++i) {
updaterServiceArgv[i + 2] = argv[i];
}
// Execute the service command by starting the service with
// the passed in arguments.
DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv);
delete[] updaterServiceArgv;
return ret;
}
/**
* Joins a base directory path with a filename.
*
* @param base The base directory path of size MAX_PATH + 1
* @param extra The filename to append
* @return TRUE if the file name was successful appended to base
*/
BOOL
PathAppendSafe(LPWSTR base, LPCWSTR extra)
{
if (wcslen(base) + wcslen(extra) >= MAX_PATH) {
return FALSE;
}
return PathAppendW(base, extra);
}
/**
* 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)
{
WCHAR updateStatusFilePath[MAX_PATH + 1];
wcscpy(updateStatusFilePath, updateDirPath);
if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
return FALSE;
}
const char pending[] = "pending";
HANDLE 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);
CloseHandle(statusFile);
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)
{
WCHAR updateStatusFilePath[MAX_PATH + 1];
wcscpy(updateStatusFilePath, updateDirPath);
if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
return FALSE;
}
HANDLE 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);
CloseHandle(statusFile);
return ok && wrote == toWrite;
}
/**
* Waits for a service to enter a stopped state.
* This function does not stop the service, it just blocks until the service
* is stopped.
*
* @param serviceName The service to wait for.
* @param maxWaitSeconds The maximum number of seconds to wait
* @return state of the service after a timeout or when stopped.
* A value of 255 is returned for an error. Typical values are:
* SERVICE_STOPPED 0x00000001
* SERVICE_START_PENDING 0x00000002
* SERVICE_STOP_PENDING 0x00000003
* SERVICE_RUNNING 0x00000004
* SERVICE_CONTINUE_PENDING 0x00000005
* SERVICE_PAUSE_PENDING 0x00000006
* SERVICE_PAUSED 0x00000007
* last status not set 0x000000CF
* Could no query status 0x000000DF
* Could not open service, access denied 0x000000EB
* Could not open service, invalid handle 0x000000EC
* Could not open service, invalid name 0x000000ED
* Could not open service, does not exist 0x000000EE
* Could not open service, other error 0x000000EF
* Could not open SCM, access denied 0x000000FD
* Could not open SCM, database does not exist 0x000000FE;
* Could not open SCM, other error 0x000000FF;
* Note: The strange choice of error codes above SERVICE_PAUSED are chosen
* in case Windows comes out with other service stats higher than 7, they
* would likely call it 8 and above. JS code that uses this in TestAUSHelper
* only handles values up to 255 so that's why we don't use GetLastError
* directly.
*/
DWORD
WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
{
// 0x000000CF is defined above to be not set
DWORD lastServiceState = 0x000000CF;
// Get a handle to the SCM database.
SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
SC_MANAGER_CONNECT |
SC_MANAGER_ENUMERATE_SERVICE);
if (!serviceManager) {
DWORD lastError = GetLastError();
switch(lastError) {
case ERROR_ACCESS_DENIED:
return 0x000000FD;
case ERROR_DATABASE_DOES_NOT_EXIST:
return 0x000000FE;
default:
return 0x000000FF;
}
}
// Get a handle to the service.
SC_HANDLE service = OpenServiceW(serviceManager,
serviceName,
SERVICE_QUERY_STATUS);
if (!service) {
DWORD lastError = GetLastError();
CloseServiceHandle(serviceManager);
switch(lastError) {
case ERROR_ACCESS_DENIED:
return 0x000000EB;
case ERROR_INVALID_HANDLE:
return 0x000000EC;
case ERROR_INVALID_NAME:
return 0x000000ED;
case ERROR_SERVICE_DOES_NOT_EXIST:
return 0x000000EE;
default:
return 0x000000EF;
}
}
DWORD currentWaitMS = 0;
SERVICE_STATUS_PROCESS ssp;
ssp.dwCurrentState = lastServiceState;
while (currentWaitMS < maxWaitSeconds * 1000) {
DWORD bytesNeeded;
if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
DWORD lastError = GetLastError();
switch (lastError) {
case ERROR_INVALID_HANDLE:
ssp.dwCurrentState = 0x000000D9;
break;
case ERROR_ACCESS_DENIED:
ssp.dwCurrentState = 0x000000DA;
break;
case ERROR_INSUFFICIENT_BUFFER:
ssp.dwCurrentState = 0x000000DB;
break;
case ERROR_INVALID_PARAMETER:
ssp.dwCurrentState = 0x000000DC;
break;
case ERROR_INVALID_LEVEL:
ssp.dwCurrentState = 0x000000DD;
break;
case ERROR_SHUTDOWN_IN_PROGRESS:
ssp.dwCurrentState = 0x000000DE;
break;
// These 3 errors can occur when the service is not yet stopped but
// it is stopping.
case ERROR_INVALID_SERVICE_CONTROL:
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
case ERROR_SERVICE_NOT_ACTIVE:
currentWaitMS += 50;
Sleep(50);
continue;
default:
ssp.dwCurrentState = 0x000000DF;
}
// We couldn't query the status so just break out
break;
}
// The service is already in use.
if (ssp.dwCurrentState == SERVICE_STOPPED) {
break;
}
currentWaitMS += 50;
Sleep(50);
}
lastServiceState = ssp.dwCurrentState;
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
return lastServiceState;
}
/**
* Determines if there is at least one process running for the specified
* application. A match will be found across any session for any user.
*
* @param process The process to check for existance
* @return ERROR_NOT_FOUND if the process was not found
* @ ERROR_SUCCESS if the process was found and there were no errors
* @ Other Win32 system error code for other errors
**/
DWORD
IsProcessRunning(LPCWSTR filename)
{
// Take a snapshot of all processes in the system.
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == snapshot) {
return GetLastError();
}
PROCESSENTRY32W processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32FirstW(snapshot, &processEntry)) {
DWORD lastError = GetLastError();
CloseHandle(snapshot);
return lastError;
}
do {
if (wcsicmp(filename, processEntry.szExeFile) == 0) {
CloseHandle(snapshot);
return ERROR_SUCCESS;
}
} while (Process32NextW(snapshot, &processEntry));
CloseHandle(snapshot);
return ERROR_NOT_FOUND;
}
/**
* Waits for the specified applicaiton to exit.
*
* @param filename The application to wait for.
* @param maxSeconds The maximum amount of seconds to wait for all
* instances of the application to exit.
* @return ERROR_SUCCESS if no instances of the application exist
* WAIT_TIMEOUT if the process is still running after maxSeconds.
* Any other Win32 system error code.
*/
DWORD
WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
{
DWORD applicationRunningError = WAIT_TIMEOUT;
for(DWORD i = 0; i < maxSeconds; i++) {
DWORD applicationRunningError = IsProcessRunning(filename);
if (ERROR_NOT_FOUND == applicationRunningError) {
return ERROR_SUCCESS;
}
Sleep(1000);
}
if (ERROR_SUCCESS == applicationRunningError) {
return WAIT_TIMEOUT;
}
return applicationRunningError;
}

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

@ -0,0 +1,50 @@
/* ***** 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 *installationDir,
const WCHAR *updateInfoDir,
bool forceSync,
HANDLE userToken);
BOOL StartServiceUpdate(int argc, LPWSTR *argv);
BOOL GetUpdateDirectoryPath(LPWSTR path);
DWORD LaunchServiceSoftwareUpdateCommand(DWORD argc, LPCWSTR *argv);
BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
BOOL WriteStatusPending(LPCWSTR updateDirPath);
DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
#define SVC_NAME L"MozillaMaintenance"

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

@ -0,0 +1,94 @@
/* ***** 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() : 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::Flush()
{
if (!logFP)
return;
fflush(logFP);
}
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,75 @@
/* ***** 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()
{
static UpdateLog primaryLog;
return primaryLog;
}
void Init(NS_tchar* sourcePath, NS_tchar* fileName);
void Finish();
void Flush();
void Printf(const char *fmt, ... );
~UpdateLog()
{
Finish();
}
protected:
UpdateLog();
FILE *logFP;
NS_tchar* sourcePath;
};
#define LOG(args) UpdateLog::GetPrimaryLog().Printf args
#define LogInit(PATHNAME_, FILENAME_) \
UpdateLog::GetPrimaryLog().Init(PATHNAME_, FILENAME_)
#define LogFinish() UpdateLog::GetPrimaryLog().Finish()
#define LogFlush() UpdateLog::GetPrimaryLog().Flush()
#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,13 @@ 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_UPDATER_COMPARE_ERROR = 16003;
const SERVICE_UPDATER_IDENTITY_ERROR = 16004;
const SERVICE_STILL_APPLYING_ON_SUCCESS = 16005;
const SERVICE_STILL_APPLYING_ON_FAILURE = 16006;
const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
@ -149,6 +161,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 +477,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 +546,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 +566,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 +579,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 +605,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 +1431,37 @@ 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_UPDATER_COMPARE_ERROR ||
update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR ||
update.errorCode == SERVICE_STILL_APPLYING_ON_SUCCESS ||
update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE) {
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 +1584,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 +2073,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 +2257,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 +2592,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 +2645,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 +2655,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 +2683,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 +2911,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

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

@ -3,7 +3,12 @@
*/
#ifdef XP_WIN
#pragma comment(lib, "wintrust.lib")
#pragma comment(lib, "crypt32.lib")
# include <windows.h>
# include <wintrust.h>
# include <tlhelp32.h>
# include <softpub.h>
# include <direct.h>
# include <io.h>
typedef WCHAR NS_tchar;
@ -111,6 +116,253 @@ CheckMsg(const NS_tchar *path, const char *expected)
return strcmp(rb, expected) == 0;
}
#ifdef XP_WIN
/**
* 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.
return WinVerifyTrust(NULL, &policyGUID, &trustData);
}
/**
* Waits for a service to enter a stopped state.
* This function does not stop the service, it just blocks until the service
* is stopped.
*
* @param serviceName The service to wait for.
* @param maxWaitSeconds The maximum number of seconds to wait
* @return state of the service after a timeout or when stopped.
* A value of 255 is returned for an error. Typical values are:
* SERVICE_STOPPED 0x00000001
* SERVICE_START_PENDING 0x00000002
* SERVICE_STOP_PENDING 0x00000003
* SERVICE_RUNNING 0x00000004
* SERVICE_CONTINUE_PENDING 0x00000005
* SERVICE_PAUSE_PENDING 0x00000006
* SERVICE_PAUSED 0x00000007
* last status not set 0x000000CF
* Could no query status 0x000000DF
* Could not open service, access denied 0x000000EB
* Could not open service, invalid handle 0x000000EC
* Could not open service, invalid name 0x000000ED
* Could not open service, does not exist 0x000000EE
* Could not open service, other error 0x000000EF
* Could not open SCM, access denied 0x000000FD
* Could not open SCM, database does not exist 0x000000FE;
* Could not open SCM, other error 0x000000FF;
* Note: The strange choice of error codes above SERVICE_PAUSED are chosen
* in case Windows comes out with other service stats higher than 7, they
* would likely call it 8 and above. JS code that uses this in TestAUSHelper
* only handles values up to 255 so that's why we don't use GetLastError
* directly.
*/
DWORD
WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
{
// 0x000000CF is defined above to be not set
DWORD lastServiceState = 0x000000CF;
// Get a handle to the SCM database.
SC_HANDLE serviceManager = OpenSCManager(NULL, NULL,
SC_MANAGER_CONNECT |
SC_MANAGER_ENUMERATE_SERVICE);
if (!serviceManager) {
DWORD lastError = GetLastError();
switch(lastError) {
case ERROR_ACCESS_DENIED:
return 0x000000FD;
case ERROR_DATABASE_DOES_NOT_EXIST:
return 0x000000FE;
default:
return 0x000000FF;
}
}
// Get a handle to the service.
SC_HANDLE service = OpenServiceW(serviceManager,
serviceName,
SERVICE_QUERY_STATUS);
if (!service) {
DWORD lastError = GetLastError();
CloseServiceHandle(serviceManager);
switch(lastError) {
case ERROR_ACCESS_DENIED:
return 0x000000EB;
case ERROR_INVALID_HANDLE:
return 0x000000EC;
case ERROR_INVALID_NAME:
return 0x000000ED;
// If the service does not exist, keep trying in case it does exist soon.
// I think there might be an issue with the TALOS machines and some of
// the machines having an old maintenanceservice.exe that used to
// uninstall when upgrading. Those should already be upgraded but this
// is safer.
case ERROR_SERVICE_DOES_NOT_EXIST:
if (maxWaitSeconds == 0) {
return 0x000000EE;
} else {
Sleep(1000);
return WaitForServiceStop(serviceName, maxWaitSeconds - 1);
}
default:
return 0x000000EF;
}
}
DWORD currentWaitMS = 0;
SERVICE_STATUS_PROCESS ssp;
ssp.dwCurrentState = lastServiceState;
while (currentWaitMS < maxWaitSeconds * 1000) {
DWORD bytesNeeded;
if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
DWORD lastError = GetLastError();
switch (lastError) {
case ERROR_INVALID_HANDLE:
ssp.dwCurrentState = 0x000000D9;
break;
case ERROR_ACCESS_DENIED:
ssp.dwCurrentState = 0x000000DA;
break;
case ERROR_INSUFFICIENT_BUFFER:
ssp.dwCurrentState = 0x000000DB;
break;
case ERROR_INVALID_PARAMETER:
ssp.dwCurrentState = 0x000000DC;
break;
case ERROR_INVALID_LEVEL:
ssp.dwCurrentState = 0x000000DD;
break;
case ERROR_SHUTDOWN_IN_PROGRESS:
ssp.dwCurrentState = 0x000000DE;
break;
// These 3 errors can occur when the service is not yet stopped but
// it is stopping.
case ERROR_INVALID_SERVICE_CONTROL:
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
case ERROR_SERVICE_NOT_ACTIVE:
currentWaitMS += 50;
Sleep(50);
continue;
default:
ssp.dwCurrentState = 0x000000DF;
}
// We couldn't query the status so just break out
break;
}
// The service is already in use.
if (ssp.dwCurrentState == SERVICE_STOPPED) {
break;
}
currentWaitMS += 50;
Sleep(50);
}
lastServiceState = ssp.dwCurrentState;
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
return lastServiceState;
}
/**
* Determines if there is at least one process running for the specified
* application. A match will be found across any session for any user.
*
* @param process The process to check for existance
* @return ERROR_NOT_FOUND if the process was not found
* @ ERROR_SUCCESS if the process was found and there were no errors
* @ Other Win32 system error code for other errors
**/
DWORD
IsProcessRunning(LPCWSTR filename)
{
// Take a snapshot of all processes in the system.
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == snapshot) {
return GetLastError();
}
PROCESSENTRY32W processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32FirstW(snapshot, &processEntry)) {
DWORD lastError = GetLastError();
CloseHandle(snapshot);
return lastError;
}
do {
if (wcsicmp(filename, processEntry.szExeFile) == 0) {
CloseHandle(snapshot);
return ERROR_SUCCESS;
}
} while (Process32NextW(snapshot, &processEntry));
CloseHandle(snapshot);
return ERROR_NOT_FOUND;
}
/**
* Waits for the specified applicaiton to exit.
*
* @param filename The application to wait for.
* @param maxSeconds The maximum amount of seconds to wait for all
* instances of the application to exit.
* @return ERROR_SUCCESS if no instances of the application exist
* WAIT_TIMEOUT if the process is still running after maxSeconds.
* Any other Win32 system error code.
*/
DWORD
WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
{
DWORD applicationRunningError = WAIT_TIMEOUT;
for(DWORD i = 0; i < maxSeconds; i++) {
DWORD applicationRunningError = IsProcessRunning(filename);
if (ERROR_NOT_FOUND == applicationRunningError) {
return ERROR_SUCCESS;
}
Sleep(1000);
}
if (ERROR_SUCCESS == applicationRunningError) {
return WAIT_TIMEOUT;
}
return applicationRunningError;
}
#endif
int NS_main(int argc, NS_tchar **argv)
{
@ -121,6 +373,7 @@ int NS_main(int argc, NS_tchar **argv)
"\n" \
"Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n" \
" or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \
" or: signature-check filepath\n" \
"\n" \
" WORKINGDIR \tThe relative path to the working directory to use.\n" \
" INFILE \tThe relative path from the working directory for the file to\n" \
@ -142,6 +395,53 @@ int NS_main(int argc, NS_tchar **argv)
return 1;
}
if (!NS_tstrcmp(argv[1], NS_T("check-signature"))) {
#ifdef XP_WIN
if (ERROR_SUCCESS == VerifyCertificateTrustForFile(argv[2])) {
return 0;
} else {
return 1;
}
#else
// Not implemented on non-Windows platforms
return 1;
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
#ifdef XP_WIN
const int maxWaitSeconds = NS_ttoi(argv[3]);
LPCWSTR serviceName = argv[2];
DWORD serviceState = WaitForServiceStop(serviceName, maxWaitSeconds);
if (SERVICE_STOPPED == serviceState) {
return 0;
} else {
return serviceState;
}
#else
// Not implemented on non-Windows platforms
return 1;
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("wait-for-application-exit"))) {
#ifdef XP_WIN
const int maxWaitSeconds = NS_ttoi(argv[3]);
LPCWSTR application = argv[2];
DWORD ret = WaitForProcessExit(application, maxWaitSeconds);
if (ERROR_SUCCESS == ret) {
return 0;
} else if (WAIT_TIMEOUT == ret) {
return 1;
} else {
return 2;
}
#else
// Not implemented on non-Windows platforms
return 1;
#endif
}
int i = 0;
if (NS_tchdir(argv[1]) != 0) {

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

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

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

@ -88,6 +88,8 @@ const HELPER_BIN_FILE = "TestAUSHelper" + BIN_SUFFIX;
const MAR_COMPLETE_FILE = "data/complete.mar";
const MAR_PARTIAL_FILE = "data/partial.mar";
const UPDATER_BIN_FILE = "updater" + BIN_SUFFIX;
const MAINTENANCE_SERVICE_BIN_FILE = "maintenanceservice.exe";
const MAINTENANCE_SERVICE_INSTALLER_BIN_FILE = "maintenanceservice_installer.exe";
const UPDATES_DIR_SUFFIX = "_mar";
const CHANNEL_CHANGE_FILE = "channelchange";
@ -104,6 +106,8 @@ const ERR_RENAME_FILE = "rename_file: failed to rename file";
const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
const LOG_SVC_SUCCESSFUL_LAUNCH = "updater.exe was launched and run successfully!";
// variables are used instead of contants so tests can override these values
var gCallbackBinFile = "callback_app" + BIN_SUFFIX;
var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
@ -111,6 +115,10 @@ var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
// Time to wait for the test helper process before continuing the test
const TEST_HELPER_TIMEOUT = 100;
// Use a copy of the main application executable for the test to avoid main
// executable in use errors.
const FILE_WIN_TEST_EXE = "_aus_test_app.exe";
var gTestserver;
var gXHR;
@ -445,6 +453,343 @@ function runUpdate() {
return process.exitValue;
}
let gServiceLaunchedCallbackLog = null;
let gServiceLaunchedCallbackArgs = null;
/**
* Helper function to check whether the maintenance service updater tests should
* run. See bug 711660 for more details.
*
* @return true if the test should run and false if it shouldn't.
*/
function shouldRunServiceTest(aFirstTest) {
// In case the machine is running an old maintenance service or if it
// is not installed, and permissions exist to install it. Then install
// the newer bin that we have.
attemptServiceInstall();
const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" +
"3932ecacee736d366d6436db0f55bce4";
let key = AUS_Cc["@mozilla.org/windows-registry-key;1"].
createInstance(AUS_Ci.nsIWindowsRegKey);
try {
key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH,
AUS_Ci.nsIWindowsRegKey.ACCESS_READ | key.WOW64_64);
}
catch (e) {
logTestInfo("this test can only run on the buildbot build system at this " +
"time.");
return false;
}
let binDir = getGREDir();
let updaterBin = binDir.clone();
updaterBin.append(UPDATER_BIN_FILE);
if (!updaterBin.exists()) {
do_throw("Unable to find updater binary!");
}
let updaterBinPath = updaterBin.path;
if (/ /.test(updaterBinPath)) {
updaterBinPath = '"' + updaterBinPath + '"';
}
// Check to make sure the service is installed
let helperBin = do_get_file(HELPER_BIN_FILE);
let args = ["wait-for-service-stop", "MozillaMaintenance", "10"];
let process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(helperBin);
logTestInfo("Checking if the service exists on this machine.");
process.run(true, args, args.length);
if (process.exitValue == 0xEE) {
logTestInfo("this test can only run when the service is installed.");
return false;
} else {
logTestInfo("Service exists, return value: " + process.exitValue);
}
// If this is the first test in the series, then there is no reason the
// service should be anything but stopped, so be strict here and throw
// an error.
if (aFirstTest && process.exitValue != 0) {
do_throw("First test, check for service stopped state returned error " +
process.exitValue);
}
// Make sure the binaries are signed
args = ["check-signature", updaterBinPath];
process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(helperBin);
process.run(true, args, args.length);
if (process.exitValue == 0) {
return true;
}
logTestInfo("this test can only run on builds with signed binaries. " +
HELPER_BIN_FILE + " returned " + process.exitValue)
return false;
}
/**
* Copies the specified filename from the dist/bin
* directory into the apply-to directory.
*
* @param filename The name of the file to copy
*/
function copyBinToApplyToDir(filename) {
let binDir = getGREDir();
let fileToCopy = binDir.clone();
fileToCopy.append(filename);
if (!fileToCopy.exists()) {
do_throw("Unable to copy binary: " + filename);
}
let applyToUpdater = getApplyDirFile(null, true);
if (applyToUpdater.path != binDir.path) {
do_print("copying " + fileToCopy.path + " to: " + applyToUpdater.path);
fileToCopy.copyTo(applyToUpdater, filename);
}
}
/**
* Attempts to upgrade the maintenance service if permissions are allowed.
* This is useful for XP where we have permission to upgrade in case an
* older service installer exists. Also if the user manually installed into
* a unprivileged location.
*/
function attemptServiceInstall() {
var version = AUS_Cc["@mozilla.org/system-info;1"]
.getService(AUS_Ci.nsIPropertyBag2)
.getProperty("version");
var isVistaOrHigher = (parseFloat(version) >= 6.0);
if (isVistaOrHigher) {
return;
}
let binDir = getGREDir();
let installerFile = binDir.clone();
installerFile.append(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE);
if (!installerFile.exists()) {
do_throw(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE + " not found.");
}
let installerProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
installerProcess.init(installerFile);
logTestInfo("Starting installer process...");
installerProcess.run(true, [], 0);
}
/**
* Helper function for updater tests for launching the updater using the
* maintenance service to apply a mar file.
*
* @param aInitialStatus the initial value of update.status
* @param aExpectedStatus the expected value of update.status when the test finishes
* @param aCallback the function to be called when the update is finished
* @param aUpdatesDir the updates root directory to use (optional)
* @param aCheckSvcLog whether the service log should be checked (optional)
*/
function runUpdateUsingService(aInitialStatus, aExpectedStatus,
aCallback, aUpdatesDir, aCheckSvcLog) {
// Check the service logs for a successful update
function checkServiceLogs(aOriginalContents) {
let contents = readServiceLogFile();
logTestInfo("The contents of maintenanceservice.log:\n" + contents + "\n");
do_check_neq(contents, aOriginalContents);
do_check_neq(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1);
}
function readServiceLogFile() {
let file = AUS_Cc["@mozilla.org/file/directory_service;1"].
getService(AUS_Ci.nsIProperties).
get("CmAppData", AUS_Ci.nsIFile);
file.append("Mozilla");
file.append("logs");
file.append("maintenanceservice.log");
return readFile(file);
}
function waitServiceApps() {
// maintenanceservice_installer.exe is started async during updates.
waitForApplicationStop("maintenanceservice_installer.exe");
// maintenanceservice_tmp.exe is started async from the service installer.
waitForApplicationStop("maintenanceservice_tmp.exe");
// In case the SCM thinks the service is stopped, but process still exists.
waitForApplicationStop("maintenanceservice.exe");
}
function waitForServiceStop(aFailTest) {
waitServiceApps();
logTestInfo("Waiting for service to stop if necessary...");
// Use the helper bin to ensure the service is stopped. If not
// stopped then wait for the service to be stopped (at most 120 seconds)
let helperBin = do_get_file(HELPER_BIN_FILE);
let helperBinArgs = ["wait-for-service-stop",
"MozillaMaintenance",
"120"];
let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
helperBinProcess.init(helperBin);
logTestInfo("Stopping service...");
helperBinProcess.run(true, helperBinArgs, helperBinArgs.length);
if (helperBinProcess.exitValue == 0xEE) {
do_throw("The service does not exist on this machine. Return value: " +
helperBinProcess.exitValue);
} else if (helperBinProcess.exitValue != 0) {
if (aFailTest) {
do_throw("maintenance service did not stop, last state: " +
helperBinProcess.exitValue + ". Forcing test failure.");
} else {
logTestInfo("maintenance service did not stop, last state: " +
helperBinProcess.exitValue + ". May cause failures.");
}
} else {
logTestInfo("Service stopped.");
}
waitServiceApps();
}
function waitForApplicationStop(application) {
logTestInfo("Waiting for " + application + " to stop if " +
"necessary...");
// Use the helper bin to ensure the application is stopped.
// If not, then wait for it to be stopped (at most 120 seconds)
let helperBin = do_get_file(HELPER_BIN_FILE);
let helperBinArgs = ["wait-for-application-exit",
application,
"120"];
let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
helperBinProcess.init(helperBin);
helperBinProcess.run(true, helperBinArgs, helperBinArgs.length);
if (helperBinProcess.exitValue != 0) {
do_throw(application + " did not stop, last state: " +
helperBinProcess.exitValue + ". Forcing test failure.");
}
}
// Make sure the service from the previous test is already stopped.
waitForServiceStop(true);
// Prevent the cleanup function from begin run more than once
if (typeof(gRegisteredServiceCleanup) === "undefined") {
gRegisteredServiceCleanup = true;
do_register_cleanup(function serviceCleanup() {
resetEnvironment();
// This will delete the app console log file if it exists.
try {
getAppConsoleLogPath();
}
catch (e) {
logTestInfo("unable to remove file during cleanup. Exception: " + e);
}
// This will delete the app arguments log file if it exists.
try {
getAppArgsLogPath();
}
catch (e) {
logTestInfo("unable to remove file during cleanup. Exception: " + e);
}
});
}
if (aCheckSvcLog === undefined) {
aCheckSvcLog = true; // default to true
}
let svcOriginalLog;
if (aCheckSvcLog) {
svcOriginalLog = readServiceLogFile();
}
let appArgsLogPath = getAppArgsLogPath();
gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, "");
let updatesDir = aUpdatesDir || do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
let file = updatesDir.clone();
file.append(FILE_UPDATE_STATUS);
writeFile(file, aInitialStatus + "\n");
// sanity check
do_check_eq(readStatusFile(updatesDir), aInitialStatus);
gServiceLaunchedCallbackArgs = [
"-no-remote",
"-process-updates",
"-dump-args",
appArgsLogPath
];
let launchBin = getLaunchBin();
let args = getProcessArgs(["-dump-args", appArgsLogPath]);
logTestInfo("launching " + launchBin.path + " " + args.join(" "));
let process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(launchBin);
// Override the update root directory
gEnvUpdateRootOverride = updatesDir.path;
gEnvAppDirOverride = getApplyDirFile(null).path;
setEnvironment();
// There is a security check done by the service to make sure the updater
// we are executing is the same as the one in the apply-to dir.
// To make sure they match from tests we copy updater.exe to the apply-to dir.
copyBinToApplyToDir(UPDATER_BIN_FILE);
// The service will execute maintenanceservice_installer.exe and
// will copy maintenanceservice.exe out of the same directory from
// the installation directory. So we need to make sure both of those
// bins always exist in the installation directory.
copyBinToApplyToDir(MAINTENANCE_SERVICE_BIN_FILE);
copyBinToApplyToDir(MAINTENANCE_SERVICE_INSTALLER_BIN_FILE);
// Firefox does not wait for the service command to finish, but
// we still launch the process sync to avoid intermittent failures with
// the log file not being written out yet.
// We will rely on watching the update.status file and waiting for the service
// to stop to know the service command is done.
process.run(true, args, args.length);
resetEnvironment();
function timerCallback(timer) {
// Wait for the expected status
let status = readStatusFile(updatesDir);
// For failed status, we don't care what the failure code is
if (aExpectedStatus == STATE_FAILED) {
status = status.split(": ")[0];
}
// status will probably always be equal to STATE_APPLYING but there is a
// race condition where it would be possible on slower machines where status
// could be equal to STATE_PENDING_SVC.
if (status == STATE_APPLYING ||
status == STATE_PENDING_SVC) {
logTestInfo("Still waiting to see the " + aExpectedStatus +
" status, got " + status + " for now...");
return;
}
// Make sure all of the logs are written out.
waitForServiceStop(false);
do_check_eq(status, aExpectedStatus);
timer.cancel();
timer = null;
if (aCheckSvcLog) {
checkServiceLogs(svcOriginalLog);
}
aCallback();
}
let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK);
}
/**
* Gets the platform specific shell binary that is launched using nsIProcess and
* in turn launches the updater.
@ -700,6 +1045,7 @@ function checkUpdateLogContains(aCheckString) {
let updateLog = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX, true);
updateLog.append(FILE_UPDATE_LOG);
let updateLogContents = readFileBytes(updateLog);
logTestInfo("log file contents:\n" + updateLogContents + "\n");
do_check_true(updateLogContents.indexOf(aCheckString) != -1);
}
@ -909,6 +1255,59 @@ function checkCallbackAppLog() {
do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
}
/**
* Helper function for updater service tests for verifying the contents of the
* updater callback application log which should contain the arguments passed to
* the callback application.
*/
function checkCallbackServiceLog() {
do_check_neq(gServiceLaunchedCallbackLog, null);
let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n";
let logFile = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile);
logFile.initWithPath(gServiceLaunchedCallbackLog);
let logContents = readFile(logFile);
// It is possible for the log file contents check to occur before the log file
// contents are completely written so wait until the contents are the expected
// value. If the contents are never the expected value then the test will
// fail by timing out.
if (logContents != expectedLogContents) {
logTestInfo("callback service log not expected value, waiting longer");
do_timeout(TEST_HELPER_TIMEOUT, checkCallbackServiceLog);
return;
}
logTestInfo("testing that the callback application successfully launched " +
"and the expected command line arguments passed to it");
do_check_eq(logContents, expectedLogContents);
(function removeCallbackCopy() {
// Remove the copy of the application executable used for the test on
// Windows if it exists.
let appBinCopy = getCurrentProcessDir();
appBinCopy.append(TEST_ID + FILE_WIN_TEST_EXE);
if (appBinCopy.exists()) {
try {
appBinCopy.remove(false);
// Use a timeout to give any files that were in use additional
// time to close. Same as updater.exe without service tests.
do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
}
catch (e) {
logTestInfo("unable to remove file during cleanup. Exception: " + e);
do_timeout(TEST_HELPER_TIMEOUT, removeCallbackCopy);
}
} else {
do_timeout(TEST_HELPER_TIMEOUT, do_test_finished);
}
})();
}
/**
* Helper function for updater binary tests for verifying there are no update
* backup files left behind after an update.
@ -1168,3 +1567,419 @@ var gDirProvider = {
}
};
Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService).registerProvider(gDirProvider);
/**
* Returns the platform specific arguments used by nsIProcess when launching
* the application.
*
* @param aExtraArgs optional array of extra arguments
* @return an array of arguments to be passed to nsIProcess.
*
* Notes:
* 1. Mozilla universal binaries that contain both i386 and x86_64 on Mac OS X
* 10.5.x must be launched using the i386 architecture.
* 2. A shell is necessary to pipe the application's console output which
* would otherwise pollute the xpcshell log.
*
* Command line arguments used when launching the application:
* -no-remote prevents shell integration from being affected by an existing
* application process.
* -process-updates makes the application exits after being relaunched by the
* updater.
* 1> pipes stdout to a file.
* appConsoleLogPath is the file path to pipe the output from the shell.
* Otherwise the output from the application will end up in the xpchsell log.
* 2>&1 pipes stderr to sdout.
*/
function getProcessArgs(aExtraArgs) {
if (!aExtraArgs) {
aExtraArgs = [];
}
// Pipe the output from the launched application to a file so the output from
// its console isn't present in the xpcshell log.
let appConsoleLogPath = getAppConsoleLogPath();
let args;
if (IS_UNIX) {
let launchScript = getLaunchScript();
// Precreate the script with executable permissions
launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
let scriptContents = "#! /bin/sh\n";
// On Mac OS X versions prior to 10.6 the i386 acrhitecture must be used.
if (gIsLessThanMacOSX_10_6) {
scriptContents += "arch -arch i386 ";
}
scriptContents += gAppBinPath + " -no-remote -process-updates " +
aExtraArgs.join(" ") + " 1> " +
appConsoleLogPath + " 2>&1";
writeFile(launchScript, scriptContents);
logTestInfo("created " + launchScript.path + " containing:\n" +
scriptContents);
args = [launchScript.path];
}
else {
args = ["/D", "/Q", "/C", gAppBinPath, "-no-remote", "-process-updates"].
concat(aExtraArgs).
concat(["1>", appConsoleLogPath, "2>&1"]);
}
return args;
}
/**
* Gets a file path for piping the console output from the application so it
* doesn't appear in the xpcshell log file.
*
* @return path to the file for piping the console output from the application.
*/
function getAppConsoleLogPath() {
let appConsoleLog = do_get_file("/", true);
appConsoleLog.append(TEST_ID + "_app_console_log");
if (appConsoleLog.exists()) {
appConsoleLog.remove(false);
}
let appConsoleLogPath = appConsoleLog.path;
if (/ /.test(appConsoleLogPath)) {
appConsoleLogPath = '"' + appConsoleLogPath + '"';
}
return appConsoleLogPath;
}
/**
* Gets a file path for the application to dump its arguments into. This is used
* to verify that a callback application is launched.
*
* @return the file for the application to dump its arguments into.
*/
function getAppArgsLogPath() {
let appArgsLog = do_get_file("/", true);
appArgsLog.append("app_args_log");
if (appArgsLog.exists()) {
appArgsLog.remove(false);
}
let appArgsLogPath = appArgsLog.path;
if (/ /.test(appArgsLogPath)) {
appArgsLogPath = '"' + appArgsLogPath + '"';
}
return appArgsLogPath;
}
/**
* Gets the nsIFile reference for the shell script to launch the application. If
* the file exists it will be removed by this function.
*
* @return the nsIFile for the shell script to launch the application.
*/
function getLaunchScript() {
let launchScript = do_get_file("/", true);
launchScript.append("launch.sh");
if (launchScript.exists()) {
launchScript.remove(false);
}
return launchScript;
}
// A shell script is used to get the OS version due to nsSystemInfo not
// returning the actual OS version. It is possible to get the actual OS version
// using ctypes but it would be more complicated than using a shell script.
XPCOMUtils.defineLazyGetter(this, "gIsLessThanMacOSX_10_6", function test_gMacVer() {
if (!IS_MACOSX) {
return false;
}
let [versionScript, versionFile] = getVersionScriptAndFile();
// Precreate the script with executable permissions
versionScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
let scriptContents = "#! /bin/sh\nsw_vers -productVersion >> " + versionFile.path;
writeFile(versionScript, scriptContents);
logTestInfo("created " + versionScript.path + " shell script containing:\n" +
scriptContents);
let versionScriptPath = versionScript.path;
if (/ /.test(versionScriptPath)) {
versionScriptPath = '"' + versionScriptPath + '"';
}
let launchBin = getLaunchBin();
let args = [versionScriptPath];
let process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(launchBin);
process.run(true, args, args.length);
if (process.exitValue != 0) {
do_throw("Version script exited with " + process.exitValue + "... unable " +
"to get Mac OS X version!");
}
let version = readFile(versionFile).split("\n")[0];
logTestInfo("executing on Mac OS X verssion " + version);
return (Services.vc.compare(version, "10.6") < 0)
});
/**
* Checks for the existence of a platform specific application binary that can
* be used for the test and gets its path if it is found.
*
* Note: The application shell scripts for launching the application work on all
* platforms that provide a launch shell script except for Mac OS X 10.5 which
* is why this test uses the binaries to launch the application.
*/
XPCOMUtils.defineLazyGetter(this, "gAppBinPath", function test_gAppBinPath() {
let processDir = getAppDir();
let appBin = processDir.clone();
appBin.append(APP_BIN_NAME + APP_BIN_SUFFIX);
if (appBin.exists()) {
if (IS_WIN) {
let appBinCopy = processDir.clone();
appBinCopy.append(TEST_ID + FILE_WIN_TEST_EXE);
if (appBinCopy.exists()) {
appBinCopy.remove(false);
}
appBin.copyTo(processDir, TEST_ID + FILE_WIN_TEST_EXE);
appBin = processDir.clone();
appBin.append(TEST_ID + FILE_WIN_TEST_EXE);
}
let appBinPath = appBin.path;
if (/ /.test(appBinPath)) {
appBinPath = '"' + appBinPath + '"';
}
return appBinPath;
}
return null;
});
let gWindowsBinDir = null;
/**
* This dummy function just returns false. Tests which wish to adjust the app
* directory on Mac OS X should define a real version of this function.
*/
function shouldAdjustPathsOnMac() {
return false;
}
/**
* This function returns the current process directory on Windows and Linux, and
* the application bundle directory on Mac.
*/
function getAppDir() {
let dir = getCurrentProcessDir();
if (shouldAdjustPathsOnMac()) {
// objdir/dist/bin/../NightlyDebug.app/Contents/MacOS
dir = dir.parent;
dir.append(BUNDLE_NAME);
dir.append("Contents");
dir.append("MacOS");
} else if (IS_WIN && gWindowsBinDir) {
dir = gWindowsBinDir.clone();
}
return dir;
}
/**
* Gets the nsIFile references for the shell script to retrieve the Mac OS X
* version and the nsIFile to pipe the output of the shell script. If either of
* these files exist they will be removed by this function.
*
* @return array containing two nsIFile references. The first array member is
* the nsIFile for the shell script to launch to get the Mac OS X
* version and the second array member is the nsIFile for the piped
* output from the shell script.
*/
function getVersionScriptAndFile() {
let versionScript = do_get_file("/", true);
let versionFile = versionScript.clone();
versionScript.append("get_version.sh");
if (versionScript.exists()) {
versionScript.remove(false);
}
versionFile.append("version.out");
if (versionFile.exists()) {
versionFile.remove(false);
}
return [versionScript, versionFile];
}
// Environment related globals
let gShouldResetEnv = undefined;
let gAddedEnvXRENoWindowsCrashDialog = false;
let gEnvXPCOMDebugBreak;
let gEnvXPCOMMemLeakLog;
let gEnvDyldLibraryPath;
let gEnvLdLibraryPath;
let gEnvUpdateRootOverride = null;
let gEnvAppDirOverride = null;
/**
* Sets the environment that will be used by the application process when it is
* launched.
*/
function setEnvironment() {
// Prevent setting the environment more than once.
if (gShouldResetEnv !== undefined)
return;
gShouldResetEnv = true;
let env = AUS_Cc["@mozilla.org/process/environment;1"].
getService(AUS_Ci.nsIEnvironment);
if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
gAddedEnvXRENoWindowsCrashDialog = true;
logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
"variable to 1... previously it didn't exist");
env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
}
if (IS_UNIX) {
let appGreDir = Services.dirsvc.get("GreD", AUS_Ci.nsIFile);
let envGreDir = AUS_Cc["@mozilla.org/file/local;1"].
createInstance(AUS_Ci.nsILocalFile);
let shouldSetEnv = true;
if (IS_MACOSX) {
if (env.exists("DYLD_LIBRARY_PATH")) {
gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH");
envGreDir.initWithPath(gEnvDyldLibraryPath);
if (envGreDir.path == appGreDir.path) {
gEnvDyldLibraryPath = null;
shouldSetEnv = false;
}
}
if (shouldSetEnv) {
logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " +
appGreDir.path);
env.set("DYLD_LIBRARY_PATH", appGreDir.path);
}
}
else {
if (env.exists("LD_LIBRARY_PATH")) {
gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH");
envGreDir.initWithPath(gEnvLdLibraryPath);
if (envGreDir.path == appGreDir.path) {
gEnvLdLibraryPath = null;
shouldSetEnv = false;
}
}
if (shouldSetEnv) {
logTestInfo("setting LD_LIBRARY_PATH environment variable value to " +
appGreDir.path);
env.set("LD_LIBRARY_PATH", appGreDir.path);
}
}
}
if (env.exists("XPCOM_MEM_LEAK_LOG")) {
gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG");
logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
"previous value " + gEnvXPCOMMemLeakLog);
env.set("XPCOM_MEM_LEAK_LOG", "");
}
if (env.exists("XPCOM_DEBUG_BREAK")) {
gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK");
logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
"warn... previous value " + gEnvXPCOMDebugBreak);
}
else {
logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
"warn... previously it didn't exist");
}
env.set("XPCOM_DEBUG_BREAK", "warn");
if (gEnvUpdateRootOverride) {
logTestInfo("setting the MOZ_UPDATE_ROOT_OVERRIDE environment variable to " +
gEnvUpdateRootOverride + "\n");
env.set("MOZ_UPDATE_ROOT_OVERRIDE", gEnvUpdateRootOverride);
}
if (gEnvAppDirOverride) {
logTestInfo("setting the MOZ_UPDATE_APPDIR_OVERRIDE environment variable to " +
gEnvAppDirOverride + "\n");
env.set("MOZ_UPDATE_APPDIR_OVERRIDE", gEnvAppDirOverride);
}
logTestInfo("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1");
env.set("MOZ_NO_SERVICE_FALLBACK", "1");
}
/**
* Sets the environment back to the original values after launching the
* application.
*/
function resetEnvironment() {
// Prevent resetting the environment more than once.
if (gShouldResetEnv !== true)
return;
gShouldResetEnv = false;
let env = AUS_Cc["@mozilla.org/process/environment;1"].
getService(AUS_Ci.nsIEnvironment);
if (gEnvXPCOMMemLeakLog) {
logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
gEnvXPCOMMemLeakLog);
env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
}
if (gEnvXPCOMDebugBreak) {
logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " +
gEnvXPCOMDebugBreak);
env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
}
else {
logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable");
env.set("XPCOM_DEBUG_BREAK", "");
}
if (IS_UNIX) {
if (IS_MACOSX) {
if (gEnvDyldLibraryPath) {
logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " +
"back to " + gEnvDyldLibraryPath);
env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
}
else {
logTestInfo("removing DYLD_LIBRARY_PATH environment variable");
env.set("DYLD_LIBRARY_PATH", "");
}
}
else {
if (gEnvLdLibraryPath) {
logTestInfo("setting LD_LIBRARY_PATH environment variable value back " +
"to " + gEnvLdLibraryPath);
env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
}
else {
logTestInfo("removing LD_LIBRARY_PATH environment variable");
env.set("LD_LIBRARY_PATH", "");
}
}
}
if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
"variable");
env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
}
if (gEnvUpdateRootOverride) {
logTestInfo("removing the MOZ_UPDATE_ROOT_OVERRIDE environment variable\n");
env.set("MOZ_UPDATE_ROOT_OVERRIDE", "");
gEnvUpdateRootOverride = null;
}
if (gEnvAppDirOverride) {
logTestInfo("removing the MOZ_UPDATE_APPDIR_OVERRIDE environment variable\n");
env.set("MOZ_UPDATE_APPDIR_OVERRIDE", "");
gEnvAppDirOverride = null;
}
logTestInfo("removing MOZ_NO_SERVICE_FALLBACK environment variable");
env.set("MOZ_NO_SERVICE_FALLBACK", "");
}

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

@ -9,9 +9,7 @@
* manifest file (e.g. updatev2.manifest).
*/
// Use a copy of the main application executable for the test to avoid main
// executable in use errors.
const FILE_WIN_TEST_EXE = "aus_test_app.exe";
const TEST_ID = "0200";
// Backup the updater.ini and use a custom one to prevent the updater from
// launching a post update executable.
@ -27,85 +25,6 @@ const APP_TIMER_TIMEOUT = 15000;
let gAppTimer;
let gProcess;
// Environment related globals
let gShouldResetEnv = undefined;
let gAddedEnvXRENoWindowsCrashDialog = false;
let gEnvXPCOMDebugBreak;
let gEnvXPCOMMemLeakLog;
let gEnvDyldLibraryPath;
let gEnvLdLibraryPath;
// A shell script is used to get the OS version due to nsSystemInfo not
// returning the actual OS version. It is possible to get the actual OS version
// using ctypes but it would be more complicated than using a shell script.
XPCOMUtils.defineLazyGetter(this, "gIsLessThanMacOSX_10_6", function test_gMacVer() {
if (!IS_MACOSX) {
return false;
}
let [versionScript, versionFile] = getVersionScriptAndFile();
// Precreate the script with executable permissions
versionScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
let scriptContents = "#! /bin/sh\nsw_vers -productVersion >> " + versionFile.path;
writeFile(versionScript, scriptContents);
logTestInfo("created " + versionScript.path + " shell script containing:\n" +
scriptContents);
let versionScriptPath = versionScript.path;
if (/ /.test(versionScriptPath)) {
versionScriptPath = '"' + versionScriptPath + '"';
}
let launchBin = getLaunchBin();
let args = [versionScriptPath];
let process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(launchBin);
process.run(true, args, args.length);
if (process.exitValue != 0) {
do_throw("Version script exited with " + process.exitValue + "... unable " +
"to get Mac OS X version!");
}
let version = readFile(versionFile).split("\n")[0];
logTestInfo("executing on Mac OS X verssion " + version);
return (Services.vc.compare(version, "10.6") < 0)
});
/**
* Checks for the existence of a platform specific application binary that can
* be used for the test and gets its path if it is found.
*
* Note: The application shell scripts for launching the application work on all
* platforms that provide a launch shell script except for Mac OS X 10.5 which
* is why this test uses the binaries to launch the application.
*/
XPCOMUtils.defineLazyGetter(this, "gAppBinPath", function test_gAppBinPath() {
let processDir = getCurrentProcessDir();
let appBin = processDir.clone();
appBin.append(APP_BIN_NAME + APP_BIN_SUFFIX);
if (appBin.exists()) {
if (IS_WIN) {
let appBinCopy = processDir.clone();
appBinCopy.append(FILE_WIN_TEST_EXE);
if (appBinCopy.exists()) {
appBinCopy.remove(false);
}
appBin.copyTo(processDir, FILE_WIN_TEST_EXE);
appBin = processDir.clone();
appBin.append(FILE_WIN_TEST_EXE);
}
let appBinPath = appBin.path;
if (/ /.test(appBinPath)) {
appBinPath = '"' + appBinPath + '"';
}
return appBinPath;
}
return null;
});
function run_test() {
do_test_pending();
do_register_cleanup(end_test);
@ -292,199 +211,6 @@ let gTimerCallback = {
QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback])
};
/**
* Sets the environment that will be used by the application process when it is
* launched.
*/
function setEnvironment() {
// Prevent setting the environment more than once.
if (gShouldResetEnv !== undefined)
return;
gShouldResetEnv = true;
let env = AUS_Cc["@mozilla.org/process/environment;1"].
getService(AUS_Ci.nsIEnvironment);
if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) {
gAddedEnvXRENoWindowsCrashDialog = true;
logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
"variable to 1... previously it didn't exist");
env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1");
}
if (IS_UNIX) {
let appGreDir = Services.dirsvc.get("GreD", AUS_Ci.nsIFile);
let envGreDir = AUS_Cc["@mozilla.org/file/local;1"].
createInstance(AUS_Ci.nsILocalFile);
let shouldSetEnv = true;
if (IS_MACOSX) {
if (env.exists("DYLD_LIBRARY_PATH")) {
gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH");
envGreDir.initWithPath(gEnvDyldLibraryPath);
if (envGreDir.path == appGreDir.path) {
gEnvDyldLibraryPath = null;
shouldSetEnv = false;
}
}
if (shouldSetEnv) {
logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " +
appGreDir.path);
env.set("DYLD_LIBRARY_PATH", appGreDir.path);
}
}
else {
if (env.exists("LD_LIBRARY_PATH")) {
gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH");
envGreDir.initWithPath(gEnvLdLibraryPath);
if (envGreDir.path == appGreDir.path) {
gEnvLdLibraryPath = null;
shouldSetEnv = false;
}
}
if (shouldSetEnv) {
logTestInfo("setting LD_LIBRARY_PATH environment variable value to " +
appGreDir.path);
env.set("LD_LIBRARY_PATH", appGreDir.path);
}
}
}
if (env.exists("XPCOM_MEM_LEAK_LOG")) {
gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG");
logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " +
"previous value " + gEnvXPCOMMemLeakLog);
env.set("XPCOM_MEM_LEAK_LOG", "");
}
if (env.exists("XPCOM_DEBUG_BREAK")) {
gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK");
logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
"warn... previous value " + gEnvXPCOMDebugBreak);
}
else {
logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " +
"warn... previously it didn't exist");
}
env.set("XPCOM_DEBUG_BREAK", "warn");
}
/**
* Sets the environment back to the original values after launching the
* application.
*/
function resetEnvironment() {
// Prevent resetting the environment more than once.
if (gShouldResetEnv !== true)
return;
gShouldResetEnv = false;
let env = AUS_Cc["@mozilla.org/process/environment;1"].
getService(AUS_Ci.nsIEnvironment);
if (gEnvXPCOMMemLeakLog) {
logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " +
gEnvXPCOMMemLeakLog);
env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog);
}
if (gEnvXPCOMDebugBreak) {
logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " +
gEnvXPCOMDebugBreak);
env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak);
}
else {
logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable");
env.set("XPCOM_DEBUG_BREAK", "");
}
if (IS_UNIX) {
if (IS_MACOSX) {
if (gEnvDyldLibraryPath) {
logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " +
"back to " + gEnvDyldLibraryPath);
env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath);
}
else {
logTestInfo("removing DYLD_LIBRARY_PATH environment variable");
env.set("DYLD_LIBRARY_PATH", "");
}
}
else {
if (gEnvLdLibraryPath) {
logTestInfo("setting LD_LIBRARY_PATH environment variable value back " +
"to " + gEnvLdLibraryPath);
env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath);
}
else {
logTestInfo("removing LD_LIBRARY_PATH environment variable");
env.set("LD_LIBRARY_PATH", "");
}
}
}
if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) {
logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " +
"variable");
env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "");
}
}
/**
* Returns the platform specific arguments used by nsIProcess when launching
* the application.
*
* @return an array of arguments to be passed to nsIProcess.
*
* Notes:
* 1. Mozilla universal binaries that contain both i386 and x86_64 on Mac OS X
* 10.5.x must be launched using the i386 architecture.
* 2. A shell is necessary to pipe the application's console output which
* would otherwise pollute the xpcshell log.
*
* Command line arguments used when launching the application:
* -no-remote prevents shell integration from being affected by an existing
* application process.
* -process-updates makes the application exits after being relaunched by the
* updater.
* 1> pipes stdout to a file.
* appConsoleLogPath is the file path to pipe the output from the shell.
* Otherwise the output from the application will end up in the xpchsell log.
* 2>&1 pipes stderr to sdout.
*/
function getProcessArgs() {
// Pipe the output from the launched application to a file so the output from
// its console isn't present in the xpcshell log.
let appConsoleLogPath = getAppConsoleLogPath();
let args;
if (IS_UNIX) {
let launchScript = getLaunchScript();
// Precreate the script with executable permissions
launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
let scriptContents = "#! /bin/sh\n";
// On Mac OS X versions prior to 10.6 the i386 acrhitecture must be used.
if (gIsLessThanMacOSX_10_6) {
scriptContents += "arch -arch i386 ";
}
scriptContents += gAppBinPath + " -no-remote -process-updates 1> " +
appConsoleLogPath + " 2>&1";
writeFile(launchScript, scriptContents);
logTestInfo("created " + launchScript.path + " containing:\n" +
scriptContents);
args = [launchScript.path];
}
else {
args = ["/D", "/Q", "/C", gAppBinPath, "-no-remote", "-process-updates",
"1>", appConsoleLogPath, "2>&1"];
}
return args;
}
/**
* Gets the directory where the update adds / removes the files contained in the
* update.
@ -501,64 +227,6 @@ function getUpdateTestDir() {
return updateTestDir;
}
/**
* Gets a file path for piping the console output from the application so it
* doesn't appear in the xpcshell log file.
*
* @return path to the file for piping the console output from the application.
*/
function getAppConsoleLogPath() {
let appConsoleLog = do_get_file("/", true);
appConsoleLog.append("app_console_log");
if (appConsoleLog.exists()) {
appConsoleLog.remove(false);
}
let appConsoleLogPath = appConsoleLog.path;
if (/ /.test(appConsoleLogPath)) {
appConsoleLogPath = '"' + appConsoleLogPath + '"';
}
return appConsoleLogPath;
}
/**
* Gets the nsIFile references for the shell script to retrieve the Mac OS X
* version and the nsIFile to pipe the output of the shell script. If either of
* these files exist they will be removed by this function.
*
* @return array containing two nsIFile references. The first array member is
* the nsIFile for the shell script to launch to get the Mac OS X
* version and the second array member is the nsIFile for the piped
* output from the shell script.
*/
function getVersionScriptAndFile() {
let versionScript = do_get_file("/", true);
let versionFile = versionScript.clone();
versionScript.append("get_version.sh");
if (versionScript.exists()) {
versionScript.remove(false);
}
versionFile.append("version.out");
if (versionFile.exists()) {
versionFile.remove(false);
}
return [versionScript, versionFile];
}
/**
* Gets the nsIFile reference for the shell script to launch the application. If
* the file exists it will be removed by this function.
*
* @return the nsIFile for the shell script to launch the application.
*/
function getLaunchScript() {
let launchScript = do_get_file("/", true);
launchScript.append("launch.sh");
if (launchScript.exists()) {
launchScript.remove(false);
}
return launchScript;
}
/**
* Checks if the update has finished and if it has finished performs checks for
* the test.

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

@ -0,0 +1,71 @@
# ***** 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 the Application Update Service.
#
# The Initial Developer of the Original Code is
# Ehsan Akhgari <ehsan@mozilla.com>.
#
# Portions created by the Initial Developer are Copyright (C) 2011
# the Mozilla Foundation. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either of 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@
relativesrcdir = toolkit/mozapps/update/test_svc
include $(DEPTH)/config/autoconf.mk
XPCSHELL_TESTS = \
unit \
$(NULL)
TESTROOT = $(call core_abspath,$(DEPTH))/_tests/xpcshell/$(relativesrcdir)
DEFINES += \
-DAB_CD=$(AB_CD) \
-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
-DBIN_SUFFIX=$(BIN_SUFFIX) \
-DNS_NO_XPCOM \
$(NULL)
include $(topsrcdir)/config/rules.mk
libs:: ../test/unit/head_update.js.in
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) $^ > $(TESTROOT)/unit/head_update.js
ifneq ($(OS_TARGET),Android)
ifndef MOZ_PROFILE_GENERATE
libs::
$(INSTALL) ../test/TestAUSHelper$(BIN_SUFFIX) $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit
$(INSTALL) -D $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit/data
$(INSTALL) $(topsrcdir)/$(relativesrcdir)/../test/unit/data/* $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit/data
endif
endif # Not Android

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

@ -0,0 +1,44 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Bootstrap the tests using the service by installing our own version of the service */
const TEST_ID = "0000_svc";
const TEST_FILES = [
{
description : "the dummy file to make sure that the update worked",
fileName : "dummy",
relPathDir : "/",
originalContents : null,
compareContents : "",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}
];
function run_test() {
if (!shouldRunServiceTest(true)) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied, null, false);
}
function checkUpdateApplied() {
checkFilesAfterUpdateSuccess();
// We need to check the service log even though this is a bootstrap
// because the app bin could be in use by this test by the time the next
// test runs.
checkCallbackServiceLog();
}

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

@ -0,0 +1,298 @@
/* ***** 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.org code.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
*
* 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 *****
*/
/* General Complete MAR File Patch Apply Test */
const TEST_ID = "0110_svc";
// All we care about is that the last modified time has changed so that Mac OS
// X Launch Services invalidates its cache so the test allows up to one minute
// difference in the last modified time.
const MAX_TIME_DIFFERENCE = 60000;
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : 0767,
comparePerms : 0767
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/complete_precomplete",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/complete_removed-files",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0777,
comparePerms : 0755
}, {
description : "Added by update.manifest (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0767,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0777,
comparePerms : 0755
}, {
description : "Added by update.manifest (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0677,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : 0776,
comparePerms : 0644
}, {
description : "Removed by precomplete (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Removed by precomplete (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : true
}, {
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,308 @@
/* ***** 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.org code.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
*
* 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 *****
*/
/* General Partial MAR File Patch Apply Test */
const TEST_ID = "0111_svc";
// All we care about is that the last modified time has changed so that Mac OS
// X Launch Services invalidates its cache so the test allows up to one minute
// difference in the last modified time.
const MAX_TIME_DIFFERENCE = 60000;
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : 0644,
comparePerms : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/complete_precomplete",
compareFile : "data/partial_precomplete",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Patched by update.manifest (patch)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0755,
comparePerms : 0755
}, {
description : "Patched by update.manifest (patch)",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0755,
comparePerms : 0755
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Patched by update.manifest (patch)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Added by update.manifest (add)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/partial.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "00text2",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Removed by update.manifest (remove)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Removed by update.manifest (remove)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/10/",
dirRemoved : true
}, {
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_PARTIAL_FILE);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
// Check that trying to change channels for a partial update doesn't change
// the update channel (the channel-prefs.js file should not be updated).
let force = updatesDir.clone();
force.append(CHANNEL_CHANGE_FILE);
force.create(AUS_Ci.nsIFile.FILE_TYPE, PERMS_FILE);
// apply the partial mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,303 @@
/* ***** 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.org code.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
*
* 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 *****
*/
/* General Partial MAR File Patch Apply Failure Test */
const TEST_ID = "0112_svc";
// All we care about is that the last modified time has changed so that Mac OS
// X Launch Services invalidates its cache so the test allows up to one minute
// difference in the last modified time.
const MAX_TIME_DIFFERENCE = 60000;
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : 0767,
comparePerms : null
}, {
description : "Not added for failed update (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/complete_precomplete",
compareFile : "data/complete_precomplete",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not added for failed update (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0775
}, {
description : "Not patched for failed update (patch-if)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not patched for failed update (patch-if)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0644,
comparePerms : 0644
}, {
description : "Not patched for failed update (patch)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0755,
comparePerms : 0755
}, {
description : "Not patched for failed update (patch) and causes " +
"LoadSourceFile failed",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png",
originalPerms : 0755,
comparePerms : 0755
}, {
description : "Not added for failed update (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Not patched for failed update (patch)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not added for failed update (add)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Not added for failed update (add)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Not added for failed update (add)",
fileName : "00text2",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Not removed for failed update (remove)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ShouldNotBeDeleted\n",
compareContents : "ShouldNotBeDeleted\n",
originalFile : null,
compareFile : null,
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Not removed for failed update (remove)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ShouldNotBeDeleted\n",
compareContents : "ShouldNotBeDeleted\n",
originalFile : null,
compareFile : null,
originalPerms : 0666,
comparePerms : 0666
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Not removed for failed update (rmdir)",
relPathDir : "a/b/1/10/",
dirRemoved : false
}, {
description : "Not removed for failed update (rmdir)",
relPathDir : "a/b/1/",
dirRemoved : false
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_PARTIAL_FILE);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
// Check that trying to change channels for a failed partial update doesn't
// change the update channel (the channel-prefs.js file should not be updated).
let force = updatesDir.clone();
force.append(CHANNEL_CHANGE_FILE);
force.create(AUS_Ci.nsIFile.FILE_TYPE, PERMS_FILE);
// apply the partial mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED, checkUpdateApplied);
}
function checkUpdateApplied() {
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
logTestInfo("testing update.status should be " + STATE_FAILED);
// The update status format for a failure is failed: # where # is the error
// code for the failure.
do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
checkFilesAfterUpdateFailure();
checkUpdateLogContents(LOG_PARTIAL_FAILURE);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,268 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Channel change complete MAR file patch apply test */
const TEST_ID = "0120_svc";
// All we care about is that the last modified time has changed so that Mac OS
// X Launch Services invalidates its cache so the test allows up to one minute
// difference in the last modified time.
const MAX_TIME_DIFFERENCE = 60000;
// The files are in the same order as they are applied from the mar
const TEST_FILES = [
{
description : "Added by update.manifest (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0767,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/complete_precomplete",
originalPerms : 0755,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/complete_removed-files",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0777,
comparePerms : 0755
}, {
description : "Added by update.manifest (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0767,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png",
originalPerms : 0777,
comparePerms : 0755
}, {
description : "Added by update.manifest (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0677,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png",
originalPerms : 0776,
comparePerms : 0644
}, {
description : "Removed by precomplete (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Removed by precomplete (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : true
}, {
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
// Check that trying to change channels for a complete update changes the
// update channel (the channel-prefs.js file should be updated).
let channelchange = updatesDir.clone();
channelchange.append(CHANNEL_CHANGE_FILE);
channelchange.create(AUS_Ci.nsIFile.FILE_TYPE, PERMS_FILE);
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContents(LOG_COMPLETE_CC_SUCCESS);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,222 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Replace app binary complete MAR file patch apply success test */
const TEST_ID = "0150_svc";
const MAR_COMPLETE_WIN_FILE = "data/complete_win.mar";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/complete_precomplete"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/complete_removed-files"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_after.exe",
compareFile : "data/partial_in_use_win_before.exe"
}, {
description : "Added by update.manifest (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add) file in use",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_after.exe",
compareFile : "data/partial_in_use_win_before.exe"
}, {
description : "Added by update.manifest (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Removed by precomplete (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Removed by precomplete (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : true
}, {
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_WIN_FILE);
gCallbackBinFile = "exe0.exe";
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,224 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Patch app binary partial MAR file patch apply success test */
const TEST_ID = "0151_svc";
const MAR_IN_USE_WIN_FILE = "data/partial_win.mar";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/complete_precomplete",
compareFile : "data/partial_precomplete"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest (patch)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_before.exe",
compareFile : "data/partial_in_use_win_after.exe"
}, {
description : "Patched by update.manifest (patch) file in use",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_before.exe",
compareFile : "data/partial_in_use_win_after.exe"
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest (patch)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest (add)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest (add)",
fileName : "00text2",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Removed by update.manifest (remove)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Removed by update.manifest (remove)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/10/",
dirRemoved : true
}, {
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_IN_USE_WIN_FILE);
gCallbackBinFile = "exe0.exe";
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,238 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Application in use complete MAR file patch apply failure test */
const TEST_ID = "0160_svc";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/complete_precomplete"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/complete_removed-files"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_before.exe",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add) file in use",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_after.exe",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Removed by precomplete (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Removed by precomplete (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed for complete update (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : true
}, {
description : "Removed for complete update (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
// Launch the callback helper application so it is in use during the update
let callbackApp = getApplyDirFile("a/b/" + gCallbackBinFile);
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20"];
let callbackAppProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
callbackAppProcess.init(callbackApp);
callbackAppProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
// The update status format for a failure is failed: # where # is the error
// code for the failure.
do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,240 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File locked complete MAR file patch apply failure test */
const TEST_ID = "0170_svc";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/partial_precomplete"
}, {
description : "Not added for failed update (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png"
}, {
description : "Not added for failed update (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png"
}, {
description : "Not added for failed update (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/partial_removed-files"
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png"
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png"
}, {
description : "Not added for failed update (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png"
}, {
description : "Not removed for failed update (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ShouldNotBeDeleted\n",
compareContents : "ShouldNotBeDeleted\n",
originalFile : null,
compareFile : null
}, {
description : "Not removed for failed update (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ShouldNotBeDeleted\n",
compareContents : "ShouldNotBeDeleted\n",
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Not removed for failed update (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : false
}, {
description : "Not removed for failed update (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : false
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
// Exclusively lock an existing file so it is in use during the update
let helperBin = do_get_file(HELPER_BIN_FILE);
let helperDestDir = getApplyDirFile("a/b/");
helperBin.copyTo(helperDestDir, HELPER_BIN_FILE);
helperBin = getApplyDirFile("a/b/" + HELPER_BIN_FILE);
// Strip off the first two directories so the path has to be from the helper's
// working directory.
let lockFileRelPath = TEST_FILES[3].relPathDir.split("/");
lockFileRelPath = lockFileRelPath.slice(2);
lockFileRelPath = lockFileRelPath.join("/") + "/" + TEST_FILES[3].fileName;
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20", lockFileRelPath];
let lockFileProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
lockFileProcess.init(helperBin);
lockFileProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_FAILED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
// The update status format for a failure is failed: # where # is the error
// code for the failure.
do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
checkFilesAfterUpdateFailure();
checkUpdateLogContains(ERR_RENAME_FILE);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,241 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File locked partial MAR file patch apply failure test */
const TEST_ID = "0171_svc";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/complete_precomplete",
compareFile : "data/complete_precomplete"
}, {
description : "Not added for failed update (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not patched for failed update (patch-if)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not patched for failed update (patch-if)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not added for failed update (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not patched for failed update (patch-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not patched for failed update (patch)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not patched for failed update (patch) and causes " +
"LoadSourceFile failed",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/partial.png"
}, {
description : "Not added for failed update (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Not patched for failed update (patch)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/complete.png"
}, {
description : "Not added for failed update (add)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not added for failed update (add)",
fileName : "00text2",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Not removed for failed update (remove)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ShouldNotBeDeleted\n",
compareContents : "ShouldNotBeDeleted\n",
originalFile : null,
compareFile : null
}, {
description : "Not removed for failed update (remove)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ShouldNotBeDeleted\n",
compareContents : "ShouldNotBeDeleted\n",
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Not removed for failed update (rmdir)",
relPathDir : "a/b/1/10/",
dirRemoved : false
}, {
description : "Not removed for failed update (rmdir)",
relPathDir : "a/b/1/",
dirRemoved : false
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_PARTIAL_FILE);
// Exclusively lock an existing file so it is in use during the update
let helperBin = do_get_file(HELPER_BIN_FILE);
let helperDestDir = getApplyDirFile("a/b/");
helperBin.copyTo(helperDestDir, HELPER_BIN_FILE);
helperBin = getApplyDirFile("a/b/" + HELPER_BIN_FILE);
// Strip off the first two directories so the path has to be from the helper's
// working directory.
let lockFileRelPath = TEST_FILES[3].relPathDir.split("/");
lockFileRelPath = lockFileRelPath.slice(2);
lockFileRelPath = lockFileRelPath.join("/") + "/" + TEST_FILES[3].fileName;
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20", lockFileRelPath];
let lockFileProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
lockFileProcess.init(helperBin);
lockFileProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_FAILED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
// The update status format for a failure is failed: # where # is the error
// code for the failure.
do_check_eq(readStatusFile(updatesDir).split(": ")[0], STATE_FAILED);
checkFilesAfterUpdateFailure();
checkUpdateLogContains(ERR_UNABLE_OPEN_DEST);
logTestInfo("testing tobedeleted directory doesn't exist");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_false(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,237 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use complete MAR file patch apply success test */
const TEST_ID = "0180_svc";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/complete_precomplete"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/complete_removed-files"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : HELPER_BIN_FILE,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add) file in use",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : HELPER_BIN_FILE,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Removed by precomplete (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Removed by precomplete (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : true
}, {
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
// Launch an existing file so it is in use during the update
let fileInUseBin = getApplyDirFile(TEST_FILES[14].relPathDir +
TEST_FILES[14].fileName);
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20"];
let fileInUseProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContains(ERR_BACKUP_DISCARD);
logTestInfo("testing tobedeleted directory exists");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_true(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,240 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use partial MAR file patch apply success test */
const TEST_ID = "0181_svc";
const MAR_IN_USE_WIN_FILE = "data/partial_win.mar";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/complete_precomplete",
compareFile : "data/partial_precomplete"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Patched by update.manifest (patch)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_before.exe",
compareFile : "data/partial_in_use_win_after.exe"
}, {
description : "Patched by update.manifest (patch) file in use",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_in_use_win_before.exe",
compareFile : "data/partial_in_use_win_after.exe"
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Patched by update.manifest (patch)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest (add)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/partial.png"
}, {
description : "Added by update.manifest (add)",
fileName : "00text2",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null
}, {
description : "Removed by update.manifest (remove)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Removed by update.manifest (remove)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/10/",
dirRemoved : true
}, {
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_IN_USE_WIN_FILE);
// Launch an existing file so it is in use during the update
let fileInUseBin = getApplyDirFile(TEST_FILES[12].relPathDir +
TEST_FILES[12].fileName);
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20"];
let fileInUseProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContains(ERR_BACKUP_DISCARD);
logTestInfo("testing tobedeleted directory exists");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_true(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,247 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use inside removed dir complete MAR file patch apply success test */
const TEST_ID = "0182_svc";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/partial_precomplete",
compareFile : "data/complete_precomplete"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "removed-files",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/partial_removed-files",
compareFile : "data/complete_removed-files"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/partial.png",
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : HELPER_BIN_FILE,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add) file in use",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : HELPER_BIN_FILE,
compareFile : "data/complete.png"
}, {
description : "Added by update.manifest (add)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromComplete\n",
compareContents : "FromComplete\n",
originalFile : null,
compareFile : null
}, {
description : "Added by update.manifest (add)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/complete.png"
}, {
description : "Removed by precomplete (remove)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}, {
description : "Removed by precomplete (remove)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/20/",
dirRemoved : true
}, {
description : "Removed by precomplete (rmdir)",
relPathDir : "a/b/2/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_COMPLETE_FILE);
let fileInUseBin = getApplyDirFile(TEST_DIRS[4].relPathDir +
TEST_DIRS[4].subDirs[0] +
TEST_DIRS[4].subDirFiles[0]);
// Remove the empty file created for the test so the helper application can
// replace it.
fileInUseBin.remove(false);
let helperBin = do_get_file(HELPER_BIN_FILE);
let fileInUseDir = getApplyDirFile(TEST_DIRS[4].relPathDir +
TEST_DIRS[4].subDirs[0]);
helperBin.copyTo(fileInUseDir, TEST_DIRS[4].subDirFiles[0]);
// Launch an existing file so it is in use during the update
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20"];
let fileInUseProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContains(ERR_BACKUP_DISCARD);
logTestInfo("testing tobedeleted directory exists");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_true(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,288 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use inside removed dir partial MAR file patch apply success test */
const TEST_ID = "0183_svc";
const MAR_IN_USE_WIN_FILE = "data/partial.mar";
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
{
description : "Only added by update.manifest for complete updates " +
"when there is a channel change (add-cc)",
fileName : "channel-prefs.js",
relPathDir : "a/b/defaults/pref/",
originalContents : "ShouldNotBeReplaced\n",
compareContents : "ShouldNotBeReplaced\n",
originalFile : null,
compareFile : null,
originalPerms : 0644,
comparePerms : null
}, {
description : "Added by update.manifest (add)",
fileName : "precomplete",
relPathDir : "",
originalContents : null,
compareContents : null,
originalFile : "data/complete_precomplete",
compareFile : "data/partial_precomplete",
originalPerms : 0666,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "searchpluginstext0",
relPathDir : "a/b/searchplugins/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : 0775,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng1.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Patched by update.manifest if the file exists " +
"(patch-if)",
fileName : "searchpluginspng0.png",
relPathDir : "a/b/searchplugins/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions1text0",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png1.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions1png0.png",
relPathDir : "a/b/extensions/extensions1/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Added by update.manifest if the parent directory " +
"exists (add-if)",
fileName : "extensions0text0",
relPathDir : "a/b/extensions/extensions0/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png1.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Patched by update.manifest if the parent directory " +
"exists (patch-if)",
fileName : "extensions0png0.png",
relPathDir : "a/b/extensions/extensions0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Patched by update.manifest (patch)",
fileName : "exe0.exe",
relPathDir : "a/b/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0755,
comparePerms : null
}, {
description : "Patched by update.manifest (patch)",
fileName : "0exe0.exe",
relPathDir : "a/b/0/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0755,
comparePerms : null
}, {
description : "Added by update.manifest (add)",
fileName : "00text0",
relPathDir : "a/b/0/00/",
originalContents : "ToBeReplacedWithFromPartial\n",
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : 0644,
comparePerms : null
}, {
description : "Patched by update.manifest (patch)",
fileName : "00png0.png",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : null,
originalFile : "data/complete.png",
compareFile : "data/partial.png",
originalPerms : 0666,
comparePerms : 0666
}, {
description : "Added by update.manifest (add)",
fileName : "20text0",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "20png0.png",
relPathDir : "a/b/2/20/",
originalContents : null,
compareContents : null,
originalFile : null,
compareFile : "data/partial.png",
originalPerms : null,
comparePerms : 0644
}, {
description : "Added by update.manifest (add)",
fileName : "00text2",
relPathDir : "a/b/0/00/",
originalContents : null,
compareContents : "FromPartial\n",
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : 0644
}, {
description : "Removed by update.manifest (remove)",
fileName : "10text0",
relPathDir : "a/b/1/10/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}, {
description : "Removed by update.manifest (remove)",
fileName : "00text1",
relPathDir : "a/b/0/00/",
originalContents : "ToBeDeleted\n",
compareContents : null,
originalFile : null,
compareFile : null,
originalPerms : null,
comparePerms : null
}];
ADDITIONAL_TEST_DIRS = [
{
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/10/",
dirRemoved : true
}, {
description : "Removed by update.manifest (rmdir)",
relPathDir : "a/b/1/",
dirRemoved : true
}];
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_IN_USE_WIN_FILE);
let fileInUseBin = getApplyDirFile(TEST_DIRS[2].relPathDir +
TEST_DIRS[2].files[0]);
// Remove the empty file created for the test so the helper application can
// replace it.
fileInUseBin.remove(false);
let helperBin = do_get_file(HELPER_BIN_FILE);
let fileInUseDir = getApplyDirFile(TEST_DIRS[2].relPathDir);
helperBin.copyTo(fileInUseDir, TEST_DIRS[2].files[0]);
// Launch an existing file so it is in use during the update
let args = [getApplyDirPath() + "a/b/", "input", "output", "-s", "20"];
let fileInUseProcess = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
// apply the complete mar
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
logTestInfo("testing update.status should be " + STATE_SUCCEEDED);
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
do_check_eq(readStatusFile(updatesDir), STATE_SUCCEEDED);
checkFilesAfterUpdateSuccess();
checkUpdateLogContains(ERR_BACKUP_DISCARD);
logTestInfo("testing tobedeleted directory exists");
let toBeDeletedDir = getApplyDirFile("tobedeleted", true);
do_check_true(toBeDeletedDir.exists());
checkCallbackServiceLog();
}

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

@ -0,0 +1,256 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Test applying an update by staging an update and launching an application */
/**
* The MAR file used for this test should not contain a version 2 update
* manifest file (e.g. updatev2.manifest).
*/
const TEST_ID = "0200_svc";
// Backup the updater.ini and use a custom one to prevent the updater from
// launching a post update executable.
const FILE_UPDATER_INI_BAK = "updater.ini.bak";
// Number of milliseconds for each do_timeout call.
const CHECK_TIMEOUT_MILLI = 1000;
// Maximum number of milliseconds the process that is launched can run before
// the test will try to kill it.
const APP_TIMER_TIMEOUT = 15000;
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
do_test_pending();
do_register_cleanup(end_test);
removeUpdateDirsAndFiles();
if (!gAppBinPath) {
do_throw("Main application binary not found... expected: " +
APP_BIN_NAME + APP_BIN_SUFFIX);
return;
}
let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
let patches = getLocalPatchString(null, null, null, null, null, "true",
STATE_PENDING);
let updates = getLocalUpdateString(patches, null, null, null, null, null,
null, null, null, null, null, null,
null, "true", channel);
writeUpdatesToXMLFile(getLocalUpdatesXMLString(updates), true);
// Read the application.ini and use its application version
let processDir = getCurrentProcessDir();
let file = processDir.clone();
file.append("application.ini");
let ini = AUS_Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
getService(AUS_Ci.nsIINIParserFactory).
createINIParser(file);
let version = ini.getString("App", "Version");
writeVersionFile(version);
// This is the directory where the update files will be located
let updateTestDir = getUpdateTestDir();
try {
removeDirRecursive(updateTestDir);
}
catch (e) {
logTestInfo("unable to remove directory - path: " + updateTestDir.path +
", exception: " + e);
}
// Add the directory where the update files will be added and add files that
// will be removed.
if (!updateTestDir.exists()) {
updateTestDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
}
logTestInfo("update test directory path: " + updateTestDir.path);
file = updateTestDir.clone();
file.append("UpdateTestRemoveFile");
writeFile(file, "ToBeRemoved");
file = updateTestDir.clone();
file.append("UpdateTestAddFile");
writeFile(file, "ToBeReplaced");
file = updateTestDir.clone();
file.append("removed-files");
writeFile(file, "ToBeReplaced");
let updatesPatchDir = getUpdatesDir();
updatesPatchDir.append("0");
let mar = do_get_file("data/simple.mar");
mar.copyTo(updatesPatchDir, FILE_UPDATE_ARCHIVE);
// Backup the updater.ini
let updaterIni = processDir.clone();
updaterIni.append(FILE_UPDATER_INI);
updaterIni.moveTo(processDir, FILE_UPDATER_INI_BAK);
// Create a new updater.ini to avoid applications that provide a post update
// executable.
let updaterIniContents = "[Strings]\n" +
"Title=Update Test\n" +
"Info=Application Update XPCShell Test - " +
"test_0200_general.js\n";
updaterIni = processDir.clone();
updaterIni.append(FILE_UPDATER_INI);
writeFile(updaterIni, updaterIniContents);
let updatesRootDir = processDir.clone();
updatesRootDir.append("updates");
updatesRootDir.append("0");
getApplyDirPath = function() {
return processDir.path;
}
getApplyDirFile = function (aRelPath, allowNonexistent) {
let base = AUS_Cc["@mozilla.org/file/local;1"].
createInstance(AUS_Ci.nsILocalFile);
base.initWithPath(getApplyDirPath());
let path = (aRelPath ? aRelPath : "");
let bits = path.split("/");
for (let i = 0; i < bits.length; i++) {
if (bits[i]) {
if (bits[i] == "..")
base = base.parent;
else
base.append(bits[i]);
}
}
if (!allowNonexistent && !base.exists()) {
_passed = false;
var stack = Components.stack.caller;
_dump("TEST-UNEXPECTED-FAIL | " + stack.filename + " | [" +
stack.name + " : " + stack.lineNumber + "] " + base.path +
" does not exist\n");
}
return base;
}
runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED, checkUpdateFinished, updatesRootDir);
}
function end_test() {
resetEnvironment();
let processDir = getCurrentProcessDir();
// Restore the backed up updater.ini
let updaterIni = processDir.clone();
updaterIni.append(FILE_UPDATER_INI_BAK);
updaterIni.moveTo(processDir, FILE_UPDATER_INI);
// Remove the copy of the application executable used for the test on
// Windows if it exists.
let appBinCopy = processDir.clone();
appBinCopy.append(FILE_WIN_TEST_EXE);
if (appBinCopy.exists()) {
appBinCopy.remove(false);
}
// Remove the files added by the update.
let updateTestDir = getUpdateTestDir();
try {
logTestInfo("removing update test directory " + updateTestDir.path);
removeDirRecursive(updateTestDir);
}
catch (e) {
logTestInfo("unable to remove directory - path: " + updateTestDir.path +
", exception: " + e);
}
// This will delete the app console log file if it exists.
getAppConsoleLogPath();
cleanUp();
}
/**
* Gets the directory where the update adds / removes the files contained in the
* update.
*
* @return nsIFile for the directory where the update adds / removes the files
* contained in the update mar.
*/
function getUpdateTestDir() {
let updateTestDir = getCurrentProcessDir();
updateTestDir.append("update_test");
return updateTestDir;
}
/**
* Checks if the update has finished and if it has finished performs checks for
* the test.
*/
function checkUpdateFinished() {
// Don't proceed until the update.log has been created.
let log = getUpdatesDir();
log.append("0");
log.append(FILE_UPDATE_LOG);
if (!log.exists()) {
do_timeout(CHECK_TIMEOUT_MILLI, checkUpdateFinished);
return;
}
// Log the contents of the update.log so it is simpler to diagnose a test
// failure. For example, on Windows if the application binary is in use the
// updater will not apply the update.
let contents = readFile(log);
logTestInfo("contents of " + log.path + ":\n" +
contents.replace(/\r\n/g, "\n"));
if (contents.indexOf("NS_main: file in use") != -1) {
do_throw("the application can't be in use when running this test");
}
standardInit();
let update = gUpdateManager.getUpdateAt(0);
do_check_eq(update.state, STATE_SUCCEEDED);
let updateTestDir = getUpdateTestDir();
let file = updateTestDir.clone();
file.append("UpdateTestRemoveFile");
do_check_false(file.exists());
file = updateTestDir.clone();
file.append("UpdateTestAddFile");
do_check_true(file.exists());
do_check_eq(readFileBytes(file), "UpdateTestAddFile\n");
file = updateTestDir.clone();
file.append("removed-files");
do_check_true(file.exists());
do_check_eq(readFileBytes(file), "update_test/UpdateTestRemoveFile\n");
let updatesDir = getUpdatesDir();
let log = updatesDir.clone();
log.append("0");
log.append(FILE_UPDATE_LOG);
logTestInfo("testing " + log.path + " shouldn't exist");
do_check_false(log.exists());
log = updatesDir.clone();
log.append(FILE_LAST_LOG);
logTestInfo("testing " + log.path + " should exist");
do_check_true(log.exists());
log = updatesDir.clone();
log.append(FILE_BACKUP_LOG);
logTestInfo("testing " + log.path + " shouldn't exist");
do_check_false(log.exists());
updatesDir.append("0");
logTestInfo("testing " + updatesDir.path + " should exist");
do_check_true(updatesDir.exists());
do_timeout(CHECK_TIMEOUT_MILLI, do_test_finished);
}

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

@ -0,0 +1,19 @@
[DEFAULT]
head = head_update.js
tail =
[test_0000_bootstrap_svc.js]
[test_0110_general_svc.js]
[test_0111_general_svc.js]
[test_0112_general_svc.js]
[test_0120_channelChange_complete_svc.js]
[test_0150_appBinReplaced_xp_win_complete_svc.js]
[test_0151_appBinPatched_xp_win_partial_svc.js]
[test_0160_appInUse_xp_win_complete_svc.js]
[test_0170_fileLocked_xp_win_complete_svc.js]
[test_0171_fileLocked_xp_win_partial_svc.js]
[test_0180_fileInUse_xp_win_complete_svc.js]
[test_0181_fileInUse_xp_win_partial_svc.js]
[test_0182_rmrfdirFileInUse_xp_win_complete_svc.js]
[test_0183_rmrfdirFileInUse_xp_win_partial_svc.js]
[test_0200_app_launch_apply_update_svc.js]

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

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

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

@ -6,6 +6,7 @@
#define IDC_PROGRESS 1000
#define IDC_INFO 1002
#define IDI_DIALOG 1003
#define IDS_UPDATER_IDENTITY 1006
// Next default values for new objects
//
@ -13,7 +14,7 @@
#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_CONTROL_VALUE 1007
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

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

@ -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 "updatehelper.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,100 +1382,15 @@ 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"
#include "pathhash.h"
#endif
static void
LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv)
LaunchCallbackApp(const NS_tchar *workingDir,
int argc,
NS_tchar **argv,
bool usingService)
{
putenv(const_cast<char*>("NO_EM_RESTART="));
putenv(const_cast<char*>("MOZ_LAUNCHED_CHILD=1"));
@ -1620,15 +1398,20 @@ 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(XP_WIN)
WinLaunchChild(argv[0], argc, argv);
// Do not allow the callback to run when running an update through the
// service as session 0. The unelevated updater.exe will do the launching.
if (!usingService) {
WinLaunchChild(argv[0], argc, argv, NULL);
}
#else
# warning "Need implementaton of LaunchCallbackApp"
#endif
@ -1659,6 +1442,88 @@ WriteStatusFile(int status)
fwrite(text, strlen(text), 1, file);
}
static bool
WriteStatusApplying()
{
NS_tchar filename[MAXPATHLEN];
NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
NS_T("%s/update.status"), gSourcePath);
AutoFile file = NS_tfopen(filename, NS_T("wb+"));
if (file == NULL)
return false;
static const char kApplying[] = "applying";
if (fwrite(kApplying, strlen(kApplying), 1, file) != 1)
return false;
return true;
}
/*
* Read the update.status file and sets isPendingService to true if
* the status is set to pending-service.
*
* @param isPendingService Out parameter for specifying if the status
* is set to pending-service or not.
* @return true if the information was retrieved and it is pending
* or pending-service.
*/
static bool
IsUpdateStatusPending(bool &isPendingService)
{
bool isPending = false;
isPendingService = false;
NS_tchar filename[MAXPATHLEN];
NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
NS_T("%s/update.status"), gSourcePath);
AutoFile file = NS_tfopen(filename, NS_T("rb"));
if (file == NULL)
return false;
char buf[32] = { 0 };
fread(buf, sizeof(buf), 1, file);
const char kPending[] = "pending";
const char kPendingService[] = "pending-service";
isPending = strncmp(buf, kPending,
sizeof(kPending) - 1) == 0;
isPendingService = strncmp(buf, kPendingService,
sizeof(kPendingService) - 1) == 0;
return isPending;
}
/*
* Read the update.status file and sets isSuccess to true if
* the status is set to succeeded.
*
* @param isSucceeded Out parameter for specifying if the status
* is set to succeeded or not.
* @return true if the information was retrieved and it is succeeded.
*/
static bool
IsUpdateStatusSucceeded(bool &isSucceeded)
{
isSucceeded = false;
NS_tchar filename[MAXPATHLEN];
NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
NS_T("%s/update.status"), gSourcePath);
AutoFile file = NS_tfopen(filename, NS_T("rb"));
if (file == NULL)
return false;
char buf[32] = { 0 };
fread(buf, sizeof(buf), 1, file);
const char kSucceeded[] = "succeeded";
isSucceeded = strncmp(buf, kSucceeded,
sizeof(kSucceeded) - 1) == 0;
return true;
}
static void
UpdateThreadFunc(void *param)
{
@ -1730,6 +1595,43 @@ int NS_main(int argc, NS_tchar **argv)
return 1;
}
// The directory containing the update information.
gSourcePath = argv[1];
#ifdef XP_WIN
// Disable every privilege we don't need. Processes started using
// CreateProcess will use the same token as this process.
UACHelper::DisablePrivileges(NULL);
bool useService = false;
bool testOnlyFallbackKeyExists = false;
bool noServiceFallback = getenv("MOZ_NO_SERVICE_FALLBACK") != NULL;
putenv(const_cast<char*>("MOZ_NO_SERVICE_FALLBACK="));
// We never want the service to be used unless we build with
// the maintenance service.
#ifdef MOZ_MAINTENANCE_SERVICE
IsUpdateStatusPending(useService);
// 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.
HKEY testOnlyFallbackKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
TEST_ONLY_FALLBACK_KEY_PATH, 0,
KEY_READ | KEY_WOW64_64KEY,
&testOnlyFallbackKey) == ERROR_SUCCESS) {
testOnlyFallbackKeyExists = true;
RegCloseKey(testOnlyFallbackKey);
}
#endif
#endif
if (!WriteStatusApplying()) {
LOG(("failed setting status to 'applying'\n"));
return 1;
}
#ifdef XP_WIN
// Remove everything except close window from the context menu
{
@ -1773,20 +1675,27 @@ int NS_main(int argc, NS_tchar **argv)
#endif
}
// The directory containing the update information.
gSourcePath = argv[1];
// The callback is the remaining arguments starting at callbackIndex.
// The argument specified by callbackIndex is the callback executable and the
// argument prior to callbackIndex is the working directory.
const int callbackIndex = 5;
bool usingService = false;
#if defined(XP_WIN)
usingService = getenv("MOZ_USING_SERVICE") != NULL;
putenv(const_cast<char*>("MOZ_USING_SERVICE="));
// lastFallbackError keeps track of the last error for the service not being
// used, in case of an error when fallback is not enabled we write the
// error to the update.status file.
// When fallback is disabled (MOZ_NO_SERVICE_FALLBACK does not exist) then
// we will instead fallback to not using the service and display a UAC prompt.
int lastFallbackError = FALLBACKKEY_UNKNOWN_ERROR;
// Launch a second instance of the updater with the runas verb on Windows
// when write access is denied to the installation directory.
HANDLE updateLockFileHandle;
NS_tchar elevatedLockFilePath[MAXPATHLEN];
if (argc > callbackIndex) {
if (argc > callbackIndex && !usingService) {
NS_tchar updateLockFilePath[MAXPATHLEN];
NS_tsnprintf(updateLockFilePath,
sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
@ -1813,7 +1722,8 @@ int NS_main(int argc, NS_tchar **argv)
sizeof(elevatedLockFilePath)/sizeof(elevatedLockFilePath[0]),
NS_T("%s/update_elevated.lock"), argv[1]);
if (updateLockFileHandle == INVALID_HANDLE_VALUE) {
if (updateLockFileHandle == INVALID_HANDLE_VALUE ||
(useService && testOnlyFallbackKeyExists && noServiceFallback)) {
if (!_waccess(elevatedLockFilePath, F_OK) &&
NS_tremove(elevatedLockFilePath) != 0) {
fprintf(stderr, "Update already elevated! Exiting\n");
@ -1840,39 +1750,137 @@ int NS_main(int argc, NS_tchar **argv)
return 1;
}
SHELLEXECUTEINFO sinfo;
memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO));
sinfo.cbSize = sizeof(SHELLEXECUTEINFO);
sinfo.fMask = SEE_MASK_FLAG_NO_UI |
SEE_MASK_FLAG_DDEWAIT |
SEE_MASK_NOCLOSEPROCESS;
sinfo.hwnd = NULL;
sinfo.lpFile = argv[0];
sinfo.lpParameters = cmdLine;
sinfo.lpVerb = L"runas";
sinfo.nShow = SW_SHOWNORMAL;
bool result = ShellExecuteEx(&sinfo);
free(cmdLine);
if (result) {
WaitForSingleObject(sinfo.hProcess, INFINITE);
CloseHandle(sinfo.hProcess);
} else {
WriteStatusFile(ELEVATION_CANCELED);
// Make sure the service registry entries for the instsallation path
// are available. If not don't use the service.
if (useService) {
WCHAR maintenanceServiceKey[MAX_PATH + 1];
if (CalculateRegistryPathFromFilePath(argv[2], maintenanceServiceKey)) {
HKEY baseKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
maintenanceServiceKey, 0,
KEY_READ | KEY_WOW64_64KEY,
&baseKey) == ERROR_SUCCESS) {
RegCloseKey(baseKey);
} else {
useService = testOnlyFallbackKeyExists;
if (!useService) {
lastFallbackError = FALLBACKKEY_NOKEY_ERROR;
}
}
} else {
useService = false;
lastFallbackError = FALLBACKKEY_REGPATH_ERROR;
}
}
if (argc > callbackIndex) {
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
// Originally we used to write "pending" to update.status before
// launching the service command. This is no longer needed now
// since the service command is launched from updater.exe. If anything
// fails in between, we can fall back to using the normal update process
// on our own.
// If we still want to use the service try to launch the service
// comamnd for the update.
if (useService) {
// If the update couldn't be started, then set useService to false so
// we do the update the old way.
DWORD ret = LaunchServiceSoftwareUpdateCommand(argc, (LPCWSTR *)argv);
useService = (ret == ERROR_SUCCESS);
// If the command was launched then wait for the service to be done.
if (useService) {
DWORD lastState = WaitForServiceStop(SVC_NAME, 600);
if (lastState != SERVICE_STOPPED) {
// If the service doesn't stop after 10 minutes there is
// something seriously wrong.
lastFallbackError = FALLBACKKEY_SERVICE_NO_STOP_ERROR;
useService = false;
}
} else {
lastFallbackError = FALLBACKKEY_LAUNCH_ERROR;
}
}
// If we started the service command, and it finished, check the
// update.status file to make sure it succeeded, and if it did
// we need to manually start the PostUpdate process from the
// current user's session of this unelevated updater.exe the
// current process is running as.
if (useService) {
bool updateStatusSucceeded = false;
if (IsUpdateStatusSucceeded(updateStatusSucceeded) &&
updateStatusSucceeded) {
if (!LaunchWinPostProcess(argv[2], gSourcePath, false, NULL)) {
fprintf(stderr, "The post update process which runs as the user"
" for service update could not be launched.");
}
}
}
// If we didn't want to use the service at all, or if an update was
// already happening, or launching the service command failed, then
// launch the elevated updater.exe as we do without the service.
// We don't launch the elevated updater in the case that we did have
// write access all along because in that case the only reason we're
// using the service is because we are testing.
if (!useService && !noServiceFallback &&
updateLockFileHandle == INVALID_HANDLE_VALUE) {
SHELLEXECUTEINFO sinfo;
memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO));
sinfo.cbSize = sizeof(SHELLEXECUTEINFO);
sinfo.fMask = SEE_MASK_FLAG_NO_UI |
SEE_MASK_FLAG_DDEWAIT |
SEE_MASK_NOCLOSEPROCESS;
sinfo.hwnd = NULL;
sinfo.lpFile = argv[0];
sinfo.lpParameters = cmdLine;
sinfo.lpVerb = L"runas";
sinfo.nShow = SW_SHOWNORMAL;
bool result = ShellExecuteEx(&sinfo);
free(cmdLine);
if (result) {
WaitForSingleObject(sinfo.hProcess, INFINITE);
CloseHandle(sinfo.hProcess);
} else {
WriteStatusFile(ELEVATION_CANCELED);
}
}
// We are already contained in a condition where argc > callbackIndex
LaunchCallbackApp(argv[4], argc - callbackIndex,
argv + callbackIndex, usingService);
CloseHandle(elevatedFileHandle);
return 0;
if (!useService && !noServiceFallback &&
INVALID_HANDLE_VALUE == updateLockFileHandle) {
// We didn't use the service and we did run the elevated updater.exe.
// The elevated updater.exe is responsible for writing out the
// update.status file.
return 0;
} else if(useService) {
// The service command was launched. The service is responsible for
// writing out the update.status file.
if (updateLockFileHandle != INVALID_HANDLE_VALUE) {
CloseHandle(updateLockFileHandle);
}
return 0;
} else {
// Otherwise the service command was not launched at all.
// We are only reaching this code path because we had write access
// all along to the directory and a fallback key existed, and we
// have fallback disabled (MOZ_NO_SERVICE_FALLBACK env var exists).
// We only currently use this env var from XPCShell tests.
CloseHandle(updateLockFileHandle);
WriteStatusFile(lastFallbackError);
return 0;
}
}
}
#endif
LogInit();
LogInit(gSourcePath, NS_T("update.log"));
LOG(("SOURCE DIRECTORY " LOG_S "\n", argv[1]));
LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[2]));
@ -1899,15 +1907,22 @@ int NS_main(int argc, NS_tchar **argv)
HANDLE callbackFile = INVALID_HANDLE_VALUE;
if (argc > callbackIndex) {
// If the callback executable is specified it must exist for a successful
// update.
// update. It is important we null out the whole buffer here because later
// we make the assumption that the callback application is inside the
// apply-to dir. If we don't have a fully null'ed out buffer it can lead
// to stack corruption which causes crashes and other problems.
NS_tchar callbackLongPath[MAXPATHLEN];
ZeroMemory(callbackLongPath, sizeof(callbackLongPath));
if (!GetLongPathNameW(argv[callbackIndex], callbackLongPath,
sizeof(callbackLongPath)/sizeof(callbackLongPath[0]))) {
LOG(("NS_main: unable to find callback file: " LOG_S "\n", argv[callbackIndex]));
LogFinish();
WriteStatusFile(WRITE_ERROR);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
LaunchCallbackApp(argv[4],
argc - callbackIndex,
argv + callbackIndex,
usingService);
return 1;
}
@ -1918,7 +1933,10 @@ int NS_main(int argc, NS_tchar **argv)
LogFinish();
WriteStatusFile(WRITE_ERROR);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
LaunchCallbackApp(argv[4],
argc - callbackIndex,
argv + callbackIndex,
usingService);
return 1;
}
@ -1979,7 +1997,10 @@ int NS_main(int argc, NS_tchar **argv)
WriteStatusFile(WRITE_ERROR);
NS_tremove(gCallbackBackupPath);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
LaunchCallbackApp(argv[4],
argc - callbackIndex,
argv + callbackIndex,
usingService);
return 1;
}
}
@ -2035,7 +2056,19 @@ 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_USING_SERVICE will not exist.
if (!usingService) {
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 */
@ -2044,7 +2077,10 @@ int NS_main(int argc, NS_tchar **argv)
LaunchMacPostProcess(argv[callbackIndex]);
}
#endif /* XP_MACOSX */
LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
LaunchCallbackApp(argv[4],
argc - callbackIndex,
argv + callbackIndex,
usingService);
}
return 0;

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

@ -35,6 +35,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
IDI_DIALOG ICON "updater.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Embedded an identifier to uniquely identiy this as a Mozilla updater.
//
STRINGTABLE
{
IDS_UPDATER_IDENTITY, "moz-updater.exe-4cdccec4-5ee0-4a06-9817-4cd899a9db49"
}
/////////////////////////////////////////////////////////////////////////////
//
// Dialog

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

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

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

@ -3131,6 +3131,25 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
if (NS_FAILED(rv))
updRoot = dirProvider.GetAppDir();
// If the MOZ_PROCESS_UPDATES environment variable already exists, then
// we are being called from the callback application.
if (EnvHasValue("MOZ_PROCESS_UPDATES")) {
// If the caller has asked us to log our arguments, do so. This is used
// to make sure that the maintenance service successfully launches the
// callback application.
const char *logFile = nsnull;
if (ARG_FOUND == CheckArg("dump-args", false, &logFile)) {
FILE* logFP = fopen(logFile, "wb");
if (logFP) {
for (i = 1; i < gRestartArgc; ++i) {
fprintf(logFP, "%s\n", gRestartArgv[i]);
}
fclose(logFP);
}
}
return 0;
}
// Support for processing an update and exiting. The MOZ_PROCESS_UPDATES
// environment variable will be part of the updater's environment and the
// application that is relaunched by the updater. When the application is

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

@ -137,7 +137,8 @@ WriteConsoleLog();
#ifdef XP_WIN
BOOL
WinLaunchChild(const PRUnichar *exePath, int argc, char **argv);
WinLaunchChild(const PRUnichar *exePath, int argc,
char **argv, HANDLE userToken = NULL);
#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
@ -205,22 +206,8 @@ IsPending(nsILocalFile *statusFile)
return false;
const char kPending[] = "pending";
return (strncmp(buf, kPending, sizeof(kPending) - 1) == 0);
}
static bool
SetStatusApplying(nsILocalFile *statusFile)
{
PRFileDesc *fd = nsnull;
nsresult rv = statusFile->OpenNSPRFileDesc(PR_WRONLY, 0660, &fd);
if (NS_FAILED(rv))
return false;
static const char kApplying[] = "Applying\n";
PR_Write(fd, kApplying, sizeof(kApplying) - 1);
PR_Close(fd);
return true;
bool isPending = (strncmp(buf, kPending, sizeof(kPending) - 1) == 0);
return isPending;
}
static bool
@ -437,10 +424,11 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsILocalFile *statusFile,
if (NS_FAILED(rv))
return;
if (!SetStatusApplying(statusFile)) {
LOG(("failed setting status to 'applying'\n"));
return;
}
// We used to write out "Applying" to the update.status file here.
// Instead we do this from within the updater application now.
// This is so that we don't overwrite the status of pending-service
// in the Windows case. This change was made for all platforms so
// that it stays consistent across all OS.
// Construct the PID argument for this process. If we are using execv, then
// we pass "0" which is then ignored by the updater.
@ -480,8 +468,13 @@ 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))
// 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);
@ -514,9 +507,37 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
if (NS_FAILED(rv))
return rv;
const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES");
if (processingUpdates && *processingUpdates) {
// Enable the tests to request us to use a different update root directory
const char *updRootOverride = PR_GetEnv("MOZ_UPDATE_ROOT_OVERRIDE");
if (updRootOverride && *updRootOverride) {
nsCOMPtr<nsILocalFile> overrideDir;
nsCAutoString path(updRootOverride);
rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(overrideDir));
if (NS_FAILED(rv)) {
return rv;
}
updatesDir = do_QueryInterface(overrideDir);
}
// Enable the tests to request us to use a different app directory
const char *appDirOverride = PR_GetEnv("MOZ_UPDATE_APPDIR_OVERRIDE");
if (appDirOverride && *appDirOverride) {
nsCOMPtr<nsILocalFile> overrideDir;
nsCAutoString path(appDirOverride);
rv = NS_NewNativeLocalFile(path, false, getter_AddRefs(overrideDir));
if (NS_FAILED(rv)) {
return rv;
}
NS_RELEASE(appDir);
NS_ADDREF(appDir = overrideDir);
}
}
nsCOMPtr<nsILocalFile> statusFile;
if (GetStatusFile(updatesDir, statusFile) && IsPending(statusFile)) {
if (GetStatusFile(updatesDir, statusFile) &&
IsPending(statusFile)) {
nsCOMPtr<nsILocalFile> versionFile;
nsCOMPtr<nsILocalFile> channelChangeFile;
// Remove the update if the update application version file doesn't exist
@ -527,7 +548,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);
}
}

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

@ -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
@ -49,21 +50,9 @@
#include <shellapi.h>
#ifndef ERROR_ELEVATION_REQUIRED
#define ERROR_ELEVATION_REQUIRED 740L
#endif
BOOL (WINAPI *pCreateProcessWithTokenW)(HANDLE,
DWORD,
LPCWSTR,
LPWSTR,
DWORD,
LPVOID,
LPCWSTR,
LPSTARTUPINFOW,
LPPROCESS_INFORMATION);
BOOL (WINAPI *pIsUserAnAdmin)(VOID);
// Needed for CreateEnvironmentBlock
#include <userenv.h>
#pragma comment(lib, "userenv.lib")
/**
* Get the length that the string will take and takes into account the
@ -162,7 +151,7 @@ static PRUnichar* ArgToString(PRUnichar *d, const PRUnichar *s)
*
* argv is UTF8
*/
static PRUnichar*
PRUnichar*
MakeCommandLine(int argc, PRUnichar **argv)
{
int i;
@ -231,10 +220,14 @@ FreeAllocStrings(int argc, PRUnichar **argv)
*/
BOOL
WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv);
WinLaunchChild(const PRUnichar *exePath,
int argc, PRUnichar **argv,
HANDLE userToken = NULL);
BOOL
WinLaunchChild(const PRUnichar *exePath, int argc, char **argv)
WinLaunchChild(const PRUnichar *exePath,
int argc, char **argv,
HANDLE userToken)
{
PRUnichar** argvConverted = new PRUnichar*[argc];
if (!argvConverted)
@ -248,34 +241,73 @@ WinLaunchChild(const PRUnichar *exePath, int argc, char **argv)
}
}
BOOL ok = WinLaunchChild(exePath, argc, argvConverted);
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);
DWORD creationFlags = 0;
#ifdef DEBUG
creationFlags |= CREATE_NEW_CONSOLE;
#endif
if (userToken == NULL) {
ok = CreateProcessW(exePath,
cl,
NULL, // no special security attributes
NULL, // no special thread attributes
FALSE, // don't inherit filehandles
creationFlags,
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 +315,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 +332,4 @@ WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv)
return ok;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше