chromium/chrome/browser/win/conflicts/installed_applications.h

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

#ifndef CHROME_BROWSER_WIN_CONFLICTS_INSTALLED_APPLICATIONS_H_
#define CHROME_BROWSER_WIN_CONFLICTS_INSTALLED_APPLICATIONS_H_

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/win/registry.h"
#include "base/win/windows_types.h"

class MsiUtil;

// This class inspects the user's installed applications and builds a mapping of
// files to its associated application.
//
// Installed applications are found by searching the
// "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" registry key and
// its variants. There are 2 cases that are covered:
//
// 1 - If the application's installer did its due dilligence, it populated the
//     "InstallLocation" registry key with the directory where it was installed,
//     and all the files under that directory are assumed to be owned by this
//     application.
//
//     In the event of 2 conflicting "InstallLocation", both are ignored as this
//     method doesn't let us know for sure who is the owner of any enclosing
//     files.
//
// 2 - If the application's entry is a valid MSI Product GUID, the complete list
// of
//     associated file is used to exactly match a given file to a application.
//
//     If multiple products installed the same file as the same component,
//     Windows keeps a reference count of that component so that the file
//     doesn't get removed if one of them is uninstalled. So both applications
//     are returned by GetInstalledApplications().
//
//  Note: Applications may be skipped and so would not be returned by
//        GetInstalledApplications() for the following reasons:
//        - The application is owned by Microsoft.
//        - The uninstall entry is marked as a system component.
//        - The uninstall entry has no display name.
//        - The uninstall entry has no UninstallString.
//
class InstalledApplications {
 public:
  struct ApplicationInfo {
    std::wstring name;

    // Holds the path to the uninstall entry in the registry.
    HKEY registry_root;
    std::wstring registry_key_path;
    REGSAM registry_wow64_access;
  };

  // Initializes this instance with the list of installed applications. While
  // the constructor must be called in a sequence that allows blocking, its
  // public method can be used without such restrictions.
  InstalledApplications();

  InstalledApplications(const InstalledApplications&) = delete;
  InstalledApplications& operator=(const InstalledApplications&) = delete;

  virtual ~InstalledApplications();

  virtual std::vector<std::pair<HKEY, REGSAM>> GenRegistryKeyCombinations()
      const;

  // Given a |file|, checks if it matches an installed application on the user's
  // machine and appends all the matching applications to |applications|.
  // Virtual to allow mocking.
  virtual bool GetInstalledApplications(
      const base::FilePath& file,
      std::vector<ApplicationInfo>* applications) const;

 protected:
  // Protected so that tests can subclass InstalledApplications and access it.
  explicit InstalledApplications(std::unique_ptr<MsiUtil> msi_util);

 private:
  FRIEND_TEST_ALL_PREFIXES(InstalledApplicationsTest, NoDuplicates);

  // If the registry key references a valid installed application, this function
  // adds an entry to |applications_| with its list of files or installation
  // directory to their associated vector.
  void CheckRegistryKeyForInstalledApplication(HKEY hkey,
                                               const std::wstring& key_path,
                                               REGSAM wow64access,
                                               const std::wstring& key_name,
                                               const MsiUtil& msi_util,
                                               const std::wstring& user_sid);

  bool GetApplicationsFromInstalledFiles(
      const base::FilePath& file,
      std::vector<ApplicationInfo>* applications) const;
  bool GetApplicationsFromInstallDirectories(
      const base::FilePath& file,
      std::vector<ApplicationInfo>* applications) const;

  // Applications are stored in this vector because multiple entries in
  // |installed_files| could point to the same one. This is to avoid
  // duplicating them.
  std::vector<ApplicationInfo> applications_;

  // Contains all the files from applications installed via Microsoft Installer.
  // The second part of the pair is the index into |applications|.
  std::vector<std::pair<base::FilePath, size_t>> installed_files_;

  // For some applications, the best information available is the directory of
  // the installation. The compare functor treats file paths where one is the
  // parent of the other as equal.
  // The second part of the pair is the index into |applications|.
  std::vector<std::pair<base::FilePath, size_t>> install_directories_;
};

bool operator<(const InstalledApplications::ApplicationInfo& lhs,
               const InstalledApplications::ApplicationInfo& rhs);

#endif  // CHROME_BROWSER_WIN_CONFLICTS_INSTALLED_APPLICATIONS_H_