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