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