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