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