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