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, &current_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