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