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