setup_main.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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#include "chrome/installer/setup/setup_main.h" 6 7#include <windows.h> 8#include <msi.h> 9#include <shellapi.h> 10#include <shlobj.h> 11 12#include <string> 13 14#include "base/at_exit.h" 15#include "base/basictypes.h" 16#include "base/command_line.h" 17#include "base/file_util.h" 18#include "base/file_version_info.h" 19#include "base/files/file_path.h" 20#include "base/files/scoped_temp_dir.h" 21#include "base/memory/scoped_ptr.h" 22#include "base/path_service.h" 23#include "base/process/launch.h" 24#include "base/strings/string16.h" 25#include "base/strings/string_number_conversions.h" 26#include "base/strings/string_util.h" 27#include "base/strings/stringprintf.h" 28#include "base/strings/utf_string_conversions.h" 29#include "base/values.h" 30#include "base/win/registry.h" 31#include "base/win/scoped_com_initializer.h" 32#include "base/win/scoped_comptr.h" 33#include "base/win/scoped_handle.h" 34#include "base/win/win_util.h" 35#include "base/win/windows_version.h" 36#include "breakpad/src/client/windows/handler/exception_handler.h" 37#include "chrome/common/chrome_constants.h" 38#include "chrome/common/chrome_paths.h" 39#include "chrome/common/chrome_switches.h" 40#include "chrome/installer/setup/archive_patch_helper.h" 41#include "chrome/installer/setup/install.h" 42#include "chrome/installer/setup/install_worker.h" 43#include "chrome/installer/setup/setup_constants.h" 44#include "chrome/installer/setup/setup_util.h" 45#include "chrome/installer/setup/uninstall.h" 46#include "chrome/installer/util/browser_distribution.h" 47#include "chrome/installer/util/channel_info.h" 48#include "chrome/installer/util/delete_after_reboot_helper.h" 49#include "chrome/installer/util/delete_tree_work_item.h" 50#include "chrome/installer/util/eula_util.h" 51#include "chrome/installer/util/google_update_constants.h" 52#include "chrome/installer/util/google_update_settings.h" 53#include "chrome/installer/util/google_update_util.h" 54#include "chrome/installer/util/helper.h" 55#include "chrome/installer/util/html_dialog.h" 56#include "chrome/installer/util/install_util.h" 57#include "chrome/installer/util/installation_state.h" 58#include "chrome/installer/util/installation_validator.h" 59#include "chrome/installer/util/installer_state.h" 60#include "chrome/installer/util/l10n_string_util.h" 61#include "chrome/installer/util/logging_installer.h" 62#include "chrome/installer/util/lzma_util.h" 63#include "chrome/installer/util/master_preferences.h" 64#include "chrome/installer/util/master_preferences_constants.h" 65#include "chrome/installer/util/self_cleaning_temp_dir.h" 66#include "chrome/installer/util/shell_util.h" 67#include "chrome/installer/util/user_experiment.h" 68 69#include "installer_util_strings.h" // NOLINT 70 71using installer::InstallerState; 72using installer::InstallationState; 73using installer::InstallationValidator; 74using installer::MasterPreferences; 75using installer::Product; 76using installer::ProductState; 77using installer::Products; 78 79const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; 80const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; 81const wchar_t kSystemPrincipalSid[] = L"S-1-5-18"; 82 83const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( 84 MiniDumpWithProcessThreadData | // Get PEB and TEB. 85 MiniDumpWithUnloadedModules | // Get unloaded modules when available. 86 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. 87 88namespace { 89 90// Returns NULL if no compressed archive is available for processing, otherwise 91// returns a patch helper configured to uncompress and patch. 92scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper( 93 const base::FilePath& setup_exe, 94 const CommandLine& command_line, 95 const installer::InstallerState& installer_state, 96 const base::FilePath& working_directory) { 97 // A compressed archive is ordinarily given on the command line by the mini 98 // installer. If one was not given, look for chrome.packed.7z next to the 99 // running program. 100 base::FilePath compressed_archive( 101 command_line.GetSwitchValuePath(installer::switches::kInstallArchive)); 102 bool compressed_archive_specified = !compressed_archive.empty(); 103 if (!compressed_archive_specified) { 104 compressed_archive = setup_exe.DirName().Append( 105 installer::kChromeCompressedArchive); 106 } 107 108 // Fail if no compressed archive is found. 109 if (!base::PathExists(compressed_archive)) { 110 if (compressed_archive_specified) { 111 LOG(ERROR) << installer::switches::kInstallArchive << "=" 112 << compressed_archive.value() << " not found."; 113 } 114 return scoped_ptr<installer::ArchivePatchHelper>(); 115 } 116 117 // chrome.7z is either extracted directly from the compressed archive into the 118 // working dir or is the target of patching in the working dir. 119 base::FilePath target(working_directory.Append(installer::kChromeArchive)); 120 DCHECK(!base::PathExists(target)); 121 122 // Specify an empty path for the patch source since it isn't yet known that 123 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it 124 // is. 125 return scoped_ptr<installer::ArchivePatchHelper>( 126 new installer::ArchivePatchHelper(working_directory, 127 compressed_archive, 128 base::FilePath(), 129 target)); 130} 131 132// Workhorse for producing an uncompressed archive (chrome.7z) given a 133// chrome.packed.7z containing either a patch file based on the version of 134// chrome being updated or the full uncompressed archive. Returns true on 135// success, in which case |archive_type| is populated based on what was found. 136// Returns false on failure, in which case |install_status| contains the error 137// code and the result is written to the registry (via WriteInstallerResult). 138bool UncompressAndPatchChromeArchive( 139 const installer::InstallationState& original_state, 140 const installer::InstallerState& installer_state, 141 installer::ArchivePatchHelper* archive_helper, 142 installer::ArchiveType* archive_type, 143 installer::InstallStatus* install_status) { 144 installer_state.UpdateStage(installer::UNCOMPRESSING); 145 if (!archive_helper->Uncompress(NULL)) { 146 *install_status = installer::UNCOMPRESSION_FAILED; 147 installer_state.WriteInstallerResult(*install_status, 148 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 149 NULL); 150 return false; 151 } 152 153 // Short-circuit if uncompression produced the uncompressed archive rather 154 // than a patch file. 155 if (base::PathExists(archive_helper->target())) { 156 *archive_type = installer::FULL_ARCHIVE_TYPE; 157 return true; 158 } 159 160 // Find the installed version's archive to serve as the source for patching. 161 base::FilePath patch_source(installer::FindArchiveToPatch(original_state, 162 installer_state)); 163 if (patch_source.empty()) { 164 LOG(ERROR) << "Failed to find archive to patch."; 165 *install_status = installer::DIFF_PATCH_SOURCE_MISSING; 166 installer_state.WriteInstallerResult(*install_status, 167 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 168 NULL); 169 return false; 170 } 171 archive_helper->set_patch_source(patch_source); 172 173 // Try courgette first. Failing that, try bspatch. 174 if ((installer_state.UpdateStage(installer::ENSEMBLE_PATCHING), 175 !archive_helper->EnsemblePatch()) && 176 (installer_state.UpdateStage(installer::BINARY_PATCHING), 177 !archive_helper->BinaryPatch())) { 178 *install_status = installer::APPLY_DIFF_PATCH_FAILED; 179 installer_state.WriteInstallerResult(*install_status, 180 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 181 NULL); 182 return false; 183 } 184 185 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE; 186 return true; 187} 188 189// In multi-install, adds all products to |installer_state| that are 190// multi-installed and must be updated along with the products already present 191// in |installer_state|. 192void AddExistingMultiInstalls(const InstallationState& original_state, 193 InstallerState* installer_state) { 194 if (installer_state->is_multi_install()) { 195 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { 196 BrowserDistribution::Type type = 197 static_cast<BrowserDistribution::Type>(i); 198 199 if (!installer_state->FindProduct(type)) { 200 const ProductState* state = 201 original_state.GetProductState(installer_state->system_install(), 202 type); 203 if ((state != NULL) && state->is_multi_install()) { 204 installer_state->AddProductFromState(type, *state); 205 VLOG(1) << "Product already installed and must be included: " 206 << BrowserDistribution::GetSpecificDistribution(type)-> 207 GetDisplayName(); 208 } 209 } 210 } 211 } 212} 213 214// This function is called when --rename-chrome-exe option is specified on 215// setup.exe command line. This function assumes an in-use update has happened 216// for Chrome so there should be a file called new_chrome.exe on the file 217// system and a key called 'opv' in the registry. This function will move 218// new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation. 219// This function also deletes elevation policies associated with the old version 220// if they exist. 221installer::InstallStatus RenameChromeExecutables( 222 const InstallationState& original_state, 223 InstallerState* installer_state) { 224 // See what products are already installed in multi mode. When we do the 225 // rename for multi installs, we must update all installations since they 226 // share the binaries. 227 AddExistingMultiInstalls(original_state, installer_state); 228 const base::FilePath &target_path = installer_state->target_path(); 229 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe)); 230 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe)); 231 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe)); 232 233 // Create a temporary backup directory on the same volume as chrome.exe so 234 // that moving in-use files doesn't lead to trouble. 235 installer::SelfCleaningTempDir temp_path; 236 if (!temp_path.Initialize(target_path.DirName(), 237 installer::kInstallTempDir)) { 238 PLOG(ERROR) << "Failed to create Temp directory " 239 << target_path.DirName() 240 .Append(installer::kInstallTempDir).value(); 241 return installer::RENAME_FAILED; 242 } 243 scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); 244 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe. 245 install_list->AddMoveTreeWorkItem(chrome_exe.value(), 246 chrome_old_exe.value(), 247 temp_path.path().value(), 248 WorkItem::ALWAYS_MOVE); 249 install_list->AddMoveTreeWorkItem(chrome_new_exe.value(), 250 chrome_exe.value(), 251 temp_path.path().value(), 252 WorkItem::ALWAYS_MOVE); 253 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path()); 254 // old_chrome.exe is still in use in most cases, so ignore failures here. 255 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())-> 256 set_ignore_failure(true); 257 258 // Add work items to delete the "opv", "cpv", and "cmd" values from all 259 // products we're operating on (which including the multi-install binaries). 260 const Products& products = installer_state->products(); 261 HKEY reg_root = installer_state->root_key(); 262 base::string16 version_key; 263 for (Products::const_iterator it = products.begin(); it < products.end(); 264 ++it) { 265 version_key = (*it)->distribution()->GetVersionKey(); 266 install_list->AddDeleteRegValueWorkItem( 267 reg_root, version_key, google_update::kRegOldVersionField); 268 install_list->AddDeleteRegValueWorkItem( 269 reg_root, version_key, google_update::kRegCriticalVersionField); 270 install_list->AddDeleteRegValueWorkItem( 271 reg_root, version_key, google_update::kRegRenameCmdField); 272 } 273 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL; 274 if (!install_list->Do()) { 275 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes."; 276 install_list->Rollback(); 277 ret = installer::RENAME_FAILED; 278 } 279 // temp_path's dtor will take care of deleting or scheduling itself for 280 // deletion at reboot when this scope closes. 281 VLOG(1) << "Deleting temporary directory " << temp_path.path().value(); 282 283 return ret; 284} 285 286// For each product that is being updated (i.e., already installed at an earlier 287// version), see if that product has an update policy override that differs from 288// that for the binaries. If any are found, fail with an error indicating that 289// the Group Policy settings are in an inconsistent state. Do not do this test 290// for same-version installs, since it would be unkind to block attempts to 291// repair a corrupt installation. This function returns false when installation 292// should be halted, in which case |status| contains the relevant exit code and 293// the proper installer result has been written to the registry. 294bool CheckGroupPolicySettings(const InstallationState& original_state, 295 const InstallerState& installer_state, 296 const Version& new_version, 297 installer::InstallStatus* status) { 298#if !defined(GOOGLE_CHROME_BUILD) 299 // Chromium builds are not updated via Google Update, so there are no 300 // Group Policy settings to consult. 301 return true; 302#else 303 DCHECK(status); 304 305 // Single installs are always in good shape. 306 if (!installer_state.is_multi_install()) 307 return true; 308 309 bool settings_are_valid = true; 310 const bool is_system_install = installer_state.system_install(); 311 BrowserDistribution* const binaries_dist = 312 installer_state.multi_package_binaries_distribution(); 313 314 // Get the update policy for the binaries. 315 const GoogleUpdateSettings::UpdatePolicy binaries_policy = 316 GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist->GetAppGuid(), 317 NULL); 318 319 // Check for differing update policies for all of the products being updated. 320 const Products& products = installer_state.products(); 321 Products::const_iterator scan = products.begin(); 322 for (Products::const_iterator end = products.end(); scan != end; ++scan) { 323 BrowserDistribution* dist = (*scan)->distribution(); 324 const ProductState* product_state = 325 original_state.GetProductState(is_system_install, dist->GetType()); 326 // Is an earlier version of this product already installed? 327 if (product_state != NULL && 328 product_state->version().CompareTo(new_version) < 0) { 329 bool is_overridden = false; 330 GoogleUpdateSettings::UpdatePolicy app_policy = 331 GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(), 332 &is_overridden); 333 if (is_overridden && app_policy != binaries_policy) { 334 LOG(ERROR) << "Found legacy Group Policy setting for " 335 << dist->GetDisplayName() << " (value: " << app_policy 336 << ") that does not match the setting for " 337 << binaries_dist->GetDisplayName() 338 << " (value: " << binaries_policy << ")."; 339 settings_are_valid = false; 340 } 341 } 342 } 343 344 if (!settings_are_valid) { 345 // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this 346 // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once 347 // we have a help center article that explains why this error is being 348 // reported and how to resolve it. 349 LOG(ERROR) << "Cannot apply update on account of inconsistent " 350 "Google Update Group Policy settings. Use the Group Policy " 351 "Editor to set the update policy override for the " 352 << binaries_dist->GetDisplayName() 353 << " application and try again."; 354 *status = installer::INCONSISTENT_UPDATE_POLICY; 355 installer_state.WriteInstallerResult( 356 *status, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE, NULL); 357 } 358 359 return settings_are_valid; 360#endif // defined(GOOGLE_CHROME_BUILD) 361} 362 363// If only the binaries are being updated, fail. 364// If any product is being installed in single-mode that already exists in 365// multi-mode, fail. 366bool CheckMultiInstallConditions(const InstallationState& original_state, 367 InstallerState* installer_state, 368 installer::InstallStatus* status) { 369 const Products& products = installer_state->products(); 370 DCHECK(products.size()); 371 372 const bool system_level = installer_state->system_install(); 373 374 if (installer_state->is_multi_install()) { 375 const Product* chrome = 376 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 377 const Product* app_host = 378 installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST); 379 const Product* binaries = 380 installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES); 381 const ProductState* chrome_state = 382 original_state.GetProductState(system_level, 383 BrowserDistribution::CHROME_BROWSER); 384 385 if (binaries) { 386 if (products.size() == 1) { 387 // There are no products aside from the binaries, so there is no update 388 // to be applied. This can happen after multi-install Chrome Frame is 389 // migrated to single-install. This is treated as an update failure 390 // unless the binaries are not in-use, in which case they will be 391 // uninstalled and success will be reported (see handling in wWinMain). 392 VLOG(1) << "No products to be updated."; 393 *status = installer::UNUSED_BINARIES; 394 installer_state->WriteInstallerResult(*status, 0, NULL); 395 return false; 396 } 397 } else { 398 // This will only be hit if --multi-install is given with no products, or 399 // if the app host is being installed and doesn't need the binaries at 400 // user-level. 401 // The former case might be due to a request by an orphaned Application 402 // Host to re-install the binaries. Thus we add them to the installation. 403 // The latter case is fine and we let it be. 404 // If this is not an app host install and the binaries are not already 405 // present, the installation will fail later due to a lack of products to 406 // install. 407 if (app_host && !chrome && !chrome_state) { 408 DCHECK(!system_level); 409 // App Host may use Chrome/Chrome binaries at system-level. 410 if (original_state.GetProductState( 411 true, // system 412 BrowserDistribution::CHROME_BROWSER) || 413 original_state.GetProductState( 414 true, // system 415 BrowserDistribution::CHROME_BINARIES)) { 416 VLOG(1) << "Installing/updating App Launcher without binaries."; 417 } else { 418 // Somehow the binaries were present when the quick-enable app host 419 // command was run, but now they appear to be missing. 420 // Force binaries to be installed/updated. 421 scoped_ptr<Product> binaries_to_add(new Product( 422 BrowserDistribution::GetSpecificDistribution( 423 BrowserDistribution::CHROME_BINARIES))); 424 binaries_to_add->SetOption(installer::kOptionMultiInstall, true); 425 binaries = installer_state->AddProduct(&binaries_to_add); 426 VLOG(1) << 427 "Adding binaries for pre-existing App Launcher installation."; 428 } 429 } 430 431 return true; 432 } 433 434 if (!chrome && chrome_state) { 435 // A product other than Chrome is being installed in multi-install mode, 436 // and Chrome is already present. Add Chrome to the set of products 437 // (making it multi-install in the process) so that it is updated, too. 438 scoped_ptr<Product> multi_chrome(new Product( 439 BrowserDistribution::GetSpecificDistribution( 440 BrowserDistribution::CHROME_BROWSER))); 441 multi_chrome->SetOption(installer::kOptionMultiInstall, true); 442 chrome = installer_state->AddProduct(&multi_chrome); 443 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode."; 444 } 445 } else { 446 // This is a non-multi installation. 447 448 // Check for an existing installation of the product. 449 const ProductState* product_state = original_state.GetProductState( 450 system_level, products[0]->distribution()->GetType()); 451 if (product_state != NULL) { 452 // Block downgrades from multi-install to single-install. 453 if (product_state->is_multi_install()) { 454 LOG(ERROR) << "Multi-install " 455 << products[0]->distribution()->GetDisplayName() 456 << " exists; aborting single install."; 457 *status = installer::MULTI_INSTALLATION_EXISTS; 458 installer_state->WriteInstallerResult(*status, 459 IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL); 460 return false; 461 } 462 } 463 } 464 465 return true; 466} 467 468// Checks app host pre-install conditions, specifically that this is a 469// user-level multi-install. When the pre-install conditions are not 470// satisfied, the result is written to the registry (via WriteInstallerResult), 471// |status| is set appropriately, and false is returned. 472bool CheckAppHostPreconditions(const InstallationState& original_state, 473 InstallerState* installer_state, 474 installer::InstallStatus* status) { 475 if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) { 476 if (!installer_state->is_multi_install()) { 477 LOG(DFATAL) << "App Launcher requires multi install"; 478 *status = installer::APP_HOST_REQUIRES_MULTI_INSTALL; 479 // No message string since there is nothing a user can do. 480 installer_state->WriteInstallerResult(*status, 0, NULL); 481 return false; 482 } 483 484 if (installer_state->system_install()) { 485 LOG(DFATAL) << "App Launcher may only be installed at user-level."; 486 *status = installer::APP_HOST_REQUIRES_USER_LEVEL; 487 // No message string since there is nothing a user can do. 488 installer_state->WriteInstallerResult(*status, 0, NULL); 489 return false; 490 } 491 } 492 493 return true; 494} 495 496// Checks for compatibility between the current state of the system and the 497// desired operation. Also applies policy that mutates the desired operation; 498// specifically, the |installer_state| object. 499// Also blocks simultaneous user-level and system-level installs. In the case 500// of trying to install user-level Chrome when system-level exists, the 501// existing system-level Chrome is launched. 502// When the pre-install conditions are not satisfied, the result is written to 503// the registry (via WriteInstallerResult), |status| is set appropriately, and 504// false is returned. 505bool CheckPreInstallConditions(const InstallationState& original_state, 506 InstallerState* installer_state, 507 installer::InstallStatus* status) { 508 if (!CheckAppHostPreconditions(original_state, installer_state, status)) { 509 DCHECK_NE(*status, installer::UNKNOWN_STATUS); 510 return false; 511 } 512 513 // See what products are already installed in multi mode. When we do multi 514 // installs, we must upgrade all installations since they share the binaries. 515 AddExistingMultiInstalls(original_state, installer_state); 516 517 if (!CheckMultiInstallConditions(original_state, installer_state, status)) { 518 DCHECK_NE(*status, installer::UNKNOWN_STATUS); 519 return false; 520 } 521 522 const Products& products = installer_state->products(); 523 if (products.empty()) { 524 // We haven't been given any products on which to operate. 525 LOG(ERROR) 526 << "Not given any products to install and no products found to update."; 527 *status = installer::CHROME_NOT_INSTALLED; 528 installer_state->WriteInstallerResult(*status, 529 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL); 530 return false; 531 } 532 533 if (!installer_state->system_install()) { 534 // This is a user-level installation. Make sure that we are not installing 535 // on top of an existing system-level installation. 536 for (Products::const_iterator it = products.begin(); it < products.end(); 537 ++it) { 538 const Product& product = **it; 539 BrowserDistribution* browser_dist = product.distribution(); 540 541 // Skip over the binaries, as it's okay for them to be at both levels 542 // for different products. 543 if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES) 544 continue; 545 546 const ProductState* user_level_product_state = 547 original_state.GetProductState(false, browser_dist->GetType()); 548 const ProductState* system_level_product_state = 549 original_state.GetProductState(true, browser_dist->GetType()); 550 551 // Allow upgrades to proceed so that out-of-date versions are not left 552 // around. 553 if (user_level_product_state) 554 continue; 555 556 // This is a new user-level install... 557 558 if (system_level_product_state) { 559 // ... and the product already exists at system-level. 560 LOG(ERROR) << "Already installed version " 561 << system_level_product_state->version().GetString() 562 << " at system-level conflicts with this one at user-level."; 563 if (product.is_chrome()) { 564 // Instruct Google Update to launch the existing system-level Chrome. 565 // There should be no error dialog. 566 base::FilePath install_path(installer::GetChromeInstallPath( 567 true, // system 568 browser_dist)); 569 if (install_path.empty()) { 570 // Give up if we failed to construct the install path. 571 *status = installer::OS_ERROR; 572 installer_state->WriteInstallerResult(*status, 573 IDS_INSTALL_OS_ERROR_BASE, 574 NULL); 575 } else { 576 *status = installer::EXISTING_VERSION_LAUNCHED; 577 base::FilePath chrome_exe = 578 install_path.Append(installer::kChromeExe); 579 CommandLine cmd(chrome_exe); 580 cmd.AppendSwitch(switches::kForceFirstRun); 581 installer_state->WriteInstallerResult(*status, 0, NULL); 582 VLOG(1) << "Launching existing system-level chrome instead."; 583 base::LaunchProcess(cmd, base::LaunchOptions(), NULL); 584 } 585 } else { 586 // It's no longer possible for |product| to be anything other than 587 // Chrome. 588 NOTREACHED(); 589 } 590 return false; 591 } 592 } 593 594 } else { // System-level install. 595 // --ensure-google-update-present is supported for user-level only. 596 // The flag is generic, but its primary use case involves App Host. 597 if (installer_state->ensure_google_update_present()) { 598 LOG(DFATAL) << "--" << installer::switches::kEnsureGoogleUpdatePresent 599 << " is supported for user-level only."; 600 *status = installer::APP_HOST_REQUIRES_USER_LEVEL; 601 // No message string since there is nothing a user can do. 602 installer_state->WriteInstallerResult(*status, 0, NULL); 603 return false; 604 } 605 } 606 607 return true; 608} 609 610// Initializes |temp_path| to "Temp" within the target directory, and 611// |unpack_path| to a random directory beginning with "source" within 612// |temp_path|. Returns false on error. 613bool CreateTemporaryAndUnpackDirectories( 614 const InstallerState& installer_state, 615 installer::SelfCleaningTempDir* temp_path, 616 base::FilePath* unpack_path) { 617 DCHECK(temp_path && unpack_path); 618 619 if (!temp_path->Initialize(installer_state.target_path().DirName(), 620 installer::kInstallTempDir)) { 621 PLOG(ERROR) << "Could not create temporary path."; 622 return false; 623 } 624 VLOG(1) << "Created path " << temp_path->path().value(); 625 626 if (!base::CreateTemporaryDirInDir(temp_path->path(), 627 installer::kInstallSourceDir, 628 unpack_path)) { 629 PLOG(ERROR) << "Could not create temporary path for unpacked archive."; 630 return false; 631 } 632 633 return true; 634} 635 636installer::InstallStatus UninstallProduct( 637 const InstallationState& original_state, 638 const InstallerState& installer_state, 639 const base::FilePath& setup_exe, 640 const CommandLine& cmd_line, 641 bool remove_all, 642 bool force_uninstall, 643 const Product& product) { 644 const ProductState* product_state = 645 original_state.GetProductState(installer_state.system_install(), 646 product.distribution()->GetType()); 647 if (product_state != NULL) { 648 VLOG(1) << "version on the system: " 649 << product_state->version().GetString(); 650 } else if (!force_uninstall) { 651 LOG(ERROR) << product.distribution()->GetDisplayName() 652 << " not found for uninstall."; 653 return installer::CHROME_NOT_INSTALLED; 654 } 655 656 return installer::UninstallProduct( 657 original_state, installer_state, setup_exe, product, remove_all, 658 force_uninstall, cmd_line); 659} 660 661installer::InstallStatus UninstallProducts( 662 const InstallationState& original_state, 663 const InstallerState& installer_state, 664 const base::FilePath& setup_exe, 665 const CommandLine& cmd_line) { 666 const Products& products = installer_state.products(); 667 668 // System-level Chrome will be launched via this command if its program gets 669 // set below. 670 CommandLine system_level_cmd(CommandLine::NO_PROGRAM); 671 672 const Product* chrome = 673 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); 674 if (chrome) { 675 // InstallerState::Initialize always puts Chrome first, and we rely on that 676 // here for this reason: if Chrome is in-use, the user will be prompted to 677 // confirm uninstallation. Upon cancel, we should not continue with the 678 // other products. 679 DCHECK(products[0]->is_chrome()); 680 681 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) && 682 !installer_state.system_install()) { 683 BrowserDistribution* dist = chrome->distribution(); 684 const base::FilePath system_exe_path( 685 installer::GetChromeInstallPath(true, dist) 686 .Append(installer::kChromeExe)); 687 system_level_cmd.SetProgram(system_exe_path); 688 } 689 } 690 if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) { 691 // Chrome Binaries should be last; if something else is cancelled, they 692 // should stay. 693 DCHECK(products[products.size() - 1]->is_chrome_binaries()); 694 } 695 696 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL; 697 installer::InstallStatus prod_status = installer::UNKNOWN_STATUS; 698 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall); 699 const bool remove_all = !cmd_line.HasSwitch( 700 installer::switches::kDoNotRemoveSharedItems); 701 702 for (Products::const_iterator it = products.begin(); 703 install_status != installer::UNINSTALL_CANCELLED && it < products.end(); 704 ++it) { 705 prod_status = UninstallProduct(original_state, installer_state, setup_exe, 706 cmd_line, remove_all, force, **it); 707 if (prod_status != installer::UNINSTALL_SUCCESSFUL) 708 install_status = prod_status; 709 } 710 711 installer::CleanUpInstallationDirectoryAfterUninstall( 712 original_state, installer_state, setup_exe, &install_status); 713 714 // The app and vendor dirs may now be empty. Make a last-ditch attempt to 715 // delete them. 716 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path()); 717 718 // Trigger Active Setup if it was requested for the chrome product. This needs 719 // to be done after the UninstallProduct calls as some of them might 720 // otherwise terminate the process launched by TriggerActiveSetupCommand(). 721 if (chrome && cmd_line.HasSwitch(installer::switches::kTriggerActiveSetup)) 722 InstallUtil::TriggerActiveSetupCommand(); 723 724 if (!system_level_cmd.GetProgram().empty()) 725 base::LaunchProcess(system_level_cmd, base::LaunchOptions(), NULL); 726 727 // Tell Google Update that an uninstall has taken place. 728 // Ignore the return value: success or failure of Google Update 729 // has no bearing on the success or failure of Chrome's uninstallation. 730 google_update::UninstallGoogleUpdate(installer_state.system_install()); 731 732 return install_status; 733} 734 735// Uninstall the binaries if they are the only product present and they're not 736// in-use. 737void UninstallBinariesIfUnused( 738 const InstallationState& original_state, 739 const InstallerState& installer_state, 740 installer::InstallStatus* install_status) { 741 // Early exit if the binaries are still in use. 742 if (*install_status != installer::UNUSED_BINARIES || 743 installer_state.AreBinariesInUse(original_state)) { 744 return; 745 } 746 747 LOG(INFO) << "Uninstalling unused binaries"; 748 installer_state.UpdateStage(installer::UNINSTALLING_BINARIES); 749 750 // Simulate the uninstall as coming from the installed version. 751 const ProductState* binaries_state = 752 original_state.GetProductState(installer_state.system_install(), 753 BrowserDistribution::CHROME_BINARIES); 754 const CommandLine& uninstall_cmd(binaries_state->uninstall_command()); 755 MasterPreferences uninstall_prefs(uninstall_cmd); 756 InstallerState uninstall_state; 757 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, original_state); 758 759 *install_status = UninstallProducts(original_state, uninstall_state, 760 uninstall_cmd.GetProgram(), 761 uninstall_cmd); 762 763 // Report that the binaries were uninstalled if they were. This translates 764 // into a successful install return code. 765 if (IsUninstallSuccess(*install_status)) { 766 *install_status = installer::UNUSED_BINARIES_UNINSTALLED; 767 installer_state.WriteInstallerResult(*install_status, 0, NULL); 768 } 769} 770 771installer::InstallStatus InstallProducts( 772 const InstallationState& original_state, 773 const base::FilePath& setup_exe, 774 const CommandLine& cmd_line, 775 const MasterPreferences& prefs, 776 InstallerState* installer_state, 777 base::FilePath* installer_directory) { 778 DCHECK(installer_state); 779 const bool system_install = installer_state->system_install(); 780 installer::InstallStatus install_status = installer::UNKNOWN_STATUS; 781 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE; 782 bool delegated_to_existing = false; 783 installer_state->UpdateStage(installer::PRECONDITIONS); 784 // The stage provides more fine-grained information than -multifail, so remove 785 // the -multifail suffix from the Google Update "ap" value. 786 BrowserDistribution::GetSpecificDistribution(installer_state->state_type())-> 787 UpdateInstallStatus(system_install, archive_type, install_status); 788 if (CheckPreInstallConditions(original_state, installer_state, 789 &install_status)) { 790 VLOG(1) << "Installing to " << installer_state->target_path().value(); 791 install_status = InstallProductsHelper( 792 original_state, setup_exe, cmd_line, prefs, *installer_state, 793 installer_directory, &archive_type, &delegated_to_existing); 794 } else { 795 // CheckPreInstallConditions must set the status on failure. 796 DCHECK_NE(install_status, installer::UNKNOWN_STATUS); 797 } 798 799 // Delete the master preferences file if present. Note that we do not care 800 // about rollback here and we schedule for deletion on reboot if the delete 801 // fails. As such, we do not use DeleteTreeWorkItem. 802 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) { 803 base::FilePath prefs_path(cmd_line.GetSwitchValuePath( 804 installer::switches::kInstallerData)); 805 if (!base::DeleteFile(prefs_path, false)) { 806 LOG(ERROR) << "Failed deleting master preferences file " 807 << prefs_path.value() 808 << ", scheduling for deletion after reboot."; 809 ScheduleFileSystemEntityForDeletion(prefs_path); 810 } 811 } 812 813 // Early exit if this setup.exe delegated to another, since that one would 814 // have taken care of UpdateInstallStatus and UpdateStage. 815 if (delegated_to_existing) 816 return install_status; 817 818 const Products& products = installer_state->products(); 819 for (Products::const_iterator it = products.begin(); it < products.end(); 820 ++it) { 821 (*it)->distribution()->UpdateInstallStatus( 822 system_install, archive_type, install_status); 823 } 824 825 UninstallBinariesIfUnused(original_state, *installer_state, &install_status); 826 827 installer_state->UpdateStage(installer::NO_STAGE); 828 return install_status; 829} 830 831installer::InstallStatus ShowEULADialog(const base::string16& inner_frame) { 832 VLOG(1) << "About to show EULA"; 833 base::string16 eula_path = installer::GetLocalizedEulaResource(); 834 if (eula_path.empty()) { 835 LOG(ERROR) << "No EULA path available"; 836 return installer::EULA_REJECTED; 837 } 838 // Newer versions of the caller pass an inner frame parameter that must 839 // be given to the html page being launched. 840 installer::EulaHTMLDialog dlg(eula_path, inner_frame); 841 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal(); 842 if (installer::EulaHTMLDialog::REJECTED == outcome) { 843 LOG(ERROR) << "EULA rejected or EULA failure"; 844 return installer::EULA_REJECTED; 845 } 846 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) { 847 VLOG(1) << "EULA accepted (opt-in)"; 848 return installer::EULA_ACCEPTED_OPT_IN; 849 } 850 VLOG(1) << "EULA accepted (no opt-in)"; 851 return installer::EULA_ACCEPTED; 852} 853 854// Creates the sentinel indicating that the EULA was required and has been 855// accepted. 856bool CreateEULASentinel(BrowserDistribution* dist) { 857 base::FilePath eula_sentinel; 858 if (!InstallUtil::GetSentinelFilePath(installer::kEULASentinelFile, dist, 859 &eula_sentinel)) { 860 return false; 861 } 862 863 return (base::CreateDirectory(eula_sentinel.DirName()) && 864 base::WriteFile(eula_sentinel, "", 0) != -1); 865} 866 867void ActivateMetroChrome() { 868 // Check to see if we're per-user or not. Need to do this since we may 869 // not have been invoked with --system-level even for a machine install. 870 wchar_t exe_path[MAX_PATH * 2] = {}; 871 GetModuleFileName(NULL, exe_path, arraysize(exe_path)); 872 bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path); 873 874 base::string16 app_model_id = ShellUtil::GetBrowserModelId( 875 BrowserDistribution::GetDistribution(), is_per_user_install); 876 877 base::win::ScopedComPtr<IApplicationActivationManager> activator; 878 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager); 879 if (SUCCEEDED(hr)) { 880 DWORD pid = 0; 881 hr = activator->ActivateApplication( 882 app_model_id.c_str(), L"open", AO_NONE, &pid); 883 } 884 885 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. " 886 << "hr=" << std::hex << hr; 887} 888 889installer::InstallStatus RegisterDevChrome( 890 const InstallationState& original_state, 891 const InstallerState& installer_state, 892 const base::FilePath& setup_exe, 893 const CommandLine& cmd_line) { 894 BrowserDistribution* chrome_dist = 895 BrowserDistribution::GetSpecificDistribution( 896 BrowserDistribution::CHROME_BROWSER); 897 898 // Only proceed with registering a dev chrome if no real Chrome installation 899 // of the same distribution are present on this system. 900 const ProductState* existing_chrome = 901 original_state.GetProductState(false, 902 BrowserDistribution::CHROME_BROWSER); 903 if (!existing_chrome) { 904 existing_chrome = 905 original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER); 906 } 907 if (existing_chrome) { 908 static const wchar_t kPleaseUninstallYourChromeMessage[] = 909 L"You already have a full-installation (non-dev) of %1ls, please " 910 L"uninstall it first using Add/Remove Programs in the control panel."; 911 base::string16 name(chrome_dist->GetDisplayName()); 912 base::string16 message( 913 base::StringPrintf(kPleaseUninstallYourChromeMessage, name.c_str())); 914 915 LOG(ERROR) << "Aborting operation: another installation of " << name 916 << " was found, as a last resort (if the product is not present " 917 "in Add/Remove Programs), try executing: " 918 << existing_chrome->uninstall_command().GetCommandLineString(); 919 MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR); 920 return installer::INSTALL_FAILED; 921 } 922 923 base::FilePath chrome_exe( 924 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome)); 925 if (chrome_exe.empty()) 926 chrome_exe = setup_exe.DirName().Append(installer::kChromeExe); 927 if (!chrome_exe.IsAbsolute()) 928 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe); 929 930 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS; 931 if (base::PathExists(chrome_exe)) { 932 Product chrome(chrome_dist); 933 934 // Create the Start menu shortcut and pin it to the taskbar. 935 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER); 936 chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties); 937 shortcut_properties.set_dual_mode(true); 938 shortcut_properties.set_pin_to_taskbar(true); 939 ShellUtil::CreateOrUpdateShortcut( 940 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, chrome_dist, 941 shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS); 942 943 // Register Chrome at user-level and make it default. 944 scoped_ptr<WorkItemList> delegate_execute_list( 945 WorkItem::CreateWorkItemList()); 946 installer::AddDelegateExecuteWorkItems( 947 installer_state, chrome_exe.DirName(), Version(), chrome, 948 delegate_execute_list.get()); 949 delegate_execute_list->Do(); 950 if (ShellUtil::CanMakeChromeDefaultUnattended()) { 951 ShellUtil::MakeChromeDefault( 952 chrome_dist, ShellUtil::CURRENT_USER, chrome_exe.value(), true); 953 } else { 954 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe.value()); 955 } 956 } else { 957 LOG(ERROR) << "Path not found: " << chrome_exe.value(); 958 status = installer::INSTALL_FAILED; 959 } 960 return status; 961} 962 963// This method processes any command line options that make setup.exe do 964// various tasks other than installation (renaming chrome.exe, showing eula 965// among others). This function returns true if any such command line option 966// has been found and processed (so setup.exe should exit at that point). 967bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, 968 const base::FilePath& setup_exe, 969 const CommandLine& cmd_line, 970 InstallerState* installer_state, 971 int* exit_code) { 972 // TODO(gab): Add a local |status| variable which each block below sets; 973 // only determine the |exit_code| from |status| at the end (this will allow 974 // this method to validate that 975 // (!handled || status != installer::UNKNOWN_STATUS)). 976 bool handled = true; 977 // TODO(tommi): Split these checks up into functions and use a data driven 978 // map of switch->function. 979 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) { 980 installer::InstallStatus status = installer::SETUP_PATCH_FAILED; 981 // If --update-setup-exe command line option is given, we apply the given 982 // patch to current exe, and store the resulting binary in the path 983 // specified by --new-setup-exe. But we need to first unpack the file 984 // given in --update-setup-exe. 985 base::ScopedTempDir temp_path; 986 if (!temp_path.CreateUniqueTempDir()) { 987 PLOG(ERROR) << "Could not create temporary path."; 988 } else { 989 base::FilePath compressed_archive(cmd_line.GetSwitchValuePath( 990 installer::switches::kUpdateSetupExe)); 991 VLOG(1) << "Opening archive " << compressed_archive.value(); 992 if (installer::ArchivePatchHelper::UncompressAndPatch( 993 temp_path.path(), 994 compressed_archive, 995 setup_exe, 996 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) { 997 status = installer::NEW_VERSION_UPDATED; 998 } 999 if (!temp_path.Delete()) { 1000 // PLOG would be nice, but Delete() doesn't leave a meaningful value in 1001 // the Windows last-error code. 1002 LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value() 1003 << " for deletion at reboot."; 1004 ScheduleDirectoryForDeletion(temp_path.path()); 1005 } 1006 } 1007 1008 *exit_code = InstallUtil::GetInstallReturnCode(status); 1009 if (*exit_code) { 1010 LOG(WARNING) << "setup.exe patching failed."; 1011 installer_state->WriteInstallerResult( 1012 status, IDS_SETUP_PATCH_FAILED_BASE, NULL); 1013 } 1014 // We will be exiting normally, so clear the stage indicator. 1015 installer_state->UpdateStage(installer::NO_STAGE); 1016 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) { 1017 // Check if we need to show the EULA. If it is passed as a command line 1018 // then the dialog is shown and regardless of the outcome setup exits here. 1019 base::string16 inner_frame = 1020 cmd_line.GetSwitchValueNative(installer::switches::kShowEula); 1021 *exit_code = ShowEULADialog(inner_frame); 1022 1023 if (installer::EULA_REJECTED != *exit_code) { 1024 if (GoogleUpdateSettings::SetEULAConsent( 1025 original_state, BrowserDistribution::GetDistribution(), true)) { 1026 CreateEULASentinel(BrowserDistribution::GetDistribution()); 1027 } 1028 // For a metro-originated launch, we now need to launch back into metro. 1029 if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro)) 1030 ActivateMetroChrome(); 1031 } 1032 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) { 1033 // NOTE: Should the work done here, on kConfigureUserSettings, change: 1034 // kActiveSetupVersion in install_worker.cc needs to be increased for Active 1035 // Setup to invoke this again for all users of this install. 1036 const Product* chrome_install = 1037 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1038 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION; 1039 if (chrome_install && installer_state->system_install()) { 1040 bool force = 1041 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings); 1042 installer::HandleActiveSetupForBrowser(installer_state->target_path(), 1043 *chrome_install, force); 1044 status = installer::INSTALL_REPAIRED; 1045 } else { 1046 LOG(DFATAL) << "chrome_install:" << chrome_install 1047 << ", system_install:" << installer_state->system_install(); 1048 } 1049 *exit_code = InstallUtil::GetInstallReturnCode(status); 1050 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) { 1051 installer::InstallStatus status = RegisterDevChrome( 1052 original_state, *installer_state, setup_exe, cmd_line); 1053 *exit_code = InstallUtil::GetInstallReturnCode(status); 1054 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) { 1055 installer::InstallStatus status = installer::UNKNOWN_STATUS; 1056 const Product* chrome_install = 1057 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1058 if (chrome_install) { 1059 // If --register-chrome-browser option is specified, register all 1060 // Chrome protocol/file associations, as well as register it as a valid 1061 // browser for Start Menu->Internet shortcut. This switch will also 1062 // register Chrome as a valid handler for a set of URL protocols that 1063 // Chrome may become the default handler for, either by the user marking 1064 // Chrome as the default browser, through the Windows Default Programs 1065 // control panel settings, or through website use of 1066 // registerProtocolHandler. These protocols are found in 1067 // ShellUtil::kPotentialProtocolAssociations. 1068 // The --register-url-protocol will additionally register Chrome as a 1069 // potential handler for the supplied protocol, and is used if a website 1070 // registers a handler for a protocol not found in 1071 // ShellUtil::kPotentialProtocolAssociations. 1072 // These options should only be used when setup.exe is launched with admin 1073 // rights. We do not make any user specific changes with this option. 1074 DCHECK(IsUserAnAdmin()); 1075 base::string16 chrome_exe(cmd_line.GetSwitchValueNative( 1076 installer::switches::kRegisterChromeBrowser)); 1077 base::string16 suffix; 1078 if (cmd_line.HasSwitch( 1079 installer::switches::kRegisterChromeBrowserSuffix)) { 1080 suffix = cmd_line.GetSwitchValueNative( 1081 installer::switches::kRegisterChromeBrowserSuffix); 1082 } 1083 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) { 1084 base::string16 protocol = cmd_line.GetSwitchValueNative( 1085 installer::switches::kRegisterURLProtocol); 1086 // ShellUtil::RegisterChromeForProtocol performs all registration 1087 // done by ShellUtil::RegisterChromeBrowser, as well as registering 1088 // with Windows as capable of handling the supplied protocol. 1089 if (ShellUtil::RegisterChromeForProtocol( 1090 chrome_install->distribution(), chrome_exe, suffix, protocol, 1091 false)) 1092 status = installer::IN_USE_UPDATED; 1093 } else { 1094 if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(), 1095 chrome_exe, suffix, false)) 1096 status = installer::IN_USE_UPDATED; 1097 } 1098 } else { 1099 LOG(DFATAL) << "Can't register browser - Chrome distribution not found"; 1100 } 1101 *exit_code = InstallUtil::GetInstallReturnCode(status); 1102 } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) { 1103 // If --rename-chrome-exe is specified, we want to rename the executables 1104 // and exit. 1105 *exit_code = RenameChromeExecutables(original_state, installer_state); 1106 } else if (cmd_line.HasSwitch( 1107 installer::switches::kRemoveChromeRegistration)) { 1108 // This is almost reverse of --register-chrome-browser option above. 1109 // Here we delete Chrome browser registration. This option should only 1110 // be used when setup.exe is launched with admin rights. We do not 1111 // make any user specific changes in this option. 1112 base::string16 suffix; 1113 if (cmd_line.HasSwitch( 1114 installer::switches::kRegisterChromeBrowserSuffix)) { 1115 suffix = cmd_line.GetSwitchValueNative( 1116 installer::switches::kRegisterChromeBrowserSuffix); 1117 } 1118 installer::InstallStatus tmp = installer::UNKNOWN_STATUS; 1119 const Product* chrome_install = 1120 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1121 DCHECK(chrome_install); 1122 if (chrome_install) { 1123 installer::DeleteChromeRegistrationKeys(*installer_state, 1124 chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp); 1125 } 1126 *exit_code = tmp; 1127 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) { 1128 const Product* chrome_install = 1129 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); 1130 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION; 1131 if (chrome_install) { 1132 installer::HandleOsUpgradeForBrowser(*installer_state, 1133 *chrome_install); 1134 status = installer::INSTALL_REPAIRED; 1135 } else { 1136 LOG(DFATAL) << "Chrome product not found."; 1137 } 1138 *exit_code = InstallUtil::GetInstallReturnCode(status); 1139 } else if (cmd_line.HasSwitch(installer::switches::kQueryEULAAcceptance)) { 1140 *exit_code = installer::IsEULAAccepted(installer_state->system_install()); 1141 } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) { 1142 // Launch the inactive user toast experiment. 1143 int flavor = -1; 1144 base::StringToInt(cmd_line.GetSwitchValueNative( 1145 installer::switches::kInactiveUserToast), &flavor); 1146 std::string experiment_group = 1147 cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup); 1148 DCHECK_NE(-1, flavor); 1149 if (flavor == -1) { 1150 *exit_code = installer::UNKNOWN_STATUS; 1151 } else { 1152 // This code is called (via setup.exe relaunch) only if a product is known 1153 // to run user experiments, so no check is required. 1154 const Products& products = installer_state->products(); 1155 for (Products::const_iterator it = products.begin(); it < products.end(); 1156 ++it) { 1157 const Product& product = **it; 1158 installer::InactiveUserToastExperiment( 1159 flavor, base::ASCIIToUTF16(experiment_group), product, 1160 installer_state->target_path()); 1161 } 1162 } 1163 } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { 1164 const Products& products = installer_state->products(); 1165 for (Products::const_iterator it = products.begin(); it < products.end(); 1166 ++it) { 1167 const Product& product = **it; 1168 BrowserDistribution* browser_dist = product.distribution(); 1169 // We started as system-level and have been re-launched as user level 1170 // to continue with the toast experiment. 1171 Version installed_version; 1172 InstallUtil::GetChromeVersion(browser_dist, true, &installed_version); 1173 if (!installed_version.IsValid()) { 1174 LOG(ERROR) << "No installation of " 1175 << browser_dist->GetDisplayName() 1176 << " found for system-level toast."; 1177 } else { 1178 product.LaunchUserExperiment( 1179 setup_exe, installer::REENTRY_SYS_UPDATE, true); 1180 } 1181 } 1182 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) { 1183 const std::string patch_type_str( 1184 cmd_line.GetSwitchValueASCII(installer::switches::kPatch)); 1185 const base::FilePath input_file( 1186 cmd_line.GetSwitchValuePath(installer::switches::kInputFile)); 1187 const base::FilePath patch_file( 1188 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile)); 1189 const base::FilePath output_file( 1190 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile)); 1191 1192 if (patch_type_str == installer::kCourgette) { 1193 *exit_code = installer::CourgettePatchFiles(input_file, 1194 patch_file, 1195 output_file); 1196 } else if (patch_type_str == installer::kBsdiff) { 1197 *exit_code = installer::BsdiffPatchFiles(input_file, 1198 patch_file, 1199 output_file); 1200 } else { 1201 *exit_code = installer::PATCH_INVALID_ARGUMENTS; 1202 } 1203 } else if (cmd_line.HasSwitch(installer::switches::kReenableAutoupdates)) { 1204 // setup.exe has been asked to attempt to reenable updates for Chrome. 1205 // Figure out whether we should do so for the multi binaries or the main 1206 // Chrome product. 1207 BrowserDistribution::Type dist_type = BrowserDistribution::CHROME_BROWSER; 1208 if (installer_state->is_multi_install()) 1209 dist_type = BrowserDistribution::CHROME_BINARIES; 1210 1211 BrowserDistribution* dist = 1212 BrowserDistribution::GetSpecificDistribution(dist_type); 1213 bool updates_enabled = 1214 GoogleUpdateSettings::ReenableAutoupdatesForApp(dist->GetAppGuid()); 1215 *exit_code = updates_enabled ? installer::REENABLE_UPDATES_SUCCEEDED : 1216 installer::REENABLE_UPDATES_FAILED; 1217 } else { 1218 handled = false; 1219 } 1220 1221 return handled; 1222} 1223 1224bool ShowRebootDialog() { 1225 // Get a token for this process. 1226 HANDLE token; 1227 if (!OpenProcessToken(GetCurrentProcess(), 1228 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 1229 &token)) { 1230 LOG(ERROR) << "Failed to open token."; 1231 return false; 1232 } 1233 1234 // Use a ScopedHandle to keep track of and eventually close our handle. 1235 // TODO(robertshield): Add a Receive() method to base's ScopedHandle. 1236 base::win::ScopedHandle scoped_handle(token); 1237 1238 // Get the LUID for the shutdown privilege. 1239 TOKEN_PRIVILEGES tkp = {0}; 1240 LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); 1241 tkp.PrivilegeCount = 1; 1242 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1243 1244 // Get the shutdown privilege for this process. 1245 AdjustTokenPrivileges(token, FALSE, &tkp, 0, 1246 reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0); 1247 if (GetLastError() != ERROR_SUCCESS) { 1248 LOG(ERROR) << "Unable to get shutdown privileges."; 1249 return false; 1250 } 1251 1252 // Popup a dialog that will prompt to reboot using the default system message. 1253 // TODO(robertshield): Add a localized, more specific string to the prompt. 1254 RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG); 1255 return true; 1256} 1257 1258// Returns the Custom information for the client identified by the exe path 1259// passed in. This information is used for crash reporting. 1260google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) { 1261 base::string16 product; 1262 base::string16 version; 1263 scoped_ptr<FileVersionInfo> version_info( 1264 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path))); 1265 if (version_info.get()) { 1266 version = version_info->product_version(); 1267 product = version_info->product_short_name(); 1268 } 1269 1270 if (version.empty()) 1271 version = L"0.1.0.0"; 1272 1273 if (product.empty()) 1274 product = L"Chrome Installer"; 1275 1276 static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str()); 1277 static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str()); 1278 static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32"); 1279 static google_breakpad::CustomInfoEntry type_entry(L"ptype", 1280 L"Chrome Installer"); 1281 static google_breakpad::CustomInfoEntry entries[] = { 1282 ver_entry, prod_entry, plat_entry, type_entry }; 1283 static google_breakpad::CustomClientInfo custom_info = { 1284 entries, arraysize(entries) }; 1285 return &custom_info; 1286} 1287 1288// Initialize crash reporting for this process. This involves connecting to 1289// breakpad, etc. 1290scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting( 1291 bool system_install) { 1292 // Only report crashes if the user allows it. 1293 if (!GoogleUpdateSettings::GetCollectStatsConsent()) 1294 return scoped_ptr<google_breakpad::ExceptionHandler>(); 1295 1296 // Get the alternate dump directory. We use the temp path. 1297 base::FilePath temp_directory; 1298 if (!base::GetTempDir(&temp_directory) || temp_directory.empty()) 1299 return scoped_ptr<google_breakpad::ExceptionHandler>(); 1300 1301 wchar_t exe_path[MAX_PATH * 2] = {0}; 1302 GetModuleFileName(NULL, exe_path, arraysize(exe_path)); 1303 1304 // Build the pipe name. It can be either: 1305 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" 1306 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" 1307 base::string16 user_sid = kSystemPrincipalSid; 1308 1309 if (!system_install) { 1310 if (!base::win::GetUserSidString(&user_sid)) { 1311 return scoped_ptr<google_breakpad::ExceptionHandler>(); 1312 } 1313 } 1314 1315 base::string16 pipe_name = kGoogleUpdatePipeName; 1316 pipe_name += user_sid; 1317 1318 return scoped_ptr<google_breakpad::ExceptionHandler>( 1319 new google_breakpad::ExceptionHandler( 1320 temp_directory.value(), NULL, NULL, NULL, 1321 google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType, 1322 pipe_name.c_str(), GetCustomInfo(exe_path))); 1323} 1324 1325// Uninstalls multi-install Chrome Frame if the current operation is a 1326// multi-install install or update. The operation is performed directly rather 1327// than delegated to the existing install since there is no facility in older 1328// versions of setup.exe to uninstall GCF without touching the binaries. The 1329// binaries will be uninstalled during later processing if they are not in-use 1330// (see UninstallBinariesIfUnused). |original_state| and |installer_state| are 1331// updated to reflect the state of the world following the operation. 1332void UninstallMultiChromeFrameIfPresent(const CommandLine& cmd_line, 1333 const MasterPreferences& prefs, 1334 InstallationState* original_state, 1335 InstallerState* installer_state) { 1336 // Early exit if not installing or updating multi-install product(s). 1337 if (installer_state->operation() != InstallerState::MULTI_INSTALL && 1338 installer_state->operation() != InstallerState::MULTI_UPDATE) { 1339 return; 1340 } 1341 1342 // Early exit if Chrome Frame is not present as multi-install. 1343 const ProductState* chrome_frame_state = 1344 original_state->GetProductState(installer_state->system_install(), 1345 BrowserDistribution::CHROME_FRAME); 1346 if (!chrome_frame_state || !chrome_frame_state->is_multi_install()) 1347 return; 1348 1349 LOG(INFO) << "Uninstalling multi-install Chrome Frame."; 1350 installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME); 1351 1352 // Uninstall Chrome Frame without touching the multi-install binaries. 1353 // Simulate the uninstall as coming from the installed version. 1354 const CommandLine& uninstall_cmd(chrome_frame_state->uninstall_command()); 1355 MasterPreferences uninstall_prefs(uninstall_cmd); 1356 InstallerState uninstall_state; 1357 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state); 1358 const Product* chrome_frame_product = uninstall_state.FindProduct( 1359 BrowserDistribution::CHROME_FRAME); 1360 if (chrome_frame_product) { 1361 // No shared state should be left behind. 1362 const bool remove_all = true; 1363 // Don't accept no for an answer. 1364 const bool force_uninstall = true; 1365 installer::InstallStatus uninstall_status = 1366 installer::UninstallProduct(*original_state, uninstall_state, 1367 uninstall_cmd.GetProgram(), 1368 *chrome_frame_product, remove_all, 1369 force_uninstall, cmd_line); 1370 1371 VLOG(1) << "Uninstallation of Chrome Frame returned status " 1372 << uninstall_status; 1373 } else { 1374 LOG(ERROR) << "Chrome Frame not found for uninstall."; 1375 } 1376 1377 // Refresh state for the continuation of the original install/update. 1378 original_state->Initialize(); 1379 installer_state->Initialize(cmd_line, prefs, *original_state); 1380} 1381 1382} // namespace 1383 1384namespace installer { 1385 1386InstallStatus InstallProductsHelper( 1387 const InstallationState& original_state, 1388 const base::FilePath& setup_exe, 1389 const CommandLine& cmd_line, 1390 const MasterPreferences& prefs, 1391 const InstallerState& installer_state, 1392 base::FilePath* installer_directory, 1393 ArchiveType* archive_type, 1394 bool* delegated_to_existing) { 1395 DCHECK(archive_type); 1396 DCHECK(delegated_to_existing); 1397 const bool system_install = installer_state.system_install(); 1398 InstallStatus install_status = UNKNOWN_STATUS; 1399 1400 // Drop to background processing mode if the process was started below the 1401 // normal process priority class. 1402 bool entered_background_mode = AdjustProcessPriority(); 1403 VLOG_IF(1, entered_background_mode) << "Entered background processing mode."; 1404 1405 // Create a temp folder where we will unpack Chrome archive. If it fails, 1406 // then we are doomed, so return immediately and no cleanup is required. 1407 SelfCleaningTempDir temp_path; 1408 base::FilePath unpack_path; 1409 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path, 1410 &unpack_path)) { 1411 installer_state.WriteInstallerResult(TEMP_DIR_FAILED, 1412 IDS_INSTALL_TEMP_DIR_FAILED_BASE, 1413 NULL); 1414 return TEMP_DIR_FAILED; 1415 } 1416 1417 // Uncompress and optionally patch the archive if an uncompressed archive was 1418 // not specified on the command line and a compressed archive is found. 1419 *archive_type = UNKNOWN_ARCHIVE_TYPE; 1420 base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath( 1421 switches::kUncompressedArchive)); 1422 if (uncompressed_archive.empty()) { 1423 scoped_ptr<ArchivePatchHelper> archive_helper( 1424 CreateChromeArchiveHelper(setup_exe, cmd_line, installer_state, 1425 unpack_path)); 1426 if (archive_helper) { 1427 VLOG(1) << "Installing Chrome from compressed archive " 1428 << archive_helper->compressed_archive().value(); 1429 if (!UncompressAndPatchChromeArchive(original_state, 1430 installer_state, 1431 archive_helper.get(), 1432 archive_type, 1433 &install_status)) { 1434 DCHECK_NE(install_status, UNKNOWN_STATUS); 1435 return install_status; 1436 } 1437 uncompressed_archive = archive_helper->target(); 1438 DCHECK(!uncompressed_archive.empty()); 1439 } 1440 } 1441 1442 // Check for an uncompressed archive alongside the current executable if one 1443 // was not given or generated. 1444 if (uncompressed_archive.empty()) 1445 uncompressed_archive = setup_exe.DirName().Append(kChromeArchive); 1446 1447 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) { 1448 // An archive was not uncompressed or patched above. 1449 if (uncompressed_archive.empty() || 1450 !base::PathExists(uncompressed_archive)) { 1451 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive."; 1452 installer_state.WriteInstallerResult( 1453 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); 1454 return INVALID_ARCHIVE; 1455 } 1456 *archive_type = FULL_ARCHIVE_TYPE; 1457 } 1458 1459 // Unpack the uncompressed archive. 1460 if (LzmaUtil::UnPackArchive(uncompressed_archive.value(), 1461 unpack_path.value(), 1462 NULL)) { 1463 installer_state.WriteInstallerResult( 1464 UNCOMPRESSION_FAILED, 1465 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, 1466 NULL); 1467 return UNCOMPRESSION_FAILED; 1468 } 1469 1470 VLOG(1) << "unpacked to " << unpack_path.value(); 1471 base::FilePath src_path( 1472 unpack_path.Append(kInstallSourceChromeDir)); 1473 scoped_ptr<Version> 1474 installer_version(GetMaxVersionFromArchiveDir(src_path)); 1475 if (!installer_version.get()) { 1476 LOG(ERROR) << "Did not find any valid version in installer."; 1477 install_status = INVALID_ARCHIVE; 1478 installer_state.WriteInstallerResult(install_status, 1479 IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); 1480 } else { 1481 VLOG(1) << "version to install: " << installer_version->GetString(); 1482 bool proceed_with_installation = true; 1483 1484 if (installer_state.operation() == InstallerState::MULTI_INSTALL) { 1485 // This is a new install of a multi-install product. Rather than give up 1486 // in case a higher version of the binaries (including a single-install 1487 // of Chrome, which can safely be migrated to multi-install by way of 1488 // CheckMultiInstallConditions) is already installed, delegate to the 1489 // installed setup.exe to install the product at hand. 1490 base::FilePath existing_setup_exe; 1491 if (GetExistingHigherInstaller(original_state, system_install, 1492 *installer_version, &existing_setup_exe)) { 1493 VLOG(1) << "Deferring to existing installer."; 1494 installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION); 1495 if (DeferToExistingInstall(existing_setup_exe, cmd_line, 1496 installer_state, temp_path.path(), 1497 &install_status)) { 1498 *delegated_to_existing = true; 1499 return install_status; 1500 } 1501 } 1502 } 1503 1504 uint32 higher_products = 0; 1505 COMPILE_ASSERT( 1506 sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES, 1507 too_many_distribution_types_); 1508 const Products& products = installer_state.products(); 1509 for (Products::const_iterator it = products.begin(); it < products.end(); 1510 ++it) { 1511 const Product& product = **it; 1512 const ProductState* product_state = 1513 original_state.GetProductState(system_install, 1514 product.distribution()->GetType()); 1515 if (product_state != NULL && 1516 (product_state->version().CompareTo(*installer_version) > 0)) { 1517 LOG(ERROR) << "Higher version of " 1518 << product.distribution()->GetDisplayName() 1519 << " is already installed."; 1520 higher_products |= (1 << product.distribution()->GetType()); 1521 } 1522 } 1523 1524 if (higher_products != 0) { 1525 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4, 1526 add_support_for_new_products_here_); 1527 const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER; 1528 const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST; 1529 int message_id = 0; 1530 1531 proceed_with_installation = false; 1532 install_status = HIGHER_VERSION_EXISTS; 1533 switch (higher_products) { 1534 case kBrowserBit: 1535 message_id = IDS_INSTALL_HIGHER_VERSION_BASE; 1536 break; 1537 default: 1538 message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE; 1539 break; 1540 } 1541 1542 installer_state.WriteInstallerResult(install_status, message_id, NULL); 1543 } 1544 1545 proceed_with_installation = 1546 proceed_with_installation && 1547 CheckGroupPolicySettings(original_state, installer_state, 1548 *installer_version, &install_status); 1549 1550 if (proceed_with_installation) { 1551 // If Google Update is absent at user-level, install it using the 1552 // Google Update installer from an existing system-level installation. 1553 // This is for quick-enable App Host install from a system-level 1554 // Chrome Binaries installation. 1555 if (!system_install && installer_state.ensure_google_update_present()) { 1556 if (!google_update::EnsureUserLevelGoogleUpdatePresent()) { 1557 LOG(ERROR) << "Failed to install Google Update"; 1558 proceed_with_installation = false; 1559 install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED; 1560 installer_state.WriteInstallerResult(install_status, 0, NULL); 1561 } 1562 } 1563 1564 } 1565 1566 if (proceed_with_installation) { 1567 base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative( 1568 switches::kInstallerData)); 1569 install_status = InstallOrUpdateProduct( 1570 original_state, installer_state, setup_exe, uncompressed_archive, 1571 temp_path.path(), src_path, prefs_source_path, prefs, 1572 *installer_version); 1573 1574 int install_msg_base = IDS_INSTALL_FAILED_BASE; 1575 base::string16 chrome_exe; 1576 base::string16 quoted_chrome_exe; 1577 if (install_status == SAME_VERSION_REPAIR_FAILED) { 1578 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE; 1579 } else if (install_status != INSTALL_FAILED) { 1580 if (installer_state.target_path().empty()) { 1581 // If we failed to construct install path, it means the OS call to 1582 // get %ProgramFiles% or %AppData% failed. Report this as failure. 1583 install_msg_base = IDS_INSTALL_OS_ERROR_BASE; 1584 install_status = OS_ERROR; 1585 } else { 1586 chrome_exe = installer_state.target_path().Append(kChromeExe).value(); 1587 quoted_chrome_exe = L"\"" + chrome_exe + L"\""; 1588 install_msg_base = 0; 1589 } 1590 } 1591 1592 installer_state.UpdateStage(FINISHING); 1593 1594 // Only do Chrome-specific stuff (like launching the browser) if 1595 // Chrome was specifically requested (rather than being upgraded as 1596 // part of a multi-install). 1597 const Product* chrome_install = prefs.install_chrome() ? 1598 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) : 1599 NULL; 1600 1601 bool do_not_register_for_update_launch = false; 1602 if (chrome_install) { 1603 prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch, 1604 &do_not_register_for_update_launch); 1605 } else { 1606 do_not_register_for_update_launch = true; // Never register. 1607 } 1608 1609 bool write_chrome_launch_string = 1610 (!do_not_register_for_update_launch && 1611 install_status != IN_USE_UPDATED); 1612 1613 installer_state.WriteInstallerResult(install_status, install_msg_base, 1614 write_chrome_launch_string ? "ed_chrome_exe : NULL); 1615 1616 if (install_status == FIRST_INSTALL_SUCCESS) { 1617 VLOG(1) << "First install successful."; 1618 if (chrome_install) { 1619 // We never want to launch Chrome in system level install mode. 1620 bool do_not_launch_chrome = false; 1621 prefs.GetBool(master_preferences::kDoNotLaunchChrome, 1622 &do_not_launch_chrome); 1623 if (!system_install && !do_not_launch_chrome) 1624 chrome_install->LaunchChrome(installer_state.target_path()); 1625 } 1626 } else if ((install_status == NEW_VERSION_UPDATED) || 1627 (install_status == IN_USE_UPDATED)) { 1628 const Product* chrome = installer_state.FindProduct( 1629 BrowserDistribution::CHROME_BROWSER); 1630 if (chrome != NULL) { 1631 DCHECK_NE(chrome_exe, base::string16()); 1632 RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe); 1633 } 1634 } 1635 1636 if (prefs.install_chrome_app_launcher() && 1637 InstallUtil::GetInstallReturnCode(install_status) == 0) { 1638 std::string webstore_item(google_update::GetUntrustedDataValue( 1639 kInstallFromWebstore)); 1640 if (!webstore_item.empty()) { 1641 bool success = InstallFromWebstore(webstore_item); 1642 VLOG_IF(1, !success) << "Failed to launch app installation."; 1643 } 1644 } 1645 } 1646 } 1647 1648 // There might be an experiment (for upgrade usually) that needs to happen. 1649 // An experiment's outcome can include chrome's uninstallation. If that is 1650 // the case we would not do that directly at this point but in another 1651 // instance of setup.exe 1652 // 1653 // There is another way to reach this same function if this is a system 1654 // level install. See HandleNonInstallCmdLineOptions(). 1655 { 1656 // If installation failed, use the path to the currently running setup. 1657 // If installation succeeded, use the path to setup in the installer dir. 1658 base::FilePath setup_path(setup_exe); 1659 if (InstallUtil::GetInstallReturnCode(install_status) == 0) { 1660 setup_path = installer_state.GetInstallerDirectory(*installer_version) 1661 .Append(setup_path.BaseName()); 1662 } 1663 const Products& products = installer_state.products(); 1664 for (Products::const_iterator it = products.begin(); it < products.end(); 1665 ++it) { 1666 const Product& product = **it; 1667 product.LaunchUserExperiment(setup_path, install_status, system_install); 1668 } 1669 } 1670 1671 // If installation completed successfully, return the path to the directory 1672 // containing the newly installed setup.exe and uncompressed archive if the 1673 // caller requested it. 1674 if (installer_directory && 1675 InstallUtil::GetInstallReturnCode(install_status) == 0) { 1676 *installer_directory = 1677 installer_state.GetInstallerDirectory(*installer_version); 1678 } 1679 1680 // temp_path's dtor will take care of deleting or scheduling itself for 1681 // deletion at reboot when this scope closes. 1682 VLOG(1) << "Deleting temporary directory " << temp_path.path().value(); 1683 1684 return install_status; 1685} 1686 1687} // namespace installer 1688 1689int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, 1690 wchar_t* command_line, int show_command) { 1691 // Check to see if the CPU is supported before doing anything else. There's 1692 // very little than can safely be accomplished if the CPU isn't supported 1693 // since dependent libraries (e.g., base) may use invalid instructions. 1694 if (!installer::IsProcessorSupported()) 1695 return installer::CPU_NOT_SUPPORTED; 1696 1697 // The exit manager is in charge of calling the dtors of singletons. 1698 base::AtExitManager exit_manager; 1699 CommandLine::Init(0, NULL); 1700 1701 // install_util uses chrome paths. 1702 chrome::RegisterPathProvider(); 1703 1704 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); 1705 installer::InitInstallerLogging(prefs); 1706 1707 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); 1708 VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString(); 1709 1710 VLOG(1) << "multi install is " << prefs.is_multi_install(); 1711 bool system_install = false; 1712 prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install); 1713 VLOG(1) << "system install is " << system_install; 1714 1715 scoped_ptr<google_breakpad::ExceptionHandler> breakpad( 1716 InitializeCrashReporting(system_install)); 1717 1718 InstallationState original_state; 1719 original_state.Initialize(); 1720 1721 InstallerState installer_state; 1722 installer_state.Initialize(cmd_line, prefs, original_state); 1723 const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall); 1724 1725 // Check to make sure current system is WinXP or later. If not, log 1726 // error message and get out. 1727 if (!InstallUtil::IsOSSupported()) { 1728 LOG(ERROR) << "Chrome only supports Windows XP or later."; 1729 installer_state.WriteInstallerResult( 1730 installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL); 1731 return installer::OS_NOT_SUPPORTED; 1732 } 1733 1734 // Initialize COM for use later. 1735 base::win::ScopedCOMInitializer com_initializer; 1736 if (!com_initializer.succeeded()) { 1737 installer_state.WriteInstallerResult( 1738 installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL); 1739 return installer::OS_ERROR; 1740 } 1741 1742 // Some command line options don't work with SxS install/uninstall 1743 if (InstallUtil::IsChromeSxSProcess()) { 1744 if (system_install || 1745 prefs.is_multi_install() || 1746 cmd_line.HasSwitch(installer::switches::kForceUninstall) || 1747 cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) || 1748 cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) || 1749 cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) || 1750 cmd_line.HasSwitch(installer::switches::kInactiveUserToast) || 1751 cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { 1752 return installer::SXS_OPTION_NOT_SUPPORTED; 1753 } 1754 } 1755 1756 // Some command line options are no longer supported and must error out. 1757 if (installer::ContainsUnsupportedSwitch(cmd_line)) 1758 return installer::UNSUPPORTED_OPTION; 1759 1760 // A variety of installer operations require the path to the current 1761 // executable. Get it once here for use throughout these operations. Note that 1762 // the path service is the authoritative source for this path. One might think 1763 // that CommandLine::GetProgram would suffice, but it won't since 1764 // CreateProcess may have been called with a command line that is somewhat 1765 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file 1766 // extension), in which case CommandLineToArgv will not yield an argv with the 1767 // true path to the program at position 0. 1768 base::FilePath setup_exe; 1769 if (!PathService::Get(base::FILE_EXE, &setup_exe)) { 1770 NOTREACHED(); 1771 return installer::OS_ERROR; 1772 } 1773 1774 int exit_code = 0; 1775 if (HandleNonInstallCmdLineOptions( 1776 original_state, setup_exe, cmd_line, &installer_state, &exit_code)) { 1777 return exit_code; 1778 } 1779 1780 if (system_install && !IsUserAnAdmin()) { 1781 if (base::win::GetVersion() >= base::win::VERSION_VISTA && 1782 !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) { 1783 CommandLine new_cmd(CommandLine::NO_PROGRAM); 1784 new_cmd.AppendArguments(cmd_line, true); 1785 // Append --run-as-admin flag to let the new instance of setup.exe know 1786 // that we already tried to launch ourselves as admin. 1787 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin); 1788 // If system_install became true due to an environment variable, append 1789 // it to the command line here since env vars may not propagate past the 1790 // elevation. 1791 if (!new_cmd.HasSwitch(installer::switches::kSystemLevel)) 1792 new_cmd.AppendSwitch(installer::switches::kSystemLevel); 1793 1794 DWORD exit_code = installer::UNKNOWN_STATUS; 1795 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code); 1796 return exit_code; 1797 } else { 1798 LOG(ERROR) << "Non admin user can not install system level Chrome."; 1799 installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS, 1800 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL); 1801 return installer::INSUFFICIENT_RIGHTS; 1802 } 1803 } 1804 1805 UninstallMultiChromeFrameIfPresent(cmd_line, prefs, 1806 &original_state, &installer_state); 1807 1808 base::FilePath installer_directory; 1809 installer::InstallStatus install_status = installer::UNKNOWN_STATUS; 1810 // If --uninstall option is given, uninstall the identified product(s) 1811 if (is_uninstall) { 1812 install_status = 1813 UninstallProducts(original_state, installer_state, setup_exe, cmd_line); 1814 } else { 1815 // If --uninstall option is not specified, we assume it is install case. 1816 install_status = 1817 InstallProducts(original_state, setup_exe, cmd_line, prefs, 1818 &installer_state, &installer_directory); 1819 } 1820 1821 // Validate that the machine is now in a good state following the operation. 1822 // TODO(grt): change this to log at DFATAL once we're convinced that the 1823 // validator handles all cases properly. 1824 InstallationValidator::InstallationType installation_type = 1825 InstallationValidator::NO_PRODUCTS; 1826 LOG_IF(ERROR, 1827 !InstallationValidator::ValidateInstallationType(system_install, 1828 &installation_type)); 1829 1830 int return_code = 0; 1831 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will 1832 // rollback the action. If we're uninstalling we want to avoid this, so always 1833 // report success, squashing any more informative return codes. 1834 if (!(installer_state.is_msi() && is_uninstall)) { 1835 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT 1836 // to pass through, since this is only returned on uninstall which is 1837 // never invoked directly by Google Update. 1838 return_code = InstallUtil::GetInstallReturnCode(install_status); 1839 } 1840 1841 VLOG(1) << "Installation complete, returning: " << return_code; 1842 1843 return return_code; 1844} 1845