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