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