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