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