chrome_launcher_support.cc revision a3f7b4e666c476898878fa745f637129375cd889
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/installer/launcher_support/chrome_launcher_support.h" 6 7#include <windows.h> 8#include <tchar.h> 9 10#include "base/command_line.h" 11#include "base/file_util.h" 12#include "base/files/file_path.h" 13#include "base/logging.h" 14#include "base/process/launch.h" 15#include "base/strings/string16.h" 16#include "base/win/registry.h" 17 18#ifndef OFFICIAL_BUILD 19#include "base/path_service.h" 20#endif 21 22namespace chrome_launcher_support { 23 24namespace { 25 26// TODO(huangs) Refactor the constants: http://crbug.com/148538 27const wchar_t kGoogleRegClientStateKey[] = 28 L"Software\\Google\\Update\\ClientState"; 29const wchar_t kGoogleRegClientsKey[] = L"Software\\Google\\Update\\Clients"; 30const wchar_t kRegVersionField[] = L"pv"; 31 32// Copied from binaries_installer_internal.cc 33const wchar_t kAppHostAppId[] = L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}"; 34 35// Copied from chrome_appid.cc. 36const wchar_t kBinariesAppGuid[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; 37 38// Copied from google_chrome_distribution.cc. 39const wchar_t kBrowserAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; 40 41// Copied from util_constants.cc. 42const wchar_t kChromeAppHostExe[] = L"app_host.exe"; 43const char kChromeAppLauncher[] = "app-launcher"; 44const wchar_t kChromeExe[] = L"chrome.exe"; 45const wchar_t kUninstallArgumentsField[] = L"UninstallArguments"; 46const wchar_t kUninstallStringField[] = L"UninstallString"; 47 48#ifndef OFFICIAL_BUILD 49base::FilePath GetDevelopmentExe(const wchar_t* exe_file) { 50 base::FilePath current_directory; 51 if (PathService::Get(base::DIR_EXE, ¤t_directory)) { 52 base::FilePath chrome_exe_path(current_directory.Append(exe_file)); 53 if (base::PathExists(chrome_exe_path)) 54 return chrome_exe_path; 55 } 56 return base::FilePath(); 57} 58#endif 59 60// Reads a string value from the specified product's "ClientState" registry key. 61// Returns true iff the value is present and successfully read. 62bool GetClientStateValue(InstallationLevel level, 63 const wchar_t* app_guid, 64 const wchar_t* value_name, 65 string16* value) { 66 HKEY root_key = (level == USER_LEVEL_INSTALLATION) ? 67 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 68 string16 subkey(kGoogleRegClientStateKey); 69 subkey.append(1, L'\\').append(app_guid); 70 base::win::RegKey reg_key; 71 // Google Update always uses 32bit hive. 72 if (reg_key.Open(root_key, subkey.c_str(), 73 KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) { 74 if (reg_key.ReadValue(value_name, value) == ERROR_SUCCESS) { 75 return true; 76 } 77 } 78 return false; 79} 80 81// Determines whether the specified product has a key in "Clients". This 82// indicates whether the product is installed at the given level. 83bool IsProductInstalled(InstallationLevel level, const wchar_t* app_guid) { 84 HKEY root_key = (level == USER_LEVEL_INSTALLATION) ? 85 HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; 86 string16 subkey(kGoogleRegClientsKey); 87 subkey.append(1, L'\\').append(app_guid); 88 base::win::RegKey reg_key; 89 // Google Update always uses 32bit hive. 90 return reg_key.Open(root_key, subkey.c_str(), 91 KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS && 92 reg_key.HasValue(kRegVersionField); 93} 94 95bool IsAppLauncherEnabledAtLevel(InstallationLevel level) { 96 string16 uninstall_arguments; 97 if (GetClientStateValue(level, 98 kAppHostAppId, 99 kUninstallArgumentsField, 100 &uninstall_arguments)) { 101 return CommandLine::FromString(L"dummy.exe " + uninstall_arguments) 102 .HasSwitch(kChromeAppLauncher) && 103 !GetAppHostPathForInstallationLevel(level).empty(); 104 } 105 return false; 106} 107 108// Reads the path to setup.exe from the value "UninstallString" within the 109// specified product's "ClientState" registry key. Returns an empty FilePath if 110// an error occurs or the product is not installed at the specified level. 111base::FilePath GetSetupExeFromRegistry(InstallationLevel level, 112 const wchar_t* app_guid) { 113 string16 uninstall; 114 if (GetClientStateValue(level, app_guid, kUninstallStringField, &uninstall)) { 115 base::FilePath setup_exe_path(uninstall); 116 if (base::PathExists(setup_exe_path)) 117 return setup_exe_path; 118 } 119 return base::FilePath(); 120} 121 122// Returns the path to an installed |exe_file| (e.g. chrome.exe, app_host.exe) 123// at the specified level, given |setup_exe_path| from Omaha client state. 124// Returns empty base::FilePath if none found, or if |setup_exe_path| is empty. 125base::FilePath FindExeRelativeToSetupExe(const base::FilePath setup_exe_path, 126 const wchar_t* exe_file) { 127 if (!setup_exe_path.empty()) { 128 // The uninstall path contains the path to setup.exe, which is two levels 129 // down from |exe_file|. Move up two levels (plus one to drop the file 130 // name) and look for chrome.exe from there. 131 base::FilePath exe_path( 132 setup_exe_path.DirName().DirName().DirName().Append(exe_file)); 133 if (base::PathExists(exe_path)) 134 return exe_path; 135 // By way of mild future proofing, look up one to see if there's a 136 // |exe_file| in the version directory 137 exe_path = setup_exe_path.DirName().DirName().Append(exe_file); 138 if (base::PathExists(exe_path)) 139 return exe_path; 140 } 141 return base::FilePath(); 142} 143 144} // namespace 145 146void UninstallLegacyAppLauncher(InstallationLevel level) { 147 base::FilePath setup_exe(GetSetupExeFromRegistry(level, kAppHostAppId)); 148 if (setup_exe.empty()) 149 return; 150 string16 uninstall_arguments; 151 if (GetClientStateValue(level, 152 kAppHostAppId, 153 kUninstallArgumentsField, 154 &uninstall_arguments)) { 155 CommandLine uninstall_cmd = CommandLine::FromString( 156 L"\"" + setup_exe.value() + L"\" " + uninstall_arguments); 157 158 VLOG(1) << "Uninstalling legacy app launcher with command line: " 159 << uninstall_cmd.GetCommandLineString(); 160 base::LaunchProcess(uninstall_cmd, base::LaunchOptions(), NULL); 161 } 162} 163 164base::FilePath GetSetupExeForInstallationLevel(InstallationLevel level) { 165 // Look in the registry for Chrome Binaries first. 166 base::FilePath setup_exe_path( 167 GetSetupExeFromRegistry(level, kBinariesAppGuid)); 168 // If the above fails, look in the registry for Chrome next. 169 if (setup_exe_path.empty()) 170 setup_exe_path = GetSetupExeFromRegistry(level, kBrowserAppGuid); 171 // If we fail again, then setup_exe_path would be empty. 172 return setup_exe_path; 173} 174 175base::FilePath GetChromePathForInstallationLevel(InstallationLevel level) { 176 return FindExeRelativeToSetupExe( 177 GetSetupExeForInstallationLevel(level), kChromeExe); 178} 179 180base::FilePath GetAppHostPathForInstallationLevel(InstallationLevel level) { 181 return FindExeRelativeToSetupExe( 182 GetSetupExeFromRegistry(level, kAppHostAppId), kChromeAppHostExe); 183} 184 185base::FilePath GetAnyChromePath() { 186 base::FilePath chrome_path; 187#ifndef OFFICIAL_BUILD 188 // For development mode, chrome.exe should be in same dir as the stub. 189 chrome_path = GetDevelopmentExe(kChromeExe); 190#endif 191 if (chrome_path.empty()) 192 chrome_path = GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION); 193 if (chrome_path.empty()) 194 chrome_path = GetChromePathForInstallationLevel(USER_LEVEL_INSTALLATION); 195 return chrome_path; 196} 197 198base::FilePath GetAnyAppHostPath() { 199 base::FilePath app_host_path; 200#ifndef OFFICIAL_BUILD 201 // For development mode, app_host.exe should be in same dir as chrome.exe. 202 app_host_path = GetDevelopmentExe(kChromeAppHostExe); 203#endif 204 if (app_host_path.empty()) { 205 app_host_path = GetAppHostPathForInstallationLevel( 206 SYSTEM_LEVEL_INSTALLATION); 207 } 208 if (app_host_path.empty()) 209 app_host_path = GetAppHostPathForInstallationLevel(USER_LEVEL_INSTALLATION); 210 return app_host_path; 211} 212 213bool IsAppHostPresent() { 214 base::FilePath app_host_exe = GetAnyAppHostPath(); 215 return !app_host_exe.empty(); 216} 217 218InstallationState GetAppLauncherInstallationState() { 219 if (IsAppLauncherEnabledAtLevel(SYSTEM_LEVEL_INSTALLATION)) 220 return INSTALLED_AT_SYSTEM_LEVEL; 221 222 if (IsAppLauncherEnabledAtLevel(USER_LEVEL_INSTALLATION)) 223 return INSTALLED_AT_USER_LEVEL; 224 225 return NOT_INSTALLED; 226} 227 228bool IsAppLauncherPresent() { 229 return GetAppLauncherInstallationState() != NOT_INSTALLED; 230} 231 232bool IsChromeBrowserPresent() { 233 return IsProductInstalled(USER_LEVEL_INSTALLATION, kBrowserAppGuid) || 234 IsProductInstalled(SYSTEM_LEVEL_INSTALLATION, kBrowserAppGuid); 235} 236 237} // namespace chrome_launcher_support 238