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