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