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