This source file includes following definitions.
- CreateUACForegroundWindow
- GetActiveSetupPath
- TriggerActiveSetupCommand
- ExecuteExeAsAdmin
- GetChromeUninstallCmd
- GetChromeVersion
- GetCriticalUpdateVersion
- IsOSSupported
- AddInstallerResultItems
- UpdateInstallerStage
- IsPerUserInstall
- IsMultiInstall
- CheckIsChromeSxSProcess
- IsChromeSxSProcess
- IsFirstRunSentinelPresent
- GetSentinelFilePath
- DeleteRegistryKey
- DeleteRegistryValue
- DeleteRegistryKeyIf
- DeleteRegistryValueIf
- Evaluate
- GetInstallReturnCode
- MakeUninstallCommand
- GetCurrentDate
- OpenForInfo
- GetInfo
- file_info_
- Evaluate
- EvaluatePath
#include "chrome/installer/util/install_util.h"
#include <shellapi.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <algorithm>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/platform_file.h"
#include "base/process/launch.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_info.h"
#include "base/values.h"
#include "base/version.h"
#include "base/win/metro.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/helper.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/l10n_string_util.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/work_item_list.h"
using base::win::RegKey;
using installer::ProductState;
namespace {
const wchar_t kStageBinaryPatching[] = L"binary_patching";
const wchar_t kStageBuilding[] = L"building";
const wchar_t kStageConfiguringAutoLaunch[] = L"configuring_auto_launch";
const wchar_t kStageCopyingPreferencesFile[] = L"copying_prefs";
const wchar_t kStageCreatingShortcuts[] = L"creating_shortcuts";
const wchar_t kStageEnsemblePatching[] = L"ensemble_patching";
const wchar_t kStageExecuting[] = L"executing";
const wchar_t kStageFinishing[] = L"finishing";
const wchar_t kStagePreconditions[] = L"preconditions";
const wchar_t kStageRefreshingPolicy[] = L"refreshing_policy";
const wchar_t kStageRegisteringChrome[] = L"registering_chrome";
const wchar_t kStageRemovingOldVersions[] = L"removing_old_ver";
const wchar_t kStageRollingback[] = L"rollingback";
const wchar_t kStageUncompressing[] = L"uncompressing";
const wchar_t kStageUnpacking[] = L"unpacking";
const wchar_t kStageUpdatingChannels[] = L"updating_channels";
const wchar_t kStageCreatingVisualManifest[] = L"creating_visual_manifest";
const wchar_t kStageDeferringToHigherVersion[] = L"deferring_to_higher_version";
const wchar_t kStageUninstallingBinaries[] = L"uninstalling_binaries";
const wchar_t kStageUninstallingChromeFrame[] = L"uninstalling_chrome_frame";
const wchar_t* const kStages[] = {
NULL,
kStagePreconditions,
kStageUncompressing,
kStageEnsemblePatching,
kStageBinaryPatching,
kStageUnpacking,
kStageBuilding,
kStageExecuting,
kStageRollingback,
kStageRefreshingPolicy,
kStageUpdatingChannels,
kStageCopyingPreferencesFile,
kStageCreatingShortcuts,
kStageRegisteringChrome,
kStageRemovingOldVersions,
kStageFinishing,
kStageConfiguringAutoLaunch,
kStageCreatingVisualManifest,
kStageDeferringToHigherVersion,
kStageUninstallingBinaries,
kStageUninstallingChromeFrame,
};
COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages),
kStages_disagrees_with_Stage_comma_they_must_match_bang);
HWND CreateUACForegroundWindow() {
HWND foreground_window = ::CreateWindowEx(WS_EX_TOOLWINDOW,
L"STATIC",
NULL,
WS_POPUP | WS_VISIBLE,
0, 0, 0, 0,
NULL, NULL,
::GetModuleHandle(NULL),
NULL);
if (foreground_window) {
HMONITOR monitor = ::MonitorFromWindow(foreground_window,
MONITOR_DEFAULTTONEAREST);
if (monitor) {
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
::GetMonitorInfo(monitor, &mi);
RECT screen_rect = mi.rcWork;
int x_offset = (screen_rect.right - screen_rect.left) / 2;
int y_offset = (screen_rect.bottom - screen_rect.top) / 2;
::MoveWindow(foreground_window,
screen_rect.left + x_offset,
screen_rect.top + y_offset,
0, 0, FALSE);
} else {
NOTREACHED() << "Unable to get default monitor";
}
::SetForegroundWindow(foreground_window);
}
return foreground_window;
}
}
base::string16 InstallUtil::GetActiveSetupPath(BrowserDistribution* dist) {
static const wchar_t kInstalledComponentsPath[] =
L"Software\\Microsoft\\Active Setup\\Installed Components\\";
return kInstalledComponentsPath + dist->GetActiveSetupGuid();
}
void InstallUtil::TriggerActiveSetupCommand() {
base::string16 active_setup_reg(
GetActiveSetupPath(BrowserDistribution::GetDistribution()));
base::win::RegKey active_setup_key(
HKEY_LOCAL_MACHINE, active_setup_reg.c_str(), KEY_QUERY_VALUE);
base::string16 cmd_str;
LONG read_status = active_setup_key.ReadValue(L"StubPath", &cmd_str);
if (read_status != ERROR_SUCCESS) {
LOG(ERROR) << active_setup_reg << ", " << read_status;
return;
}
CommandLine cmd(CommandLine::FromString(cmd_str));
cmd.AppendSwitch(installer::switches::kForceConfigureUserSettings);
base::LaunchOptions launch_options;
if (base::win::IsMetroProcess())
launch_options.force_breakaway_from_job_ = true;
if (!base::LaunchProcess(cmd.GetCommandLineString(), launch_options, NULL))
PLOG(ERROR) << cmd.GetCommandLineString();
}
bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) {
base::FilePath::StringType program(cmd.GetProgram().value());
DCHECK(!program.empty());
DCHECK_NE(program[0], L'\"');
CommandLine::StringType params(cmd.GetCommandLineString());
if (params[0] == '"') {
DCHECK_EQ('"', params[program.length() + 1]);
DCHECK_EQ(program, params.substr(1, program.length()));
params = params.substr(program.length() + 2);
} else {
DCHECK_EQ(program, params.substr(0, program.length()));
params = params.substr(program.length());
}
base::TrimWhitespace(params, base::TRIM_ALL, ¶ms);
HWND uac_foreground_window = CreateUACForegroundWindow();
SHELLEXECUTEINFO info = {0};
info.cbSize = sizeof(SHELLEXECUTEINFO);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.hwnd = uac_foreground_window;
info.lpVerb = L"runas";
info.lpFile = program.c_str();
info.lpParameters = params.c_str();
info.nShow = SW_SHOW;
bool success = false;
if (::ShellExecuteEx(&info) == TRUE) {
::WaitForSingleObject(info.hProcess, INFINITE);
DWORD ret_val = 0;
if (::GetExitCodeProcess(info.hProcess, &ret_val)) {
success = true;
if (exit_code)
*exit_code = ret_val;
}
}
if (uac_foreground_window) {
DestroyWindow(uac_foreground_window);
}
return success;
}
CommandLine InstallUtil::GetChromeUninstallCmd(
bool system_install, BrowserDistribution::Type distribution_type) {
ProductState state;
if (state.Initialize(system_install, distribution_type)) {
return state.uninstall_command();
}
return CommandLine(CommandLine::NO_PROGRAM);
}
void InstallUtil::GetChromeVersion(BrowserDistribution* dist,
bool system_install,
Version* version) {
DCHECK(dist);
RegKey key;
HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
LONG result = key.Open(reg_root, dist->GetVersionKey().c_str(),
KEY_QUERY_VALUE);
base::string16 version_str;
if (result == ERROR_SUCCESS)
result = key.ReadValue(google_update::kRegVersionField, &version_str);
*version = Version();
if (result == ERROR_SUCCESS && !version_str.empty()) {
VLOG(1) << "Existing " << dist->GetDisplayName() << " version found "
<< version_str;
*version = Version(base::UTF16ToASCII(version_str));
} else {
DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
VLOG(1) << "No existing " << dist->GetDisplayName()
<< " install found.";
}
}
void InstallUtil::GetCriticalUpdateVersion(BrowserDistribution* dist,
bool system_install,
Version* version) {
DCHECK(dist);
RegKey key;
HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
LONG result =
key.Open(reg_root, dist->GetVersionKey().c_str(), KEY_QUERY_VALUE);
base::string16 version_str;
if (result == ERROR_SUCCESS)
result = key.ReadValue(google_update::kRegCriticalVersionField,
&version_str);
*version = Version();
if (result == ERROR_SUCCESS && !version_str.empty()) {
VLOG(1) << "Critical Update version for " << dist->GetDisplayName()
<< " found " << version_str;
*version = Version(base::UTF16ToASCII(version_str));
} else {
DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
VLOG(1) << "No existing " << dist->GetDisplayName()
<< " install found.";
}
}
bool InstallUtil::IsOSSupported() {
VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
<< base::SysInfo::OperatingSystemVersion();
base::win::Version version = base::win::GetVersion();
return (version > base::win::VERSION_XP) ||
((version == base::win::VERSION_XP) &&
(base::win::OSInfo::GetInstance()->service_pack().major >= 2));
}
void InstallUtil::AddInstallerResultItems(
bool system_install,
const base::string16& state_key,
installer::InstallStatus status,
int string_resource_id,
const base::string16* const launch_cmd,
WorkItemList* install_list) {
DCHECK(install_list);
const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
DWORD installer_result = (GetInstallReturnCode(status) == 0) ? 0 : 1;
install_list->AddCreateRegKeyWorkItem(root, state_key);
install_list->AddSetRegValueWorkItem(root, state_key,
installer::kInstallerResult,
installer_result, true);
install_list->AddSetRegValueWorkItem(root, state_key,
installer::kInstallerError,
static_cast<DWORD>(status), true);
if (string_resource_id != 0) {
base::string16 msg = installer::GetLocalizedString(string_resource_id);
install_list->AddSetRegValueWorkItem(root, state_key,
installer::kInstallerResultUIString, msg, true);
}
if (launch_cmd != NULL && !launch_cmd->empty()) {
install_list->AddSetRegValueWorkItem(root, state_key,
installer::kInstallerSuccessLaunchCmdLine, *launch_cmd, true);
}
}
void InstallUtil::UpdateInstallerStage(bool system_install,
const base::string16& state_key_path,
installer::InstallerStage stage) {
DCHECK_LE(static_cast<installer::InstallerStage>(0), stage);
DCHECK_GT(installer::NUM_STAGES, stage);
const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
RegKey state_key;
LONG result = state_key.Open(root, state_key_path.c_str(),
KEY_QUERY_VALUE | KEY_SET_VALUE);
if (result == ERROR_SUCCESS) {
if (stage == installer::NO_STAGE) {
result = state_key.DeleteValue(installer::kInstallerExtraCode1);
LOG_IF(ERROR, result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
<< "Failed deleting installer stage from " << state_key_path
<< "; result: " << result;
} else {
const DWORD extra_code_1 = static_cast<DWORD>(stage);
result = state_key.WriteValue(installer::kInstallerExtraCode1,
extra_code_1);
LOG_IF(ERROR, result != ERROR_SUCCESS)
<< "Failed writing installer stage to " << state_key_path
<< "; result: " << result;
}
installer::ChannelInfo channel_info;
channel_info.Initialize(state_key);
if (channel_info.SetStage(kStages[stage]) &&
!channel_info.Write(&state_key)) {
LOG(ERROR) << "Failed writing installer stage to " << state_key_path;
}
} else {
LOG(ERROR) << "Failed opening " << state_key_path
<< " to update installer stage; result: " << result;
}
}
bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) {
wchar_t program_files_path[MAX_PATH] = {0};
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
SHGFP_TYPE_CURRENT, program_files_path))) {
return !StartsWith(exe_path, program_files_path, false);
} else {
NOTREACHED();
}
return true;
}
bool InstallUtil::IsMultiInstall(BrowserDistribution* dist,
bool system_install) {
DCHECK(dist);
ProductState state;
return state.Initialize(system_install, dist) && state.is_multi_install();
}
bool CheckIsChromeSxSProcess() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
CHECK(command_line);
if (command_line->HasSwitch(installer::switches::kChromeSxS))
return true;
base::FilePath exe_dir;
PathService::Get(base::DIR_EXE, &exe_dir);
base::string16 chrome_sxs_dir(installer::kGoogleChromeInstallSubDir2);
chrome_sxs_dir.append(installer::kSxSSuffix);
std::vector<base::FilePath::StringType> components;
exe_dir.GetComponents(&components);
DCHECK(!components.empty());
typedef std::vector<base::FilePath::StringType>::const_reverse_iterator
ComponentsIterator;
for (ComponentsIterator current = components.rbegin(), parent = current + 1;
parent != components.rend(); current = parent++) {
if (base::FilePath::CompareEqualIgnoreCase(
*current, installer::kInstallBinaryDir) &&
base::FilePath::CompareEqualIgnoreCase(*parent, chrome_sxs_dir)) {
return true;
}
}
return false;
}
bool InstallUtil::IsChromeSxSProcess() {
static bool sxs = CheckIsChromeSxSProcess();
return sxs;
}
bool InstallUtil::IsFirstRunSentinelPresent() {
base::FilePath user_data_dir;
return !PathService::Get(chrome::DIR_USER_DATA, &user_data_dir) ||
base::PathExists(user_data_dir.Append(chrome::kFirstRunSentinel));
}
bool InstallUtil::GetSentinelFilePath(const base::FilePath::CharType* file,
BrowserDistribution* dist,
base::FilePath* path) {
base::FilePath user_data_dir(installer::GetChromeUserDataPath(dist));
if (user_data_dir.empty())
return false;
*path = user_data_dir.Append(file);
return true;
}
bool InstallUtil::DeleteRegistryKey(HKEY root_key,
const base::string16& key_path) {
VLOG(1) << "Deleting registry key " << key_path;
LONG result = ::SHDeleteKey(root_key, key_path.c_str());
if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
LOG(ERROR) << "Failed to delete registry key: " << key_path
<< " error: " << result;
return false;
}
return true;
}
bool InstallUtil::DeleteRegistryValue(HKEY reg_root,
const base::string16& key_path,
const base::string16& value_name) {
RegKey key;
LONG result = key.Open(reg_root, key_path.c_str(), KEY_SET_VALUE);
if (result == ERROR_SUCCESS)
result = key.DeleteValue(value_name.c_str());
if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
LOG(ERROR) << "Failed to delete registry value: " << value_name
<< " error: " << result;
return false;
}
return true;
}
InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryKeyIf(
HKEY root_key,
const base::string16& key_to_delete_path,
const base::string16& key_to_test_path,
const wchar_t* value_name,
const RegistryValuePredicate& predicate) {
DCHECK(root_key);
ConditionalDeleteResult delete_result = NOT_FOUND;
RegKey key;
base::string16 actual_value;
if (key.Open(root_key, key_to_test_path.c_str(),
KEY_QUERY_VALUE) == ERROR_SUCCESS &&
key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
predicate.Evaluate(actual_value)) {
key.Close();
delete_result = DeleteRegistryKey(root_key, key_to_delete_path)
? DELETED : DELETE_FAILED;
}
return delete_result;
}
InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf(
HKEY root_key,
const wchar_t* key_path,
const wchar_t* value_name,
const RegistryValuePredicate& predicate) {
DCHECK(root_key);
DCHECK(key_path);
ConditionalDeleteResult delete_result = NOT_FOUND;
RegKey key;
base::string16 actual_value;
if (key.Open(root_key, key_path,
KEY_QUERY_VALUE | KEY_SET_VALUE) == ERROR_SUCCESS &&
key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
predicate.Evaluate(actual_value)) {
LONG result = key.DeleteValue(value_name);
if (result != ERROR_SUCCESS) {
LOG(ERROR) << "Failed to delete registry value: "
<< (value_name ? value_name : L"(Default)")
<< " error: " << result;
delete_result = DELETE_FAILED;
}
delete_result = DELETED;
}
return delete_result;
}
bool InstallUtil::ValueEquals::Evaluate(const base::string16& value) const {
return value == value_to_match_;
}
int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) {
switch (status) {
case installer::FIRST_INSTALL_SUCCESS:
case installer::INSTALL_REPAIRED:
case installer::NEW_VERSION_UPDATED:
case installer::IN_USE_UPDATED:
case installer::UNUSED_BINARIES_UNINSTALLED:
return 0;
default:
return status;
}
}
void InstallUtil::MakeUninstallCommand(const base::string16& program,
const base::string16& arguments,
CommandLine* command_line) {
*command_line = CommandLine::FromString(L"\"" + program + L"\" " + arguments);
}
base::string16 InstallUtil::GetCurrentDate() {
static const wchar_t kDateFormat[] = L"yyyyMMdd";
wchar_t date_str[arraysize(kDateFormat)] = {0};
int len = GetDateFormatW(LOCALE_INVARIANT, 0, NULL, kDateFormat,
date_str, arraysize(date_str));
if (len) {
--len;
} else {
PLOG(DFATAL) << "GetDateFormat";
}
return base::string16(date_str, len);
}
bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path,
base::File* file) {
DCHECK(file);
file->Initialize(path, base::File::FLAG_OPEN);
return file->IsValid();
}
bool InstallUtil::ProgramCompare::GetInfo(const base::File& file,
BY_HANDLE_FILE_INFORMATION* info) {
DCHECK(file.IsValid());
return GetFileInformationByHandle(file.GetPlatformFile(), info) != 0;
}
InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match)
: path_to_match_(path_to_match),
file_info_() {
DCHECK(!path_to_match_.empty());
if (!OpenForInfo(path_to_match_, &file_)) {
PLOG(WARNING) << "Failed opening " << path_to_match_.value()
<< "; falling back to path string comparisons.";
} else if (!GetInfo(file_, &file_info_)) {
PLOG(WARNING) << "Failed getting information for "
<< path_to_match_.value()
<< "; falling back to path string comparisons.";
file_.Close();
}
}
InstallUtil::ProgramCompare::~ProgramCompare() {
}
bool InstallUtil::ProgramCompare::Evaluate(const base::string16& value) const {
base::FilePath program(CommandLine::FromString(value).GetProgram());
if (program.empty()) {
LOG(WARNING) << "Failed to parse an executable name from command line: \""
<< value << "\"";
return false;
}
return EvaluatePath(program);
}
bool InstallUtil::ProgramCompare::EvaluatePath(
const base::FilePath& path) const {
if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(),
path.value()))
return true;
if (!file_.IsValid())
return false;
base::File file;
BY_HANDLE_FILE_INFORMATION info = {};
return (OpenForInfo(path, &file) &&
GetInfo(file, &info) &&
info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber &&
info.nFileIndexHigh == file_info_.nFileIndexHigh &&
info.nFileIndexLow == file_info_.nFileIndexLow);
}