install_worker.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 " + base::UTF16ToASCII(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 // In the past, we copied rather than moved for system level installs so that 504 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path| 505 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA% 506 // otherwise), there is no need to do this. 507 // Note that we pass true for check_duplicates to avoid failing on in-use 508 // repair runs if the current_version is the same as the new_version. 509 bool check_for_duplicates = (current_version && 510 current_version->Equals(new_version)); 511 install_list->AddMoveTreeWorkItem( 512 src_path.AppendASCII(new_version.GetString()).value(), 513 target_path.AppendASCII(new_version.GetString()).value(), 514 temp_path.value(), 515 check_for_duplicates ? WorkItem::CHECK_DUPLICATES : 516 WorkItem::ALWAYS_MOVE); 517 518 // Delete any old_chrome.exe if present (ignore failure if it's in use). 519 install_list->AddDeleteTreeWorkItem( 520 target_path.Append(installer::kChromeOldExe), temp_path)-> 521 set_ignore_failure(true); 522} 523 524// Probes COM machinery to get an instance of delegate_execute.exe's 525// CommandExecuteImpl class. This is required so that COM purges its cache of 526// the path to the binary, which changes on updates. This callback 527// unconditionally returns true since an install should not be aborted if the 528// probe fails. 529bool ProbeCommandExecuteCallback(const base::string16& command_execute_id, 530 const CallbackWorkItem& work_item) { 531 // Noop on rollback. 532 if (work_item.IsRollback()) 533 return true; 534 535 CLSID class_id = {}; 536 537 HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id); 538 if (FAILED(hr)) { 539 LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to " 540 "CLSID; hr=0x" << std::hex << hr; 541 } else { 542 base::win::ScopedComPtr<IUnknown> command_execute_impl; 543 hr = command_execute_impl.CreateInstance(class_id, NULL, 544 CLSCTX_LOCAL_SERVER); 545 if (hr != REGDB_E_CLASSNOTREG) { 546 LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x" 547 << std::hex << hr; 548 } 549 } 550 551 return true; 552} 553 554void AddUninstallDelegateExecuteWorkItems( 555 HKEY root, 556 const base::string16& delegate_execute_path, 557 WorkItemList* list) { 558 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in " 559 << root; 560 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path); 561 562 // In the past, the ICommandExecuteImpl interface and a TypeLib were both 563 // registered. Remove these since this operation may be updating a machine 564 // that had the old registrations. 565 list->AddDeleteRegKeyWorkItem(root, 566 L"Software\\Classes\\Interface\\" 567 L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}"); 568 list->AddDeleteRegKeyWorkItem(root, 569 L"Software\\Classes\\TypeLib\\" 570#if defined(GOOGLE_CHROME_BUILD) 571 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}" 572#else 573 L"{7779FB70-B399-454A-AA1A-BAA850032B10}" 574#endif 575 ); 576} 577 578// Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0 579// (exclusively -- crrev.com/132596), registered a DelegateExecute class by 580// mistake (with the same GUID as Chrome). The fix stopped registering the bad 581// value, but didn't delete it. This is a problem for users who had installed 582// Canary before 20.0.1106.0 and now have a system-level Chrome, as the 583// left-behind Canary registrations in HKCU mask the HKLM registrations for the 584// same GUID. Cleanup those registrations if they still exist and belong to this 585// Canary (i.e., the registered delegate_execute's path is under |target_path|). 586void CleanupBadCanaryDelegateExecuteRegistration( 587 const base::FilePath& target_path, 588 WorkItemList* list) { 589 base::string16 google_chrome_delegate_execute_path( 590 L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}"); 591 base::string16 google_chrome_local_server_32( 592 google_chrome_delegate_execute_path + L"\\LocalServer32"); 593 594 RegKey local_server_32_key; 595 base::string16 registered_server; 596 if (local_server_32_key.Open(HKEY_CURRENT_USER, 597 google_chrome_local_server_32.c_str(), 598 KEY_QUERY_VALUE) == ERROR_SUCCESS && 599 local_server_32_key.ReadValue(L"ServerExecutable", 600 ®istered_server) == ERROR_SUCCESS && 601 target_path.IsParent(base::FilePath(registered_server))) { 602 scoped_ptr<WorkItemList> no_rollback_list( 603 WorkItem::CreateNoRollbackWorkItemList()); 604 AddUninstallDelegateExecuteWorkItems( 605 HKEY_CURRENT_USER, google_chrome_delegate_execute_path, 606 no_rollback_list.get()); 607 list->AddWorkItem(no_rollback_list.release()); 608 VLOG(1) << "Added deletion items for bad Canary registrations."; 609 } 610} 611 612} // namespace 613 614// This method adds work items to create (or update) Chrome uninstall entry in 615// either the Control Panel->Add/Remove Programs list or in the Omaha client 616// state key if running under an MSI installer. 617void AddUninstallShortcutWorkItems(const InstallerState& installer_state, 618 const base::FilePath& setup_path, 619 const Version& new_version, 620 const Product& product, 621 WorkItemList* install_list) { 622 HKEY reg_root = installer_state.root_key(); 623 BrowserDistribution* browser_dist = product.distribution(); 624 DCHECK(browser_dist); 625 626 // When we are installed via an MSI, we need to store our uninstall strings 627 // in the Google Update client state key. We do this even for non-MSI 628 // managed installs to avoid breaking the edge case whereby an MSI-managed 629 // install is updated by a non-msi installer (which would confuse the MSI 630 // machinery if these strings were not also updated). The UninstallString 631 // value placed in the client state key is also used by the mini_installer to 632 // locate the setup.exe instance used for binary patching. 633 // Do not quote the command line for the MSI invocation. 634 base::FilePath install_path(installer_state.target_path()); 635 base::FilePath installer_path( 636 installer_state.GetInstallerDirectory(new_version)); 637 installer_path = installer_path.Append(setup_path.BaseName()); 638 639 CommandLine uninstall_arguments(CommandLine::NO_PROGRAM); 640 AppendUninstallCommandLineFlags(installer_state, product, 641 &uninstall_arguments); 642 643 base::string16 update_state_key(browser_dist->GetStateKey()); 644 install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key); 645 install_list->AddSetRegValueWorkItem(reg_root, update_state_key, 646 installer::kUninstallStringField, installer_path.value(), true); 647 install_list->AddSetRegValueWorkItem(reg_root, update_state_key, 648 installer::kUninstallArgumentsField, 649 uninstall_arguments.GetCommandLineString(), true); 650 651 // MSI installations will manage their own uninstall shortcuts. 652 if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) { 653 // We need to quote the command line for the Add/Remove Programs dialog. 654 CommandLine quoted_uninstall_cmd(installer_path); 655 DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"'); 656 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false); 657 658 base::string16 uninstall_reg = browser_dist->GetUninstallRegPath(); 659 install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg); 660 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 661 installer::kUninstallDisplayNameField, browser_dist->GetDisplayName(), 662 true); 663 install_list->AddSetRegValueWorkItem(reg_root, 664 uninstall_reg, installer::kUninstallStringField, 665 quoted_uninstall_cmd.GetCommandLineString(), true); 666 install_list->AddSetRegValueWorkItem(reg_root, 667 uninstall_reg, 668 L"InstallLocation", 669 install_path.value(), 670 true); 671 672 BrowserDistribution* dist = product.distribution(); 673 base::string16 chrome_icon = ShellUtil::FormatIconLocation( 674 install_path.Append(dist->GetIconFilename()).value(), 675 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME)); 676 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 677 L"DisplayIcon", chrome_icon, true); 678 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 679 L"NoModify", static_cast<DWORD>(1), 680 true); 681 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 682 L"NoRepair", static_cast<DWORD>(1), 683 true); 684 685 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 686 L"Publisher", 687 browser_dist->GetPublisherName(), 688 true); 689 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 690 L"Version", 691 ASCIIToWide(new_version.GetString()), 692 true); 693 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 694 L"DisplayVersion", 695 ASCIIToWide(new_version.GetString()), 696 true); 697 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 698 L"InstallDate", 699 InstallUtil::GetCurrentDate(), 700 false); 701 702 const std::vector<uint16>& version_components = new_version.components(); 703 if (version_components.size() == 4) { 704 // Our version should be in major.minor.build.rev. 705 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 706 L"VersionMajor", static_cast<DWORD>(version_components[2]), true); 707 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, 708 L"VersionMinor", static_cast<DWORD>(version_components[3]), true); 709 } 710 } 711} 712 713// Create Version key for a product (if not already present) and sets the new 714// product version as the last step. 715void AddVersionKeyWorkItems(HKEY root, 716 BrowserDistribution* dist, 717 const Version& new_version, 718 bool add_language_identifier, 719 WorkItemList* list) { 720 // Create Version key for each distribution (if not already present) and set 721 // the new product version as the last step. 722 base::string16 version_key(dist->GetVersionKey()); 723 list->AddCreateRegKeyWorkItem(root, version_key); 724 725 base::string16 product_name(dist->GetDisplayName()); 726 list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField, 727 product_name, true); // overwrite name also 728 list->AddSetRegValueWorkItem(root, version_key, 729 google_update::kRegOopcrashesField, 730 static_cast<DWORD>(1), 731 false); // set during first install 732 if (add_language_identifier) { 733 // Write the language identifier of the current translation. Omaha's set of 734 // languages is a superset of Chrome's set of translations with this one 735 // exception: what Chrome calls "en-us", Omaha calls "en". sigh. 736 base::string16 language(GetCurrentTranslation()); 737 if (LowerCaseEqualsASCII(language, "en-us")) 738 language.resize(2); 739 list->AddSetRegValueWorkItem(root, version_key, 740 google_update::kRegLangField, language, 741 false); // do not overwrite language 742 } 743 list->AddSetRegValueWorkItem(root, version_key, 744 google_update::kRegVersionField, 745 ASCIIToWide(new_version.GetString()), 746 true); // overwrite version 747} 748 749// Mirror oeminstall the first time anything is installed multi. There is no 750// need to update the value on future install/update runs since this value never 751// changes. Note that the value is removed by Google Update after EULA 752// acceptance is processed. 753void AddOemInstallWorkItems(const InstallationState& original_state, 754 const InstallerState& installer_state, 755 WorkItemList* install_list) { 756 DCHECK(installer_state.is_multi_install()); 757 const bool system_install = installer_state.system_install(); 758 if (!original_state.GetProductState(system_install, 759 BrowserDistribution::CHROME_BINARIES)) { 760 const HKEY root_key = installer_state.root_key(); 761 base::string16 multi_key( 762 installer_state.multi_package_binaries_distribution()->GetStateKey()); 763 764 // Copy the value from Chrome unless Chrome isn't installed or being 765 // installed. 766 BrowserDistribution::Type source_type; 767 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) { 768 source_type = BrowserDistribution::CHROME_BROWSER; 769 } else if (!installer_state.products().empty()) { 770 // Pick a product, any product. 771 source_type = installer_state.products()[0]->distribution()->GetType(); 772 } else { 773 // Nothing is being installed? Entirely unexpected, so do no harm. 774 LOG(ERROR) << "No products found in AddOemInstallWorkItems"; 775 return; 776 } 777 const ProductState* source_product = 778 original_state.GetNonVersionedProductState(system_install, source_type); 779 780 base::string16 oem_install; 781 if (source_product->GetOemInstall(&oem_install)) { 782 VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from " 783 << BrowserDistribution::GetSpecificDistribution(source_type)-> 784 GetDisplayName(); 785 install_list->AddCreateRegKeyWorkItem(root_key, multi_key); 786 // Always overwrite an old value. 787 install_list->AddSetRegValueWorkItem(root_key, multi_key, 788 google_update::kRegOemInstallField, 789 oem_install, true); 790 } else { 791 // Clear any old value. 792 install_list->AddDeleteRegValueWorkItem( 793 root_key, multi_key, google_update::kRegOemInstallField); 794 } 795 } 796} 797 798// Mirror eulaaccepted the first time anything is installed multi. There is no 799// need to update the value on future install/update runs since 800// GoogleUpdateSettings::SetEULAConsent will modify the value for both the 801// relevant product and for the binaries. 802void AddEulaAcceptedWorkItems(const InstallationState& original_state, 803 const InstallerState& installer_state, 804 WorkItemList* install_list) { 805 DCHECK(installer_state.is_multi_install()); 806 const bool system_install = installer_state.system_install(); 807 if (!original_state.GetProductState(system_install, 808 BrowserDistribution::CHROME_BINARIES)) { 809 const HKEY root_key = installer_state.root_key(); 810 base::string16 multi_key( 811 installer_state.multi_package_binaries_distribution()->GetStateKey()); 812 813 // Copy the value from the product with the greatest value. 814 bool have_eula_accepted = false; 815 BrowserDistribution::Type product_type; 816 DWORD eula_accepted; 817 const Products& products = installer_state.products(); 818 for (Products::const_iterator it = products.begin(); it < products.end(); 819 ++it) { 820 const Product& product = **it; 821 if (product.is_chrome_binaries()) 822 continue; 823 DWORD dword_value = 0; 824 BrowserDistribution::Type this_type = product.distribution()->GetType(); 825 const ProductState* product_state = 826 original_state.GetNonVersionedProductState( 827 system_install, this_type); 828 if (product_state->GetEulaAccepted(&dword_value) && 829 (!have_eula_accepted || dword_value > eula_accepted)) { 830 have_eula_accepted = true; 831 eula_accepted = dword_value; 832 product_type = this_type; 833 } 834 } 835 836 if (have_eula_accepted) { 837 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from " 838 << BrowserDistribution::GetSpecificDistribution(product_type)-> 839 GetDisplayName(); 840 install_list->AddCreateRegKeyWorkItem(root_key, multi_key); 841 install_list->AddSetRegValueWorkItem( 842 root_key, multi_key, google_update::kRegEULAAceptedField, 843 eula_accepted, true); 844 } else { 845 // Clear any old value. 846 install_list->AddDeleteRegValueWorkItem( 847 root_key, multi_key, google_update::kRegEULAAceptedField); 848 } 849 } 850} 851 852// Adds work items that make registry adjustments for Google Update. 853void AddGoogleUpdateWorkItems(const InstallationState& original_state, 854 const InstallerState& installer_state, 855 WorkItemList* install_list) { 856 // Is a multi-install product being installed or over-installed? 857 if (installer_state.operation() != InstallerState::MULTI_INSTALL && 858 installer_state.operation() != InstallerState::MULTI_UPDATE) { 859 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation(); 860 return; 861 } 862 863 const bool system_install = installer_state.system_install(); 864 const HKEY root_key = installer_state.root_key(); 865 base::string16 multi_key( 866 installer_state.multi_package_binaries_distribution()->GetStateKey()); 867 868 // For system-level installs, make sure the ClientStateMedium key for the 869 // binaries exists. 870 if (system_install) { 871 install_list->AddCreateRegKeyWorkItem( 872 root_key, 873 installer_state.multi_package_binaries_distribution()-> 874 GetStateMediumKey().c_str()); 875 } 876 877 // Creating the ClientState key for binaries, if we're migrating to multi then 878 // copy over Chrome's brand code if it has one. 879 if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) { 880 const ProductState* chrome_product_state = 881 original_state.GetNonVersionedProductState( 882 system_install, BrowserDistribution::CHROME_BROWSER); 883 884 const base::string16& brand(chrome_product_state->brand()); 885 if (!brand.empty()) { 886 install_list->AddCreateRegKeyWorkItem(root_key, multi_key); 887 // Write Chrome's brand code to the multi key. Never overwrite the value 888 // if one is already present (although this shouldn't happen). 889 install_list->AddSetRegValueWorkItem(root_key, 890 multi_key, 891 google_update::kRegBrandField, 892 brand, 893 false); 894 } 895 } 896 897 AddOemInstallWorkItems(original_state, installer_state, install_list); 898 AddEulaAcceptedWorkItems(original_state, installer_state, install_list); 899 AddUsageStatsWorkItems(original_state, installer_state, install_list); 900 901 // TODO(grt): check for other keys/values we should put in the package's 902 // ClientState and/or Clients key. 903} 904 905void AddUsageStatsWorkItems(const InstallationState& original_state, 906 const InstallerState& installer_state, 907 WorkItemList* install_list) { 908 DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL || 909 installer_state.operation() == InstallerState::MULTI_UPDATE); 910 911 HKEY root_key = installer_state.root_key(); 912 bool value_found = false; 913 DWORD usagestats = 0; 914 const Products& products = installer_state.products(); 915 916 // Search for an existing usagestats value for any product. 917 for (Products::const_iterator scan = products.begin(), end = products.end(); 918 !value_found && scan != end; ++scan) { 919 if ((*scan)->is_chrome_binaries()) 920 continue; 921 BrowserDistribution* dist = (*scan)->distribution(); 922 const ProductState* product_state = 923 original_state.GetNonVersionedProductState( 924 installer_state.system_install(), dist->GetType()); 925 value_found = product_state->GetUsageStats(&usagestats); 926 } 927 928 // If a value was found, write it in the appropriate location for the 929 // binaries and remove all values from the products. 930 if (value_found) { 931 base::string16 state_key( 932 installer_state.multi_package_binaries_distribution()->GetStateKey()); 933 install_list->AddCreateRegKeyWorkItem(root_key, state_key); 934 // Overwrite any existing value so that overinstalls (where Omaha writes a 935 // new value into a product's state key) pick up the correct value. 936 install_list->AddSetRegValueWorkItem(root_key, state_key, 937 google_update::kRegUsageStatsField, 938 usagestats, true); 939 940 for (Products::const_iterator scan = products.begin(), end = products.end(); 941 scan != end; ++scan) { 942 if ((*scan)->is_chrome_binaries()) 943 continue; 944 BrowserDistribution* dist = (*scan)->distribution(); 945 if (installer_state.system_install()) { 946 install_list->AddDeleteRegValueWorkItem( 947 root_key, dist->GetStateMediumKey(), 948 google_update::kRegUsageStatsField); 949 // Previous versions of Chrome also wrote a value in HKCU even for 950 // system-level installs, so clean that up. 951 install_list->AddDeleteRegValueWorkItem( 952 HKEY_CURRENT_USER, dist->GetStateKey(), 953 google_update::kRegUsageStatsField); 954 } 955 install_list->AddDeleteRegValueWorkItem( 956 root_key, dist->GetStateKey(), google_update::kRegUsageStatsField); 957 } 958 } 959} 960 961bool AppendPostInstallTasks(const InstallerState& installer_state, 962 const base::FilePath& setup_path, 963 const Version* current_version, 964 const Version& new_version, 965 const base::FilePath& temp_path, 966 WorkItemList* post_install_task_list) { 967 DCHECK(post_install_task_list); 968 969 HKEY root = installer_state.root_key(); 970 const Products& products = installer_state.products(); 971 base::FilePath new_chrome_exe( 972 installer_state.target_path().Append(installer::kChromeNewExe)); 973 974 // Append work items that will only be executed if this was an update. 975 // We update the 'opv' value with the current version that is active, 976 // the 'cpv' value with the critical update version (if present), and the 977 // 'cmd' value with the rename command to run. 978 { 979 scoped_ptr<WorkItemList> in_use_update_work_items( 980 WorkItem::CreateConditionalWorkItemList( 981 new ConditionRunIfFileExists(new_chrome_exe))); 982 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList"); 983 984 // |critical_version| will be valid only if this in-use update includes a 985 // version considered critical relative to the version being updated. 986 Version critical_version(installer_state.DetermineCriticalVersion( 987 current_version, new_version)); 988 base::FilePath installer_path( 989 installer_state.GetInstallerDirectory(new_version).Append( 990 setup_path.BaseName())); 991 992 CommandLine rename(installer_path); 993 rename.AppendSwitch(switches::kRenameChromeExe); 994 if (installer_state.system_install()) 995 rename.AppendSwitch(switches::kSystemLevel); 996 997 if (installer_state.verbose_logging()) 998 rename.AppendSwitch(switches::kVerboseLogging); 999 1000 base::string16 version_key; 1001 for (size_t i = 0; i < products.size(); ++i) { 1002 BrowserDistribution* dist = products[i]->distribution(); 1003 version_key = dist->GetVersionKey(); 1004 1005 if (current_version) { 1006 in_use_update_work_items->AddSetRegValueWorkItem(root, version_key, 1007 google_update::kRegOldVersionField, 1008 ASCIIToWide(current_version->GetString()), true); 1009 } 1010 if (critical_version.IsValid()) { 1011 in_use_update_work_items->AddSetRegValueWorkItem(root, version_key, 1012 google_update::kRegCriticalVersionField, 1013 ASCIIToWide(critical_version.GetString()), true); 1014 } else { 1015 in_use_update_work_items->AddDeleteRegValueWorkItem(root, version_key, 1016 google_update::kRegCriticalVersionField); 1017 } 1018 1019 // Adding this registry entry for all products (but the binaries) is 1020 // overkill. However, as it stands, we don't have a way to know which 1021 // product will check the key and run the command, so we add it for all. 1022 // The first to run it will perform the operation and clean up the other 1023 // values. 1024 if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) { 1025 CommandLine product_rename_cmd(rename); 1026 products[i]->AppendRenameFlags(&product_rename_cmd); 1027 in_use_update_work_items->AddSetRegValueWorkItem( 1028 root, version_key, google_update::kRegRenameCmdField, 1029 product_rename_cmd.GetCommandLineString(), true); 1030 } 1031 } 1032 1033 post_install_task_list->AddWorkItem(in_use_update_work_items.release()); 1034 } 1035 1036 // Append work items that will be executed if this was NOT an in-use update. 1037 { 1038 scoped_ptr<WorkItemList> regular_update_work_items( 1039 WorkItem::CreateConditionalWorkItemList( 1040 new Not(new ConditionRunIfFileExists(new_chrome_exe)))); 1041 regular_update_work_items->set_log_message("RegularUpdateWorkItemList"); 1042 1043 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys. 1044 for (size_t i = 0; i < products.size(); ++i) { 1045 BrowserDistribution* dist = products[i]->distribution(); 1046 base::string16 version_key(dist->GetVersionKey()); 1047 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key, 1048 google_update::kRegOldVersionField); 1049 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key, 1050 google_update::kRegCriticalVersionField); 1051 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key, 1052 google_update::kRegRenameCmdField); 1053 } 1054 1055 post_install_task_list->AddWorkItem(regular_update_work_items.release()); 1056 } 1057 1058 AddRegisterComDllWorkItemsForPackage(installer_state, current_version, 1059 new_version, post_install_task_list); 1060 1061 // If we're told that we're an MSI install, make sure to set the marker 1062 // in the client state key so that future updates do the right thing. 1063 if (installer_state.is_msi()) { 1064 for (size_t i = 0; i < products.size(); ++i) { 1065 const Product* product = products[i]; 1066 AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true, 1067 post_install_task_list); 1068 1069 // We want MSI installs to take over the Add/Remove Programs shortcut. 1070 // Make a best-effort attempt to delete any shortcuts left over from 1071 // previous non-MSI installations for the same type of install (system or 1072 // per user). 1073 if (product->ShouldCreateUninstallEntry()) { 1074 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product, 1075 temp_path, 1076 post_install_task_list); 1077 } 1078 } 1079 } 1080 1081 return true; 1082} 1083 1084void AddInstallWorkItems(const InstallationState& original_state, 1085 const InstallerState& installer_state, 1086 const base::FilePath& setup_path, 1087 const base::FilePath& archive_path, 1088 const base::FilePath& src_path, 1089 const base::FilePath& temp_path, 1090 const Version* current_version, 1091 const Version& new_version, 1092 WorkItemList* install_list) { 1093 DCHECK(install_list); 1094 1095 const base::FilePath& target_path = installer_state.target_path(); 1096 1097 // A temp directory that work items need and the actual install directory. 1098 install_list->AddCreateDirWorkItem(temp_path); 1099 install_list->AddCreateDirWorkItem(target_path); 1100 1101 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) || 1102 installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) { 1103 AddChromeWorkItems(original_state, 1104 installer_state, 1105 setup_path, 1106 archive_path, 1107 src_path, 1108 temp_path, 1109 current_version, 1110 new_version, 1111 install_list); 1112 } 1113 1114 if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 1115 install_list->AddCopyTreeWorkItem( 1116 src_path.Append(installer::kChromeAppHostExe).value(), 1117 target_path.Append(installer::kChromeAppHostExe).value(), 1118 temp_path.value(), 1119 WorkItem::ALWAYS, 1120 L""); 1121 } 1122 1123 // Copy installer in install directory 1124 AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path, 1125 new_version, install_list); 1126 1127 const HKEY root = installer_state.root_key(); 1128 // Only set "lang" for user-level installs since for system-level, the install 1129 // language may not be related to a given user's runtime language. 1130 const bool add_language_identifier = !installer_state.system_install(); 1131 1132 const Products& products = installer_state.products(); 1133 for (Products::const_iterator it = products.begin(); it < products.end(); 1134 ++it) { 1135 const Product& product = **it; 1136 1137 AddUninstallShortcutWorkItems(installer_state, setup_path, new_version, 1138 product, install_list); 1139 1140 AddVersionKeyWorkItems(root, product.distribution(), new_version, 1141 add_language_identifier, install_list); 1142 1143 AddDelegateExecuteWorkItems(installer_state, target_path, new_version, 1144 product, install_list); 1145 1146 AddActiveSetupWorkItems(installer_state, setup_path, new_version, product, 1147 install_list); 1148 } 1149 1150 // TODO(huangs): Implement actual migration code and remove the hack below. 1151 // If installing Chrome without the legacy stand-alone App Launcher (to be 1152 // handled later), add "shadow" App Launcher registry keys so Google Update 1153 // would recognize the "dr" value in the App Launcher ClientState key. 1154 // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome. 1155 if (installer_state.is_multi_install() && 1156 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) && 1157 !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 1158 BrowserDistribution* shadow_app_launcher_dist = 1159 BrowserDistribution::GetSpecificDistribution( 1160 BrowserDistribution::CHROME_APP_HOST); 1161 AddVersionKeyWorkItems(root, shadow_app_launcher_dist, new_version, 1162 add_language_identifier, install_list); 1163 } 1164 1165 // Add any remaining work items that involve special settings for 1166 // each product. 1167 AddProductSpecificWorkItems(original_state, installer_state, setup_path, 1168 new_version, install_list); 1169 1170 // Copy over brand, usagestats, and other values. 1171 AddGoogleUpdateWorkItems(original_state, installer_state, install_list); 1172 1173 // Append the tasks that run after the installation. 1174 AppendPostInstallTasks(installer_state, 1175 setup_path, 1176 current_version, 1177 new_version, 1178 temp_path, 1179 install_list); 1180} 1181 1182void AddRegisterComDllWorkItems(const base::FilePath& dll_folder, 1183 const std::vector<base::FilePath>& dll_list, 1184 bool system_level, 1185 bool do_register, 1186 bool ignore_failures, 1187 WorkItemList* work_item_list) { 1188 DCHECK(work_item_list); 1189 if (dll_list.empty()) { 1190 VLOG(1) << "No COM DLLs to register"; 1191 } else { 1192 std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin()); 1193 for (; dll_iter != dll_list.end(); ++dll_iter) { 1194 base::FilePath dll_path = dll_folder.Append(*dll_iter); 1195 WorkItem* work_item = work_item_list->AddSelfRegWorkItem( 1196 dll_path.value(), do_register, !system_level); 1197 DCHECK(work_item); 1198 work_item->set_ignore_failure(ignore_failures); 1199 } 1200 } 1201} 1202 1203void AddSetMsiMarkerWorkItem(const InstallerState& installer_state, 1204 BrowserDistribution* dist, 1205 bool set, 1206 WorkItemList* work_item_list) { 1207 DCHECK(work_item_list); 1208 DWORD msi_value = set ? 1 : 0; 1209 WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem( 1210 installer_state.root_key(), dist->GetStateKey(), 1211 google_update::kRegMSIField, msi_value, true); 1212 DCHECK(set_msi_work_item); 1213 set_msi_work_item->set_ignore_failure(true); 1214 set_msi_work_item->set_log_message("Could not write MSI marker!"); 1215} 1216 1217void AddDelegateExecuteWorkItems(const InstallerState& installer_state, 1218 const base::FilePath& target_path, 1219 const Version& new_version, 1220 const Product& product, 1221 WorkItemList* list) { 1222 base::string16 handler_class_uuid; 1223 BrowserDistribution* dist = product.distribution(); 1224 if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) { 1225 if (InstallUtil::IsChromeSxSProcess()) { 1226 CleanupBadCanaryDelegateExecuteRegistration(target_path, list); 1227 } else { 1228 VLOG(1) << "No DelegateExecute verb handler processing to do for " 1229 << dist->GetDisplayName(); 1230 } 1231 return; 1232 } 1233 1234 HKEY root = installer_state.root_key(); 1235 base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\"); 1236 delegate_execute_path.append(handler_class_uuid); 1237 1238 // Unconditionally remove registration regardless of whether or not it is 1239 // needed since builds after r132190 included it when it wasn't strictly 1240 // necessary. Do this removal before adding in the new key to ensure that 1241 // the COM probe/flush below does its job. 1242 AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list); 1243 1244 // Add work items to register the handler iff it is present. 1245 // See also shell_util.cc's GetProgIdEntries. 1246 if (installer_state.operation() != InstallerState::UNINSTALL) { 1247 VLOG(1) << "Adding registration items for DelegateExecute verb handler."; 1248 1249 // Force COM to flush its cache containing the path to the old handler. 1250 list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback, 1251 handler_class_uuid)); 1252 1253 // The path to the exe (in the version directory). 1254 base::FilePath delegate_execute(target_path); 1255 if (new_version.IsValid()) 1256 delegate_execute = delegate_execute.AppendASCII(new_version.GetString()); 1257 delegate_execute = delegate_execute.Append(kDelegateExecuteExe); 1258 1259 // Command-line featuring the quoted path to the exe. 1260 base::string16 command(1, L'"'); 1261 command.append(delegate_execute.value()).append(1, L'"'); 1262 1263 // Register the CommandExecuteImpl class in Software\Classes\CLSID\... 1264 list->AddCreateRegKeyWorkItem(root, delegate_execute_path); 1265 list->AddSetRegValueWorkItem(root, delegate_execute_path, L"", 1266 L"CommandExecuteImpl Class", true); 1267 base::string16 subkey(delegate_execute_path); 1268 subkey.append(L"\\LocalServer32"); 1269 list->AddCreateRegKeyWorkItem(root, subkey); 1270 list->AddSetRegValueWorkItem(root, subkey, L"", command, true); 1271 list->AddSetRegValueWorkItem(root, subkey, L"ServerExecutable", 1272 delegate_execute.value(), true); 1273 1274 subkey.assign(delegate_execute_path).append(L"\\Programmable"); 1275 list->AddCreateRegKeyWorkItem(root, subkey); 1276 } 1277} 1278 1279void AddActiveSetupWorkItems(const InstallerState& installer_state, 1280 const base::FilePath& setup_path, 1281 const Version& new_version, 1282 const Product& product, 1283 WorkItemList* list) { 1284 DCHECK(installer_state.operation() != InstallerState::UNINSTALL); 1285 BrowserDistribution* dist = product.distribution(); 1286 1287 if (!product.is_chrome() || !installer_state.system_install()) { 1288 const char* install_level = 1289 installer_state.system_install() ? "system" : "user"; 1290 VLOG(1) << "No Active Setup processing to do for " << install_level 1291 << "-level " << dist->GetDisplayName(); 1292 return; 1293 } 1294 DCHECK(installer_state.RequiresActiveSetup()); 1295 1296 const HKEY root = HKEY_LOCAL_MACHINE; 1297 const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist)); 1298 1299 VLOG(1) << "Adding registration items for Active Setup."; 1300 list->AddCreateRegKeyWorkItem(root, active_setup_path); 1301 list->AddSetRegValueWorkItem(root, active_setup_path, L"", 1302 dist->GetDisplayName(), true); 1303 1304 base::FilePath active_setup_exe(installer_state.GetInstallerDirectory( 1305 new_version).Append(kActiveSetupExe)); 1306 CommandLine cmd(active_setup_exe); 1307 cmd.AppendSwitch(installer::switches::kConfigureUserSettings); 1308 cmd.AppendSwitch(installer::switches::kVerboseLogging); 1309 cmd.AppendSwitch(installer::switches::kSystemLevel); 1310 product.AppendProductFlags(&cmd); 1311 list->AddSetRegValueWorkItem(root, active_setup_path, L"StubPath", 1312 cmd.GetCommandLineString(), true); 1313 1314 // TODO(grt): http://crbug.com/75152 Write a reference to a localized 1315 // resource. 1316 list->AddSetRegValueWorkItem(root, active_setup_path, L"Localized Name", 1317 dist->GetDisplayName(), true); 1318 1319 list->AddSetRegValueWorkItem(root, active_setup_path, L"IsInstalled", 1320 static_cast<DWORD>(1U), true); 1321 1322 list->AddSetRegValueWorkItem(root, active_setup_path, L"Version", 1323 kActiveSetupVersion, true); 1324} 1325 1326void AddDeleteOldIELowRightsPolicyWorkItems( 1327 const InstallerState& installer_state, 1328 WorkItemList* install_list) { 1329 DCHECK(install_list); 1330 1331 base::string16 key_path; 1332 GetOldIELowRightsElevationPolicyKeyPath(&key_path); 1333 install_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), key_path); 1334} 1335 1336void AppendUninstallCommandLineFlags(const InstallerState& installer_state, 1337 const Product& product, 1338 CommandLine* uninstall_cmd) { 1339 DCHECK(uninstall_cmd); 1340 1341 uninstall_cmd->AppendSwitch(installer::switches::kUninstall); 1342 1343 // Append the product-specific uninstall flags. 1344 product.AppendProductFlags(uninstall_cmd); 1345 if (installer_state.is_msi()) 1346 uninstall_cmd->AppendSwitch(installer::switches::kMsi); 1347 if (installer_state.system_install()) 1348 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel); 1349 if (installer_state.verbose_logging()) 1350 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging); 1351} 1352 1353void RefreshElevationPolicy() { 1354 const wchar_t kIEFrameDll[] = L"ieframe.dll"; 1355 const char kIERefreshPolicy[] = "IERefreshElevationPolicy"; 1356 1357 HMODULE ieframe = LoadLibrary(kIEFrameDll); 1358 if (ieframe) { 1359 typedef HRESULT (__stdcall *IERefreshPolicy)(); 1360 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>( 1361 GetProcAddress(ieframe, kIERefreshPolicy)); 1362 1363 if (ie_refresh_policy) { 1364 ie_refresh_policy(); 1365 } else { 1366 VLOG(1) << kIERefreshPolicy << " not supported."; 1367 } 1368 1369 FreeLibrary(ieframe); 1370 } else { 1371 VLOG(1) << "Cannot load " << kIEFrameDll; 1372 } 1373} 1374 1375void AddOsUpgradeWorkItems(const InstallerState& installer_state, 1376 const base::FilePath& setup_path, 1377 const Version& new_version, 1378 const Product& product, 1379 WorkItemList* install_list) { 1380 const HKEY root_key = installer_state.root_key(); 1381 base::string16 cmd_key( 1382 GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade)); 1383 1384 if (installer_state.operation() == InstallerState::UNINSTALL) { 1385 install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)-> 1386 set_log_message("Removing OS upgrade command"); 1387 } else { 1388 // Register with Google Update to have setup.exe --on-os-upgrade called on 1389 // OS upgrade. 1390 CommandLine cmd_line(installer_state 1391 .GetInstallerDirectory(new_version) 1392 .Append(setup_path.BaseName())); 1393 // Add the main option to indicate OS upgrade flow. 1394 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade); 1395 // Add product-specific options. 1396 product.AppendProductFlags(&cmd_line); 1397 if (installer_state.system_install()) 1398 cmd_line.AppendSwitch(installer::switches::kSystemLevel); 1399 // Log everything for now. 1400 cmd_line.AppendSwitch(installer::switches::kVerboseLogging); 1401 1402 AppCommand cmd(cmd_line.GetCommandLineString()); 1403 cmd.set_is_auto_run_on_os_upgrade(true); 1404 cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list); 1405 } 1406} 1407 1408void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state, 1409 const base::FilePath& setup_path, 1410 const Version& new_version, 1411 const Product& product, 1412 WorkItemList* work_item_list) { 1413 const HKEY root_key = installer_state.root_key(); 1414 base::string16 cmd_key( 1415 GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance)); 1416 if (installer_state.operation() == InstallerState::UNINSTALL) { 1417 work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)-> 1418 set_log_message("Removing query EULA acceptance command"); 1419 } else { 1420 CommandLine cmd_line(installer_state 1421 .GetInstallerDirectory(new_version) 1422 .Append(setup_path.BaseName())); 1423 cmd_line.AppendSwitch(switches::kQueryEULAAcceptance); 1424 if (installer_state.system_install()) 1425 cmd_line.AppendSwitch(installer::switches::kSystemLevel); 1426 if (installer_state.verbose_logging()) 1427 cmd_line.AppendSwitch(installer::switches::kVerboseLogging); 1428 AppCommand cmd(cmd_line.GetCommandLineString()); 1429 cmd.set_is_web_accessible(true); 1430 cmd.set_is_run_as_user(true); 1431 cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list); 1432 } 1433} 1434 1435void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state, 1436 WorkItemList* work_item_list) { 1437 DCHECK(work_item_list); 1438 1439 base::string16 cmd_key( 1440 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution( 1441 BrowserDistribution::CHROME_BINARIES), 1442 kCmdQuickEnableCf)); 1443 1444 // Unconditionally remove the legacy Quick Enable command from the binaries. 1445 // Do this even if multi-install Chrome isn't installed to ensure that it is 1446 // not left behind in any case. 1447 work_item_list->AddDeleteRegKeyWorkItem( 1448 installer_state.root_key(), cmd_key)->set_log_message( 1449 "removing " + base::UTF16ToASCII(kCmdQuickEnableCf) + " command"); 1450 1451} 1452 1453} // namespace installer 1454