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