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