зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-central and mozilla-inbound
This commit is contained in:
Коммит
ce3b90daa1
|
@ -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@
|
||||
|
|
18
configure.in
18
configure.in
|
@ -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
|
Двоичный файл не отображается.
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче