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