install_worker.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4// 5// This file contains the definitions of the installer functions that build 6// the WorkItemList used to install the application. 7 8#include "chrome/installer/setup/install_worker.h" 9 10#include <oaidl.h> 11#include <shlobj.h> 12#include <time.h> 13 14#include <vector> 15 16#include "base/bind.h" 17#include "base/command_line.h" 18#include "base/files/file_path.h" 19#include "base/files/file_util.h" 20#include "base/logging.h" 21#include "base/memory/scoped_ptr.h" 22#include "base/path_service.h" 23#include "base/strings/string_util.h" 24#include "base/strings/utf_string_conversions.h" 25#include "base/version.h" 26#include "base/win/registry.h" 27#include "base/win/scoped_comptr.h" 28#include "base/win/windows_version.h" 29#include "chrome/common/chrome_constants.h" 30#include "chrome/common/chrome_switches.h" 31#include "chrome/installer/setup/install.h" 32#include "chrome/installer/setup/setup_constants.h" 33#include "chrome/installer/setup/setup_util.h" 34#include "chrome/installer/util/browser_distribution.h" 35#include "chrome/installer/util/callback_work_item.h" 36#include "chrome/installer/util/conditional_work_item_list.h" 37#include "chrome/installer/util/create_reg_key_work_item.h" 38#include "chrome/installer/util/firewall_manager_win.h" 39#include "chrome/installer/util/google_update_constants.h" 40#include "chrome/installer/util/helper.h" 41#include "chrome/installer/util/install_util.h" 42#include "chrome/installer/util/installation_state.h" 43#include "chrome/installer/util/installer_state.h" 44#include "chrome/installer/util/l10n_string_util.h" 45#include "chrome/installer/util/product.h" 46#include "chrome/installer/util/set_reg_value_work_item.h" 47#include "chrome/installer/util/shell_util.h" 48#include "chrome/installer/util/util_constants.h" 49#include "chrome/installer/util/work_item_list.h" 50 51using base::ASCIIToWide; 52using base::win::RegKey; 53 54namespace installer { 55 56namespace { 57 58// The version identifying the work done by setup.exe --configure-user-settings 59// on user login by way of Active Setup. Increase this value if the work done 60// in setup_main.cc's handling of kConfigureUserSettings changes and should be 61// executed again for all users. 62const wchar_t kActiveSetupVersion[] = L"24,0,0,0"; 63 64// Although the UUID of the ChromeFrame class is used for the "current" value, 65// this is done only as a convenience; there is no need for the GUID of the Low 66// Rights policies to match the ChromeFrame class's GUID. Hence, it is safe to 67// use this completely unrelated GUID for the "old" policies. 68const wchar_t kIELowRightsPolicyOldGuid[] = 69 L"{6C288DD7-76FB-4721-B628-56FAC252E199}"; 70 71const wchar_t kElevationPolicyKeyPath[] = 72 L"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\"; 73 74void GetOldIELowRightsElevationPolicyKeyPath(base::string16* key_path) { 75 key_path->assign(kElevationPolicyKeyPath, 76 arraysize(kElevationPolicyKeyPath) - 1); 77 key_path->append(kIELowRightsPolicyOldGuid, 78 arraysize(kIELowRightsPolicyOldGuid)- 1); 79} 80 81// Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of 82// products managed by a given package. 83// |old_version| can be NULL to indicate no Chrome is currently installed. 84void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state, 85 const Version* old_version, 86 const Version& new_version, 87 WorkItemList* work_item_list) { 88 // First collect the list of DLLs to be registered from each product. 89 std::vector<base::FilePath> com_dll_list; 90 installer_state.AddComDllList(&com_dll_list); 91 92 // Then, if we got some, attempt to unregister the DLLs from the old 93 // version directory and then re-register them in the new one. 94 // Note that if we are migrating the install directory then we will not 95 // successfully unregister the old DLLs. 96 // TODO(robertshield): See whether we need to fix the migration case. 97 // TODO(robertshield): If we ever remove a DLL from a product, this will 98 // not unregister it on update. We should build the unregistration list from 99 // saved state instead of assuming it is the same as the registration list. 100 if (!com_dll_list.empty()) { 101 if (old_version) { 102 base::FilePath old_dll_path(installer_state.target_path().AppendASCII( 103 old_version->GetString())); 104 105 installer::AddRegisterComDllWorkItems(old_dll_path, 106 com_dll_list, 107 installer_state.system_install(), 108 false, // Unregister 109 true, // May fail 110 work_item_list); 111 } 112 113 base::FilePath dll_path(installer_state.target_path().AppendASCII( 114 new_version.GetString())); 115 installer::AddRegisterComDllWorkItems(dll_path, 116 com_dll_list, 117 installer_state.system_install(), 118 true, // Register 119 false, // Must succeed. 120 work_item_list); 121 } 122} 123 124void AddInstallerCopyTasks(const InstallerState& installer_state, 125 const base::FilePath& setup_path, 126 const base::FilePath& archive_path, 127 const base::FilePath& temp_path, 128 const Version& new_version, 129 WorkItemList* install_list) { 130 DCHECK(install_list); 131 base::FilePath installer_dir( 132 installer_state.GetInstallerDirectory(new_version)); 133 install_list->AddCreateDirWorkItem(installer_dir); 134 135 base::FilePath exe_dst(installer_dir.Append(setup_path.BaseName())); 136 137 if (exe_dst != setup_path) { 138 install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(), 139 temp_path.value(), WorkItem::ALWAYS); 140 } 141 142 if (installer_state.RequiresActiveSetup()) { 143 // Make a copy of setup.exe with a different name so that Active Setup 144 // doesn't require an admin on XP thanks to Application Compatibility. 145 base::FilePath active_setup_exe(installer_dir.Append(kActiveSetupExe)); 146 install_list->AddCopyTreeWorkItem( 147 setup_path.value(), active_setup_exe.value(), temp_path.value(), 148 WorkItem::ALWAYS); 149 } 150 151 // If only the App Host (not even the Chrome Binaries) is being installed, 152 // this must be a user-level App Host piggybacking on system-level Chrome 153 // Binaries. Only setup.exe is required, and only for uninstall. 154 if (installer_state.products().size() != 1 || 155 !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 156 base::FilePath archive_dst(installer_dir.Append(archive_path.BaseName())); 157 if (archive_path != archive_dst) { 158 // In the past, we copied rather than moved for system level installs so 159 // that the permissions of %ProgramFiles% would be picked up. Now that 160 // |temp_path| is in %ProgramFiles% for system level installs (and in 161 // %LOCALAPPDATA% otherwise), there is no need to do this for the archive. 162 // Setup.exe, on the other hand, is created elsewhere so it must always be 163 // copied. 164 if (temp_path.IsParent(archive_path)) { 165 install_list->AddMoveTreeWorkItem(archive_path.value(), 166 archive_dst.value(), 167 temp_path.value(), 168 WorkItem::ALWAYS_MOVE); 169 } else { 170 // This may occur when setup is run out of an existing installation 171 // directory. For example, when quick-enabling user-level App Launcher 172 // from system-level Binaries. We can't (and don't want to) remove the 173 // system-level archive. 174 install_list->AddCopyTreeWorkItem(archive_path.value(), 175 archive_dst.value(), 176 temp_path.value(), 177 WorkItem::ALWAYS); 178 } 179 } 180 } 181} 182 183base::string16 GetRegCommandKey(BrowserDistribution* dist, 184 const wchar_t* name) { 185 base::string16 cmd_key(dist->GetVersionKey()); 186 cmd_key.append(1, base::FilePath::kSeparators[0]) 187 .append(google_update::kRegCommandsKey) 188 .append(1, base::FilePath::kSeparators[0]) 189 .append(name); 190 return cmd_key; 191} 192 193// Adds work items to create (or delete if uninstalling) app commands to launch 194// the app with a switch. The following criteria should be true: 195// 1. The switch takes one parameter. 196// 2. The command send pings. 197// 3. The command is web accessible. 198// 4. The command is run as the user. 199void AddCommandWithParameterWorkItems(const InstallerState& installer_state, 200 const InstallationState& machine_state, 201 const Version& new_version, 202 const Product& product, 203 const wchar_t* command_key, 204 const wchar_t* app, 205 const char* command_with_parameter, 206 WorkItemList* work_item_list) { 207 DCHECK(command_key); 208 DCHECK(app); 209 DCHECK(command_with_parameter); 210 DCHECK(work_item_list); 211 212 base::string16 full_cmd_key( 213 GetRegCommandKey(product.distribution(), command_key)); 214 215 if (installer_state.operation() == InstallerState::UNINSTALL) { 216 work_item_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), 217 full_cmd_key, 218 KEY_WOW64_32KEY) 219 ->set_log_message("removing " + base::UTF16ToASCII(command_key) + 220 " command"); 221 } else { 222 CommandLine cmd_line(installer_state.target_path().Append(app)); 223 cmd_line.AppendSwitchASCII(command_with_parameter, "%1"); 224 225 AppCommand cmd(cmd_line.GetCommandLineString()); 226 cmd.set_sends_pings(true); 227 cmd.set_is_web_accessible(true); 228 cmd.set_is_run_as_user(true); 229 cmd.AddWorkItems(installer_state.root_key(), full_cmd_key, work_item_list); 230 } 231} 232 233void AddInstallAppCommandWorkItems(const InstallerState& installer_state, 234 const InstallationState& machine_state, 235 const Version& new_version, 236 const Product& product, 237 WorkItemList* work_item_list) { 238 DCHECK(product.is_chrome_app_host()); 239 AddCommandWithParameterWorkItems(installer_state, machine_state, new_version, 240 product, kCmdInstallApp, 241 installer::kChromeAppHostExe, 242 ::switches::kInstallFromWebstore, 243 work_item_list); 244} 245 246void AddInstallExtensionCommandWorkItem(const InstallerState& installer_state, 247 const InstallationState& machine_state, 248 const base::FilePath& setup_path, 249 const Version& new_version, 250 const Product& product, 251 WorkItemList* work_item_list) { 252 DCHECK(product.is_chrome()); 253 AddCommandWithParameterWorkItems(installer_state, machine_state, new_version, 254 product, kCmdInstallExtension, 255 installer::kChromeExe, 256 ::switches::kLimitedInstallFromWebstore, 257 work_item_list); 258} 259 260// A callback invoked by |work_item| that adds firewall rules for Chrome. Rules 261// are left in-place on rollback unless |remove_on_rollback| is true. This is 262// the case for new installs only. Updates and overinstalls leave the rule 263// in-place on rollback since a previous install of Chrome will be used in that 264// case. 265bool AddFirewallRulesCallback(bool system_level, 266 BrowserDistribution* dist, 267 const base::FilePath& chrome_path, 268 bool remove_on_rollback, 269 const CallbackWorkItem& work_item) { 270 // There is no work to do on rollback if this is not a new install. 271 if (work_item.IsRollback() && !remove_on_rollback) 272 return true; 273 274 scoped_ptr<FirewallManager> manager = 275 FirewallManager::Create(dist, chrome_path); 276 if (!manager) { 277 LOG(ERROR) << "Failed creating a FirewallManager. Continuing with install."; 278 return true; 279 } 280 281 if (work_item.IsRollback()) { 282 manager->RemoveFirewallRules(); 283 return true; 284 } 285 286 // Adding the firewall rule is expected to fail for user-level installs on 287 // Vista+. Try anyway in case the installer is running elevated. 288 if (!manager->AddFirewallRules()) 289 LOG(ERROR) << "Failed creating a firewall rules. Continuing with install."; 290 291 // Don't abort installation if the firewall rule couldn't be added. 292 return true; 293} 294 295// Adds work items to |list| to create firewall rules. 296void AddFirewallRulesWorkItems(const InstallerState& installer_state, 297 BrowserDistribution* dist, 298 bool is_new_install, 299 WorkItemList* list) { 300 list->AddCallbackWorkItem( 301 base::Bind(&AddFirewallRulesCallback, 302 installer_state.system_install(), 303 dist, 304 installer_state.target_path().Append(kChromeExe), 305 is_new_install)); 306} 307 308// Returns the basic CommandLine to setup.exe for a quick-enable operation on 309// the binaries. This will unconditionally include --multi-install as well as 310// --verbose-logging if the current installation was launched with 311// --verbose-logging. |setup_path| and |new_version| are optional only when 312// the operation is an uninstall. 313CommandLine GetGenericQuickEnableCommand( 314 const InstallerState& installer_state, 315 const InstallationState& machine_state, 316 const base::FilePath& setup_path, 317 const Version& new_version) { 318 // Only valid for multi-install operations. 319 DCHECK(installer_state.is_multi_install()); 320 // Only valid when Chrome Binaries aren't being uninstalled. 321 DCHECK(installer_state.operation() != InstallerState::UNINSTALL || 322 !installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)); 323 // setup_path and new_version are required when not uninstalling. 324 DCHECK(installer_state.operation() == InstallerState::UNINSTALL || 325 (!setup_path.empty() && new_version.IsValid())); 326 327 // The path to setup.exe contains the version of the Chrome Binaries, so it 328 // takes a little work to get it right. 329 base::FilePath binaries_setup_path; 330 if (installer_state.operation() == InstallerState::UNINSTALL) { 331 // One or more products are being uninstalled, but not Chrome Binaries. 332 // Use the path to the currently installed Chrome Binaries' setup.exe. 333 const ProductState* product_state = machine_state.GetProductState( 334 installer_state.system_install(), 335 BrowserDistribution::CHROME_BINARIES); 336 DCHECK(product_state); 337 binaries_setup_path = product_state->uninstall_command().GetProgram(); 338 } else { 339 // Chrome Binaries are being installed, updated, or otherwise operated on. 340 // Use the path to the given |setup_path| in the normal location of 341 // multi-install Chrome Binaries of the given |version|. 342 binaries_setup_path = installer_state.GetInstallerDirectory(new_version) 343 .Append(setup_path.BaseName()); 344 } 345 DCHECK(!binaries_setup_path.empty()); 346 347 CommandLine cmd_line(binaries_setup_path); 348 cmd_line.AppendSwitch(switches::kMultiInstall); 349 if (installer_state.verbose_logging()) 350 cmd_line.AppendSwitch(switches::kVerboseLogging); 351 return cmd_line; 352} 353 354// Adds work items to add the "quick-enable-application-host" command to the 355// multi-installer binaries' version key on the basis of the current operation 356// (represented in |installer_state|) and the pre-existing machine configuration 357// (represented in |machine_state|). 358void AddQuickEnableApplicationLauncherWorkItems( 359 const InstallerState& installer_state, 360 const InstallationState& machine_state, 361 const base::FilePath& setup_path, 362 const Version& new_version, 363 WorkItemList* work_item_list) { 364 DCHECK(work_item_list); 365 366 bool will_have_chrome_binaries = 367 WillProductBePresentAfterSetup(installer_state, machine_state, 368 BrowserDistribution::CHROME_BINARIES); 369 370 // For system-level binaries there is no way to keep the command state in sync 371 // with the installation/uninstallation of the Application Launcher (which is 372 // always at user-level). So we do not try to remove the command, i.e., it 373 // will always be installed if the Chrome Binaries are installed. 374 if (will_have_chrome_binaries) { 375 base::string16 cmd_key( 376 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution( 377 BrowserDistribution::CHROME_BINARIES), 378 kCmdQuickEnableApplicationHost)); 379 CommandLine cmd_line(GetGenericQuickEnableCommand(installer_state, 380 machine_state, 381 setup_path, 382 new_version)); 383 // kMultiInstall and kVerboseLogging were processed above. 384 cmd_line.AppendSwitch(switches::kChromeAppLauncher); 385 cmd_line.AppendSwitch(switches::kEnsureGoogleUpdatePresent); 386 AppCommand cmd(cmd_line.GetCommandLineString()); 387 cmd.set_sends_pings(true); 388 cmd.set_is_web_accessible(true); 389 cmd.set_is_run_as_user(true); 390 cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list); 391 } 392} 393 394void AddProductSpecificWorkItems(const InstallationState& original_state, 395 const InstallerState& installer_state, 396 const base::FilePath& setup_path, 397 const Version& new_version, 398 bool is_new_install, 399 WorkItemList* list) { 400 const Products& products = installer_state.products(); 401 for (Products::const_iterator it = products.begin(); it < products.end(); 402 ++it) { 403 const Product& p = **it; 404 if (p.is_chrome_app_host()) { 405 AddInstallAppCommandWorkItems(installer_state, original_state, 406 new_version, p, list); 407 } 408 if (p.is_chrome()) { 409 AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p, 410 list); 411 AddInstallExtensionCommandWorkItem(installer_state, original_state, 412 setup_path, new_version, p, list); 413 AddFirewallRulesWorkItems( 414 installer_state, p.distribution(), is_new_install, list); 415 } 416 if (p.is_chrome_binaries()) { 417 AddQueryEULAAcceptanceWorkItems( 418 installer_state, setup_path, new_version, p, list); 419 AddQuickEnableChromeFrameWorkItems(installer_state, list); 420 AddQuickEnableApplicationLauncherWorkItems( 421 installer_state, original_state, setup_path, new_version, list); 422 } 423 } 424} 425 426// This is called when an MSI installation is run. It may be that a user is 427// attempting to install the MSI on top of a non-MSI managed installation. 428// If so, try and remove any existing uninstallation shortcuts, as we want the 429// uninstall to be managed entirely by the MSI machinery (accessible via the 430// Add/Remove programs dialog). 431void AddDeleteUninstallShortcutsForMSIWorkItems( 432 const InstallerState& installer_state, 433 const Product& product, 434 const base::FilePath& temp_path, 435 WorkItemList* work_item_list) { 436 DCHECK(installer_state.is_msi()) 437 << "This must only be called for MSI installations!"; 438 439 // First attempt to delete the old installation's ARP dialog entry. 440 HKEY reg_root = installer_state.root_key(); 441 base::string16 uninstall_reg(product.distribution()->GetUninstallRegPath()); 442 443 WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem( 444 reg_root, uninstall_reg, KEY_WOW64_32KEY); 445 delete_reg_key->set_ignore_failure(true); 446 447 // Then attempt to delete the old installation's start menu shortcut. 448 base::FilePath uninstall_link; 449 if (installer_state.system_install()) { 450 PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link); 451 } else { 452 PathService::Get(base::DIR_START_MENU, &uninstall_link); 453 } 454 455 if (uninstall_link.empty()) { 456 LOG(ERROR) << "Failed to get location for shortcut."; 457 } else { 458 uninstall_link = uninstall_link.Append( 459 product.distribution()->GetStartMenuShortcutSubfolder( 460 BrowserDistribution::SUBFOLDER_CHROME)); 461 uninstall_link = uninstall_link.Append( 462 product.distribution()->GetUninstallLinkName() + installer::kLnkExt); 463 VLOG(1) << "Deleting old uninstall shortcut (if present): " 464 << uninstall_link.value(); 465 WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem( 466 uninstall_link, temp_path); 467 delete_link->set_ignore_failure(true); 468 delete_link->set_log_message( 469 "Failed to delete old uninstall shortcut."); 470 } 471} 472 473// Adds Chrome specific install work items to |install_list|. 474// |current_version| can be NULL to indicate no Chrome is currently installed. 475void AddChromeWorkItems(const InstallationState& original_state, 476 const InstallerState& installer_state, 477 const base::FilePath& setup_path, 478 const base::FilePath& archive_path, 479 const base::FilePath& src_path, 480 const base::FilePath& temp_path, 481 const Version* current_version, 482 const Version& new_version, 483 WorkItemList* install_list) { 484 const base::FilePath& target_path = installer_state.target_path(); 485 486 if (current_version) { 487 // Delete the archive from an existing install to save some disk space. We 488 // make this an unconditional work item since there's no need to roll this 489 // back; if installation fails we'll be moved to the "-full" channel anyway. 490 base::FilePath old_installer_dir( 491 installer_state.GetInstallerDirectory(*current_version)); 492 base::FilePath old_archive( 493 old_installer_dir.Append(installer::kChromeArchive)); 494 // Don't delete the archive that we are actually installing from. 495 if (archive_path != old_archive) { 496 install_list->AddDeleteTreeWorkItem(old_archive, temp_path)-> 497 set_ignore_failure(true); 498 } 499 } 500 501 // Delete any new_chrome.exe if present (we will end up creating a new one 502 // if required) and then copy chrome.exe 503 base::FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe)); 504 505 install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path); 506 507 // TODO(grt): Remove this check in M35. 508 if (installer_state.IsChromeFrameRunning(original_state)) { 509 VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe"; 510 install_list->AddCopyTreeWorkItem( 511 src_path.Append(installer::kChromeExe).value(), 512 new_chrome_exe.value(), 513 temp_path.value(), 514 WorkItem::ALWAYS); 515 } else { 516 install_list->AddCopyTreeWorkItem( 517 src_path.Append(installer::kChromeExe).value(), 518 target_path.Append(installer::kChromeExe).value(), 519 temp_path.value(), 520 WorkItem::NEW_NAME_IF_IN_USE, 521 new_chrome_exe.value()); 522 } 523 524 // Extra executable for 64 bit systems. 525 // NOTE: We check for "not disabled" so that if the API call fails, we play it 526 // safe and copy the executable anyway. 527 // NOTE: the file wow_helper.exe is only needed for Vista and below. 528 if (base::win::OSInfo::GetInstance()->wow64_status() != 529 base::win::OSInfo::WOW64_DISABLED && 530 base::win::GetVersion() <= base::win::VERSION_VISTA) { 531 install_list->AddMoveTreeWorkItem( 532 src_path.Append(installer::kWowHelperExe).value(), 533 target_path.Append(installer::kWowHelperExe).value(), 534 temp_path.value(), 535 WorkItem::ALWAYS_MOVE); 536 } 537 538 // Install kVisualElementsManifest if it is present in |src_path|. No need to 539 // make this a conditional work item as if the file is not there now, it will 540 // never be. 541 if (base::PathExists( 542 src_path.Append(installer::kVisualElementsManifest))) { 543 install_list->AddMoveTreeWorkItem( 544 src_path.Append(installer::kVisualElementsManifest).value(), 545 target_path.Append(installer::kVisualElementsManifest).value(), 546 temp_path.value(), 547 WorkItem::ALWAYS_MOVE); 548 } else { 549 // We do not want to have an old VisualElementsManifest pointing to an old 550 // version directory. Delete it as there wasn't a new one to replace it. 551 install_list->AddDeleteTreeWorkItem( 552 target_path.Append(installer::kVisualElementsManifest), 553 temp_path); 554 } 555 556 // In the past, we copied rather than moved for system level installs so that 557 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path| 558 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA% 559 // otherwise), there is no need to do this. 560 // Note that we pass true for check_duplicates to avoid failing on in-use 561 // repair runs if the current_version is the same as the new_version. 562 bool check_for_duplicates = (current_version && 563 current_version->Equals(new_version)); 564 install_list->AddMoveTreeWorkItem( 565 src_path.AppendASCII(new_version.GetString()).value(), 566 target_path.AppendASCII(new_version.GetString()).value(), 567 temp_path.value(), 568 check_for_duplicates ? WorkItem::CHECK_DUPLICATES : 569 WorkItem::ALWAYS_MOVE); 570 571 // Delete any old_chrome.exe if present (ignore failure if it's in use). 572 install_list->AddDeleteTreeWorkItem( 573 target_path.Append(installer::kChromeOldExe), temp_path)-> 574 set_ignore_failure(true); 575} 576 577// Probes COM machinery to get an instance of delegate_execute.exe's 578// CommandExecuteImpl class. This is required so that COM purges its cache of 579// the path to the binary, which changes on updates. This callback 580// unconditionally returns true since an install should not be aborted if the 581// probe fails. 582bool ProbeCommandExecuteCallback(const base::string16& command_execute_id, 583 const CallbackWorkItem& work_item) { 584 // Noop on rollback. 585 if (work_item.IsRollback()) 586 return true; 587 588 CLSID class_id = {}; 589 590 HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id); 591 if (FAILED(hr)) { 592 LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to " 593 "CLSID; hr=0x" << std::hex << hr; 594 } else { 595 base::win::ScopedComPtr<IUnknown> command_execute_impl; 596 hr = command_execute_impl.CreateInstance(class_id, NULL, 597 CLSCTX_LOCAL_SERVER); 598 if (hr != REGDB_E_CLASSNOTREG) { 599 LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x" 600 << std::hex << hr; 601 } 602 } 603 604 return true; 605} 606 607void AddUninstallDelegateExecuteWorkItems( 608 HKEY root, 609 const base::string16& delegate_execute_path, 610 WorkItemList* list) { 611 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in " 612 << root; 613 // Delete both 64 and 32 keys to handle 32->64 or 64->32 migration. 614 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_32KEY); 615 616 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_64KEY); 617 618 // In the past, the ICommandExecuteImpl interface and a TypeLib were both 619 // registered. Remove these since this operation may be updating a machine 620 // that had the old registrations. 621 list->AddDeleteRegKeyWorkItem(root, 622 L"Software\\Classes\\Interface\\" 623 L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}", 624 KEY_WOW64_32KEY); 625 list->AddDeleteRegKeyWorkItem(root, 626 L"Software\\Classes\\TypeLib\\" 627#if defined(GOOGLE_CHROME_BUILD) 628 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}", 629#else 630 L"{7779FB70-B399-454A-AA1A-BAA850032B10}", 631#endif 632 KEY_WOW64_32KEY); 633} 634 635// Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0 636// (exclusively -- crrev.com/132596), registered a DelegateExecute class by 637// mistake (with the same GUID as Chrome). The fix stopped registering the bad 638// value, but didn't delete it. This is a problem for users who had installed 639// Canary before 20.0.1106.0 and now have a system-level Chrome, as the 640// left-behind Canary registrations in HKCU mask the HKLM registrations for the 641// same GUID. Cleanup those registrations if they still exist and belong to this 642// Canary (i.e., the registered delegate_execute's path is under |target_path|). 643void CleanupBadCanaryDelegateExecuteRegistration( 644 const base::FilePath& target_path, 645 WorkItemList* list) { 646 base::string16 google_chrome_delegate_execute_path( 647 L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}"); 648 base::string16 google_chrome_local_server_32( 649 google_chrome_delegate_execute_path + L"\\LocalServer32"); 650 651 RegKey local_server_32_key; 652 base::string16 registered_server; 653 if (local_server_32_key.Open(HKEY_CURRENT_USER, 654 google_chrome_local_server_32.c_str(), 655 KEY_QUERY_VALUE) == ERROR_SUCCESS && 656 local_server_32_key.ReadValue(L"ServerExecutable", 657 ®istered_server) == ERROR_SUCCESS && 658 target_path.IsParent(base::FilePath(registered_server))) { 659 scoped_ptr<WorkItemList> no_rollback_list( 660 WorkItem::CreateNoRollbackWorkItemList()); 661 AddUninstallDelegateExecuteWorkItems( 662 HKEY_CURRENT_USER, google_chrome_delegate_execute_path, 663 no_rollback_list.get()); 664 list->AddWorkItem(no_rollback_list.release()); 665 VLOG(1) << "Added deletion items for bad Canary registrations."; 666 } 667} 668 669} // namespace 670 671// This method adds work items to create (or update) Chrome uninstall entry in 672// either the Control Panel->Add/Remove Programs list or in the Omaha client 673// state key if running under an MSI installer. 674void AddUninstallShortcutWorkItems(const InstallerState& installer_state, 675 const base::FilePath& setup_path, 676 const Version& new_version, 677 const Product& product, 678 WorkItemList* install_list) { 679 HKEY reg_root = installer_state.root_key(); 680 BrowserDistribution* browser_dist = product.distribution(); 681 DCHECK(browser_dist); 682 683 // When we are installed via an MSI, we need to store our uninstall strings 684 // in the Google Update client state key. We do this even for non-MSI 685 // managed installs to avoid breaking the edge case whereby an MSI-managed 686 // install is updated by a non-msi installer (which would confuse the MSI 687 // machinery if these strings were not also updated). The UninstallString 688 // value placed in the client state key is also used by the mini_installer to 689 // locate the setup.exe instance used for binary patching. 690 // Do not quote the command line for the MSI invocation. 691 base::FilePath install_path(installer_state.target_path()); 692 base::FilePath installer_path( 693 installer_state.GetInstallerDirectory(new_version)); 694 installer_path = installer_path.Append(setup_path.BaseName()); 695 696 CommandLine uninstall_arguments(CommandLine::NO_PROGRAM); 697 AppendUninstallCommandLineFlags(installer_state, product, 698 &uninstall_arguments); 699 700 base::string16 update_state_key(browser_dist->GetStateKey()); 701 install_list->AddCreateRegKeyWorkItem( 702 reg_root, update_state_key, KEY_WOW64_32KEY); 703 install_list->AddSetRegValueWorkItem(reg_root, 704 update_state_key, 705 KEY_WOW64_32KEY, 706 installer::kUninstallStringField, 707 installer_path.value(), 708 true); 709 install_list->AddSetRegValueWorkItem( 710 reg_root, 711 update_state_key, 712 KEY_WOW64_32KEY, 713 installer::kUninstallArgumentsField, 714 uninstall_arguments.GetCommandLineString(), 715 true); 716 717 // MSI installations will manage their own uninstall shortcuts. 718 if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) { 719 // We need to quote the command line for the Add/Remove Programs dialog. 720 CommandLine quoted_uninstall_cmd(installer_path); 721 DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"'); 722 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false); 723 724 base::string16 uninstall_reg = browser_dist->GetUninstallRegPath(); 725 install_list->AddCreateRegKeyWorkItem( 726 reg_root, uninstall_reg, KEY_WOW64_32KEY); 727 install_list->AddSetRegValueWorkItem(reg_root, 728 uninstall_reg, 729 KEY_WOW64_32KEY, 730 installer::kUninstallDisplayNameField, 731 browser_dist->GetDisplayName(), 732 true); 733 install_list->AddSetRegValueWorkItem( 734 reg_root, 735 uninstall_reg, 736 KEY_WOW64_32KEY, 737 installer::kUninstallStringField, 738 quoted_uninstall_cmd.GetCommandLineString(), 739 true); 740 install_list->AddSetRegValueWorkItem(reg_root, 741 uninstall_reg, 742 KEY_WOW64_32KEY, 743 L"InstallLocation", 744 install_path.value(), 745 true); 746 747 BrowserDistribution* dist = product.distribution(); 748 base::string16 chrome_icon = ShellUtil::FormatIconLocation( 749 install_path.Append(dist->GetIconFilename()).value(), 750 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)); 751 install_list->AddSetRegValueWorkItem(reg_root, 752 uninstall_reg, 753 KEY_WOW64_32KEY, 754 L"DisplayIcon", 755 chrome_icon, 756 true); 757 install_list->AddSetRegValueWorkItem(reg_root, 758 uninstall_reg, 759 KEY_WOW64_32KEY, 760 L"NoModify", 761 static_cast<DWORD>(1), 762 true); 763 install_list->AddSetRegValueWorkItem(reg_root, 764 uninstall_reg, 765 KEY_WOW64_32KEY, 766 L"NoRepair", 767 static_cast<DWORD>(1), 768 true); 769 770 install_list->AddSetRegValueWorkItem(reg_root, 771 uninstall_reg, 772 KEY_WOW64_32KEY, 773 L"Publisher", 774 browser_dist->GetPublisherName(), 775 true); 776 install_list->AddSetRegValueWorkItem(reg_root, 777 uninstall_reg, 778 KEY_WOW64_32KEY, 779 L"Version", 780 ASCIIToWide(new_version.GetString()), 781 true); 782 install_list->AddSetRegValueWorkItem(reg_root, 783 uninstall_reg, 784 KEY_WOW64_32KEY, 785 L"DisplayVersion", 786 ASCIIToWide(new_version.GetString()), 787 true); 788 // TODO(wfh): Ensure that this value is preserved in the 64-bit hive when 789 // 64-bit installs place the uninstall information into the 64-bit registry. 790 install_list->AddSetRegValueWorkItem(reg_root, 791 uninstall_reg, 792 KEY_WOW64_32KEY, 793 L"InstallDate", 794 InstallUtil::GetCurrentDate(), 795 false); 796 797 const std::vector<uint16>& version_components = new_version.components(); 798 if (version_components.size() == 4) { 799 // Our version should be in major.minor.build.rev. 800 install_list->AddSetRegValueWorkItem( 801 reg_root, 802 uninstall_reg, 803 KEY_WOW64_32KEY, 804 L"VersionMajor", 805 static_cast<DWORD>(version_components[2]), 806 true); 807 install_list->AddSetRegValueWorkItem( 808 reg_root, 809 uninstall_reg, 810 KEY_WOW64_32KEY, 811 L"VersionMinor", 812 static_cast<DWORD>(version_components[3]), 813 true); 814 } 815 } 816} 817 818// Create Version key for a product (if not already present) and sets the new 819// product version as the last step. 820void AddVersionKeyWorkItems(HKEY root, 821 const base::string16& version_key, 822 const base::string16& product_name, 823 const Version& new_version, 824 bool add_language_identifier, 825 WorkItemList* list) { 826 list->AddCreateRegKeyWorkItem(root, version_key, KEY_WOW64_32KEY); 827 828 list->AddSetRegValueWorkItem(root, 829 version_key, 830 KEY_WOW64_32KEY, 831 google_update::kRegNameField, 832 product_name, 833 true); // overwrite name also 834 list->AddSetRegValueWorkItem(root, 835 version_key, 836 KEY_WOW64_32KEY, 837 google_update::kRegOopcrashesField, 838 static_cast<DWORD>(1), 839 false); // set during first install 840 if (add_language_identifier) { 841 // Write the language identifier of the current translation. Omaha's set of 842 // languages is a superset of Chrome's set of translations with this one 843 // exception: what Chrome calls "en-us", Omaha calls "en". sigh. 844 base::string16 language(GetCurrentTranslation()); 845 if (LowerCaseEqualsASCII(language, "en-us")) 846 language.resize(2); 847 list->AddSetRegValueWorkItem(root, 848 version_key, 849 KEY_WOW64_32KEY, 850 google_update::kRegLangField, 851 language, 852 false); // do not overwrite language 853 } 854 list->AddSetRegValueWorkItem(root, 855 version_key, 856 KEY_WOW64_32KEY, 857 google_update::kRegVersionField, 858 ASCIIToWide(new_version.GetString()), 859 true); // overwrite version 860} 861 862// Mirror oeminstall the first time anything is installed multi. There is no 863// need to update the value on future install/update runs since this value never 864// changes. Note that the value is removed by Google Update after EULA 865// acceptance is processed. 866void AddOemInstallWorkItems(const InstallationState& original_state, 867 const InstallerState& installer_state, 868 WorkItemList* install_list) { 869 DCHECK(installer_state.is_multi_install()); 870 const bool system_install = installer_state.system_install(); 871 if (!original_state.GetProductState(system_install, 872 BrowserDistribution::CHROME_BINARIES)) { 873 const HKEY root_key = installer_state.root_key(); 874 base::string16 multi_key( 875 installer_state.multi_package_binaries_distribution()->GetStateKey()); 876 877 // Copy the value from Chrome unless Chrome isn't installed or being 878 // installed. 879 BrowserDistribution::Type source_type; 880 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) { 881 source_type = BrowserDistribution::CHROME_BROWSER; 882 } else if (!installer_state.products().empty()) { 883 // Pick a product, any product. 884 source_type = installer_state.products()[0]->distribution()->GetType(); 885 } else { 886 // Nothing is being installed? Entirely unexpected, so do no harm. 887 LOG(ERROR) << "No products found in AddOemInstallWorkItems"; 888 return; 889 } 890 const ProductState* source_product = 891 original_state.GetNonVersionedProductState(system_install, source_type); 892 893 base::string16 oem_install; 894 if (source_product->GetOemInstall(&oem_install)) { 895 VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from " 896 << BrowserDistribution::GetSpecificDistribution(source_type)-> 897 GetDisplayName(); 898 install_list->AddCreateRegKeyWorkItem( 899 root_key, multi_key, KEY_WOW64_32KEY); 900 // Always overwrite an old value. 901 install_list->AddSetRegValueWorkItem(root_key, 902 multi_key, 903 KEY_WOW64_32KEY, 904 google_update::kRegOemInstallField, 905 oem_install, 906 true); 907 } else { 908 // Clear any old value. 909 install_list->AddDeleteRegValueWorkItem( 910 root_key, 911 multi_key, 912 KEY_WOW64_32KEY, 913 google_update::kRegOemInstallField); 914 } 915 } 916} 917 918// Mirror eulaaccepted the first time anything is installed multi. There is no 919// need to update the value on future install/update runs since 920// GoogleUpdateSettings::SetEULAConsent will modify the value for both the 921// relevant product and for the binaries. 922void AddEulaAcceptedWorkItems(const InstallationState& original_state, 923 const InstallerState& installer_state, 924 WorkItemList* install_list) { 925 DCHECK(installer_state.is_multi_install()); 926 const bool system_install = installer_state.system_install(); 927 if (!original_state.GetProductState(system_install, 928 BrowserDistribution::CHROME_BINARIES)) { 929 const HKEY root_key = installer_state.root_key(); 930 base::string16 multi_key( 931 installer_state.multi_package_binaries_distribution()->GetStateKey()); 932 933 // Copy the value from the product with the greatest value. 934 bool have_eula_accepted = false; 935 BrowserDistribution::Type product_type = BrowserDistribution::NUM_TYPES; 936 DWORD eula_accepted = 0; 937 const Products& products = installer_state.products(); 938 for (Products::const_iterator it = products.begin(); it < products.end(); 939 ++it) { 940 const Product& product = **it; 941 if (product.is_chrome_binaries()) 942 continue; 943 DWORD dword_value = 0; 944 BrowserDistribution::Type this_type = product.distribution()->GetType(); 945 const ProductState* product_state = 946 original_state.GetNonVersionedProductState( 947 system_install, this_type); 948 if (product_state->GetEulaAccepted(&dword_value) && 949 (!have_eula_accepted || dword_value > eula_accepted)) { 950 have_eula_accepted = true; 951 eula_accepted = dword_value; 952 product_type = this_type; 953 } 954 } 955 956 if (have_eula_accepted) { 957 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from " 958 << BrowserDistribution::GetSpecificDistribution(product_type)-> 959 GetDisplayName(); 960 install_list->AddCreateRegKeyWorkItem( 961 root_key, multi_key, KEY_WOW64_32KEY); 962 install_list->AddSetRegValueWorkItem(root_key, 963 multi_key, 964 KEY_WOW64_32KEY, 965 google_update::kRegEULAAceptedField, 966 eula_accepted, 967 true); 968 } else { 969 // Clear any old value. 970 install_list->AddDeleteRegValueWorkItem( 971 root_key, 972 multi_key, 973 KEY_WOW64_32KEY, 974 google_update::kRegEULAAceptedField); 975 } 976 } 977} 978 979// Adds work items that make registry adjustments for Google Update. 980void AddGoogleUpdateWorkItems(const InstallationState& original_state, 981 const InstallerState& installer_state, 982 WorkItemList* install_list) { 983 // Is a multi-install product being installed or over-installed? 984 if (installer_state.operation() != InstallerState::MULTI_INSTALL && 985 installer_state.operation() != InstallerState::MULTI_UPDATE) { 986 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation(); 987 return; 988 } 989 990 const bool system_install = installer_state.system_install(); 991 const HKEY root_key = installer_state.root_key(); 992 base::string16 multi_key( 993 installer_state.multi_package_binaries_distribution()->GetStateKey()); 994 995 // For system-level installs, make sure the ClientStateMedium key for the 996 // binaries exists. 997 if (system_install) { 998 install_list->AddCreateRegKeyWorkItem( 999 root_key, 1000 installer_state.multi_package_binaries_distribution() 1001 ->GetStateMediumKey() 1002 .c_str(), 1003 KEY_WOW64_32KEY); 1004 } 1005 1006 // Creating the ClientState key for binaries, if we're migrating to multi then 1007 // copy over Chrome's brand code if it has one. 1008 if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) { 1009 const ProductState* chrome_product_state = 1010 original_state.GetNonVersionedProductState( 1011 system_install, BrowserDistribution::CHROME_BROWSER); 1012 1013 const base::string16& brand(chrome_product_state->brand()); 1014 if (!brand.empty()) { 1015 install_list->AddCreateRegKeyWorkItem( 1016 root_key, multi_key, KEY_WOW64_32KEY); 1017 // Write Chrome's brand code to the multi key. Never overwrite the value 1018 // if one is already present (although this shouldn't happen). 1019 install_list->AddSetRegValueWorkItem(root_key, 1020 multi_key, 1021 KEY_WOW64_32KEY, 1022 google_update::kRegBrandField, 1023 brand, 1024 false); 1025 } 1026 } 1027 1028 AddOemInstallWorkItems(original_state, installer_state, install_list); 1029 AddEulaAcceptedWorkItems(original_state, installer_state, install_list); 1030 AddUsageStatsWorkItems(original_state, installer_state, install_list); 1031 1032 // TODO(grt): check for other keys/values we should put in the package's 1033 // ClientState and/or Clients key. 1034} 1035 1036void AddUsageStatsWorkItems(const InstallationState& original_state, 1037 const InstallerState& installer_state, 1038 WorkItemList* install_list) { 1039 DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL || 1040 installer_state.operation() == InstallerState::MULTI_UPDATE); 1041 1042 HKEY root_key = installer_state.root_key(); 1043 bool value_found = false; 1044 DWORD usagestats = 0; 1045 const Products& products = installer_state.products(); 1046 1047 // Search for an existing usagestats value for any product. 1048 for (Products::const_iterator scan = products.begin(), end = products.end(); 1049 !value_found && scan != end; ++scan) { 1050 if ((*scan)->is_chrome_binaries()) 1051 continue; 1052 BrowserDistribution* dist = (*scan)->distribution(); 1053 const ProductState* product_state = 1054 original_state.GetNonVersionedProductState( 1055 installer_state.system_install(), dist->GetType()); 1056 value_found = product_state->GetUsageStats(&usagestats); 1057 } 1058 1059 // If a value was found, write it in the appropriate location for the 1060 // binaries and remove all values from the products. 1061 if (value_found) { 1062 base::string16 state_key( 1063 installer_state.multi_package_binaries_distribution()->GetStateKey()); 1064 install_list->AddCreateRegKeyWorkItem(root_key, state_key, KEY_WOW64_32KEY); 1065 // Overwrite any existing value so that overinstalls (where Omaha writes a 1066 // new value into a product's state key) pick up the correct value. 1067 install_list->AddSetRegValueWorkItem(root_key, 1068 state_key, 1069 KEY_WOW64_32KEY, 1070 google_update::kRegUsageStatsField, 1071 usagestats, 1072 true); 1073 1074 for (Products::const_iterator scan = products.begin(), end = products.end(); 1075 scan != end; ++scan) { 1076 if ((*scan)->is_chrome_binaries()) 1077 continue; 1078 BrowserDistribution* dist = (*scan)->distribution(); 1079 if (installer_state.system_install()) { 1080 install_list->AddDeleteRegValueWorkItem( 1081 root_key, 1082 dist->GetStateMediumKey(), 1083 KEY_WOW64_32KEY, 1084 google_update::kRegUsageStatsField); 1085 // Previous versions of Chrome also wrote a value in HKCU even for 1086 // system-level installs, so clean that up. 1087 install_list->AddDeleteRegValueWorkItem( 1088 HKEY_CURRENT_USER, 1089 dist->GetStateKey(), 1090 KEY_WOW64_32KEY, 1091 google_update::kRegUsageStatsField); 1092 } 1093 install_list->AddDeleteRegValueWorkItem( 1094 root_key, 1095 dist->GetStateKey(), 1096 KEY_WOW64_32KEY, 1097 google_update::kRegUsageStatsField); 1098 } 1099 } 1100} 1101 1102bool AppendPostInstallTasks(const InstallerState& installer_state, 1103 const base::FilePath& setup_path, 1104 const Version* current_version, 1105 const Version& new_version, 1106 const base::FilePath& temp_path, 1107 WorkItemList* post_install_task_list) { 1108 DCHECK(post_install_task_list); 1109 1110 HKEY root = installer_state.root_key(); 1111 const Products& products = installer_state.products(); 1112 base::FilePath new_chrome_exe( 1113 installer_state.target_path().Append(installer::kChromeNewExe)); 1114 1115 // Append work items that will only be executed if this was an update. 1116 // We update the 'opv' value with the current version that is active, 1117 // the 'cpv' value with the critical update version (if present), and the 1118 // 'cmd' value with the rename command to run. 1119 { 1120 scoped_ptr<WorkItemList> in_use_update_work_items( 1121 WorkItem::CreateConditionalWorkItemList( 1122 new ConditionRunIfFileExists(new_chrome_exe))); 1123 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList"); 1124 1125 // |critical_version| will be valid only if this in-use update includes a 1126 // version considered critical relative to the version being updated. 1127 Version critical_version(installer_state.DetermineCriticalVersion( 1128 current_version, new_version)); 1129 base::FilePath installer_path( 1130 installer_state.GetInstallerDirectory(new_version).Append( 1131 setup_path.BaseName())); 1132 1133 CommandLine rename(installer_path); 1134 rename.AppendSwitch(switches::kRenameChromeExe); 1135 if (installer_state.system_install()) 1136 rename.AppendSwitch(switches::kSystemLevel); 1137 1138 if (installer_state.verbose_logging()) 1139 rename.AppendSwitch(switches::kVerboseLogging); 1140 1141 base::string16 version_key; 1142 for (size_t i = 0; i < products.size(); ++i) { 1143 BrowserDistribution* dist = products[i]->distribution(); 1144 version_key = dist->GetVersionKey(); 1145 1146 if (current_version) { 1147 in_use_update_work_items->AddSetRegValueWorkItem( 1148 root, 1149 version_key, 1150 KEY_WOW64_32KEY, 1151 google_update::kRegOldVersionField, 1152 ASCIIToWide(current_version->GetString()), 1153 true); 1154 } 1155 if (critical_version.IsValid()) { 1156 in_use_update_work_items->AddSetRegValueWorkItem( 1157 root, 1158 version_key, 1159 KEY_WOW64_32KEY, 1160 google_update::kRegCriticalVersionField, 1161 ASCIIToWide(critical_version.GetString()), 1162 true); 1163 } else { 1164 in_use_update_work_items->AddDeleteRegValueWorkItem( 1165 root, 1166 version_key, 1167 KEY_WOW64_32KEY, 1168 google_update::kRegCriticalVersionField); 1169 } 1170 1171 // Adding this registry entry for all products (but the binaries) is 1172 // overkill. However, as it stands, we don't have a way to know which 1173 // product will check the key and run the command, so we add it for all. 1174 // The first to run it will perform the operation and clean up the other 1175 // values. 1176 if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) { 1177 CommandLine product_rename_cmd(rename); 1178 products[i]->AppendRenameFlags(&product_rename_cmd); 1179 in_use_update_work_items->AddSetRegValueWorkItem( 1180 root, 1181 version_key, 1182 KEY_WOW64_32KEY, 1183 google_update::kRegRenameCmdField, 1184 product_rename_cmd.GetCommandLineString(), 1185 true); 1186 } 1187 } 1188 1189 post_install_task_list->AddWorkItem(in_use_update_work_items.release()); 1190 } 1191 1192 // Append work items that will be executed if this was NOT an in-use update. 1193 { 1194 scoped_ptr<WorkItemList> regular_update_work_items( 1195 WorkItem::CreateConditionalWorkItemList( 1196 new Not(new ConditionRunIfFileExists(new_chrome_exe)))); 1197 regular_update_work_items->set_log_message("RegularUpdateWorkItemList"); 1198 1199 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys. 1200 for (size_t i = 0; i < products.size(); ++i) { 1201 BrowserDistribution* dist = products[i]->distribution(); 1202 base::string16 version_key(dist->GetVersionKey()); 1203 regular_update_work_items->AddDeleteRegValueWorkItem( 1204 root, 1205 version_key, 1206 KEY_WOW64_32KEY, 1207 google_update::kRegOldVersionField); 1208 regular_update_work_items->AddDeleteRegValueWorkItem( 1209 root, 1210 version_key, 1211 KEY_WOW64_32KEY, 1212 google_update::kRegCriticalVersionField); 1213 regular_update_work_items->AddDeleteRegValueWorkItem( 1214 root, 1215 version_key, 1216 KEY_WOW64_32KEY, 1217 google_update::kRegRenameCmdField); 1218 } 1219 1220 post_install_task_list->AddWorkItem(regular_update_work_items.release()); 1221 } 1222 1223 AddRegisterComDllWorkItemsForPackage(installer_state, current_version, 1224 new_version, post_install_task_list); 1225 1226 // If we're told that we're an MSI install, make sure to set the marker 1227 // in the client state key so that future updates do the right thing. 1228 if (installer_state.is_msi()) { 1229 for (size_t i = 0; i < products.size(); ++i) { 1230 const Product* product = products[i]; 1231 AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true, 1232 post_install_task_list); 1233 1234 // We want MSI installs to take over the Add/Remove Programs shortcut. 1235 // Make a best-effort attempt to delete any shortcuts left over from 1236 // previous non-MSI installations for the same type of install (system or 1237 // per user). 1238 if (product->ShouldCreateUninstallEntry()) { 1239 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product, 1240 temp_path, 1241 post_install_task_list); 1242 } 1243 } 1244 } 1245 1246 return true; 1247} 1248 1249void AddInstallWorkItems(const InstallationState& original_state, 1250 const InstallerState& installer_state, 1251 const base::FilePath& setup_path, 1252 const base::FilePath& archive_path, 1253 const base::FilePath& src_path, 1254 const base::FilePath& temp_path, 1255 const Version* current_version, 1256 const Version& new_version, 1257 WorkItemList* install_list) { 1258 DCHECK(install_list); 1259 1260 const base::FilePath& target_path = installer_state.target_path(); 1261 1262 // A temp directory that work items need and the actual install directory. 1263 install_list->AddCreateDirWorkItem(temp_path); 1264 install_list->AddCreateDirWorkItem(target_path); 1265 1266 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) || 1267 installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) { 1268 AddChromeWorkItems(original_state, 1269 installer_state, 1270 setup_path, 1271 archive_path, 1272 src_path, 1273 temp_path, 1274 current_version, 1275 new_version, 1276 install_list); 1277 } 1278 1279 if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 1280 install_list->AddCopyTreeWorkItem( 1281 src_path.Append(installer::kChromeAppHostExe).value(), 1282 target_path.Append(installer::kChromeAppHostExe).value(), 1283 temp_path.value(), 1284 WorkItem::ALWAYS, 1285 L""); 1286 } 1287 1288 // Copy installer in install directory 1289 AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path, 1290 new_version, install_list); 1291 1292 const HKEY root = installer_state.root_key(); 1293 // Only set "lang" for user-level installs since for system-level, the install 1294 // language may not be related to a given user's runtime language. 1295 const bool add_language_identifier = !installer_state.system_install(); 1296 1297 const Products& products = installer_state.products(); 1298 for (Products::const_iterator it = products.begin(); it < products.end(); 1299 ++it) { 1300 const Product& product = **it; 1301 1302 AddUninstallShortcutWorkItems(installer_state, setup_path, new_version, 1303 product, install_list); 1304 1305 BrowserDistribution* dist = product.distribution(); 1306 AddVersionKeyWorkItems(root, 1307 dist->GetVersionKey(), 1308 dist->GetDisplayName(), 1309 new_version, 1310 add_language_identifier, 1311 install_list); 1312 1313 AddDelegateExecuteWorkItems(installer_state, target_path, new_version, 1314 product, install_list); 1315 1316 AddActiveSetupWorkItems(installer_state, setup_path, new_version, product, 1317 install_list); 1318 } 1319 1320 // TODO(huangs): Implement actual migration code and remove the hack below. 1321 // If installing Chrome without the legacy stand-alone App Launcher (to be 1322 // handled later), add "shadow" App Launcher registry keys so Google Update 1323 // would recognize the "dr" value in the App Launcher ClientState key. 1324 // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome. 1325 if (installer_state.is_multi_install() && 1326 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) && 1327 !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 1328 BrowserDistribution* shadow_app_launcher_dist = 1329 BrowserDistribution::GetSpecificDistribution( 1330 BrowserDistribution::CHROME_APP_HOST); 1331 AddVersionKeyWorkItems(root, 1332 shadow_app_launcher_dist->GetVersionKey(), 1333 shadow_app_launcher_dist->GetDisplayName(), 1334 new_version, 1335 add_language_identifier, 1336 install_list); 1337 } 1338 1339 // Add any remaining work items that involve special settings for 1340 // each product. 1341 AddProductSpecificWorkItems(original_state, 1342 installer_state, 1343 setup_path, 1344 new_version, 1345 current_version == NULL, 1346 install_list); 1347 1348 // Copy over brand, usagestats, and other values. 1349 AddGoogleUpdateWorkItems(original_state, installer_state, install_list); 1350 1351 // Append the tasks that run after the installation. 1352 AppendPostInstallTasks(installer_state, 1353 setup_path, 1354 current_version, 1355 new_version, 1356 temp_path, 1357 install_list); 1358} 1359 1360void AddRegisterComDllWorkItems(const base::FilePath& dll_folder, 1361 const std::vector<base::FilePath>& dll_list, 1362 bool system_level, 1363 bool do_register, 1364 bool ignore_failures, 1365 WorkItemList* work_item_list) { 1366 DCHECK(work_item_list); 1367 if (dll_list.empty()) { 1368 VLOG(1) << "No COM DLLs to register"; 1369 } else { 1370 std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin()); 1371 for (; dll_iter != dll_list.end(); ++dll_iter) { 1372 base::FilePath dll_path = dll_folder.Append(*dll_iter); 1373 WorkItem* work_item = work_item_list->AddSelfRegWorkItem( 1374 dll_path.value(), do_register, !system_level); 1375 DCHECK(work_item); 1376 work_item->set_ignore_failure(ignore_failures); 1377 } 1378 } 1379} 1380 1381void AddSetMsiMarkerWorkItem(const InstallerState& installer_state, 1382 BrowserDistribution* dist, 1383 bool set, 1384 WorkItemList* work_item_list) { 1385 DCHECK(work_item_list); 1386 DWORD msi_value = set ? 1 : 0; 1387 WorkItem* set_msi_work_item = 1388 work_item_list->AddSetRegValueWorkItem(installer_state.root_key(), 1389 dist->GetStateKey(), 1390 KEY_WOW64_32KEY, 1391 google_update::kRegMSIField, 1392 msi_value, 1393 true); 1394 DCHECK(set_msi_work_item); 1395 set_msi_work_item->set_ignore_failure(true); 1396 set_msi_work_item->set_log_message("Could not write MSI marker!"); 1397} 1398 1399void AddDelegateExecuteWorkItems(const InstallerState& installer_state, 1400 const base::FilePath& target_path, 1401 const Version& new_version, 1402 const Product& product, 1403 WorkItemList* list) { 1404 base::string16 handler_class_uuid; 1405 BrowserDistribution* dist = product.distribution(); 1406 if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) { 1407 if (InstallUtil::IsChromeSxSProcess()) { 1408 CleanupBadCanaryDelegateExecuteRegistration(target_path, list); 1409 } else { 1410 VLOG(1) << "No DelegateExecute verb handler processing to do for " 1411 << dist->GetDisplayName(); 1412 } 1413 return; 1414 } 1415 1416 HKEY root = installer_state.root_key(); 1417 base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\"); 1418 delegate_execute_path.append(handler_class_uuid); 1419 1420 // Unconditionally remove registration regardless of whether or not it is 1421 // needed since builds after r132190 included it when it wasn't strictly 1422 // necessary. Do this removal before adding in the new key to ensure that 1423 // the COM probe/flush below does its job. 1424 AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list); 1425 1426 // Add work items to register the handler iff it is present. 1427 // See also shell_util.cc's GetProgIdEntries. 1428 if (installer_state.operation() != InstallerState::UNINSTALL) { 1429 VLOG(1) << "Adding registration items for DelegateExecute verb handler."; 1430 1431 // Force COM to flush its cache containing the path to the old handler. 1432 list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback, 1433 handler_class_uuid)); 1434 1435 // The path to the exe (in the version directory). 1436 base::FilePath delegate_execute(target_path); 1437 if (new_version.IsValid()) 1438 delegate_execute = delegate_execute.AppendASCII(new_version.GetString()); 1439 delegate_execute = delegate_execute.Append(kDelegateExecuteExe); 1440 1441 // Command-line featuring the quoted path to the exe. 1442 base::string16 command(1, L'"'); 1443 command.append(delegate_execute.value()).append(1, L'"'); 1444 1445 // Register the CommandExecuteImpl class in Software\Classes\CLSID\... 1446 list->AddCreateRegKeyWorkItem( 1447 root, delegate_execute_path, WorkItem::kWow64Default); 1448 list->AddSetRegValueWorkItem(root, 1449 delegate_execute_path, 1450 WorkItem::kWow64Default, 1451 L"", 1452 L"CommandExecuteImpl Class", 1453 true); 1454 base::string16 subkey(delegate_execute_path); 1455 subkey.append(L"\\LocalServer32"); 1456 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default); 1457 list->AddSetRegValueWorkItem( 1458 root, subkey, WorkItem::kWow64Default, L"", command, true); 1459 list->AddSetRegValueWorkItem(root, 1460 subkey, 1461 WorkItem::kWow64Default, 1462 L"ServerExecutable", 1463 delegate_execute.value(), 1464 true); 1465 1466 subkey.assign(delegate_execute_path).append(L"\\Programmable"); 1467 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default); 1468 } 1469} 1470 1471void AddActiveSetupWorkItems(const InstallerState& installer_state, 1472 const base::FilePath& setup_path, 1473 const Version& new_version, 1474 const Product& product, 1475 WorkItemList* list) { 1476 DCHECK(installer_state.operation() != InstallerState::UNINSTALL); 1477 BrowserDistribution* dist = product.distribution(); 1478 1479 if (!product.is_chrome() || !installer_state.system_install()) { 1480 const char* install_level = 1481 installer_state.system_install() ? "system" : "user"; 1482 VLOG(1) << "No Active Setup processing to do for " << install_level 1483 << "-level " << dist->GetDisplayName(); 1484 return; 1485 } 1486 DCHECK(installer_state.RequiresActiveSetup()); 1487 1488 const HKEY root = HKEY_LOCAL_MACHINE; 1489 const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist)); 1490 1491 VLOG(1) << "Adding registration items for Active Setup."; 1492 list->AddCreateRegKeyWorkItem( 1493 root, active_setup_path, WorkItem::kWow64Default); 1494 list->AddSetRegValueWorkItem(root, 1495 active_setup_path, 1496 WorkItem::kWow64Default, 1497 L"", 1498 dist->GetDisplayName(), 1499 true); 1500 1501 base::FilePath active_setup_exe(installer_state.GetInstallerDirectory( 1502 new_version).Append(kActiveSetupExe)); 1503 CommandLine cmd(active_setup_exe); 1504 cmd.AppendSwitch(installer::switches::kConfigureUserSettings); 1505 cmd.AppendSwitch(installer::switches::kVerboseLogging); 1506 cmd.AppendSwitch(installer::switches::kSystemLevel); 1507 product.AppendProductFlags(&cmd); 1508 list->AddSetRegValueWorkItem(root, 1509 active_setup_path, 1510 WorkItem::kWow64Default, 1511 L"StubPath", 1512 cmd.GetCommandLineString(), 1513 true); 1514 1515 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 1516 // resource. 1517 list->AddSetRegValueWorkItem(root, 1518 active_setup_path, 1519 WorkItem::kWow64Default, 1520 L"Localized Name", 1521 dist->GetDisplayName(), 1522 true); 1523 1524 list->AddSetRegValueWorkItem(root, 1525 active_setup_path, 1526 WorkItem::kWow64Default, 1527 L"IsInstalled", 1528 static_cast<DWORD>(1U), 1529 true); 1530 1531 list->AddSetRegValueWorkItem(root, 1532 active_setup_path, 1533 WorkItem::kWow64Default, 1534 L"Version", 1535 kActiveSetupVersion, 1536 true); 1537} 1538 1539void AddDeleteOldIELowRightsPolicyWorkItems( 1540 const InstallerState& installer_state, 1541 WorkItemList* install_list) { 1542 DCHECK(install_list); 1543 1544 base::string16 key_path; 1545 GetOldIELowRightsElevationPolicyKeyPath(&key_path); 1546 install_list->AddDeleteRegKeyWorkItem( 1547 installer_state.root_key(), key_path, WorkItem::kWow64Default); 1548} 1549 1550void AppendUninstallCommandLineFlags(const InstallerState& installer_state, 1551 const Product& product, 1552 CommandLine* uninstall_cmd) { 1553 DCHECK(uninstall_cmd); 1554 1555 uninstall_cmd->AppendSwitch(installer::switches::kUninstall); 1556 1557 // Append the product-specific uninstall flags. 1558 product.AppendProductFlags(uninstall_cmd); 1559 if (installer_state.is_msi()) 1560 uninstall_cmd->AppendSwitch(installer::switches::kMsi); 1561 if (installer_state.system_install()) 1562 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel); 1563 if (installer_state.verbose_logging()) 1564 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging); 1565} 1566 1567void RefreshElevationPolicy() { 1568 const wchar_t kIEFrameDll[] = L"ieframe.dll"; 1569 const char kIERefreshPolicy[] = "IERefreshElevationPolicy"; 1570 1571 HMODULE ieframe = LoadLibrary(kIEFrameDll); 1572 if (ieframe) { 1573 typedef HRESULT (__stdcall *IERefreshPolicy)(); 1574 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>( 1575 GetProcAddress(ieframe, kIERefreshPolicy)); 1576 1577 if (ie_refresh_policy) { 1578 ie_refresh_policy(); 1579 } else { 1580 VLOG(1) << kIERefreshPolicy << " not supported."; 1581 } 1582 1583 FreeLibrary(ieframe); 1584 } else { 1585 VLOG(1) << "Cannot load " << kIEFrameDll; 1586 } 1587} 1588 1589void AddOsUpgradeWorkItems(const InstallerState& installer_state, 1590 const base::FilePath& setup_path, 1591 const Version& new_version, 1592 const Product& product, 1593 WorkItemList* install_list) { 1594 const HKEY root_key = installer_state.root_key(); 1595 base::string16 cmd_key( 1596 GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade)); 1597 1598 if (installer_state.operation() == InstallerState::UNINSTALL) { 1599 install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY) 1600 ->set_log_message("Removing OS upgrade command"); 1601 } else { 1602 // Register with Google Update to have setup.exe --on-os-upgrade called on 1603 // OS upgrade. 1604 CommandLine cmd_line(installer_state 1605 .GetInstallerDirectory(new_version) 1606 .Append(setup_path.BaseName())); 1607 // Add the main option to indicate OS upgrade flow. 1608 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade); 1609 // Add product-specific options. 1610 product.AppendProductFlags(&cmd_line); 1611 if (installer_state.system_install()) 1612 cmd_line.AppendSwitch(installer::switches::kSystemLevel); 1613 // Log everything for now. 1614 cmd_line.AppendSwitch(installer::switches::kVerboseLogging); 1615 1616 AppCommand cmd(cmd_line.GetCommandLineString()); 1617 cmd.set_is_auto_run_on_os_upgrade(true); 1618 cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list); 1619 } 1620} 1621 1622void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state, 1623 const base::FilePath& setup_path, 1624 const Version& new_version, 1625 const Product& product, 1626 WorkItemList* work_item_list) { 1627 const HKEY root_key = installer_state.root_key(); 1628 base::string16 cmd_key( 1629 GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance)); 1630 if (installer_state.operation() == InstallerState::UNINSTALL) { 1631 work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY) 1632 ->set_log_message("Removing query EULA acceptance command"); 1633 } else { 1634 CommandLine cmd_line(installer_state 1635 .GetInstallerDirectory(new_version) 1636 .Append(setup_path.BaseName())); 1637 cmd_line.AppendSwitch(switches::kQueryEULAAcceptance); 1638 if (installer_state.system_install()) 1639 cmd_line.AppendSwitch(installer::switches::kSystemLevel); 1640 if (installer_state.verbose_logging()) 1641 cmd_line.AppendSwitch(installer::switches::kVerboseLogging); 1642 AppCommand cmd(cmd_line.GetCommandLineString()); 1643 cmd.set_is_web_accessible(true); 1644 cmd.set_is_run_as_user(true); 1645 cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list); 1646 } 1647} 1648 1649void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state, 1650 WorkItemList* work_item_list) { 1651 DCHECK(work_item_list); 1652 1653 base::string16 cmd_key( 1654 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution( 1655 BrowserDistribution::CHROME_BINARIES), 1656 kCmdQuickEnableCf)); 1657 1658 // Unconditionally remove the legacy Quick Enable command from the binaries. 1659 // Do this even if multi-install Chrome isn't installed to ensure that it is 1660 // not left behind in any case. 1661 work_item_list->AddDeleteRegKeyWorkItem( 1662 installer_state.root_key(), cmd_key, KEY_WOW64_32KEY) 1663 ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf) + 1664 " command"); 1665} 1666 1667} // namespace installer 1668