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