// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // This file contains the definitions of the installer functions that build // the WorkItemList used to install the application. #include "chrome/installer/setup/install_worker.h" #include <oaidl.h> #include <shlobj.h> #include <time.h> #include <vector> #include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/version.h" #include "base/win/registry.h" #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/installer/setup/install.h" #include "chrome/installer/setup/setup_constants.h" #include "chrome/installer/setup/setup_util.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/callback_work_item.h" #include "chrome/installer/util/conditional_work_item_list.h" #include "chrome/installer/util/create_reg_key_work_item.h" #include "chrome/installer/util/firewall_manager_win.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installer_state.h" #include "chrome/installer/util/l10n_string_util.h" #include "chrome/installer/util/product.h" #include "chrome/installer/util/set_reg_value_work_item.h" #include "chrome/installer/util/shell_util.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/work_item_list.h" using base::ASCIIToWide; using base::win::RegKey; namespace installer { namespace { // The version identifying the work done by setup.exe --configure-user-settings // on user login by way of Active Setup. Increase this value if the work done // in setup_main.cc's handling of kConfigureUserSettings changes and should be // executed again for all users. const wchar_t kActiveSetupVersion[] = L"24,0,0,0"; // Although the UUID of the ChromeFrame class is used for the "current" value, // this is done only as a convenience; there is no need for the GUID of the Low // Rights policies to match the ChromeFrame class's GUID. Hence, it is safe to // use this completely unrelated GUID for the "old" policies. const wchar_t kIELowRightsPolicyOldGuid[] = L"{6C288DD7-76FB-4721-B628-56FAC252E199}"; const wchar_t kElevationPolicyKeyPath[] = L"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\"; void GetOldIELowRightsElevationPolicyKeyPath(base::string16* key_path) { key_path->assign(kElevationPolicyKeyPath, arraysize(kElevationPolicyKeyPath) - 1); key_path->append(kIELowRightsPolicyOldGuid, arraysize(kIELowRightsPolicyOldGuid)- 1); } // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of // products managed by a given package. // |old_version| can be NULL to indicate no Chrome is currently installed. void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state, const Version* old_version, const Version& new_version, WorkItemList* work_item_list) { // First collect the list of DLLs to be registered from each product. std::vector<base::FilePath> com_dll_list; installer_state.AddComDllList(&com_dll_list); // Then, if we got some, attempt to unregister the DLLs from the old // version directory and then re-register them in the new one. // Note that if we are migrating the install directory then we will not // successfully unregister the old DLLs. // TODO(robertshield): See whether we need to fix the migration case. // TODO(robertshield): If we ever remove a DLL from a product, this will // not unregister it on update. We should build the unregistration list from // saved state instead of assuming it is the same as the registration list. if (!com_dll_list.empty()) { if (old_version) { base::FilePath old_dll_path(installer_state.target_path().AppendASCII( old_version->GetString())); installer::AddRegisterComDllWorkItems(old_dll_path, com_dll_list, installer_state.system_install(), false, // Unregister true, // May fail work_item_list); } base::FilePath dll_path(installer_state.target_path().AppendASCII( new_version.GetString())); installer::AddRegisterComDllWorkItems(dll_path, com_dll_list, installer_state.system_install(), true, // Register false, // Must succeed. work_item_list); } } void AddInstallerCopyTasks(const InstallerState& installer_state, const base::FilePath& setup_path, const base::FilePath& archive_path, const base::FilePath& temp_path, const Version& new_version, WorkItemList* install_list) { DCHECK(install_list); base::FilePath installer_dir( installer_state.GetInstallerDirectory(new_version)); install_list->AddCreateDirWorkItem(installer_dir); base::FilePath exe_dst(installer_dir.Append(setup_path.BaseName())); if (exe_dst != setup_path) { install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(), temp_path.value(), WorkItem::ALWAYS); } if (installer_state.RequiresActiveSetup()) { // Make a copy of setup.exe with a different name so that Active Setup // doesn't require an admin on XP thanks to Application Compatibility. base::FilePath active_setup_exe(installer_dir.Append(kActiveSetupExe)); install_list->AddCopyTreeWorkItem( setup_path.value(), active_setup_exe.value(), temp_path.value(), WorkItem::ALWAYS); } // If only the App Host (not even the Chrome Binaries) is being installed, // this must be a user-level App Host piggybacking on system-level Chrome // Binaries. Only setup.exe is required, and only for uninstall. if (installer_state.products().size() != 1 || !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { base::FilePath archive_dst(installer_dir.Append(archive_path.BaseName())); if (archive_path != archive_dst) { // In the past, we copied rather than moved for system level installs so // that the permissions of %ProgramFiles% would be picked up. Now that // |temp_path| is in %ProgramFiles% for system level installs (and in // %LOCALAPPDATA% otherwise), there is no need to do this for the archive. // Setup.exe, on the other hand, is created elsewhere so it must always be // copied. if (temp_path.IsParent(archive_path)) { install_list->AddMoveTreeWorkItem(archive_path.value(), archive_dst.value(), temp_path.value(), WorkItem::ALWAYS_MOVE); } else { // This may occur when setup is run out of an existing installation // directory. For example, when quick-enabling user-level App Launcher // from system-level Binaries. We can't (and don't want to) remove the // system-level archive. install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(), temp_path.value(), WorkItem::ALWAYS); } } } } base::string16 GetRegCommandKey(BrowserDistribution* dist, const wchar_t* name) { base::string16 cmd_key(dist->GetVersionKey()); cmd_key.append(1, base::FilePath::kSeparators[0]) .append(google_update::kRegCommandsKey) .append(1, base::FilePath::kSeparators[0]) .append(name); return cmd_key; } // Adds work items to create (or delete if uninstalling) app commands to launch // the app with a switch. The following criteria should be true: // 1. The switch takes one parameter. // 2. The command send pings. // 3. The command is web accessible. // 4. The command is run as the user. void AddCommandWithParameterWorkItems(const InstallerState& installer_state, const InstallationState& machine_state, const Version& new_version, const Product& product, const wchar_t* command_key, const wchar_t* app, const char* command_with_parameter, WorkItemList* work_item_list) { DCHECK(command_key); DCHECK(app); DCHECK(command_with_parameter); DCHECK(work_item_list); base::string16 full_cmd_key( GetRegCommandKey(product.distribution(), command_key)); if (installer_state.operation() == InstallerState::UNINSTALL) { work_item_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), full_cmd_key, KEY_WOW64_32KEY) ->set_log_message("removing " + base::UTF16ToASCII(command_key) + " command"); } else { CommandLine cmd_line(installer_state.target_path().Append(app)); cmd_line.AppendSwitchASCII(command_with_parameter, "%1"); AppCommand cmd(cmd_line.GetCommandLineString()); cmd.set_sends_pings(true); cmd.set_is_web_accessible(true); cmd.set_is_run_as_user(true); cmd.AddWorkItems(installer_state.root_key(), full_cmd_key, work_item_list); } } void AddInstallAppCommandWorkItems(const InstallerState& installer_state, const InstallationState& machine_state, const Version& new_version, const Product& product, WorkItemList* work_item_list) { DCHECK(product.is_chrome_app_host()); AddCommandWithParameterWorkItems(installer_state, machine_state, new_version, product, kCmdInstallApp, installer::kChromeAppHostExe, ::switches::kInstallFromWebstore, work_item_list); } void AddInstallExtensionCommandWorkItem(const InstallerState& installer_state, const InstallationState& machine_state, const base::FilePath& setup_path, const Version& new_version, const Product& product, WorkItemList* work_item_list) { DCHECK(product.is_chrome()); AddCommandWithParameterWorkItems(installer_state, machine_state, new_version, product, kCmdInstallExtension, installer::kChromeExe, ::switches::kLimitedInstallFromWebstore, work_item_list); } // A callback invoked by |work_item| that adds firewall rules for Chrome. Rules // are left in-place on rollback unless |remove_on_rollback| is true. This is // the case for new installs only. Updates and overinstalls leave the rule // in-place on rollback since a previous install of Chrome will be used in that // case. bool AddFirewallRulesCallback(bool system_level, BrowserDistribution* dist, const base::FilePath& chrome_path, bool remove_on_rollback, const CallbackWorkItem& work_item) { // There is no work to do on rollback if this is not a new install. if (work_item.IsRollback() && !remove_on_rollback) return true; scoped_ptr<FirewallManager> manager = FirewallManager::Create(dist, chrome_path); if (!manager) { LOG(ERROR) << "Failed creating a FirewallManager. Continuing with install."; return true; } if (work_item.IsRollback()) { manager->RemoveFirewallRules(); return true; } // Adding the firewall rule is expected to fail for user-level installs on // Vista+. Try anyway in case the installer is running elevated. if (!manager->AddFirewallRules()) LOG(ERROR) << "Failed creating a firewall rules. Continuing with install."; // Don't abort installation if the firewall rule couldn't be added. return true; } // Adds work items to |list| to create firewall rules. void AddFirewallRulesWorkItems(const InstallerState& installer_state, BrowserDistribution* dist, bool is_new_install, WorkItemList* list) { list->AddCallbackWorkItem( base::Bind(&AddFirewallRulesCallback, installer_state.system_install(), dist, installer_state.target_path().Append(kChromeExe), is_new_install)); } // Returns the basic CommandLine to setup.exe for a quick-enable operation on // the binaries. This will unconditionally include --multi-install as well as // --verbose-logging if the current installation was launched with // --verbose-logging. |setup_path| and |new_version| are optional only when // the operation is an uninstall. CommandLine GetGenericQuickEnableCommand( const InstallerState& installer_state, const InstallationState& machine_state, const base::FilePath& setup_path, const Version& new_version) { // Only valid for multi-install operations. DCHECK(installer_state.is_multi_install()); // Only valid when Chrome Binaries aren't being uninstalled. DCHECK(installer_state.operation() != InstallerState::UNINSTALL || !installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)); // setup_path and new_version are required when not uninstalling. DCHECK(installer_state.operation() == InstallerState::UNINSTALL || (!setup_path.empty() && new_version.IsValid())); // The path to setup.exe contains the version of the Chrome Binaries, so it // takes a little work to get it right. base::FilePath binaries_setup_path; if (installer_state.operation() == InstallerState::UNINSTALL) { // One or more products are being uninstalled, but not Chrome Binaries. // Use the path to the currently installed Chrome Binaries' setup.exe. const ProductState* product_state = machine_state.GetProductState( installer_state.system_install(), BrowserDistribution::CHROME_BINARIES); DCHECK(product_state); binaries_setup_path = product_state->uninstall_command().GetProgram(); } else { // Chrome Binaries are being installed, updated, or otherwise operated on. // Use the path to the given |setup_path| in the normal location of // multi-install Chrome Binaries of the given |version|. binaries_setup_path = installer_state.GetInstallerDirectory(new_version) .Append(setup_path.BaseName()); } DCHECK(!binaries_setup_path.empty()); CommandLine cmd_line(binaries_setup_path); cmd_line.AppendSwitch(switches::kMultiInstall); if (installer_state.verbose_logging()) cmd_line.AppendSwitch(switches::kVerboseLogging); return cmd_line; } // Adds work items to add the "quick-enable-application-host" command to the // multi-installer binaries' version key on the basis of the current operation // (represented in |installer_state|) and the pre-existing machine configuration // (represented in |machine_state|). void AddQuickEnableApplicationLauncherWorkItems( const InstallerState& installer_state, const InstallationState& machine_state, const base::FilePath& setup_path, const Version& new_version, WorkItemList* work_item_list) { DCHECK(work_item_list); bool will_have_chrome_binaries = WillProductBePresentAfterSetup(installer_state, machine_state, BrowserDistribution::CHROME_BINARIES); // For system-level binaries there is no way to keep the command state in sync // with the installation/uninstallation of the Application Launcher (which is // always at user-level). So we do not try to remove the command, i.e., it // will always be installed if the Chrome Binaries are installed. if (will_have_chrome_binaries) { base::string16 cmd_key( GetRegCommandKey(BrowserDistribution::GetSpecificDistribution( BrowserDistribution::CHROME_BINARIES), kCmdQuickEnableApplicationHost)); CommandLine cmd_line(GetGenericQuickEnableCommand(installer_state, machine_state, setup_path, new_version)); // kMultiInstall and kVerboseLogging were processed above. cmd_line.AppendSwitch(switches::kChromeAppLauncher); cmd_line.AppendSwitch(switches::kEnsureGoogleUpdatePresent); AppCommand cmd(cmd_line.GetCommandLineString()); cmd.set_sends_pings(true); cmd.set_is_web_accessible(true); cmd.set_is_run_as_user(true); cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list); } } void AddProductSpecificWorkItems(const InstallationState& original_state, const InstallerState& installer_state, const base::FilePath& setup_path, const Version& new_version, bool is_new_install, WorkItemList* list) { const Products& products = installer_state.products(); for (Products::const_iterator it = products.begin(); it < products.end(); ++it) { const Product& p = **it; if (p.is_chrome_app_host()) { AddInstallAppCommandWorkItems(installer_state, original_state, new_version, p, list); } if (p.is_chrome()) { AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p, list); AddInstallExtensionCommandWorkItem(installer_state, original_state, setup_path, new_version, p, list); AddFirewallRulesWorkItems( installer_state, p.distribution(), is_new_install, list); } if (p.is_chrome_binaries()) { AddQueryEULAAcceptanceWorkItems( installer_state, setup_path, new_version, p, list); AddQuickEnableChromeFrameWorkItems(installer_state, list); AddQuickEnableApplicationLauncherWorkItems( installer_state, original_state, setup_path, new_version, list); } } } // This is called when an MSI installation is run. It may be that a user is // attempting to install the MSI on top of a non-MSI managed installation. // If so, try and remove any existing uninstallation shortcuts, as we want the // uninstall to be managed entirely by the MSI machinery (accessible via the // Add/Remove programs dialog). void AddDeleteUninstallShortcutsForMSIWorkItems( const InstallerState& installer_state, const Product& product, const base::FilePath& temp_path, WorkItemList* work_item_list) { DCHECK(installer_state.is_msi()) << "This must only be called for MSI installations!"; // First attempt to delete the old installation's ARP dialog entry. HKEY reg_root = installer_state.root_key(); base::string16 uninstall_reg(product.distribution()->GetUninstallRegPath()); WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem( reg_root, uninstall_reg, KEY_WOW64_32KEY); delete_reg_key->set_ignore_failure(true); // Then attempt to delete the old installation's start menu shortcut. base::FilePath uninstall_link; if (installer_state.system_install()) { PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link); } else { PathService::Get(base::DIR_START_MENU, &uninstall_link); } if (uninstall_link.empty()) { LOG(ERROR) << "Failed to get location for shortcut."; } else { uninstall_link = uninstall_link.Append( product.distribution()->GetStartMenuShortcutSubfolder( BrowserDistribution::SUBFOLDER_CHROME)); uninstall_link = uninstall_link.Append( product.distribution()->GetUninstallLinkName() + installer::kLnkExt); VLOG(1) << "Deleting old uninstall shortcut (if present): " << uninstall_link.value(); WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem( uninstall_link, temp_path); delete_link->set_ignore_failure(true); delete_link->set_log_message( "Failed to delete old uninstall shortcut."); } } // Adds Chrome specific install work items to |install_list|. // |current_version| can be NULL to indicate no Chrome is currently installed. void AddChromeWorkItems(const InstallationState& original_state, const InstallerState& installer_state, const base::FilePath& setup_path, const base::FilePath& archive_path, const base::FilePath& src_path, const base::FilePath& temp_path, const Version* current_version, const Version& new_version, WorkItemList* install_list) { const base::FilePath& target_path = installer_state.target_path(); if (current_version) { // Delete the archive from an existing install to save some disk space. We // make this an unconditional work item since there's no need to roll this // back; if installation fails we'll be moved to the "-full" channel anyway. base::FilePath old_installer_dir( installer_state.GetInstallerDirectory(*current_version)); base::FilePath old_archive( old_installer_dir.Append(installer::kChromeArchive)); // Don't delete the archive that we are actually installing from. if (archive_path != old_archive) { install_list->AddDeleteTreeWorkItem(old_archive, temp_path)-> set_ignore_failure(true); } } // Delete any new_chrome.exe if present (we will end up creating a new one // if required) and then copy chrome.exe base::FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe)); install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path); // TODO(grt): Remove this check in M35. if (installer_state.IsChromeFrameRunning(original_state)) { VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe"; install_list->AddCopyTreeWorkItem( src_path.Append(installer::kChromeExe).value(), new_chrome_exe.value(), temp_path.value(), WorkItem::ALWAYS); } else { install_list->AddCopyTreeWorkItem( src_path.Append(installer::kChromeExe).value(), target_path.Append(installer::kChromeExe).value(), temp_path.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value()); } // Extra executable for 64 bit systems. // NOTE: We check for "not disabled" so that if the API call fails, we play it // safe and copy the executable anyway. // NOTE: the file wow_helper.exe is only needed for Vista and below. if (base::win::OSInfo::GetInstance()->wow64_status() != base::win::OSInfo::WOW64_DISABLED && base::win::GetVersion() <= base::win::VERSION_VISTA) { install_list->AddMoveTreeWorkItem( src_path.Append(installer::kWowHelperExe).value(), target_path.Append(installer::kWowHelperExe).value(), temp_path.value(), WorkItem::ALWAYS_MOVE); } // Install kVisualElementsManifest if it is present in |src_path|. No need to // make this a conditional work item as if the file is not there now, it will // never be. if (base::PathExists( src_path.Append(installer::kVisualElementsManifest))) { install_list->AddMoveTreeWorkItem( src_path.Append(installer::kVisualElementsManifest).value(), target_path.Append(installer::kVisualElementsManifest).value(), temp_path.value(), WorkItem::ALWAYS_MOVE); } else { // We do not want to have an old VisualElementsManifest pointing to an old // version directory. Delete it as there wasn't a new one to replace it. install_list->AddDeleteTreeWorkItem( target_path.Append(installer::kVisualElementsManifest), temp_path); } // In the past, we copied rather than moved for system level installs so that // the permissions of %ProgramFiles% would be picked up. Now that |temp_path| // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA% // otherwise), there is no need to do this. // Note that we pass true for check_duplicates to avoid failing on in-use // repair runs if the current_version is the same as the new_version. bool check_for_duplicates = (current_version && current_version->Equals(new_version)); install_list->AddMoveTreeWorkItem( src_path.AppendASCII(new_version.GetString()).value(), target_path.AppendASCII(new_version.GetString()).value(), temp_path.value(), check_for_duplicates ? WorkItem::CHECK_DUPLICATES : WorkItem::ALWAYS_MOVE); // Delete any old_chrome.exe if present (ignore failure if it's in use). install_list->AddDeleteTreeWorkItem( target_path.Append(installer::kChromeOldExe), temp_path)-> set_ignore_failure(true); } // Probes COM machinery to get an instance of delegate_execute.exe's // CommandExecuteImpl class. This is required so that COM purges its cache of // the path to the binary, which changes on updates. This callback // unconditionally returns true since an install should not be aborted if the // probe fails. bool ProbeCommandExecuteCallback(const base::string16& command_execute_id, const CallbackWorkItem& work_item) { // Noop on rollback. if (work_item.IsRollback()) return true; CLSID class_id = {}; HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id); if (FAILED(hr)) { LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to " "CLSID; hr=0x" << std::hex << hr; } else { base::win::ScopedComPtr<IUnknown> command_execute_impl; hr = command_execute_impl.CreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER); if (hr != REGDB_E_CLASSNOTREG) { LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x" << std::hex << hr; } } return true; } void AddUninstallDelegateExecuteWorkItems( HKEY root, const base::string16& delegate_execute_path, WorkItemList* list) { VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in " << root; // Delete both 64 and 32 keys to handle 32->64 or 64->32 migration. list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_32KEY); list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_64KEY); // In the past, the ICommandExecuteImpl interface and a TypeLib were both // registered. Remove these since this operation may be updating a machine // that had the old registrations. list->AddDeleteRegKeyWorkItem(root, L"Software\\Classes\\Interface\\" L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}", KEY_WOW64_32KEY); list->AddDeleteRegKeyWorkItem(root, L"Software\\Classes\\TypeLib\\" #if defined(GOOGLE_CHROME_BUILD) L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}", #else L"{7779FB70-B399-454A-AA1A-BAA850032B10}", #endif KEY_WOW64_32KEY); } // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by // mistake (with the same GUID as Chrome). The fix stopped registering the bad // value, but didn't delete it. This is a problem for users who had installed // Canary before 20.0.1106.0 and now have a system-level Chrome, as the // left-behind Canary registrations in HKCU mask the HKLM registrations for the // same GUID. Cleanup those registrations if they still exist and belong to this // Canary (i.e., the registered delegate_execute's path is under |target_path|). void CleanupBadCanaryDelegateExecuteRegistration( const base::FilePath& target_path, WorkItemList* list) { base::string16 google_chrome_delegate_execute_path( L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}"); base::string16 google_chrome_local_server_32( google_chrome_delegate_execute_path + L"\\LocalServer32"); RegKey local_server_32_key; base::string16 registered_server; if (local_server_32_key.Open(HKEY_CURRENT_USER, google_chrome_local_server_32.c_str(), KEY_QUERY_VALUE) == ERROR_SUCCESS && local_server_32_key.ReadValue(L"ServerExecutable", ®istered_server) == ERROR_SUCCESS && target_path.IsParent(base::FilePath(registered_server))) { scoped_ptr<WorkItemList> no_rollback_list( WorkItem::CreateNoRollbackWorkItemList()); AddUninstallDelegateExecuteWorkItems( HKEY_CURRENT_USER, google_chrome_delegate_execute_path, no_rollback_list.get()); list->AddWorkItem(no_rollback_list.release()); VLOG(1) << "Added deletion items for bad Canary registrations."; } } } // namespace // This method adds work items to create (or update) Chrome uninstall entry in // either the Control Panel->Add/Remove Programs list or in the Omaha client // state key if running under an MSI installer. void AddUninstallShortcutWorkItems(const InstallerState& installer_state, const base::FilePath& setup_path, const Version& new_version, const Product& product, WorkItemList* install_list) { HKEY reg_root = installer_state.root_key(); BrowserDistribution* browser_dist = product.distribution(); DCHECK(browser_dist); // When we are installed via an MSI, we need to store our uninstall strings // in the Google Update client state key. We do this even for non-MSI // managed installs to avoid breaking the edge case whereby an MSI-managed // install is updated by a non-msi installer (which would confuse the MSI // machinery if these strings were not also updated). The UninstallString // value placed in the client state key is also used by the mini_installer to // locate the setup.exe instance used for binary patching. // Do not quote the command line for the MSI invocation. base::FilePath install_path(installer_state.target_path()); base::FilePath installer_path( installer_state.GetInstallerDirectory(new_version)); installer_path = installer_path.Append(setup_path.BaseName()); CommandLine uninstall_arguments(CommandLine::NO_PROGRAM); AppendUninstallCommandLineFlags(installer_state, product, &uninstall_arguments); base::string16 update_state_key(browser_dist->GetStateKey()); install_list->AddCreateRegKeyWorkItem( reg_root, update_state_key, KEY_WOW64_32KEY); install_list->AddSetRegValueWorkItem(reg_root, update_state_key, KEY_WOW64_32KEY, installer::kUninstallStringField, installer_path.value(), true); install_list->AddSetRegValueWorkItem( reg_root, update_state_key, KEY_WOW64_32KEY, installer::kUninstallArgumentsField, uninstall_arguments.GetCommandLineString(), true); // MSI installations will manage their own uninstall shortcuts. if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) { // We need to quote the command line for the Add/Remove Programs dialog. CommandLine quoted_uninstall_cmd(installer_path); DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"'); quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false); base::string16 uninstall_reg = browser_dist->GetUninstallRegPath(); install_list->AddCreateRegKeyWorkItem( reg_root, uninstall_reg, KEY_WOW64_32KEY); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, installer::kUninstallDisplayNameField, browser_dist->GetDisplayName(), true); install_list->AddSetRegValueWorkItem( reg_root, uninstall_reg, KEY_WOW64_32KEY, installer::kUninstallStringField, quoted_uninstall_cmd.GetCommandLineString(), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"InstallLocation", install_path.value(), true); BrowserDistribution* dist = product.distribution(); base::string16 chrome_icon = ShellUtil::FormatIconLocation( install_path.Append(dist->GetIconFilename()).value(), dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"DisplayIcon", chrome_icon, true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"NoModify", static_cast<DWORD>(1), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"NoRepair", static_cast<DWORD>(1), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"Publisher", browser_dist->GetPublisherName(), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"Version", ASCIIToWide(new_version.GetString()), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"DisplayVersion", ASCIIToWide(new_version.GetString()), true); // TODO(wfh): Ensure that this value is preserved in the 64-bit hive when // 64-bit installs place the uninstall information into the 64-bit registry. install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, KEY_WOW64_32KEY, L"InstallDate", InstallUtil::GetCurrentDate(), false); const std::vector<uint16>& version_components = new_version.components(); if (version_components.size() == 4) { // Our version should be in major.minor.build.rev. install_list->AddSetRegValueWorkItem( reg_root, uninstall_reg, KEY_WOW64_32KEY, L"VersionMajor", static_cast<DWORD>(version_components[2]), true); install_list->AddSetRegValueWorkItem( reg_root, uninstall_reg, KEY_WOW64_32KEY, L"VersionMinor", static_cast<DWORD>(version_components[3]), true); } } } // Create Version key for a product (if not already present) and sets the new // product version as the last step. void AddVersionKeyWorkItems(HKEY root, BrowserDistribution* dist, const Version& new_version, bool add_language_identifier, WorkItemList* list) { // Create Version key for each distribution (if not already present) and set // the new product version as the last step. base::string16 version_key(dist->GetVersionKey()); list->AddCreateRegKeyWorkItem(root, version_key, KEY_WOW64_32KEY); base::string16 product_name(dist->GetDisplayName()); list->AddSetRegValueWorkItem(root, version_key, KEY_WOW64_32KEY, google_update::kRegNameField, product_name, true); // overwrite name also list->AddSetRegValueWorkItem(root, version_key, KEY_WOW64_32KEY, google_update::kRegOopcrashesField, static_cast<DWORD>(1), false); // set during first install if (add_language_identifier) { // Write the language identifier of the current translation. Omaha's set of // languages is a superset of Chrome's set of translations with this one // exception: what Chrome calls "en-us", Omaha calls "en". sigh. base::string16 language(GetCurrentTranslation()); if (LowerCaseEqualsASCII(language, "en-us")) language.resize(2); list->AddSetRegValueWorkItem(root, version_key, KEY_WOW64_32KEY, google_update::kRegLangField, language, false); // do not overwrite language } list->AddSetRegValueWorkItem(root, version_key, KEY_WOW64_32KEY, google_update::kRegVersionField, ASCIIToWide(new_version.GetString()), true); // overwrite version } // Mirror oeminstall the first time anything is installed multi. There is no // need to update the value on future install/update runs since this value never // changes. Note that the value is removed by Google Update after EULA // acceptance is processed. void AddOemInstallWorkItems(const InstallationState& original_state, const InstallerState& installer_state, WorkItemList* install_list) { DCHECK(installer_state.is_multi_install()); const bool system_install = installer_state.system_install(); if (!original_state.GetProductState(system_install, BrowserDistribution::CHROME_BINARIES)) { const HKEY root_key = installer_state.root_key(); base::string16 multi_key( installer_state.multi_package_binaries_distribution()->GetStateKey()); // Copy the value from Chrome unless Chrome isn't installed or being // installed. BrowserDistribution::Type source_type; if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) { source_type = BrowserDistribution::CHROME_BROWSER; } else if (!installer_state.products().empty()) { // Pick a product, any product. source_type = installer_state.products()[0]->distribution()->GetType(); } else { // Nothing is being installed? Entirely unexpected, so do no harm. LOG(ERROR) << "No products found in AddOemInstallWorkItems"; return; } const ProductState* source_product = original_state.GetNonVersionedProductState(system_install, source_type); base::string16 oem_install; if (source_product->GetOemInstall(&oem_install)) { VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from " << BrowserDistribution::GetSpecificDistribution(source_type)-> GetDisplayName(); install_list->AddCreateRegKeyWorkItem( root_key, multi_key, KEY_WOW64_32KEY); // Always overwrite an old value. install_list->AddSetRegValueWorkItem(root_key, multi_key, KEY_WOW64_32KEY, google_update::kRegOemInstallField, oem_install, true); } else { // Clear any old value. install_list->AddDeleteRegValueWorkItem( root_key, multi_key, KEY_WOW64_32KEY, google_update::kRegOemInstallField); } } } // Mirror eulaaccepted the first time anything is installed multi. There is no // need to update the value on future install/update runs since // GoogleUpdateSettings::SetEULAConsent will modify the value for both the // relevant product and for the binaries. void AddEulaAcceptedWorkItems(const InstallationState& original_state, const InstallerState& installer_state, WorkItemList* install_list) { DCHECK(installer_state.is_multi_install()); const bool system_install = installer_state.system_install(); if (!original_state.GetProductState(system_install, BrowserDistribution::CHROME_BINARIES)) { const HKEY root_key = installer_state.root_key(); base::string16 multi_key( installer_state.multi_package_binaries_distribution()->GetStateKey()); // Copy the value from the product with the greatest value. bool have_eula_accepted = false; BrowserDistribution::Type product_type; DWORD eula_accepted; const Products& products = installer_state.products(); for (Products::const_iterator it = products.begin(); it < products.end(); ++it) { const Product& product = **it; if (product.is_chrome_binaries()) continue; DWORD dword_value = 0; BrowserDistribution::Type this_type = product.distribution()->GetType(); const ProductState* product_state = original_state.GetNonVersionedProductState( system_install, this_type); if (product_state->GetEulaAccepted(&dword_value) && (!have_eula_accepted || dword_value > eula_accepted)) { have_eula_accepted = true; eula_accepted = dword_value; product_type = this_type; } } if (have_eula_accepted) { VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from " << BrowserDistribution::GetSpecificDistribution(product_type)-> GetDisplayName(); install_list->AddCreateRegKeyWorkItem( root_key, multi_key, KEY_WOW64_32KEY); install_list->AddSetRegValueWorkItem(root_key, multi_key, KEY_WOW64_32KEY, google_update::kRegEULAAceptedField, eula_accepted, true); } else { // Clear any old value. install_list->AddDeleteRegValueWorkItem( root_key, multi_key, KEY_WOW64_32KEY, google_update::kRegEULAAceptedField); } } } // Adds work items that make registry adjustments for Google Update. void AddGoogleUpdateWorkItems(const InstallationState& original_state, const InstallerState& installer_state, WorkItemList* install_list) { // Is a multi-install product being installed or over-installed? if (installer_state.operation() != InstallerState::MULTI_INSTALL && installer_state.operation() != InstallerState::MULTI_UPDATE) { VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation(); return; } const bool system_install = installer_state.system_install(); const HKEY root_key = installer_state.root_key(); base::string16 multi_key( installer_state.multi_package_binaries_distribution()->GetStateKey()); // For system-level installs, make sure the ClientStateMedium key for the // binaries exists. if (system_install) { install_list->AddCreateRegKeyWorkItem( root_key, installer_state.multi_package_binaries_distribution() ->GetStateMediumKey() .c_str(), KEY_WOW64_32KEY); } // Creating the ClientState key for binaries, if we're migrating to multi then // copy over Chrome's brand code if it has one. if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) { const ProductState* chrome_product_state = original_state.GetNonVersionedProductState( system_install, BrowserDistribution::CHROME_BROWSER); const base::string16& brand(chrome_product_state->brand()); if (!brand.empty()) { install_list->AddCreateRegKeyWorkItem( root_key, multi_key, KEY_WOW64_32KEY); // Write Chrome's brand code to the multi key. Never overwrite the value // if one is already present (although this shouldn't happen). install_list->AddSetRegValueWorkItem(root_key, multi_key, KEY_WOW64_32KEY, google_update::kRegBrandField, brand, false); } } AddOemInstallWorkItems(original_state, installer_state, install_list); AddEulaAcceptedWorkItems(original_state, installer_state, install_list); AddUsageStatsWorkItems(original_state, installer_state, install_list); // TODO(grt): check for other keys/values we should put in the package's // ClientState and/or Clients key. } void AddUsageStatsWorkItems(const InstallationState& original_state, const InstallerState& installer_state, WorkItemList* install_list) { DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL || installer_state.operation() == InstallerState::MULTI_UPDATE); HKEY root_key = installer_state.root_key(); bool value_found = false; DWORD usagestats = 0; const Products& products = installer_state.products(); // Search for an existing usagestats value for any product. for (Products::const_iterator scan = products.begin(), end = products.end(); !value_found && scan != end; ++scan) { if ((*scan)->is_chrome_binaries()) continue; BrowserDistribution* dist = (*scan)->distribution(); const ProductState* product_state = original_state.GetNonVersionedProductState( installer_state.system_install(), dist->GetType()); value_found = product_state->GetUsageStats(&usagestats); } // If a value was found, write it in the appropriate location for the // binaries and remove all values from the products. if (value_found) { base::string16 state_key( installer_state.multi_package_binaries_distribution()->GetStateKey()); install_list->AddCreateRegKeyWorkItem(root_key, state_key, KEY_WOW64_32KEY); // Overwrite any existing value so that overinstalls (where Omaha writes a // new value into a product's state key) pick up the correct value. install_list->AddSetRegValueWorkItem(root_key, state_key, KEY_WOW64_32KEY, google_update::kRegUsageStatsField, usagestats, true); for (Products::const_iterator scan = products.begin(), end = products.end(); scan != end; ++scan) { if ((*scan)->is_chrome_binaries()) continue; BrowserDistribution* dist = (*scan)->distribution(); if (installer_state.system_install()) { install_list->AddDeleteRegValueWorkItem( root_key, dist->GetStateMediumKey(), KEY_WOW64_32KEY, google_update::kRegUsageStatsField); // Previous versions of Chrome also wrote a value in HKCU even for // system-level installs, so clean that up. install_list->AddDeleteRegValueWorkItem( HKEY_CURRENT_USER, dist->GetStateKey(), KEY_WOW64_32KEY, google_update::kRegUsageStatsField); } install_list->AddDeleteRegValueWorkItem( root_key, dist->GetStateKey(), KEY_WOW64_32KEY, google_update::kRegUsageStatsField); } } } bool AppendPostInstallTasks(const InstallerState& installer_state, const base::FilePath& setup_path, const Version* current_version, const Version& new_version, const base::FilePath& temp_path, WorkItemList* post_install_task_list) { DCHECK(post_install_task_list); HKEY root = installer_state.root_key(); const Products& products = installer_state.products(); base::FilePath new_chrome_exe( installer_state.target_path().Append(installer::kChromeNewExe)); // Append work items that will only be executed if this was an update. // We update the 'opv' value with the current version that is active, // the 'cpv' value with the critical update version (if present), and the // 'cmd' value with the rename command to run. { scoped_ptr<WorkItemList> in_use_update_work_items( WorkItem::CreateConditionalWorkItemList( new ConditionRunIfFileExists(new_chrome_exe))); in_use_update_work_items->set_log_message("InUseUpdateWorkItemList"); // |critical_version| will be valid only if this in-use update includes a // version considered critical relative to the version being updated. Version critical_version(installer_state.DetermineCriticalVersion( current_version, new_version)); base::FilePath installer_path( installer_state.GetInstallerDirectory(new_version).Append( setup_path.BaseName())); CommandLine rename(installer_path); rename.AppendSwitch(switches::kRenameChromeExe); if (installer_state.system_install()) rename.AppendSwitch(switches::kSystemLevel); if (installer_state.verbose_logging()) rename.AppendSwitch(switches::kVerboseLogging); base::string16 version_key; for (size_t i = 0; i < products.size(); ++i) { BrowserDistribution* dist = products[i]->distribution(); version_key = dist->GetVersionKey(); if (current_version) { in_use_update_work_items->AddSetRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegOldVersionField, ASCIIToWide(current_version->GetString()), true); } if (critical_version.IsValid()) { in_use_update_work_items->AddSetRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegCriticalVersionField, ASCIIToWide(critical_version.GetString()), true); } else { in_use_update_work_items->AddDeleteRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegCriticalVersionField); } // Adding this registry entry for all products (but the binaries) is // overkill. However, as it stands, we don't have a way to know which // product will check the key and run the command, so we add it for all. // The first to run it will perform the operation and clean up the other // values. if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) { CommandLine product_rename_cmd(rename); products[i]->AppendRenameFlags(&product_rename_cmd); in_use_update_work_items->AddSetRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegRenameCmdField, product_rename_cmd.GetCommandLineString(), true); } } post_install_task_list->AddWorkItem(in_use_update_work_items.release()); } // Append work items that will be executed if this was NOT an in-use update. { scoped_ptr<WorkItemList> regular_update_work_items( WorkItem::CreateConditionalWorkItemList( new Not(new ConditionRunIfFileExists(new_chrome_exe)))); regular_update_work_items->set_log_message("RegularUpdateWorkItemList"); // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys. for (size_t i = 0; i < products.size(); ++i) { BrowserDistribution* dist = products[i]->distribution(); base::string16 version_key(dist->GetVersionKey()); regular_update_work_items->AddDeleteRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegOldVersionField); regular_update_work_items->AddDeleteRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegCriticalVersionField); regular_update_work_items->AddDeleteRegValueWorkItem( root, version_key, KEY_WOW64_32KEY, google_update::kRegRenameCmdField); } post_install_task_list->AddWorkItem(regular_update_work_items.release()); } AddRegisterComDllWorkItemsForPackage(installer_state, current_version, new_version, post_install_task_list); // If we're told that we're an MSI install, make sure to set the marker // in the client state key so that future updates do the right thing. if (installer_state.is_msi()) { for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true, post_install_task_list); // We want MSI installs to take over the Add/Remove Programs shortcut. // Make a best-effort attempt to delete any shortcuts left over from // previous non-MSI installations for the same type of install (system or // per user). if (product->ShouldCreateUninstallEntry()) { AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product, temp_path, post_install_task_list); } } } return true; } void AddInstallWorkItems(const InstallationState& original_state, const InstallerState& installer_state, const base::FilePath& setup_path, const base::FilePath& archive_path, const base::FilePath& src_path, const base::FilePath& temp_path, const Version* current_version, const Version& new_version, WorkItemList* install_list) { DCHECK(install_list); const base::FilePath& target_path = installer_state.target_path(); // A temp directory that work items need and the actual install directory. install_list->AddCreateDirWorkItem(temp_path); install_list->AddCreateDirWorkItem(target_path); if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) || installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) { AddChromeWorkItems(original_state, installer_state, setup_path, archive_path, src_path, temp_path, current_version, new_version, install_list); } if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { install_list->AddCopyTreeWorkItem( src_path.Append(installer::kChromeAppHostExe).value(), target_path.Append(installer::kChromeAppHostExe).value(), temp_path.value(), WorkItem::ALWAYS, L""); } // Copy installer in install directory AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path, new_version, install_list); const HKEY root = installer_state.root_key(); // Only set "lang" for user-level installs since for system-level, the install // language may not be related to a given user's runtime language. const bool add_language_identifier = !installer_state.system_install(); const Products& products = installer_state.products(); for (Products::const_iterator it = products.begin(); it < products.end(); ++it) { const Product& product = **it; AddUninstallShortcutWorkItems(installer_state, setup_path, new_version, product, install_list); AddVersionKeyWorkItems(root, product.distribution(), new_version, add_language_identifier, install_list); AddDelegateExecuteWorkItems(installer_state, target_path, new_version, product, install_list); AddActiveSetupWorkItems(installer_state, setup_path, new_version, product, install_list); } // TODO(huangs): Implement actual migration code and remove the hack below. // If installing Chrome without the legacy stand-alone App Launcher (to be // handled later), add "shadow" App Launcher registry keys so Google Update // would recognize the "dr" value in the App Launcher ClientState key. // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome. if (installer_state.is_multi_install() && installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) && !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { BrowserDistribution* shadow_app_launcher_dist = BrowserDistribution::GetSpecificDistribution( BrowserDistribution::CHROME_APP_HOST); AddVersionKeyWorkItems(root, shadow_app_launcher_dist, new_version, add_language_identifier, install_list); } // Add any remaining work items that involve special settings for // each product. AddProductSpecificWorkItems(original_state, installer_state, setup_path, new_version, current_version == NULL, install_list); // Copy over brand, usagestats, and other values. AddGoogleUpdateWorkItems(original_state, installer_state, install_list); // Append the tasks that run after the installation. AppendPostInstallTasks(installer_state, setup_path, current_version, new_version, temp_path, install_list); } void AddRegisterComDllWorkItems(const base::FilePath& dll_folder, const std::vector<base::FilePath>& dll_list, bool system_level, bool do_register, bool ignore_failures, WorkItemList* work_item_list) { DCHECK(work_item_list); if (dll_list.empty()) { VLOG(1) << "No COM DLLs to register"; } else { std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin()); for (; dll_iter != dll_list.end(); ++dll_iter) { base::FilePath dll_path = dll_folder.Append(*dll_iter); WorkItem* work_item = work_item_list->AddSelfRegWorkItem( dll_path.value(), do_register, !system_level); DCHECK(work_item); work_item->set_ignore_failure(ignore_failures); } } } void AddSetMsiMarkerWorkItem(const InstallerState& installer_state, BrowserDistribution* dist, bool set, WorkItemList* work_item_list) { DCHECK(work_item_list); DWORD msi_value = set ? 1 : 0; WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(installer_state.root_key(), dist->GetStateKey(), KEY_WOW64_32KEY, google_update::kRegMSIField, msi_value, true); DCHECK(set_msi_work_item); set_msi_work_item->set_ignore_failure(true); set_msi_work_item->set_log_message("Could not write MSI marker!"); } void AddDelegateExecuteWorkItems(const InstallerState& installer_state, const base::FilePath& target_path, const Version& new_version, const Product& product, WorkItemList* list) { base::string16 handler_class_uuid; BrowserDistribution* dist = product.distribution(); if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) { if (InstallUtil::IsChromeSxSProcess()) { CleanupBadCanaryDelegateExecuteRegistration(target_path, list); } else { VLOG(1) << "No DelegateExecute verb handler processing to do for " << dist->GetDisplayName(); } return; } HKEY root = installer_state.root_key(); base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\"); delegate_execute_path.append(handler_class_uuid); // Unconditionally remove registration regardless of whether or not it is // needed since builds after r132190 included it when it wasn't strictly // necessary. Do this removal before adding in the new key to ensure that // the COM probe/flush below does its job. AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list); // Add work items to register the handler iff it is present. // See also shell_util.cc's GetProgIdEntries. if (installer_state.operation() != InstallerState::UNINSTALL) { VLOG(1) << "Adding registration items for DelegateExecute verb handler."; // Force COM to flush its cache containing the path to the old handler. list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback, handler_class_uuid)); // The path to the exe (in the version directory). base::FilePath delegate_execute(target_path); if (new_version.IsValid()) delegate_execute = delegate_execute.AppendASCII(new_version.GetString()); delegate_execute = delegate_execute.Append(kDelegateExecuteExe); // Command-line featuring the quoted path to the exe. base::string16 command(1, L'"'); command.append(delegate_execute.value()).append(1, L'"'); // Register the CommandExecuteImpl class in Software\Classes\CLSID\... list->AddCreateRegKeyWorkItem( root, delegate_execute_path, WorkItem::kWow64Default); list->AddSetRegValueWorkItem(root, delegate_execute_path, WorkItem::kWow64Default, L"", L"CommandExecuteImpl Class", true); base::string16 subkey(delegate_execute_path); subkey.append(L"\\LocalServer32"); list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default); list->AddSetRegValueWorkItem( root, subkey, WorkItem::kWow64Default, L"", command, true); list->AddSetRegValueWorkItem(root, subkey, WorkItem::kWow64Default, L"ServerExecutable", delegate_execute.value(), true); subkey.assign(delegate_execute_path).append(L"\\Programmable"); list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default); } } void AddActiveSetupWorkItems(const InstallerState& installer_state, const base::FilePath& setup_path, const Version& new_version, const Product& product, WorkItemList* list) { DCHECK(installer_state.operation() != InstallerState::UNINSTALL); BrowserDistribution* dist = product.distribution(); if (!product.is_chrome() || !installer_state.system_install()) { const char* install_level = installer_state.system_install() ? "system" : "user"; VLOG(1) << "No Active Setup processing to do for " << install_level << "-level " << dist->GetDisplayName(); return; } DCHECK(installer_state.RequiresActiveSetup()); const HKEY root = HKEY_LOCAL_MACHINE; const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist)); VLOG(1) << "Adding registration items for Active Setup."; list->AddCreateRegKeyWorkItem( root, active_setup_path, WorkItem::kWow64Default); list->AddSetRegValueWorkItem(root, active_setup_path, WorkItem::kWow64Default, L"", dist->GetDisplayName(), true); base::FilePath active_setup_exe(installer_state.GetInstallerDirectory( new_version).Append(kActiveSetupExe)); CommandLine cmd(active_setup_exe); cmd.AppendSwitch(installer::switches::kConfigureUserSettings); cmd.AppendSwitch(installer::switches::kVerboseLogging); cmd.AppendSwitch(installer::switches::kSystemLevel); product.AppendProductFlags(&cmd); list->AddSetRegValueWorkItem(root, active_setup_path, WorkItem::kWow64Default, L"StubPath", cmd.GetCommandLineString(), true); // TODO(grt): http://crbug.com/75152 Write a reference to a localized // resource. list->AddSetRegValueWorkItem(root, active_setup_path, WorkItem::kWow64Default, L"Localized Name", dist->GetDisplayName(), true); list->AddSetRegValueWorkItem(root, active_setup_path, WorkItem::kWow64Default, L"IsInstalled", static_cast<DWORD>(1U), true); list->AddSetRegValueWorkItem(root, active_setup_path, WorkItem::kWow64Default, L"Version", kActiveSetupVersion, true); } void AddDeleteOldIELowRightsPolicyWorkItems( const InstallerState& installer_state, WorkItemList* install_list) { DCHECK(install_list); base::string16 key_path; GetOldIELowRightsElevationPolicyKeyPath(&key_path); install_list->AddDeleteRegKeyWorkItem( installer_state.root_key(), key_path, WorkItem::kWow64Default); } void AppendUninstallCommandLineFlags(const InstallerState& installer_state, const Product& product, CommandLine* uninstall_cmd) { DCHECK(uninstall_cmd); uninstall_cmd->AppendSwitch(installer::switches::kUninstall); // Append the product-specific uninstall flags. product.AppendProductFlags(uninstall_cmd); if (installer_state.is_msi()) uninstall_cmd->AppendSwitch(installer::switches::kMsi); if (installer_state.system_install()) uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel); if (installer_state.verbose_logging()) uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging); } void RefreshElevationPolicy() { const wchar_t kIEFrameDll[] = L"ieframe.dll"; const char kIERefreshPolicy[] = "IERefreshElevationPolicy"; HMODULE ieframe = LoadLibrary(kIEFrameDll); if (ieframe) { typedef HRESULT (__stdcall *IERefreshPolicy)(); IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>( GetProcAddress(ieframe, kIERefreshPolicy)); if (ie_refresh_policy) { ie_refresh_policy(); } else { VLOG(1) << kIERefreshPolicy << " not supported."; } FreeLibrary(ieframe); } else { VLOG(1) << "Cannot load " << kIEFrameDll; } } void AddOsUpgradeWorkItems(const InstallerState& installer_state, const base::FilePath& setup_path, const Version& new_version, const Product& product, WorkItemList* install_list) { const HKEY root_key = installer_state.root_key(); base::string16 cmd_key( GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade)); if (installer_state.operation() == InstallerState::UNINSTALL) { install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY) ->set_log_message("Removing OS upgrade command"); } else { // Register with Google Update to have setup.exe --on-os-upgrade called on // OS upgrade. CommandLine cmd_line(installer_state .GetInstallerDirectory(new_version) .Append(setup_path.BaseName())); // Add the main option to indicate OS upgrade flow. cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade); // Add product-specific options. product.AppendProductFlags(&cmd_line); if (installer_state.system_install()) cmd_line.AppendSwitch(installer::switches::kSystemLevel); // Log everything for now. cmd_line.AppendSwitch(installer::switches::kVerboseLogging); AppCommand cmd(cmd_line.GetCommandLineString()); cmd.set_is_auto_run_on_os_upgrade(true); cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list); } } void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state, const base::FilePath& setup_path, const Version& new_version, const Product& product, WorkItemList* work_item_list) { const HKEY root_key = installer_state.root_key(); base::string16 cmd_key( GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance)); if (installer_state.operation() == InstallerState::UNINSTALL) { work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY) ->set_log_message("Removing query EULA acceptance command"); } else { CommandLine cmd_line(installer_state .GetInstallerDirectory(new_version) .Append(setup_path.BaseName())); cmd_line.AppendSwitch(switches::kQueryEULAAcceptance); if (installer_state.system_install()) cmd_line.AppendSwitch(installer::switches::kSystemLevel); if (installer_state.verbose_logging()) cmd_line.AppendSwitch(installer::switches::kVerboseLogging); AppCommand cmd(cmd_line.GetCommandLineString()); cmd.set_is_web_accessible(true); cmd.set_is_run_as_user(true); cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list); } } void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state, WorkItemList* work_item_list) { DCHECK(work_item_list); base::string16 cmd_key( GetRegCommandKey(BrowserDistribution::GetSpecificDistribution( BrowserDistribution::CHROME_BINARIES), kCmdQuickEnableCf)); // Unconditionally remove the legacy Quick Enable command from the binaries. // Do this even if multi-install Chrome isn't installed to ensure that it is // not left behind in any case. work_item_list->AddDeleteRegKeyWorkItem( installer_state.root_key(), cmd_key, KEY_WOW64_32KEY) ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf) + " command"); } } // namespace installer