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.
GetClientStateValue(InstallationLevel level,const wchar_t * app_guid,const wchar_t * value_name,string16 * value)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.
IsProductInstalled(InstallationLevel level,const wchar_t * app_guid)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
IsAppLauncherEnabledAtLevel(InstallationLevel level)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.
GetSetupExeFromRegistry(InstallationLevel level,const wchar_t * app_guid)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.
FindExeRelativeToSetupExe(const base::FilePath setup_exe_path,const wchar_t * exe_file)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
UninstallLegacyAppLauncher(InstallationLevel level)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
GetSetupExeForInstallationLevel(InstallationLevel level)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
GetChromePathForInstallationLevel(InstallationLevel level)163 base::FilePath GetChromePathForInstallationLevel(InstallationLevel level) {
164 return FindExeRelativeToSetupExe(
165 GetSetupExeForInstallationLevel(level), kChromeExe);
166 }
167
GetAppHostPathForInstallationLevel(InstallationLevel level)168 base::FilePath GetAppHostPathForInstallationLevel(InstallationLevel level) {
169 return FindExeRelativeToSetupExe(
170 GetSetupExeFromRegistry(level, kAppHostAppId), kChromeAppHostExe);
171 }
172
GetAnyChromePath()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
GetAnyAppHostPath()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
IsAppHostPresent()193 bool IsAppHostPresent() {
194 base::FilePath app_host_exe = GetAnyAppHostPath();
195 return !app_host_exe.empty();
196 }
197
GetAppLauncherInstallationState()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
IsAppLauncherPresent()208 bool IsAppLauncherPresent() {
209 return GetAppLauncherInstallationState() != NOT_INSTALLED;
210 }
211
IsChromeBrowserPresent()212 bool IsChromeBrowserPresent() {
213 return IsProductInstalled(USER_LEVEL_INSTALLATION, kBrowserAppGuid) ||
214 IsProductInstalled(SYSTEM_LEVEL_INSTALLATION, kBrowserAppGuid);
215 }
216
217 } // namespace chrome_launcher_support
218