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