chromium/chrome/installer/launcher_support/chrome_launcher_support.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/installer/launcher_support/chrome_launcher_support.h"

#include <windows.h>

#include <string>

#include "base/files/file_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "build/branding_buildflags.h"

namespace chrome_launcher_support {

namespace {

// TODO(huangs) Refactor the constants: http://crbug.com/148538
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
const wchar_t kUpdateClientStateRegKey[] =
    L"Software\\Google\\Update\\ClientState";

const wchar_t kUpdateClientsRegKey[] = L"Software\\Google\\Update\\Clients";

// Copied from google_chrome_install_modes.cc.
const wchar_t kBrowserAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
const wchar_t kSxSBrowserAppGuid[] = L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}";
#else
const wchar_t kInstallationRegKey[] = L"Software\\Chromium";
#endif

// Copied from util_constants.cc.
const wchar_t kChromeExe[] = L"chrome.exe";
const wchar_t kUninstallStringField[] = L"UninstallString";
const wchar_t kVersionStringField[] = L"pv";

// Returns the registry path to where Client state is stored.
std::wstring GetClientStateRegKey() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  return kUpdateClientStateRegKey;
#else
  return kInstallationRegKey;
#endif
}

// Returns the registry path to where basic information about the Clients
// like name and version information are stored.
std::wstring GetClientsRegKey() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  return kUpdateClientsRegKey;
#else
  return kInstallationRegKey;
#endif
}

// Reads a string value from the specified product's registry key. Returns true
// iff the value is present and successfully read.
bool GetValueFromRegistry(InstallationLevel level,
                          const std::wstring key_path,
                          const wchar_t* app_guid,
                          const wchar_t* value_name,
                          std::wstring* value) {
  HKEY root_key = (level == USER_LEVEL_INSTALLATION) ? HKEY_CURRENT_USER
                                                     : HKEY_LOCAL_MACHINE;
  std::wstring subkey(key_path);
  if (app_guid)
    subkey.append(1, L'\\').append(app_guid);
  base::win::RegKey reg_key;
  // Google Update always uses 32bit hive.
  if (reg_key.Open(root_key, subkey.c_str(),
                   KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
    if (reg_key.ReadValue(value_name, value) == ERROR_SUCCESS) {
      return true;
    }
  }
  return false;
}

// Reads the path to setup.exe from the value "UninstallString" within the
// specified product's registry key. Returns an empty FilePath if an error
// occurs or the product is not installed at the specified level.
base::FilePath GetSetupExeFromRegistry(InstallationLevel level,
                                       const wchar_t* app_guid) {
  std::wstring uninstall;
  if (GetValueFromRegistry(level, GetClientStateRegKey(), app_guid,
                           kUninstallStringField, &uninstall)) {
    base::FilePath setup_exe_path(uninstall);
    if (base::PathExists(setup_exe_path))
      return setup_exe_path;
  }
  return base::FilePath();
}

// Returns the path to an existing setup.exe at the specified level, if it can
// be found via the registry.
base::FilePath GetSetupExeForInstallationLevel(InstallationLevel level) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // Look in the registry for Chrome.
  return GetSetupExeFromRegistry(level, kBrowserAppGuid);
#else
  // For Chromium, there are no GUIDs. Just look in the Chromium registry key.
  return GetSetupExeFromRegistry(level, nullptr);
#endif
}

// Returns the path to an installed |exe_file| (e.g. chrome.exe) at the
// specified level, given |setup_exe_path| from the registry.  Returns empty
// base::FilePath if none found, or if |setup_exe_path| is empty.
base::FilePath FindExeRelativeToSetupExe(const base::FilePath setup_exe_path,
                                         const wchar_t* exe_file) {
  if (!setup_exe_path.empty()) {
    // The uninstall path contains the path to setup.exe, which is two levels
    // down from |exe_file|. Move up two levels (plus one to drop the file
    // name) and look for chrome.exe from there.
    base::FilePath exe_path(
        setup_exe_path.DirName().DirName().DirName().Append(exe_file));
    if (base::PathExists(exe_path))
      return exe_path;
    // By way of mild future proofing, look up one to see if there's a
    // |exe_file| in the version directory
    exe_path = setup_exe_path.DirName().DirName().Append(exe_file);
    if (base::PathExists(exe_path))
      return exe_path;
  }
  return base::FilePath();
}

}  // namespace

base::FilePath GetChromePathForInstallationLevel(InstallationLevel level,
                                                 bool is_sxs) {
  if (is_sxs) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
    return FindExeRelativeToSetupExe(
        GetSetupExeFromRegistry(level, kSxSBrowserAppGuid), kChromeExe);
#else
    // There is no SxS build for Chromium.
    return base::FilePath();
#endif
  } else {
    return FindExeRelativeToSetupExe(GetSetupExeForInstallationLevel(level),
                                     kChromeExe);
  }
}

base::FilePath GetAnyChromePath(bool is_sxs) {
  base::FilePath path;
  path = GetChromePathForInstallationLevel(SYSTEM_LEVEL_INSTALLATION, is_sxs);
  if (path.empty())
    path = GetChromePathForInstallationLevel(USER_LEVEL_INSTALLATION, is_sxs);
  return path;
}

base::Version GetChromeVersionForInstallationLevel(InstallationLevel level,
                                                   bool is_sxs) {
  const wchar_t* app_guid = nullptr;  // Chromium doesn't use App GUIDs.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  app_guid = is_sxs ? kSxSBrowserAppGuid : kBrowserAppGuid;
#else
  // There is no SxS build for Chromium.
  if (is_sxs)
    return base::Version();
#endif

  std::wstring version_str;
  if (GetValueFromRegistry(level, GetClientsRegKey(), app_guid,
                           kVersionStringField, &version_str)) {
    base::Version version(base::WideToASCII(version_str));
    if (version.IsValid())
      return version;
  }
  return base::Version();
}

}  // namespace chrome_launcher_support