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