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