chromium/chrome/updater/win/setup/setup_util.cc

// Copyright 2019 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/updater/win/setup/setup_util.h"

#include <windows.h>

#include <regstr.h>
#include <shlobj.h>
#include <wrl/client.h>
#include <wrl/implements.h>

#include <cstring>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/win/registry.h"
#include "base/win/win_util.h"
#include "chrome/installer/util/install_service_work_item.h"
#include "chrome/installer/util/registry_util.h"
#include "chrome/installer/util/work_item_list.h"
#include "chrome/updater/app/server/win/updater_idl.h"
#include "chrome/updater/app/server/win/updater_internal_idl.h"
#include "chrome/updater/app/server/win/updater_legacy_idl.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/util/util.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/task_scheduler.h"
#include "chrome/updater/win/ui/resources/resources.grh"
#include "chrome/updater/win/win_constants.h"

namespace updater {
namespace {

std::wstring CreateRandomTaskName(UpdaterScope scope) {
  GUID random_guid = {0};
  return SUCCEEDED(::CoCreateGuid(&random_guid))
             ? base::StrCat(
                   {GetTaskNamePrefix(scope), StringFromGuid(random_guid)})
             : std::wstring();
}

// Adds work items to `list` to install the progid corresponding to `clsid`.
void AddInstallComProgIdWorkItems(UpdaterScope scope,
                                  CLSID clsid,
                                  WorkItemList* list) {
  const std::wstring progid(GetProgIdForClsid(clsid));
  if (!progid.empty()) {
    const HKEY root = UpdaterScopeToHKeyRoot(scope);
    const std::wstring progid_reg_path(GetComProgIdRegistryPath(progid));

    // Delete any old registrations first.
    for (const auto& key_flag : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
      list->AddDeleteRegKeyWorkItem(root, progid_reg_path, key_flag);
    }

    list->AddCreateRegKeyWorkItem(root, progid_reg_path + L"\\CLSID",
                                  WorkItem::kWow64Default);
    list->AddSetRegValueWorkItem(root, progid_reg_path + L"\\CLSID",
                                 WorkItem::kWow64Default, L"",
                                 StringFromGuid(clsid), true);
  }
}

}  // namespace

std::wstring GetTaskName(UpdaterScope scope) {
  scoped_refptr<TaskScheduler> task_scheduler =
      TaskScheduler::CreateInstance(scope);
  return task_scheduler
             ? task_scheduler->FindFirstTaskName(GetTaskNamePrefix(scope))
             : std::wstring();
}

void UnregisterWakeTask(UpdaterScope scope) {
  scoped_refptr<TaskScheduler> task_scheduler =
      TaskScheduler::CreateInstance(scope);
  if (!task_scheduler) {
    LOG(ERROR) << "Can't create a TaskScheduler instance.";
    return;
  }
  const std::wstring task_name = GetTaskName(scope);
  if (task_name.empty()) {
    LOG(ERROR) << "Empty task name during uninstall.";
    return;
  }
  if (task_scheduler->DeleteTask(task_name)) {
    VLOG(1) << "UnregisterWakeTask succeeded: " << task_name;
  } else {
    VLOG(1) << "UnregisterWakeTask failed: " << task_name;
  }
}

#define INTERFACE_PAIR(interface) \
  { __uuidof(interface), L#interface }

std::vector<std::pair<IID, std::wstring>> GetSideBySideInterfaces(
    UpdaterScope scope) {
  switch (scope) {
    case UpdaterScope::kUser:
      return {
          INTERFACE_PAIR(IUpdaterInternalUser),
          INTERFACE_PAIR(IUpdaterInternalCallbackUser),
      };
    case UpdaterScope::kSystem:
      return {
          INTERFACE_PAIR(IUpdaterInternalSystem),
          INTERFACE_PAIR(IUpdaterInternalCallbackSystem),
      };
  }
}

std::vector<std::pair<IID, std::wstring>> GetActiveInterfaces(
    UpdaterScope scope) {
  return JoinVectors(
      [&scope]() -> std::vector<std::pair<IID, std::wstring>> {
        switch (scope) {
          case UpdaterScope::kUser:
            return {
                INTERFACE_PAIR(IUpdateStateUser),
                INTERFACE_PAIR(IUpdaterUser),
                INTERFACE_PAIR(ICompleteStatusUser),
                INTERFACE_PAIR(IUpdaterObserverUser),
                INTERFACE_PAIR(IUpdaterCallbackUser),
                INTERFACE_PAIR(IUpdaterAppStateUser),
                INTERFACE_PAIR(IUpdaterAppStatesCallbackUser),

                // legacy interfaces.
                INTERFACE_PAIR(IAppVersionWebUser),
                INTERFACE_PAIR(ICurrentStateUser),
                INTERFACE_PAIR(IGoogleUpdate3WebUser),
                INTERFACE_PAIR(IAppBundleWebUser),
                INTERFACE_PAIR(IAppWebUser),
                INTERFACE_PAIR(IAppCommandWebUser),
                INTERFACE_PAIR(IPolicyStatusUser),
                INTERFACE_PAIR(IPolicyStatus2User),
                INTERFACE_PAIR(IPolicyStatus3User),
                INTERFACE_PAIR(IPolicyStatus4User),
                INTERFACE_PAIR(IPolicyStatusValueUser),
            };
          case UpdaterScope::kSystem:
            return {
                INTERFACE_PAIR(IUpdateStateSystem),
                INTERFACE_PAIR(IUpdaterSystem),
                INTERFACE_PAIR(ICompleteStatusSystem),
                INTERFACE_PAIR(IUpdaterObserverSystem),
                INTERFACE_PAIR(IUpdaterCallbackSystem),
                INTERFACE_PAIR(IUpdaterAppStateSystem),
                INTERFACE_PAIR(IUpdaterAppStatesCallbackSystem),

                // legacy interfaces.
                INTERFACE_PAIR(IAppVersionWebSystem),
                INTERFACE_PAIR(ICurrentStateSystem),
                INTERFACE_PAIR(IGoogleUpdate3WebSystem),
                INTERFACE_PAIR(IAppBundleWebSystem),
                INTERFACE_PAIR(IAppWebSystem),
                INTERFACE_PAIR(IAppCommandWebSystem),
                INTERFACE_PAIR(IPolicyStatusSystem),
                INTERFACE_PAIR(IPolicyStatus2System),
                INTERFACE_PAIR(IPolicyStatus3System),
                INTERFACE_PAIR(IPolicyStatus4System),
                INTERFACE_PAIR(IPolicyStatusValueSystem),
                INTERFACE_PAIR(IProcessLauncher),
                INTERFACE_PAIR(IProcessLauncherSystem),
                INTERFACE_PAIR(IProcessLauncher2),
                INTERFACE_PAIR(IProcessLauncher2System),
            };
        }
      }(),
      {
          // legacy interfaces.
          INTERFACE_PAIR(IAppBundleWeb),
          INTERFACE_PAIR(IAppWeb),
          INTERFACE_PAIR(IAppCommandWeb),
          INTERFACE_PAIR(IAppVersionWeb),
          INTERFACE_PAIR(ICurrentState),
          INTERFACE_PAIR(IGoogleUpdate3Web),
          INTERFACE_PAIR(IPolicyStatus),
          INTERFACE_PAIR(IPolicyStatus2),
          INTERFACE_PAIR(IPolicyStatus3),
          INTERFACE_PAIR(IPolicyStatusValue),
      });
}
#undef INTERFACE_PAIR

std::vector<std::pair<IID, std::wstring>> GetInterfaces(bool is_internal,
                                                        UpdaterScope scope) {
  return is_internal ? GetSideBySideInterfaces(scope)
                     : GetActiveInterfaces(scope);
}

std::vector<CLSID> GetSideBySideServers(UpdaterScope scope) {
  switch (scope) {
    case UpdaterScope::kUser:
      return {__uuidof(UpdaterInternalUserClass)};
    case UpdaterScope::kSystem:
      return {__uuidof(UpdaterInternalSystemClass)};
  }
}

std::vector<CLSID> GetActiveServers(UpdaterScope scope) {
  switch (scope) {
    case UpdaterScope::kUser:
      return {
          __uuidof(UpdaterUserClass),
          __uuidof(GoogleUpdate3WebUserClass),
          __uuidof(PolicyStatusUserClass),
      };
    case UpdaterScope::kSystem:
      return {
          __uuidof(UpdaterSystemClass),
          __uuidof(GoogleUpdate3WebSystemClass),
          __uuidof(GoogleUpdate3WebServiceClass),
          __uuidof(PolicyStatusSystemClass),
          __uuidof(ProcessLauncherClass),
      };
  }
}

std::vector<CLSID> GetServers(bool is_internal, UpdaterScope scope) {
  return is_internal ? GetSideBySideServers(scope) : GetActiveServers(scope);
}

bool InstallComInterfaces(UpdaterScope scope, bool is_internal) {
  VLOG(1) << __func__ << ": scope: " << scope
          << ": is_internal: " << is_internal;
  const std::optional<base::FilePath> versioned_directory =
      GetVersionedInstallDirectory(scope);
  if (!versioned_directory) {
    return false;
  }
  const base::FilePath updater_path =
      versioned_directory->Append(GetExecutableRelativePath());
  std::unique_ptr<WorkItemList> list(WorkItem::CreateWorkItemList());
  for (const auto& [iid, interface_name] : GetInterfaces(is_internal, scope)) {
    AddInstallComInterfaceWorkItems(UpdaterScopeToHKeyRoot(scope), updater_path,
                                    iid, interface_name, list.get());
  }
  return list->Do();
}

bool AreComInterfacesPresent(UpdaterScope scope, bool is_internal) {
  VLOG(1) << __func__ << ": scope: " << scope
          << ": is_internal: " << is_internal;

  bool are_interfaces_present = true;
  for (const auto& [iid, interface_name] : GetInterfaces(is_internal, scope)) {
    const HKEY root = UpdaterScopeToHKeyRoot(scope);
    const std::wstring iid_path = GetComIidRegistryPath(iid);
    const std::wstring typelib_path = GetComTypeLibRegistryPath(iid);
    for (const auto& path :
         {iid_path, base::StrCat({iid_path, L"\\", L"ProxyStubClsid32"}),
          base::StrCat({iid_path, L"\\", L"TypeLib"}), typelib_path}) {
      for (const auto& key_flag : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
        if (!base::win::RegKey(root, path.c_str(), KEY_QUERY_VALUE | key_flag)
                 .Valid()) {
          VLOG(2) << __func__ << ": interface entry missing: " << interface_name
                  << ": path: " << path << ": key_flag: " << key_flag;
          are_interfaces_present = false;
        }
      }
    }
  }
  VLOG_IF(2, are_interfaces_present) << __func__ << ": all interfaces present";
  return are_interfaces_present;
}

// Adds work items to `list` to install the interface `iid`.
void AddInstallComInterfaceWorkItems(HKEY root,
                                     const base::FilePath& typelib_path,
                                     GUID iid,
                                     const std::wstring& interface_name,
                                     WorkItemList* list) {
  const std::wstring iid_reg_path = GetComIidRegistryPath(iid);
  const std::wstring typelib_reg_path = GetComTypeLibRegistryPath(iid);

  for (const auto& key_flag : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
    // Registering the Ole Automation marshaler with the CLSID
    // {00020424-0000-0000-C000-000000000046} as the proxy/stub for the
    // interfaces.
    list->AddCreateRegKeyWorkItem(root, iid_reg_path, key_flag);
    list->AddSetRegValueWorkItem(root, iid_reg_path, key_flag, L"",
                                 interface_name, true);
    {
      const std::wstring path = iid_reg_path + L"\\ProxyStubClsid32";
      list->AddCreateRegKeyWorkItem(root, path, key_flag);
      list->AddSetRegValueWorkItem(root, path, key_flag, L"",
                                   L"{00020424-0000-0000-C000-000000000046}",
                                   true);
    }
    {
      const std::wstring path = iid_reg_path + L"\\TypeLib";
      list->AddCreateRegKeyWorkItem(root, path, key_flag);
      list->AddSetRegValueWorkItem(root, path, key_flag, L"",
                                   StringFromGuid(iid), true);
      list->AddSetRegValueWorkItem(root, path, key_flag, L"Version", L"1.0",
                                   true);
    }
  }

  // The TypeLib registration for the Ole Automation marshaler.
  const std::wstring typelib_resource_index = GetComTypeLibResourceIndex(iid);
  const base::FilePath qualified_typelib_path =
      typelib_path.Append(typelib_resource_index);
  for (const auto& path : {typelib_reg_path + L"\\1.0\\0\\win32",
                           typelib_reg_path + L"\\1.0\\0\\win64"}) {
    list->AddCreateRegKeyWorkItem(root, path, WorkItem::kWow64Default);
    list->AddSetRegValueWorkItem(root, path, WorkItem::kWow64Default, L"",
                                 qualified_typelib_path.value(), true);
  }
  list->AddSetRegValueWorkItem(
      root, typelib_reg_path + L"\\1.0", WorkItem::kWow64Default, L"",
      base::StrCat({PRODUCT_FULLNAME_STRING L" TypeLib for ", interface_name}),
      true);
}

// Adds work items to `list` to install the server `iid`.
void AddInstallServerWorkItems(HKEY root,
                               CLSID clsid,
                               const base::FilePath& com_server_path,
                               bool internal_service,
                               WorkItemList* list) {
  const std::wstring clsid_reg_path = GetComServerClsidRegistryPath(clsid);

  // Delete any old registrations first.
  for (const auto& key_flag : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
    list->AddDeleteRegKeyWorkItem(root, clsid_reg_path, key_flag);
  }

  list->AddCreateRegKeyWorkItem(root, clsid_reg_path, WorkItem::kWow64Default);
  const std::wstring local_server32_reg_path =
      base::StrCat({clsid_reg_path, L"\\LocalServer32"});
  list->AddCreateRegKeyWorkItem(root, local_server32_reg_path,
                                WorkItem::kWow64Default);

  base::CommandLine run_com_server_command(com_server_path);
  run_com_server_command.AppendSwitch(kServerSwitch);
  run_com_server_command.AppendSwitchASCII(
      kServerServiceSwitch, internal_service
                                ? kServerUpdateServiceInternalSwitchValue
                                : kServerUpdateServiceSwitchValue);
  list->AddSetRegValueWorkItem(
      root, local_server32_reg_path, WorkItem::kWow64Default, L"",
      run_com_server_command.GetCommandLineString(), true);
}

void AddComServerWorkItems(const base::FilePath& com_server_path,
                           bool is_internal,
                           WorkItemList* list) {
  CHECK(list);

  if (com_server_path.empty()) {
    LOG(DFATAL) << "com_server_path is invalid.";
    return;
  }

  for (const auto& clsid : GetServers(is_internal, UpdaterScope::kUser)) {
    AddInstallServerWorkItems(HKEY_CURRENT_USER, clsid, com_server_path,
                              is_internal, list);
    AddInstallComProgIdWorkItems(UpdaterScope::kUser, clsid, list);
  }

  for (const auto& [iid, interface_name] :
       GetInterfaces(is_internal, UpdaterScope::kUser)) {
    AddInstallComInterfaceWorkItems(HKEY_CURRENT_USER, com_server_path, iid,
                                    interface_name, list);
  }
}

void AddComServiceWorkItems(const base::FilePath& com_service_path,
                            bool internal_service,
                            WorkItemList* list) {
  CHECK(::IsUserAnAdmin());

  if (com_service_path.empty()) {
    LOG(DFATAL) << "com_service_path is invalid.";
    return;
  }

  // This assumes the COM service runs elevated and in the system updater scope.
  base::CommandLine com_service_command(com_service_path);
  com_service_command.AppendSwitch(kSystemSwitch);
  com_service_command.AppendSwitch(kWindowsServiceSwitch);
  com_service_command.AppendSwitchASCII(
      kServerServiceSwitch, internal_service
                                ? kServerUpdateServiceInternalSwitchValue
                                : kServerUpdateServiceSwitchValue);

  base::CommandLine com_switch(base::CommandLine::NO_PROGRAM);
  com_switch.AppendSwitch(kComServiceSwitch);

  const std::vector<CLSID> clsids(
      GetServers(internal_service, UpdaterScope::kSystem));

  // Delete any old registrations in the 32-bit and 64-bit hives, because
  // `installer::InstallServiceWorkItem` does not do this. This is important for
  // scenarios where the machine may have a pre-existing 32-bit installation,
  // and the current install is 64-bit. If any 32-bit keys remain, they will
  // shadow the 64-bit keys.
  for (const auto& clsid : clsids) {
    const std::wstring clsid_reg_path = GetComServerClsidRegistryPath(clsid);
    for (const auto& key_flag : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
      list->AddDeleteRegKeyWorkItem(HKEY_LOCAL_MACHINE, clsid_reg_path,
                                    key_flag);
    }
  }

  list->AddWorkItem(new installer::InstallServiceWorkItem(
      GetServiceName(internal_service).c_str(),
      GetServiceDisplayName(internal_service).c_str(), SERVICE_AUTO_START,
      com_service_command, com_switch, UPDATER_KEY, clsids, {}));

  for (const auto& clsid : clsids) {
    AddInstallComProgIdWorkItems(UpdaterScope::kSystem, clsid, list);
  }

  for (const auto& [iid, interface_name] :
       GetInterfaces(internal_service, UpdaterScope::kSystem)) {
    AddInstallComInterfaceWorkItems(HKEY_LOCAL_MACHINE, com_service_path, iid,
                                    interface_name, list);
  }
}

std::wstring GetProgIdForClsid(REFCLSID clsid) {
  auto clsid_comparator = [](REFCLSID a, REFCLSID b) {
    return std::memcmp(&a, &b, sizeof(a)) < 0;
  };

  const base::flat_map<CLSID, std::wstring, decltype(clsid_comparator)>
      kClsidToProgId = {
          {__uuidof(GoogleUpdate3WebSystemClass),
           kGoogleUpdate3WebSystemClassProgId},
          {__uuidof(GoogleUpdate3WebUserClass),
           kGoogleUpdate3WebUserClassProgId},
      };

  const auto progid = kClsidToProgId.find(clsid);
  return progid != kClsidToProgId.end() ? progid->second : L"";
}

std::wstring GetComProgIdRegistryPath(const std::wstring& progid) {
  return base::StrCat({L"Software\\Classes\\", progid});
}

std::wstring GetComServerClsidRegistryPath(REFCLSID clsid) {
  return base::StrCat({L"Software\\Classes\\CLSID\\", StringFromGuid(clsid)});
}

std::wstring GetComServerAppidRegistryPath(REFGUID appid) {
  return base::StrCat({L"Software\\Classes\\AppID\\", StringFromGuid(appid)});
}

std::wstring GetComIidRegistryPath(REFIID iid) {
  return base::StrCat({L"Software\\Classes\\Interface\\", StringFromGuid(iid)});
}

std::wstring GetComTypeLibRegistryPath(REFIID iid) {
  return base::StrCat({L"Software\\Classes\\TypeLib\\", StringFromGuid(iid)});
}

HRESULT RegisterTypeLibs(UpdaterScope scope, bool is_internal) {
  VLOG(1) << __func__ << ": scope: " << scope
          << ": is_internal: " << is_internal;

  base::FilePath exe_path;
  if (!base::PathService::Get(base::DIR_EXE, &exe_path)) {
    return E_UNEXPECTED;
  }
  exe_path = exe_path.Append(GetExecutableRelativePath());

  for (const auto& typelib_resource_index : [&]() -> std::vector<int> {
         if (IsSystemInstall(scope)) {
           if (is_internal) {
             return {TYPELIB_UPDATER_INTERNAL_IDL_SYSTEM};
           }
           return {TYPELIB_UPDATER_IDL_SYSTEM,
                   TYPELIB_UPDATER_LEGACY_IDL_SYSTEM};
         }
         if (is_internal) {
           return {TYPELIB_UPDATER_INTERNAL_IDL_USER};
         }
         return {TYPELIB_UPDATER_IDL_USER, TYPELIB_UPDATER_LEGACY_IDL_USER};
       }()) {
    const base::FilePath typelib_path =
        exe_path.AppendASCII(base::NumberToString(typelib_resource_index));

    Microsoft::WRL::ComPtr<ITypeLib> type_lib;
    if (HRESULT hr = ::LoadTypeLib(typelib_path.value().c_str(), &type_lib);
        FAILED(hr)) {
      LOG(ERROR) << __func__ << " ::LoadTypeLib failed, " << typelib_path
                 << ", " << std::hex << hr;
      return hr;
    }

    std::wstring typelib_path_str = typelib_path.value().c_str();
    const HRESULT hr =
        IsSystemInstall(scope)
            ? ::RegisterTypeLib(type_lib.Get(), &typelib_path_str[0], nullptr)
            : ::RegisterTypeLibForUser(type_lib.Get(), &typelib_path_str[0],
                                       nullptr);
    if (FAILED(hr)) {
      LOG(ERROR) << __func__ << " ::GetTypeInfoOfGuid failed" << ", "
                 << typelib_path << ", " << std::hex << hr;
      return hr;
    }
  }
  return S_OK;
}

std::wstring GetComTypeLibResourceIndex(REFIID iid) {
  // These values must be kept in sync with the numeric typelib resource
  // indexes in the resource file.
  static constexpr wchar_t kUpdaterUserIndex[] = L"1";
  static constexpr wchar_t kUpdaterInternalUserIndex[] = L"2";
  static constexpr wchar_t kUpdaterLegacyUserIndex[] = L"3";
  static constexpr wchar_t kUpdaterSystemIndex[] = L"4";
  static constexpr wchar_t kUpdaterInternalSystemIndex[] = L"5";
  static constexpr wchar_t kUpdaterLegacySystemIndex[] = L"6";

  const wchar_t* updater_legacy_index =
      IsSystemInstall() ? kUpdaterLegacySystemIndex : kUpdaterLegacyUserIndex;

  const base::flat_map<IID, const wchar_t*, IidComparator> kTypeLibIndexes = {
      // Updater typelib.
      {__uuidof(IUpdaterAppState),
       IsSystemInstall() ? kUpdaterSystemIndex : kUpdaterUserIndex},

      // Updater user typelib.
      {__uuidof(ICompleteStatusUser), kUpdaterUserIndex},
      {__uuidof(IUpdaterUser), kUpdaterUserIndex},
      {__uuidof(IUpdaterObserverUser), kUpdaterUserIndex},
      {__uuidof(IUpdateStateUser), kUpdaterUserIndex},
      {__uuidof(IUpdaterCallbackUser), kUpdaterUserIndex},
      {__uuidof(IUpdaterAppStateUser), kUpdaterUserIndex},
      {__uuidof(IUpdaterAppStatesCallbackUser), kUpdaterUserIndex},

      // Updater system typelib.
      {__uuidof(ICompleteStatusSystem), kUpdaterSystemIndex},
      {__uuidof(IUpdaterSystem), kUpdaterSystemIndex},
      {__uuidof(IUpdaterObserverSystem), kUpdaterSystemIndex},
      {__uuidof(IUpdateStateSystem), kUpdaterSystemIndex},
      {__uuidof(IUpdaterCallbackSystem), kUpdaterSystemIndex},
      {__uuidof(IUpdaterAppStateSystem), kUpdaterSystemIndex},
      {__uuidof(IUpdaterAppStatesCallbackSystem), kUpdaterSystemIndex},

      // Updater internal user typelib.
      {__uuidof(IUpdaterInternalUser), kUpdaterInternalUserIndex},
      {__uuidof(IUpdaterInternalCallbackUser), kUpdaterInternalUserIndex},

      // Updater internal system typelib.
      {__uuidof(IUpdaterInternalSystem), kUpdaterInternalSystemIndex},
      {__uuidof(IUpdaterInternalCallbackSystem), kUpdaterInternalSystemIndex},

      // Updater legacy typelib.
      {__uuidof(IAppVersionWeb), updater_legacy_index},
      {__uuidof(ICurrentState), updater_legacy_index},
      {__uuidof(IGoogleUpdate3Web), updater_legacy_index},
      {__uuidof(IAppBundleWeb), updater_legacy_index},
      {__uuidof(IAppWeb), updater_legacy_index},
      {__uuidof(IAppCommandWeb), updater_legacy_index},
      {__uuidof(IPolicyStatus), updater_legacy_index},
      {__uuidof(IPolicyStatus2), updater_legacy_index},
      {__uuidof(IPolicyStatus3), updater_legacy_index},
      {__uuidof(IPolicyStatus4), updater_legacy_index},
      {__uuidof(IPolicyStatusValue), updater_legacy_index},
      {__uuidof(IProcessLauncher), updater_legacy_index},
      {__uuidof(IProcessLauncher2), updater_legacy_index},

      // Updater legacy user typelib.
      {__uuidof(IAppVersionWebUser), kUpdaterLegacyUserIndex},
      {__uuidof(ICurrentStateUser), kUpdaterLegacyUserIndex},
      {__uuidof(IGoogleUpdate3WebUser), kUpdaterLegacyUserIndex},
      {__uuidof(IAppBundleWebUser), kUpdaterLegacyUserIndex},
      {__uuidof(IAppWebUser), kUpdaterLegacyUserIndex},
      {__uuidof(IAppCommandWebUser), kUpdaterLegacyUserIndex},
      {__uuidof(IPolicyStatusUser), kUpdaterLegacyUserIndex},
      {__uuidof(IPolicyStatus2User), kUpdaterLegacyUserIndex},
      {__uuidof(IPolicyStatus3User), kUpdaterLegacyUserIndex},
      {__uuidof(IPolicyStatus4User), kUpdaterLegacyUserIndex},
      {__uuidof(IPolicyStatusValueUser), kUpdaterLegacyUserIndex},

      // Updater legacy system typelib.
      {__uuidof(IAppVersionWebSystem), kUpdaterLegacySystemIndex},
      {__uuidof(ICurrentStateSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IGoogleUpdate3WebSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IAppBundleWebSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IAppWebSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IAppCommandWebSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IPolicyStatusSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IPolicyStatus2System), kUpdaterLegacySystemIndex},
      {__uuidof(IPolicyStatus3System), kUpdaterLegacySystemIndex},
      {__uuidof(IPolicyStatus4System), kUpdaterLegacySystemIndex},
      {__uuidof(IPolicyStatusValueSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IProcessLauncherSystem), kUpdaterLegacySystemIndex},
      {__uuidof(IProcessLauncher2System), kUpdaterLegacySystemIndex},
  };
  const auto index = kTypeLibIndexes.find(iid);
  CHECK(index != kTypeLibIndexes.end()) << StringFromGuid(iid);
  return index->second;
}

void RegisterUserRunAtStartup(const std::wstring& run_value_name,
                              const base::CommandLine& command,
                              WorkItemList* list) {
  CHECK(list);
  VLOG(1) << __func__;

  list->AddSetRegValueWorkItem(HKEY_CURRENT_USER, REGSTR_PATH_RUN, 0,
                               run_value_name, command.GetCommandLineString(),
                               true);
}

bool UnregisterUserRunAtStartup(const std::wstring& run_value_name) {
  VLOG(1) << __func__;

  return installer::DeleteRegistryValue(HKEY_CURRENT_USER, REGSTR_PATH_RUN, 0,
                                        run_value_name);
}

bool DeleteLegacyEntriesPerUser() {
  // The IProcessLauncher and IProcessLauncher2 interfaces are now only
  // registered for system since r1154562. So the code below removes these
  // interfaces from the user hive.
  bool success = true;
  for (REGSAM bitness : {KEY_WOW64_32KEY, KEY_WOW64_64KEY}) {
    for (const auto& iid :
         {__uuidof(IProcessLauncher), __uuidof(IProcessLauncher2)}) {
      for (const auto& reg_path :
           {GetComIidRegistryPath(iid), GetComTypeLibRegistryPath(iid)}) {
        if (!installer::DeleteRegistryKey(HKEY_CURRENT_USER, reg_path,
                                          bitness)) {
          success = false;
        }
      }
    }
  }
  return success;
}

RegisterWakeTaskWorkItem::RegisterWakeTaskWorkItem(
    const base::CommandLine& run_command,
    UpdaterScope scope)
    : run_command_(run_command), scope_(scope) {}

RegisterWakeTaskWorkItem::~RegisterWakeTaskWorkItem() = default;

bool RegisterWakeTaskWorkItem::DoImpl() {
  scoped_refptr<TaskScheduler> task_scheduler =
      TaskScheduler::CreateInstance(scope_);
  if (!task_scheduler) {
    LOG(ERROR) << "Can't create a TaskScheduler instance.";
    return false;
  }

  // Task already exists.
  if (!GetTaskName(scope_).empty()) {
    return true;
  }

  // Create a new task name and install.
  const std::wstring task_name = CreateRandomTaskName(scope_);
  if (task_name.empty()) {
    LOG(ERROR) << "Unexpected empty task name.";
    return false;
  }

  if (task_scheduler->IsTaskRegistered(task_name)) {
    LOG(ERROR) << "Unexpected task name found. " << task_name;
    return false;
  }

  if (!task_scheduler->RegisterTask(
          task_name, GetTaskDisplayName(scope_), run_command_,
          TaskScheduler::TriggerType::TRIGGER_TYPE_HOURLY |
              TaskScheduler::TriggerType::TRIGGER_TYPE_LOGON,
          true)) {
    return false;
  }

  task_name_ = task_name;
  return true;
}

void RegisterWakeTaskWorkItem::RollbackImpl() {
  if (task_name_.empty()) {
    return;
  }
  scoped_refptr<TaskScheduler> task_scheduler =
      TaskScheduler::CreateInstance(scope_);
  if (!task_scheduler) {
    return;
  }
  std::ignore = task_scheduler->DeleteTask(task_name_);
}

}  // namespace updater