Home | History | Annotate | Download | only in launcher_support
      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