blob: fdac65c6584d16e075ab45fde5671434282b83ae [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/installer/launcher_support/chrome_launcher_support.h"
#include <windows.h>
#include <tchar.h>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/strings/string16.h"
#include "base/win/registry.h"
#ifndef OFFICIAL_BUILD
#include "base/path_service.h"
#endif
namespace chrome_launcher_support {
namespace {
// TODO(huangs) Refactor the constants: http://crbug.com/148538
const wchar_t kGoogleRegClientStateKey[] =
L"Software\\Google\\Update\\ClientState";
const wchar_t kGoogleRegClientsKey[] = L"Software\\Google\\Update\\Clients";
const wchar_t kRegVersionField[] = L"pv";
// Copied from binaries_installer_internal.cc
const wchar_t kAppHostAppId[] = L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}";
// Copied from chrome_appid.cc.
const wchar_t kBinariesAppGuid[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
// Copied from google_chrome_distribution.cc.
const wchar_t kBrowserAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
// Copied frome google_chrome_sxs_distribution.cc.
const wchar_t kSxSBrowserAppGuid[] = L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}";
// Copied from util_constants.cc.
const wchar_t kChromeAppHostExe[] = L"app_host.exe";
const char kChromeAppLauncher[] = "app-launcher";
const wchar_t kChromeExe[] = L"chrome.exe";
const wchar_t kUninstallArgumentsField[] = L"UninstallArguments";
const wchar_t kUninstallStringField[] = L"UninstallString";
// Reads a string value from the specified product's "ClientState" registry key.
// Returns true iff the value is present and successfully read.
bool GetClientStateValue(InstallationLevel level,
const wchar_t* app_guid,
const wchar_t* value_name,
base::string16* value) {
HKEY root_key = (level == USER_LEVEL_INSTALLATION) ?
HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
base::string16 subkey(kGoogleRegClientStateKey);
subkey.append(1, L'\\').append(app_guid);
base::win::RegKey reg_key;
// Google Update always uses 32bit hive.
if (reg_key.Open(root_key, subkey.c_str(),
KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
if (reg_key.ReadValue(value_name, value) == ERROR_SUCCESS) {
return true;
}
}
return false;
}
// Determines whether the specified product has a key in "Clients". This
// indicates whether the product is installed at the given level.
bool IsProductInstalled(InstallationLevel level, const wchar_t* app_guid) {
HKEY root_key = (level == USER_LEVEL_INSTALLATION) ?
HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
base::string16 subkey(kGoogleRegClientsKey);
subkey.append(1, L'\\').append(app_guid);
base::win::RegKey reg_key;
// Google Update always uses 32bit hive.
return reg_key.Open(root_key, subkey.c_str(),
KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
reg_key.HasValue(kRegVersionField);
}
bool IsAppLauncherEnabledAtLevel(InstallationLevel level) {
base::string16 uninstall_arguments;
if (GetClientStateValue(level,
kAppHostAppId,
kUninstallArgumentsField,
&uninstall_arguments)) {
return CommandLine::FromString(L"dummy.exe " + uninstall_arguments)
.HasSwitch(kChromeAppLauncher) &&
!GetAppHostPathForInstallationLevel(level).empty();
}
return false;
}
// Reads the path to setup.exe from the value "UninstallString" within the
// specified product's "ClientState" registry key. Returns an empty FilePath if
// an error occurs or the product is not installed at the specified level.
base::FilePath GetSetupExeFromRegistry(InstallationLevel level,
const wchar_t* app_guid) {
base::string16 uninstall;
if (GetClientStateValue(level, app_guid, kUninstallStringField, &uninstall)) {
base::FilePath setup_exe_path(uninstall);
if (base::PathExists(setup_exe_path))
return setup_exe_path;
}
return base::FilePath();
}
// Returns the path to an installed |exe_file| (e.g. chrome.exe, app_host.exe)
// at the specified level, given |setup_exe_path| from Omaha client state.
// Returns empty base::FilePath if none found, or if |setup_exe_path| is empty.
base::FilePath FindExeRelativeToSetupExe(const base::FilePath setup_exe_path,
const wchar_t* exe_file) {
if (!setup_exe_path.empty()) {
// The uninstall path contains the path to setup.exe, which is two levels
// down from |exe_file|. Move up two levels (plus one to drop the file
// name) and look for chrome.exe from there.
base::FilePath exe_path(
setup_exe_path.DirName().DirName().DirName().Append(exe_file));
if (base::PathExists(exe_path))
return exe_path;
// By way of mild future proofing, look up one to see if there's a
// |exe_file| in the version directory
exe_path = setup_exe_path.DirName().DirName().Append(exe_file);
if (base::PathExists(exe_path))
return exe_path;
}
return base::FilePath();
}
} // namespace
void UninstallLegacyAppLauncher(InstallationLevel level) {
base::FilePath setup_exe(GetSetupExeFromRegistry(level, kAppHostAppId));
if (setup_exe.empty())
return;
base::string16 uninstall_arguments;
if (GetClientStateValue(level,
kAppHostAppId,
kUninstallArgumentsField,
&uninstall_arguments)) {
CommandLine uninstall_cmd = CommandLine::FromString(
L"\"" + setup_exe.value() + L"\" " + uninstall_arguments);
VLOG(1) << "Uninstalling legacy app launcher with command line: "
<< uninstall_cmd.GetCommandLineString();
base::LaunchProcess(uninstall_cmd, base::LaunchOptions(), NULL);
}
}
base::FilePath GetSetupExeForInstallationLevel(InstallationLevel level) {
// Look in the registry for Chrome Binaries first.
base::FilePath setup_exe_path(
GetSetupExeFromRegistry(level, kBinariesAppGuid));
// If the above fails, look in the registry for Chrome next.
if (setup_exe_path.empty())
setup_exe_path = GetSetupExeFromRegistry(level, kBrowserAppGuid);
// If we fail again, then setup_exe_path would be empty.
return setup_exe_path;
}
base::FilePath GetChromePathForInstallationLevel(InstallationLevel level) {
return FindExeRelativeToSetupExe(
GetSetupExeForInstallationLevel(level), kChromeExe);
}
base::FilePath GetAppHostPathForInstallationLevel(InstallationLevel level) {
return FindExeRelativeToSetupExe(
GetSetupExeFromRegistry(level, kAppHostAppId), kChromeAppHostExe);
}
base::FilePath GetChromeSxSPathForInstallationLevel(InstallationLevel level) {
return FindExeRelativeToSetupExe(
GetSetupExeFromRegistry(level, kSxSBrowserAppGuid), kChromeExe);
}
base::FilePath GetAnyChromePath() {
base::FilePath chrome_path;
if (chrome_path.empty())
chrome_path = GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION);
if (chrome_path.empty())
chrome_path = GetChromePathForInstallationLevel(USER_LEVEL_INSTALLATION);
return chrome_path;
}
base::FilePath GetAnyAppHostPath() {
base::FilePath app_host_path;
if (app_host_path.empty()) {
app_host_path = GetAppHostPathForInstallationLevel(
SYSTEM_LEVEL_INSTALLATION);
}
if (app_host_path.empty())
app_host_path = GetAppHostPathForInstallationLevel(USER_LEVEL_INSTALLATION);
return app_host_path;
}
base::FilePath GetAnyChromeSxSPath() {
base::FilePath path =
GetChromeSxSPathForInstallationLevel(USER_LEVEL_INSTALLATION);
if (path.empty())
path = GetChromeSxSPathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION);
return path;
}
bool IsAppHostPresent() {
base::FilePath app_host_exe = GetAnyAppHostPath();
return !app_host_exe.empty();
}
InstallationState GetAppLauncherInstallationState() {
if (IsAppLauncherEnabledAtLevel(SYSTEM_LEVEL_INSTALLATION))
return INSTALLED_AT_SYSTEM_LEVEL;
if (IsAppLauncherEnabledAtLevel(USER_LEVEL_INSTALLATION))
return INSTALLED_AT_USER_LEVEL;
return NOT_INSTALLED;
}
bool IsAppLauncherPresent() {
return GetAppLauncherInstallationState() != NOT_INSTALLED;
}
bool IsChromeBrowserPresent() {
return IsProductInstalled(USER_LEVEL_INSTALLATION, kBrowserAppGuid) ||
IsProductInstalled(SYSTEM_LEVEL_INSTALLATION, kBrowserAppGuid);
}
} // namespace chrome_launcher_support