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