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