// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This is a script that allows running an updater client that can use the
// functionality provided by the legacy on-demand interfaces `IGoogleUpdate3Web`
// and `IAppCommandWeb`.
//
// Usage:
// ```
// cscript legacy_update3web.js {0|1==Chromium|Chrome Branded} {AppId}
// {is_system: 0|1} {0|1|2|3==CheckForUpdate|Install|Update|LaunchCommand}
// {command_id?}`
// ```
//
// For instance, to check for Chrome updates, here is the command line and
// sample output:
// ```
// cscript legacy_update3web.js 1 {8A69D345-D564-463c-AFF1-A69D9E530F96} 1 0
// Microsoft (R) Windows Script Host Version 5.812
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// [State][3][Checking for update...]
// [Data][[Current Version][111.0.5563.65]]
// [State][3][Checking for update...]
// [Data][[Current Version][111.0.5563.65]]
// [State][16][No update available!]
// ```
//
// To launch a command registered as `test-command-exe` on `Chromium` AppId
// `{23E39345-8B3C-4DF4-9CA6-53EA97B6C7A9}`, here is the command line and sample
// output:
// ```
// cscript legacy_update3web.js 0 {23E39345-8B3C-4DF4-9CA6-53EA97B6C7A9} 1 3
// test-command-exe
// Microsoft (R) Windows Script Host Version 5.812
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// Launching command.
// Command launched.
// [State][2][Running...]
// [State][4][Exited with code 23.]
// ```
// Operations.
var CHECK_FOR_UPDATE = 0;
var INSTALL = 1;
var UPDATE = 2;
var LAUNCH_COMMAND = 3;
// The following states need to be kept in sync with the CurrentState enum in
// omaha3_idl.idl.
var STATE_INIT = 1;
var STATE_WAITING_TO_CHECK_FOR_UPDATE = STATE_INIT + 1;
var STATE_CHECKING_FOR_UPDATE = STATE_WAITING_TO_CHECK_FOR_UPDATE + 1;
var STATE_UPDATE_AVAILABLE = STATE_CHECKING_FOR_UPDATE + 1;
var STATE_WAITING_TO_DOWNLOAD = STATE_UPDATE_AVAILABLE + 1;
var STATE_RETRYING_DOWNLOAD = STATE_WAITING_TO_DOWNLOAD + 1;
var STATE_DOWNLOADING = STATE_RETRYING_DOWNLOAD + 1;
var STATE_DOWNLOAD_COMPLETE = STATE_DOWNLOADING + 1;
var STATE_EXTRACTING = STATE_DOWNLOAD_COMPLETE + 1;
var STATE_APPLYING_DIFFERENTIAL_PATCH = STATE_EXTRACTING + 1;
var STATE_READY_TO_INSTALL = STATE_APPLYING_DIFFERENTIAL_PATCH + 1;
var STATE_WAITING_TO_INSTALL = STATE_READY_TO_INSTALL + 1;
var STATE_INSTALLING = STATE_WAITING_TO_INSTALL + 1;
var STATE_INSTALL_COMPLETE = STATE_INSTALLING + 1;
var STATE_PAUSED = STATE_INSTALL_COMPLETE + 1;
var STATE_NO_UPDATE = STATE_PAUSED + 1;
var STATE_ERROR = STATE_NO_UPDATE + 1;
// The following states need to be kept in sync with the AppCommandStatus enum
// in omaha3_idl.idl.
var COMMAND_STATUS_INIT = 1;
var COMMAND_STATUS_RUNNING = COMMAND_STATUS_INIT + 1;
var COMMAND_STATUS_ERROR = COMMAND_STATUS_RUNNING + 1;
var COMMAND_STATUS_COMPLETE = COMMAND_STATUS_ERROR + 1;
function update3webProgId(is_chrome_branded, is_system) {
return (is_chrome_branded ? 'Google' : 'Chromium') + 'Update.Update3Web' +
(is_system ? 'Machine' : 'User');
}
function initializeBundle(is_chrome_branded, is_system) {
var update3web = WScript.CreateObject(update3webProgId(is_chrome_branded,
is_system));
var bundle = update3web.createAppBundleWeb();
bundle.initialize();
return bundle;
}
function initializeBundleForInstall(is_chrome_branded, is_system) {
return initializeBundle(is_chrome_branded, is_system);
}
function doCheckForUpdate(is_chrome_branded, appId, is_system) {
var bundle = initializeBundle(is_chrome_branded, is_system);
var app = bundle.createInstalledApp(appId);
bundle.checkForUpdate();
doLoopUntilDone(CHECK_FOR_UPDATE, bundle);
app = null;
bundle = null;
CollectGarbage();
}
function doInstall(is_chrome_branded, appId, is_system) {
var bundle = initializeBundleForInstall(is_chrome_branded, is_system);
bundle.createApp(appId, 'GPEZ', 'en', '');
bundle.checkForUpdate();
doLoopUntilDone(INSTALL, bundle);
}
function doUpdate(is_chrome_branded, appId, is_system) {
var bundle = initializeBundleForInstall(is_chrome_branded, is_system);
bundle.createInstalledApp(appId);
bundle.checkForUpdate();
doLoopUntilDone(UPDATE, bundle);
}
function doLaunchCommand(is_chrome_branded,
appId,
is_system,
command,
argument_list) {
var bundle = initializeBundle(is_chrome_branded, is_system);
bundle.createInstalledApp(appId);
var app = bundle.appWeb(0);
if (!app) {
WScript.Echo('App not found.');
return;
}
var cmd = app.command(command);
if (!cmd) {
WScript.Echo('Command not found.');
return;
}
try {
WScript.Echo('Launching command.');
switch (argument_list.length) {
case 0:
cmd.execute();
break;
case 1:
cmd.execute(argument_list[0]);
break;
case 2:
cmd.execute(argument_list[0], argument_list[1]);
break;
case 3:
cmd.execute(argument_list[0], argument_list[1], argument_list[2]);
break;
case 4:
cmd.execute(argument_list[0],
argument_list[1],
argument_list[2],
argument_list[3]);
break;
case 5:
cmd.execute(argument_list[0],
argument_list[1],
argument_list[2],
argument_list[3],
argument_list[4]);
case 6:
cmd.execute(argument_list[0],
argument_list[1],
argument_list[2],
argument_list[3],
argument_list[4],
argument_list[5]);
case 7:
cmd.execute(argument_list[0],
argument_list[1],
argument_list[2],
argument_list[3],
argument_list[4],
argument_list[5],
argument_list[6]);
case 8:
cmd.execute(argument_list[0],
argument_list[1],
argument_list[2],
argument_list[3],
argument_list[4],
argument_list[5],
argument_list[6],
argument_list[7]);
case 9:
cmd.execute(argument_list[0],
argument_list[1],
argument_list[2],
argument_list[3],
argument_list[4],
argument_list[5],
argument_list[6],
argument_list[7],
argument_list[8]);
break;
default: WScript.Echo('Too many arguments.'); return;
}
WScript.Echo('Command launched.');
} catch (ex) {
WScript.Echo('Error: ' + ex.description + ': ' + ex.number);
return;
}
while (true) {
var status = cmd.status;
var stateDescription = '';
switch (status) {
case COMMAND_STATUS_INIT:
stateDescription = 'Initializing...';
break;
case COMMAND_STATUS_RUNNING:
stateDescription = 'Running...';
break;
case COMMAND_STATUS_ERROR:
stateDescription = 'Error!';
break;
case COMMAND_STATUS_COMPLETE:
stateDescription = 'Exited with code ' + cmd.exitCode + '.';
break;
default:
stateDescription = 'Unhandled state!';
break;
}
WScript.Echo('[State][' + status + '][' + stateDescription + ']');
if (status != COMMAND_STATUS_RUNNING) {
return;
}
WScript.Sleep(100);
}
}
function doLoopUntilDone(operation, bundle) {
function operationDone() {
if (!bundle) {
WScript.Echo('No bundle is defined!');
return false;
}
var done = false;
var app = bundle.appWeb(0);
var state = app.currentState;
var stateDescription = '';
var extraData = '';
switch (state.stateValue) {
case STATE_INIT:
stateDescription = 'Initializating...';
break;
case STATE_WAITING_TO_CHECK_FOR_UPDATE:
case STATE_CHECKING_FOR_UPDATE:
stateDescription = 'Checking for update...';
extraData = '[Current Version][' + app.currentVersionWeb.version + ']';
break;
case STATE_UPDATE_AVAILABLE:
stateDescription = 'Update available!';
extraData = '[Next Version][' + app.nextVersionWeb.version + ']';
if (operation == CHECK_FOR_UPDATE) {
done = true;
break;
}
bundle.download();
break;
case STATE_WAITING_TO_DOWNLOAD:
case STATE_RETRYING_DOWNLOAD:
stateDescription = 'Contacting server...';
break;
case STATE_DOWNLOADING:
stateDescription = 'Downloading...';
extraData = '[Bytes downloaded][' + state.bytesDownloaded + ']' +
'[Bytes total][' + state.totalBytesToDownload + ']' +
'[Time remaining][' + state.downloadTimeRemainingMs + ']';
break;
case STATE_DOWNLOAD_COMPLETE:
case STATE_EXTRACTING:
case STATE_APPLYING_DIFFERENTIAL_PATCH:
case STATE_READY_TO_INSTALL:
stateDescription = 'Ready to install!';
extraData = '[Bytes downloaded][' + state.bytesDownloaded + ']' +
'[Bytes total][' + state.totalBytesToDownload + ']';
bundle.install();
break;
case STATE_WAITING_TO_INSTALL:
case STATE_INSTALLING:
stateDescription = 'Installing...';
extraData = '[Install Progress][' + state.installProgress + ']' +
'[Time remaining][' + state.installTimeRemainingMs + ']';
break;
case STATE_INSTALL_COMPLETE:
stateDescription = 'Done!';
done = true;
break;
case STATE_PAUSED:
stateDescription = 'Paused...';
break;
case STATE_NO_UPDATE:
stateDescription = 'No update available!';
done = true;
break;
case STATE_ERROR:
stateDescription = 'Error!';
extraData = '[errorCode][' + state.errorCode + ']' +
'[completionMessage][' + state.completionMessage + ']' +
'[installerResultCode][' + state.installerResultCode + ']';
done = true;
break;
default:
stateDescription = 'Unhandled state...';
break;
}
WScript.Echo('[State][' + state.stateValue + '][' + stateDescription + ']');
if (extraData.length > 0) {
WScript.Echo('[Data][' + extraData + ']');
}
return done;
}
while (!operationDone()) {
WScript.Sleep(100);
}
}
////////////////////////////////////////////////////////////////////////////////
// Main
////////////////////////////////////////////////////////////////////////////////
function parseAndRun() {
if (WScript.Arguments.length < 4) {
return false;
}
var is_chrome_branded = Boolean(parseInt(WScript.Arguments(0)));
var app_id = WScript.Arguments(1);
var is_system = Boolean(parseInt(WScript.Arguments(2)));
var operation = parseInt(WScript.Arguments(3));
switch (operation) {
case CHECK_FOR_UPDATE:
if (WScript.Arguments.length != 4) {
return false;
}
doCheckForUpdate(is_chrome_branded, app_id, is_system);
break;
case INSTALL:
if (WScript.Arguments.length != 4) {
return false;
}
doInstall(is_chrome_branded, app_id, is_system);
break;
case UPDATE:
if (WScript.Arguments.length != 4) {
return false;
}
doUpdate(is_chrome_branded, app_id, is_system);
break;
case LAUNCH_COMMAND:
if (WScript.Arguments.length < 5) {
return false;
}
var command = WScript.Arguments(4);
var argument_list = [];
for (var i = 5; i < WScript.Arguments.length; ++i) {
argument_list.push(WScript.Arguments(i));
}
doLaunchCommand(is_chrome_branded,
app_id,
is_system,
command,
argument_list);
break;
default:
return false;
}
return true;
}
try {
if (!parseAndRun()) {
WScript.Echo(
'Usage: ' +
'{0|1==Chromium|Chrome Branded} ' +
'{AppId} ' +
'{is_system: 0|1} ' +
'{0|1|2|3==CheckForUpdate|Install|Update|LaunchCommand} ' +
'{command_id?}');
}
} catch (ex) {
if (ex.number == -2147024703) {
WScript.Echo('ERROR_BAD_EXE_FORMAT: Try the SysWOW64 Script Host: ' +
ex.description);
} else {
WScript.Echo('Error: ' + ex.description + ': ' + ex.number);
}
}