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