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