setup_main.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Use of this source code is governed by a BSD-style license that can be
3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// found in the LICENSE file.
4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
5ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/setup_main.h"
6ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
7ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include <windows.h>
8ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include <msi.h>
9ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include <shellapi.h>
10ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include <shlobj.h>
11ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
12ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include <string>
13ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
14ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/at_exit.h"
15ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/basictypes.h"
16ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/command_line.h"
17ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/file_util.h"
18ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/file_version_info.h"
19ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/files/file_path.h"
20ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/files/scoped_temp_dir.h"
21ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/memory/scoped_ptr.h"
22ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/path_service.h"
23ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/process/launch.h"
24ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/strings/string16.h"
25ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/strings/string_number_conversions.h"
26ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/strings/string_util.h"
27ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/strings/stringprintf.h"
28ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/strings/utf_string_conversions.h"
29ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/values.h"
30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/registry.h"
31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/scoped_com_initializer.h"
32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/scoped_comptr.h"
33ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/scoped_handle.h"
34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/win_util.h"
35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "base/win/windows_version.h"
36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "breakpad/src/client/windows/handler/exception_handler.h"
37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/common/chrome_constants.h"
38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/common/chrome_paths.h"
39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/common/chrome_switches.h"
40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/archive_patch_helper.h"
41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/install.h"
42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/install_worker.h"
43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/setup_constants.h"
44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/setup_util.h"
45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/setup/uninstall.h"
46ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/browser_distribution.h"
47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/channel_info.h"
48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/delete_after_reboot_helper.h"
49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/delete_tree_work_item.h"
50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/eula_util.h"
51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/google_update_constants.h"
52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/google_update_settings.h"
53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/google_update_util.h"
54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/helper.h"
55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/html_dialog.h"
56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/install_util.h"
57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/installation_state.h"
58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/installation_validator.h"
59ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/installer_state.h"
60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/l10n_string_util.h"
61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/logging_installer.h"
62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/lzma_util.h"
63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/master_preferences.h"
64ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/master_preferences_constants.h"
65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/self_cleaning_temp_dir.h"
66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/shell_util.h"
67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "chrome/installer/util/user_experiment.h"
68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "installer_util_strings.h"  // NOLINT
70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::InstallerState;
72ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::InstallationState;
73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::InstallationValidator;
74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::MasterPreferences;
75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::Product;
76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::ProductState;
77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovusing installer::Products;
78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovconst wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovconst wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovconst wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovconst MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    MiniDumpWithProcessThreadData |  // Get PEB and TEB.
85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by stack.
87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovnamespace {
89ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Returns NULL if no compressed archive is available for processing, otherwise
91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// returns a patch helper configured to uncompress and patch.
92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovscoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const base::FilePath& setup_exe,
94ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const CommandLine& command_line,
95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const installer::InstallerState& installer_state,
96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const base::FilePath& working_directory) {
97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // A compressed archive is ordinarily given on the command line by the mini
98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // installer. If one was not given, look for chrome.packed.7z next to the
99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // running program.
100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::FilePath compressed_archive(
101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  bool compressed_archive_specified = !compressed_archive.empty();
103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!compressed_archive_specified) {
104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    compressed_archive = setup_exe.DirName().Append(
105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        installer::kChromeCompressedArchive);
106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Fail if no compressed archive is found.
109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!base::PathExists(compressed_archive)) {
110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (compressed_archive_specified) {
111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      LOG(ERROR) << installer::switches::kInstallArchive << "="
112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                 << compressed_archive.value() << " not found.";
113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return scoped_ptr<installer::ArchivePatchHelper>();
115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // chrome.7z is either extracted directly from the compressed archive into the
118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // working dir or is the target of patching in the working dir.
119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::FilePath target(working_directory.Append(installer::kChromeArchive));
120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  DCHECK(!base::PathExists(target));
121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Specify an empty path for the patch source since it isn't yet known that
123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // is.
125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return scoped_ptr<installer::ArchivePatchHelper>(
126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      new installer::ArchivePatchHelper(working_directory,
127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                        compressed_archive,
128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                        base::FilePath(),
129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                        target));
130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Workhorse for producing an uncompressed archive (chrome.7z) given a
133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// chrome.packed.7z containing either a patch file based on the version of
134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// chrome being updated or the full uncompressed archive. Returns true on
135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// success, in which case |archive_type| is populated based on what was found.
136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Returns false on failure, in which case |install_status| contains the error
137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// code and the result is written to the registry (via WriteInstallerResult).
138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovbool UncompressAndPatchChromeArchive(
139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const installer::InstallationState& original_state,
140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const installer::InstallerState& installer_state,
141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer::ArchivePatchHelper* archive_helper,
142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer::ArchiveType* archive_type,
143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer::InstallStatus* install_status) {
144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  installer_state.UpdateStage(installer::UNCOMPRESSING);
145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!archive_helper->Uncompress(NULL)) {
146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    *install_status = installer::UNCOMPRESSION_FAILED;
147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer_state.WriteInstallerResult(*install_status,
148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                         NULL);
150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return false;
151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Short-circuit if uncompression produced the uncompressed archive rather
154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // than a patch file.
155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (base::PathExists(archive_helper->target())) {
156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    *archive_type = installer::FULL_ARCHIVE_TYPE;
157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return true;
158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Find the installed version's archive to serve as the source for patching.
161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::FilePath patch_source(installer::FindArchiveToPatch(original_state,
162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                            installer_state));
163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (patch_source.empty()) {
164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    LOG(ERROR) << "Failed to find archive to patch.";
165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer_state.WriteInstallerResult(*install_status,
167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                         NULL);
169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return false;
170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  archive_helper->set_patch_source(patch_source);
172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Try courgette first. Failing that, try bspatch.
174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if ((installer_state.UpdateStage(installer::ENSEMBLE_PATCHING),
175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       !archive_helper->EnsemblePatch()) &&
176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      (installer_state.UpdateStage(installer::BINARY_PATCHING),
177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       !archive_helper->BinaryPatch())) {
178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    *install_status = installer::APPLY_DIFF_PATCH_FAILED;
179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer_state.WriteInstallerResult(*install_status,
180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                         NULL);
182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return false;
183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return true;
187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
188ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
189ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// In multi-install, adds all products to |installer_state| that are
190ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// multi-installed and must be updated along with the products already present
191ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// in |installer_state|.
192ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovvoid AddExistingMultiInstalls(const InstallationState& original_state,
193ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                              InstallerState* installer_state) {
194ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (installer_state->is_multi_install()) {
195ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
196ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      BrowserDistribution::Type type =
197ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          static_cast<BrowserDistribution::Type>(i);
198ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
199ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      if (!installer_state->FindProduct(type)) {
200ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        const ProductState* state =
201ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            original_state.GetProductState(installer_state->system_install(),
202ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                           type);
203ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if ((state != NULL) && state->is_multi_install()) {
204ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          installer_state->AddProductFromState(type, *state);
205ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          VLOG(1) << "Product already installed and must be included: "
206ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                  << BrowserDistribution::GetSpecificDistribution(type)->
207ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                         GetDisplayName();
208ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
209ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      }
210ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
211ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
212ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
213ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
214ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This function is called when --rename-chrome-exe option is specified on
215ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// setup.exe command line. This function assumes an in-use update has happened
216ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// for Chrome so there should be a file called new_chrome.exe on the file
217ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// system and a key called 'opv' in the registry. This function will move
218ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
219ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This function also deletes elevation policies associated with the old version
220ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// if they exist.
221ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovinstaller::InstallStatus RenameChromeExecutables(
222ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const InstallationState& original_state,
223ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    InstallerState* installer_state) {
224ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // See what products are already installed in multi mode.  When we do the
225ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // rename for multi installs, we must update all installations since they
226ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // share the binaries.
227ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  AddExistingMultiInstalls(original_state, installer_state);
228ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const base::FilePath &target_path = installer_state->target_path();
229ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
230ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
231ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));
232ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
233ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Create a temporary backup directory on the same volume as chrome.exe so
234ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // that moving in-use files doesn't lead to trouble.
235ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  installer::SelfCleaningTempDir temp_path;
236ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!temp_path.Initialize(target_path.DirName(),
237ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                            installer::kInstallTempDir)) {
238ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    PLOG(ERROR) << "Failed to create Temp directory "
239ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                << target_path.DirName()
240ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                       .Append(installer::kInstallTempDir).value();
241ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return installer::RENAME_FAILED;
242ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
243ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
244ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
245ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  install_list->AddMoveTreeWorkItem(chrome_exe.value(),
246ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                    chrome_old_exe.value(),
247ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                    temp_path.path().value(),
248ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                    WorkItem::ALWAYS_MOVE);
249ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  install_list->AddMoveTreeWorkItem(chrome_new_exe.value(),
250ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                    chrome_exe.value(),
251ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                    temp_path.path().value(),
252ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                    WorkItem::ALWAYS_MOVE);
253ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
254ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // old_chrome.exe is still in use in most cases, so ignore failures here.
255ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())->
256ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      set_ignore_failure(true);
257ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
258ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Add work items to delete the "opv", "cpv", and "cmd" values from all
259ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // products we're operating on (which including the multi-install binaries).
260ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const Products& products = installer_state->products();
261ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  HKEY reg_root = installer_state->root_key();
262ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  base::string16 version_key;
263ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  for (Products::const_iterator it = products.begin(); it < products.end();
264ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov       ++it) {
265ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    version_key = (*it)->distribution()->GetVersionKey();
266ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    install_list->AddDeleteRegValueWorkItem(reg_root,
267ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            version_key,
268ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            KEY_WOW64_32KEY,
269ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            google_update::kRegOldVersionField);
270ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    install_list->AddDeleteRegValueWorkItem(
271ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        reg_root,
272ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        version_key,
273ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        KEY_WOW64_32KEY,
274ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        google_update::kRegCriticalVersionField);
275ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    install_list->AddDeleteRegValueWorkItem(reg_root,
276ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            version_key,
277ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            KEY_WOW64_32KEY,
278ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                            google_update::kRegRenameCmdField);
279ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
280ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
281ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!install_list->Do()) {
282ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
283ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    install_list->Rollback();
284ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    ret = installer::RENAME_FAILED;
285ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
286ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // temp_path's dtor will take care of deleting or scheduling itself for
287ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // deletion at reboot when this scope closes.
288ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
289ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
290ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return ret;
291ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
292ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
293ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// For each product that is being updated (i.e., already installed at an earlier
294ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// version), see if that product has an update policy override that differs from
295ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// that for the binaries.  If any are found, fail with an error indicating that
296ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// the Group Policy settings are in an inconsistent state.  Do not do this test
297ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// for same-version installs, since it would be unkind to block attempts to
298ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// repair a corrupt installation.  This function returns false when installation
299ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// should be halted, in which case |status| contains the relevant exit code and
300ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// the proper installer result has been written to the registry.
301ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovbool CheckGroupPolicySettings(const InstallationState& original_state,
302ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                              const InstallerState& installer_state,
303ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                              const Version& new_version,
304ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                              installer::InstallStatus* status) {
305ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#if !defined(GOOGLE_CHROME_BUILD)
306ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Chromium builds are not updated via Google Update, so there are no
307ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Group Policy settings to consult.
308ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return true;
309ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#else
310ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  DCHECK(status);
311ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
312ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Single installs are always in good shape.
313ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!installer_state.is_multi_install())
314ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    return true;
315ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
316ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  bool settings_are_valid = true;
317ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const bool is_system_install = installer_state.system_install();
318ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  BrowserDistribution* const binaries_dist =
319ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      installer_state.multi_package_binaries_distribution();
320ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
321ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Get the update policy for the binaries.
322ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const GoogleUpdateSettings::UpdatePolicy binaries_policy =
323ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist->GetAppGuid(),
324ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                               NULL);
325ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
326ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  // Check for differing update policies for all of the products being updated.
327ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const Products& products = installer_state.products();
328ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  Products::const_iterator scan = products.begin();
329ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  for (Products::const_iterator end = products.end(); scan != end; ++scan) {
330ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    BrowserDistribution* dist = (*scan)->distribution();
331ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const ProductState* product_state =
332ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        original_state.GetProductState(is_system_install, dist->GetType());
333ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Is an earlier version of this product already installed?
334ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (product_state != NULL &&
335ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        product_state->version().CompareTo(new_version) < 0) {
336ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      bool is_overridden = false;
337ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      GoogleUpdateSettings::UpdatePolicy app_policy =
338ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(),
339ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                                   &is_overridden);
340ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      if (is_overridden && app_policy != binaries_policy) {
341ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        LOG(ERROR) << "Found legacy Group Policy setting for "
342ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                   << dist->GetDisplayName() << " (value: " << app_policy
343ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                   << ") that does not match the setting for "
344ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                   << binaries_dist->GetDisplayName()
345ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                   << " (value: " << binaries_policy << ").";
346ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        settings_are_valid = false;
347ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      }
348ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
349ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
350ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
351ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (!settings_are_valid) {
352ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this
353ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once
354ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // we have a help center article that explains why this error is being
355ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // reported and how to resolve it.
356ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    LOG(ERROR) << "Cannot apply update on account of inconsistent "
357ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                  "Google Update Group Policy settings. Use the Group Policy "
358ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                  "Editor to set the update policy override for the "
359ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov               << binaries_dist->GetDisplayName()
360ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov               << " application and try again.";
361ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    *status = installer::INCONSISTENT_UPDATE_POLICY;
362ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    installer_state.WriteInstallerResult(
363ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        *status, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE, NULL);
364ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
365ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
366ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return settings_are_valid;
367ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#endif  // defined(GOOGLE_CHROME_BUILD)
368ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
369ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
370ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// If only the binaries are being updated, fail.
371ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// If any product is being installed in single-mode that already exists in
372ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// multi-mode, fail.
373ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovbool CheckMultiInstallConditions(const InstallationState& original_state,
374ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                 InstallerState* installer_state,
375ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                 installer::InstallStatus* status) {
376ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const Products& products = installer_state->products();
377ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  DCHECK(products.size());
378ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
379ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  const bool system_level = installer_state->system_install();
380ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
381ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (installer_state->is_multi_install()) {
382ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const Product* chrome =
383ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
384ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const Product* app_host =
385ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST);
386ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const Product* binaries =
387ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES);
388ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const ProductState* chrome_state =
389ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        original_state.GetProductState(system_level,
390ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                                       BrowserDistribution::CHROME_BROWSER);
391ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
392ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (binaries) {
393ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      if (products.size() == 1) {
394ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // There are no products aside from the binaries, so there is no update
395ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // to be applied. This can happen after multi-install Chrome Frame is
396ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // migrated to single-install. This is treated as an update failure
397ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // unless the binaries are not in-use, in which case they will be
398ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // uninstalled and success will be reported (see handling in wWinMain).
399ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        VLOG(1) << "No products to be updated.";
400ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        *status = installer::UNUSED_BINARIES;
401ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        installer_state->WriteInstallerResult(*status, 0, NULL);
402ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return false;
403ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      }
404ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    } else {
405ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // This will only be hit if --multi-install is given with no products, or
406ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // if the app host is being installed and doesn't need the binaries at
407ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // user-level.
408ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // The former case might be due to a request by an orphaned Application
409ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // Host to re-install the binaries. Thus we add them to the installation.
410ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // The latter case is fine and we let it be.
411ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // If this is not an app host install and the binaries are not already
412ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // present, the installation will fail later due to a lack of products to
413ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // install.
414ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      if (app_host && !chrome && !chrome_state) {
415ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        DCHECK(!system_level);
416ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        // App Host may use Chrome/Chrome binaries at system-level.
417ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        if (original_state.GetProductState(
418ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                true,  // system
419ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                BrowserDistribution::CHROME_BROWSER) ||
420ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            original_state.GetProductState(
421ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                true,  // system
422ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                BrowserDistribution::CHROME_BINARIES)) {
423ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          VLOG(1) << "Installing/updating App Launcher without binaries.";
424ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        } else {
425ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          // Somehow the binaries were present when the quick-enable app host
426ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          // command was run, but now they appear to be missing.
427ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          // Force binaries to be installed/updated.
428ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          scoped_ptr<Product> binaries_to_add(new Product(
429ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov              BrowserDistribution::GetSpecificDistribution(
430ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                  BrowserDistribution::CHROME_BINARIES)));
431ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          binaries_to_add->SetOption(installer::kOptionMultiInstall, true);
432ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          binaries = installer_state->AddProduct(&binaries_to_add);
433ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          VLOG(1) <<
434ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov              "Adding binaries for pre-existing App Launcher installation.";
435ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        }
436ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      }
437ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
438ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      return true;
439ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
440ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
441ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!chrome && chrome_state) {
442ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // A product other than Chrome is being installed in multi-install mode,
443ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // and Chrome is already present. Add Chrome to the set of products
444ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // (making it multi-install in the process) so that it is updated, too.
445ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      scoped_ptr<Product> multi_chrome(new Product(
446ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov          BrowserDistribution::GetSpecificDistribution(
447ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov              BrowserDistribution::CHROME_BROWSER)));
448ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      multi_chrome->SetOption(installer::kOptionMultiInstall, true);
449ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      chrome = installer_state->AddProduct(&multi_chrome);
450ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
451ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
452ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  } else {
453ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // This is a non-multi installation.
454ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
455ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    // Check for an existing installation of the product.
456ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    const ProductState* product_state = original_state.GetProductState(
457ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        system_level, products[0]->distribution()->GetType());
458ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (product_state != NULL) {
459ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // Block downgrades from multi-install to single-install.
460ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      if (product_state->is_multi_install()) {
461ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        LOG(ERROR) << "Multi-install "
462ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                   << products[0]->distribution()->GetDisplayName()
463ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                   << " exists; aborting single install.";
464ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        *status = installer::MULTI_INSTALLATION_EXISTS;
465ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        installer_state->WriteInstallerResult(*status,
466ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov            IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL);
467ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov        return false;
468ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      }
469ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
470ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
471ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
472ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return true;
473ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
474ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
475ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Checks app host pre-install conditions, specifically that this is a
476ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// user-level multi-install.  When the pre-install conditions are not
477ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// satisfied, the result is written to the registry (via WriteInstallerResult),
478ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// |status| is set appropriately, and false is returned.
479ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovbool CheckAppHostPreconditions(const InstallationState& original_state,
480ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                               InstallerState* installer_state,
481ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov                               installer::InstallStatus* status) {
482ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
483ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (!installer_state->is_multi_install()) {
484ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      LOG(DFATAL) << "App Launcher requires multi install";
485ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      *status = installer::APP_HOST_REQUIRES_MULTI_INSTALL;
486ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // No message string since there is nothing a user can do.
487ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      installer_state->WriteInstallerResult(*status, 0, NULL);
488ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      return false;
489ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
490ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
491ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    if (installer_state->system_install()) {
492ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      LOG(DFATAL) << "App Launcher may only be installed at user-level.";
493ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
494ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      // No message string since there is nothing a user can do.
495ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      installer_state->WriteInstallerResult(*status, 0, NULL);
496ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov      return false;
497ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov    }
498ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  }
499ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
500ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov  return true;
501ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}
502ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov
503ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Checks for compatibility between the current state of the system and the
504ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// desired operation.  Also applies policy that mutates the desired operation;
505ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 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.
512bool 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.
620bool 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
643installer::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
668installer::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.
744void 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
778installer::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
838installer::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.
863bool 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
872void 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
894installer::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).
972bool 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    // Figure out whether we should do so for the multi binaries or the main
1211    // Chrome product.
1212    BrowserDistribution::Type dist_type = BrowserDistribution::CHROME_BROWSER;
1213    if (installer_state->is_multi_install())
1214      dist_type = BrowserDistribution::CHROME_BINARIES;
1215
1216    BrowserDistribution* dist =
1217        BrowserDistribution::GetSpecificDistribution(dist_type);
1218    bool updates_enabled =
1219        GoogleUpdateSettings::ReenableAutoupdatesForApp(dist->GetAppGuid());
1220    *exit_code = updates_enabled ? installer::REENABLE_UPDATES_SUCCEEDED :
1221                                   installer::REENABLE_UPDATES_FAILED;
1222  } else {
1223    handled = false;
1224  }
1225
1226  return handled;
1227}
1228
1229bool ShowRebootDialog() {
1230  // Get a token for this process.
1231  HANDLE token;
1232  if (!OpenProcessToken(GetCurrentProcess(),
1233                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1234                        &token)) {
1235    LOG(ERROR) << "Failed to open token.";
1236    return false;
1237  }
1238
1239  // Use a ScopedHandle to keep track of and eventually close our handle.
1240  // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
1241  base::win::ScopedHandle scoped_handle(token);
1242
1243  // Get the LUID for the shutdown privilege.
1244  TOKEN_PRIVILEGES tkp = {0};
1245  LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
1246  tkp.PrivilegeCount = 1;
1247  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1248
1249  // Get the shutdown privilege for this process.
1250  AdjustTokenPrivileges(token, FALSE, &tkp, 0,
1251                        reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0);
1252  if (GetLastError() != ERROR_SUCCESS) {
1253    LOG(ERROR) << "Unable to get shutdown privileges.";
1254    return false;
1255  }
1256
1257  // Popup a dialog that will prompt to reboot using the default system message.
1258  // TODO(robertshield): Add a localized, more specific string to the prompt.
1259  RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG);
1260  return true;
1261}
1262
1263// Returns the Custom information for the client identified by the exe path
1264// passed in. This information is used for crash reporting.
1265google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) {
1266  base::string16 product;
1267  base::string16 version;
1268  scoped_ptr<FileVersionInfo> version_info(
1269      FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path)));
1270  if (version_info.get()) {
1271    version = version_info->product_version();
1272    product = version_info->product_short_name();
1273  }
1274
1275  if (version.empty())
1276    version = L"0.1.0.0";
1277
1278  if (product.empty())
1279    product = L"Chrome Installer";
1280
1281  static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str());
1282  static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str());
1283  static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
1284  static google_breakpad::CustomInfoEntry type_entry(L"ptype",
1285                                                     L"Chrome Installer");
1286  static google_breakpad::CustomInfoEntry entries[] = {
1287      ver_entry, prod_entry, plat_entry, type_entry };
1288  static google_breakpad::CustomClientInfo custom_info = {
1289      entries, arraysize(entries) };
1290  return &custom_info;
1291}
1292
1293// Initialize crash reporting for this process. This involves connecting to
1294// breakpad, etc.
1295scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting(
1296    bool system_install) {
1297  // Only report crashes if the user allows it.
1298  if (!GoogleUpdateSettings::GetCollectStatsConsent())
1299    return scoped_ptr<google_breakpad::ExceptionHandler>();
1300
1301  // Get the alternate dump directory. We use the temp path.
1302  base::FilePath temp_directory;
1303  if (!base::GetTempDir(&temp_directory) || temp_directory.empty())
1304    return scoped_ptr<google_breakpad::ExceptionHandler>();
1305
1306  wchar_t exe_path[MAX_PATH * 2] = {0};
1307  GetModuleFileName(NULL, exe_path, arraysize(exe_path));
1308
1309  // Build the pipe name. It can be either:
1310  // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1311  // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1312  base::string16 user_sid = kSystemPrincipalSid;
1313
1314  if (!system_install) {
1315    if (!base::win::GetUserSidString(&user_sid)) {
1316      return scoped_ptr<google_breakpad::ExceptionHandler>();
1317    }
1318  }
1319
1320  base::string16 pipe_name = kGoogleUpdatePipeName;
1321  pipe_name += user_sid;
1322
1323  return scoped_ptr<google_breakpad::ExceptionHandler>(
1324      new google_breakpad::ExceptionHandler(
1325          temp_directory.value(), NULL, NULL, NULL,
1326          google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
1327          pipe_name.c_str(), GetCustomInfo(exe_path)));
1328}
1329
1330// Uninstalls multi-install Chrome Frame if the current operation is a
1331// multi-install install or update. The operation is performed directly rather
1332// than delegated to the existing install since there is no facility in older
1333// versions of setup.exe to uninstall GCF without touching the binaries. The
1334// binaries will be uninstalled during later processing if they are not in-use
1335// (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
1336// updated to reflect the state of the world following the operation.
1337void UninstallMultiChromeFrameIfPresent(const CommandLine& cmd_line,
1338                                        const MasterPreferences& prefs,
1339                                        InstallationState* original_state,
1340                                        InstallerState* installer_state) {
1341  // Early exit if not installing or updating multi-install product(s).
1342  if (installer_state->operation() != InstallerState::MULTI_INSTALL &&
1343      installer_state->operation() != InstallerState::MULTI_UPDATE) {
1344    return;
1345  }
1346
1347  // Early exit if Chrome Frame is not present as multi-install.
1348  const ProductState* chrome_frame_state =
1349      original_state->GetProductState(installer_state->system_install(),
1350                                      BrowserDistribution::CHROME_FRAME);
1351  if (!chrome_frame_state || !chrome_frame_state->is_multi_install())
1352    return;
1353
1354  LOG(INFO) << "Uninstalling multi-install Chrome Frame.";
1355  installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME);
1356
1357  // Uninstall Chrome Frame without touching the multi-install binaries.
1358  // Simulate the uninstall as coming from the installed version.
1359  const CommandLine& uninstall_cmd(chrome_frame_state->uninstall_command());
1360  MasterPreferences uninstall_prefs(uninstall_cmd);
1361  InstallerState uninstall_state;
1362  uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state);
1363  // Post M32, uninstall_prefs and uninstall_state won't have Chrome Frame in
1364  // them since they've lost the power to do Chrome Frame installs.
1365  const Product* chrome_frame_product = uninstall_state.AddProductFromState(
1366      BrowserDistribution::CHROME_FRAME, *chrome_frame_state);
1367  if (chrome_frame_product) {
1368    // No shared state should be left behind.
1369    const bool remove_all = true;
1370    // Don't accept no for an answer.
1371    const bool force_uninstall = true;
1372    installer::InstallStatus uninstall_status =
1373        installer::UninstallProduct(*original_state, uninstall_state,
1374                                    uninstall_cmd.GetProgram(),
1375                                    *chrome_frame_product, remove_all,
1376                                    force_uninstall, cmd_line);
1377
1378    VLOG(1) << "Uninstallation of Chrome Frame returned status "
1379            << uninstall_status;
1380  } else {
1381    LOG(ERROR) << "Chrome Frame not found for uninstall.";
1382  }
1383
1384  // Refresh state for the continuation of the original install/update.
1385  original_state->Initialize();
1386  installer_state->Initialize(cmd_line, prefs, *original_state);
1387}
1388
1389}  // namespace
1390
1391namespace installer {
1392
1393InstallStatus InstallProductsHelper(
1394    const InstallationState& original_state,
1395    const base::FilePath& setup_exe,
1396    const CommandLine& cmd_line,
1397    const MasterPreferences& prefs,
1398    const InstallerState& installer_state,
1399    base::FilePath* installer_directory,
1400    ArchiveType* archive_type,
1401    bool* delegated_to_existing) {
1402  DCHECK(archive_type);
1403  DCHECK(delegated_to_existing);
1404  const bool system_install = installer_state.system_install();
1405  InstallStatus install_status = UNKNOWN_STATUS;
1406
1407  // Drop to background processing mode if the process was started below the
1408  // normal process priority class.
1409  bool entered_background_mode = AdjustProcessPriority();
1410  VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
1411
1412  // Create a temp folder where we will unpack Chrome archive. If it fails,
1413  // then we are doomed, so return immediately and no cleanup is required.
1414  SelfCleaningTempDir temp_path;
1415  base::FilePath unpack_path;
1416  if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
1417                                           &unpack_path)) {
1418    installer_state.WriteInstallerResult(TEMP_DIR_FAILED,
1419                                         IDS_INSTALL_TEMP_DIR_FAILED_BASE,
1420                                         NULL);
1421    return TEMP_DIR_FAILED;
1422  }
1423
1424  // Uncompress and optionally patch the archive if an uncompressed archive was
1425  // not specified on the command line and a compressed archive is found.
1426  *archive_type = UNKNOWN_ARCHIVE_TYPE;
1427  base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
1428      switches::kUncompressedArchive));
1429  if (uncompressed_archive.empty()) {
1430    scoped_ptr<ArchivePatchHelper> archive_helper(
1431        CreateChromeArchiveHelper(setup_exe, cmd_line, installer_state,
1432                                  unpack_path));
1433    if (archive_helper) {
1434      VLOG(1) << "Installing Chrome from compressed archive "
1435              << archive_helper->compressed_archive().value();
1436      if (!UncompressAndPatchChromeArchive(original_state,
1437                                           installer_state,
1438                                           archive_helper.get(),
1439                                           archive_type,
1440                                           &install_status)) {
1441        DCHECK_NE(install_status, UNKNOWN_STATUS);
1442        return install_status;
1443      }
1444      uncompressed_archive = archive_helper->target();
1445      DCHECK(!uncompressed_archive.empty());
1446    }
1447  }
1448
1449  // Check for an uncompressed archive alongside the current executable if one
1450  // was not given or generated.
1451  if (uncompressed_archive.empty())
1452    uncompressed_archive = setup_exe.DirName().Append(kChromeArchive);
1453
1454  if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
1455    // An archive was not uncompressed or patched above.
1456    if (uncompressed_archive.empty() ||
1457        !base::PathExists(uncompressed_archive)) {
1458      LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
1459      installer_state.WriteInstallerResult(
1460          INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1461      return INVALID_ARCHIVE;
1462    }
1463    *archive_type = FULL_ARCHIVE_TYPE;
1464  }
1465
1466  // Unpack the uncompressed archive.
1467  if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
1468                              unpack_path.value(),
1469                              NULL)) {
1470    installer_state.WriteInstallerResult(
1471        UNCOMPRESSION_FAILED,
1472        IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
1473        NULL);
1474    return UNCOMPRESSION_FAILED;
1475  }
1476
1477  VLOG(1) << "unpacked to " << unpack_path.value();
1478  base::FilePath src_path(
1479      unpack_path.Append(kInstallSourceChromeDir));
1480  scoped_ptr<Version>
1481      installer_version(GetMaxVersionFromArchiveDir(src_path));
1482  if (!installer_version.get()) {
1483    LOG(ERROR) << "Did not find any valid version in installer.";
1484    install_status = INVALID_ARCHIVE;
1485    installer_state.WriteInstallerResult(install_status,
1486        IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1487  } else {
1488    VLOG(1) << "version to install: " << installer_version->GetString();
1489    bool proceed_with_installation = true;
1490
1491    if (installer_state.operation() == InstallerState::MULTI_INSTALL) {
1492      // This is a new install of a multi-install product. Rather than give up
1493      // in case a higher version of the binaries (including a single-install
1494      // of Chrome, which can safely be migrated to multi-install by way of
1495      // CheckMultiInstallConditions) is already installed, delegate to the
1496      // installed setup.exe to install the product at hand.
1497      base::FilePath existing_setup_exe;
1498      if (GetExistingHigherInstaller(original_state, system_install,
1499                                     *installer_version, &existing_setup_exe)) {
1500        VLOG(1) << "Deferring to existing installer.";
1501        installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION);
1502        if (DeferToExistingInstall(existing_setup_exe, cmd_line,
1503                                   installer_state, temp_path.path(),
1504                                   &install_status)) {
1505          *delegated_to_existing = true;
1506          return install_status;
1507        }
1508      }
1509    }
1510
1511    uint32 higher_products = 0;
1512    COMPILE_ASSERT(
1513        sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES,
1514        too_many_distribution_types_);
1515    const Products& products = installer_state.products();
1516    for (Products::const_iterator it = products.begin(); it < products.end();
1517         ++it) {
1518      const Product& product = **it;
1519      const ProductState* product_state =
1520          original_state.GetProductState(system_install,
1521                                         product.distribution()->GetType());
1522      if (product_state != NULL &&
1523          (product_state->version().CompareTo(*installer_version) > 0)) {
1524        LOG(ERROR) << "Higher version of "
1525                   << product.distribution()->GetDisplayName()
1526                   << " is already installed.";
1527        higher_products |= (1 << product.distribution()->GetType());
1528      }
1529    }
1530
1531    if (higher_products != 0) {
1532      COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4,
1533                     add_support_for_new_products_here_);
1534      const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER;
1535      const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST;
1536      int message_id = 0;
1537
1538      proceed_with_installation = false;
1539      install_status = HIGHER_VERSION_EXISTS;
1540      switch (higher_products) {
1541        case kBrowserBit:
1542          message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
1543          break;
1544        default:
1545          message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE;
1546          break;
1547      }
1548
1549      installer_state.WriteInstallerResult(install_status, message_id, NULL);
1550    }
1551
1552    proceed_with_installation =
1553        proceed_with_installation &&
1554        CheckGroupPolicySettings(original_state, installer_state,
1555                                 *installer_version, &install_status);
1556
1557    if (proceed_with_installation) {
1558      // If Google Update is absent at user-level, install it using the
1559      // Google Update installer from an existing system-level installation.
1560      // This is for quick-enable App Host install from a system-level
1561      // Chrome Binaries installation.
1562      if (!system_install && installer_state.ensure_google_update_present()) {
1563        if (!google_update::EnsureUserLevelGoogleUpdatePresent()) {
1564          LOG(ERROR) << "Failed to install Google Update";
1565          proceed_with_installation = false;
1566          install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED;
1567          installer_state.WriteInstallerResult(install_status, 0, NULL);
1568        }
1569      }
1570
1571    }
1572
1573    if (proceed_with_installation) {
1574      base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
1575          switches::kInstallerData));
1576      install_status = InstallOrUpdateProduct(
1577          original_state, installer_state, setup_exe, uncompressed_archive,
1578          temp_path.path(), src_path, prefs_source_path, prefs,
1579          *installer_version);
1580
1581      int install_msg_base = IDS_INSTALL_FAILED_BASE;
1582      base::string16 chrome_exe;
1583      base::string16 quoted_chrome_exe;
1584      if (install_status == SAME_VERSION_REPAIR_FAILED) {
1585        install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
1586      } else if (install_status != INSTALL_FAILED) {
1587        if (installer_state.target_path().empty()) {
1588          // If we failed to construct install path, it means the OS call to
1589          // get %ProgramFiles% or %AppData% failed. Report this as failure.
1590          install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
1591          install_status = OS_ERROR;
1592        } else {
1593          chrome_exe = installer_state.target_path().Append(kChromeExe).value();
1594          quoted_chrome_exe = L"\"" + chrome_exe + L"\"";
1595          install_msg_base = 0;
1596        }
1597      }
1598
1599      installer_state.UpdateStage(FINISHING);
1600
1601      // Only do Chrome-specific stuff (like launching the browser) if
1602      // Chrome was specifically requested (rather than being upgraded as
1603      // part of a multi-install).
1604      const Product* chrome_install = prefs.install_chrome() ?
1605          installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) :
1606          NULL;
1607
1608      bool do_not_register_for_update_launch = false;
1609      if (chrome_install) {
1610        prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch,
1611                      &do_not_register_for_update_launch);
1612      } else {
1613        do_not_register_for_update_launch = true;  // Never register.
1614      }
1615
1616      bool write_chrome_launch_string =
1617          (!do_not_register_for_update_launch &&
1618           install_status != IN_USE_UPDATED);
1619
1620      installer_state.WriteInstallerResult(install_status, install_msg_base,
1621          write_chrome_launch_string ? &quoted_chrome_exe : NULL);
1622
1623      if (install_status == FIRST_INSTALL_SUCCESS) {
1624        VLOG(1) << "First install successful.";
1625        if (chrome_install) {
1626          // We never want to launch Chrome in system level install mode.
1627          bool do_not_launch_chrome = false;
1628          prefs.GetBool(master_preferences::kDoNotLaunchChrome,
1629                        &do_not_launch_chrome);
1630          if (!system_install && !do_not_launch_chrome)
1631            chrome_install->LaunchChrome(installer_state.target_path());
1632        }
1633      } else if ((install_status == NEW_VERSION_UPDATED) ||
1634                 (install_status == IN_USE_UPDATED)) {
1635        const Product* chrome = installer_state.FindProduct(
1636            BrowserDistribution::CHROME_BROWSER);
1637        if (chrome != NULL) {
1638          DCHECK_NE(chrome_exe, base::string16());
1639          RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe);
1640        }
1641      }
1642
1643      if (prefs.install_chrome_app_launcher() &&
1644          InstallUtil::GetInstallReturnCode(install_status) == 0) {
1645        std::string webstore_item(google_update::GetUntrustedDataValue(
1646            kInstallFromWebstore));
1647        if (!webstore_item.empty()) {
1648          bool success = InstallFromWebstore(webstore_item);
1649          VLOG_IF(1, !success) << "Failed to launch app installation.";
1650        }
1651      }
1652    }
1653  }
1654
1655  // There might be an experiment (for upgrade usually) that needs to happen.
1656  // An experiment's outcome can include chrome's uninstallation. If that is
1657  // the case we would not do that directly at this point but in another
1658  // instance of setup.exe
1659  //
1660  // There is another way to reach this same function if this is a system
1661  // level install. See HandleNonInstallCmdLineOptions().
1662  {
1663    // If installation failed, use the path to the currently running setup.
1664    // If installation succeeded, use the path to setup in the installer dir.
1665    base::FilePath setup_path(setup_exe);
1666    if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1667      setup_path = installer_state.GetInstallerDirectory(*installer_version)
1668          .Append(setup_path.BaseName());
1669    }
1670    const Products& products = installer_state.products();
1671    for (Products::const_iterator it = products.begin(); it < products.end();
1672         ++it) {
1673      const Product& product = **it;
1674      product.LaunchUserExperiment(setup_path, install_status, system_install);
1675    }
1676  }
1677
1678  // If installation completed successfully, return the path to the directory
1679  // containing the newly installed setup.exe and uncompressed archive if the
1680  // caller requested it.
1681  if (installer_directory &&
1682      InstallUtil::GetInstallReturnCode(install_status) == 0) {
1683    *installer_directory =
1684        installer_state.GetInstallerDirectory(*installer_version);
1685  }
1686
1687  // temp_path's dtor will take care of deleting or scheduling itself for
1688  // deletion at reboot when this scope closes.
1689  VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
1690
1691  return install_status;
1692}
1693
1694}  // namespace installer
1695
1696int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
1697                    wchar_t* command_line, int show_command) {
1698  // Check to see if the CPU is supported before doing anything else. There's
1699  // very little than can safely be accomplished if the CPU isn't supported
1700  // since dependent libraries (e.g., base) may use invalid instructions.
1701  if (!installer::IsProcessorSupported())
1702    return installer::CPU_NOT_SUPPORTED;
1703
1704  // The exit manager is in charge of calling the dtors of singletons.
1705  base::AtExitManager exit_manager;
1706  CommandLine::Init(0, NULL);
1707
1708  // install_util uses chrome paths.
1709  chrome::RegisterPathProvider();
1710
1711  const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
1712  installer::InitInstallerLogging(prefs);
1713
1714  const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
1715  VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString();
1716
1717  VLOG(1) << "multi install is " << prefs.is_multi_install();
1718  bool system_install = false;
1719  prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install);
1720  VLOG(1) << "system install is " << system_install;
1721
1722  scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
1723      InitializeCrashReporting(system_install));
1724
1725  InstallationState original_state;
1726  original_state.Initialize();
1727
1728  InstallerState installer_state;
1729  installer_state.Initialize(cmd_line, prefs, original_state);
1730  const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall);
1731
1732  // Check to make sure current system is WinXP or later. If not, log
1733  // error message and get out.
1734  if (!InstallUtil::IsOSSupported()) {
1735    LOG(ERROR) << "Chrome only supports Windows XP or later.";
1736    installer_state.WriteInstallerResult(
1737        installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL);
1738    return installer::OS_NOT_SUPPORTED;
1739  }
1740
1741  // Initialize COM for use later.
1742  base::win::ScopedCOMInitializer com_initializer;
1743  if (!com_initializer.succeeded()) {
1744    installer_state.WriteInstallerResult(
1745        installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL);
1746    return installer::OS_ERROR;
1747  }
1748
1749  // Some command line options don't work with SxS install/uninstall
1750  if (InstallUtil::IsChromeSxSProcess()) {
1751    if (system_install ||
1752        prefs.is_multi_install() ||
1753        cmd_line.HasSwitch(installer::switches::kForceUninstall) ||
1754        cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) ||
1755        cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) ||
1756        cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) ||
1757        cmd_line.HasSwitch(installer::switches::kInactiveUserToast) ||
1758        cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1759      return installer::SXS_OPTION_NOT_SUPPORTED;
1760    }
1761  }
1762
1763  // Some command line options are no longer supported and must error out.
1764  if (installer::ContainsUnsupportedSwitch(cmd_line))
1765    return installer::UNSUPPORTED_OPTION;
1766
1767  // A variety of installer operations require the path to the current
1768  // executable. Get it once here for use throughout these operations. Note that
1769  // the path service is the authoritative source for this path. One might think
1770  // that CommandLine::GetProgram would suffice, but it won't since
1771  // CreateProcess may have been called with a command line that is somewhat
1772  // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file
1773  // extension), in which case CommandLineToArgv will not yield an argv with the
1774  // true path to the program at position 0.
1775  base::FilePath setup_exe;
1776  if (!PathService::Get(base::FILE_EXE, &setup_exe)) {
1777    NOTREACHED();
1778    return installer::OS_ERROR;
1779  }
1780
1781  int exit_code = 0;
1782  if (HandleNonInstallCmdLineOptions(
1783          original_state, setup_exe, cmd_line, &installer_state, &exit_code)) {
1784    return exit_code;
1785  }
1786
1787  if (system_install && !IsUserAnAdmin()) {
1788    if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
1789        !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1790      CommandLine new_cmd(CommandLine::NO_PROGRAM);
1791      new_cmd.AppendArguments(cmd_line, true);
1792      // Append --run-as-admin flag to let the new instance of setup.exe know
1793      // that we already tried to launch ourselves as admin.
1794      new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1795      // If system_install became true due to an environment variable, append
1796      // it to the command line here since env vars may not propagate past the
1797      // elevation.
1798      if (!new_cmd.HasSwitch(installer::switches::kSystemLevel))
1799        new_cmd.AppendSwitch(installer::switches::kSystemLevel);
1800
1801      DWORD exit_code = installer::UNKNOWN_STATUS;
1802      InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1803      return exit_code;
1804    } else {
1805      LOG(ERROR) << "Non admin user can not install system level Chrome.";
1806      installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS,
1807          IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL);
1808      return installer::INSUFFICIENT_RIGHTS;
1809    }
1810  }
1811
1812  UninstallMultiChromeFrameIfPresent(cmd_line, prefs,
1813                                     &original_state, &installer_state);
1814
1815  base::FilePath installer_directory;
1816  installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
1817  // If --uninstall option is given, uninstall the identified product(s)
1818  if (is_uninstall) {
1819    install_status =
1820        UninstallProducts(original_state, installer_state, setup_exe, cmd_line);
1821  } else {
1822    // If --uninstall option is not specified, we assume it is install case.
1823    install_status =
1824        InstallProducts(original_state, setup_exe, cmd_line, prefs,
1825                        &installer_state, &installer_directory);
1826  }
1827
1828  // Validate that the machine is now in a good state following the operation.
1829  // TODO(grt): change this to log at DFATAL once we're convinced that the
1830  // validator handles all cases properly.
1831  InstallationValidator::InstallationType installation_type =
1832      InstallationValidator::NO_PRODUCTS;
1833  LOG_IF(ERROR,
1834         !InstallationValidator::ValidateInstallationType(system_install,
1835                                                          &installation_type));
1836
1837  int return_code = 0;
1838  // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1839  // rollback the action. If we're uninstalling we want to avoid this, so always
1840  // report success, squashing any more informative return codes.
1841  if (!(installer_state.is_msi() && is_uninstall)) {
1842    // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1843    // to pass through, since this is only returned on uninstall which is
1844    // never invoked directly by Google Update.
1845    return_code = InstallUtil::GetInstallReturnCode(install_status);
1846  }
1847
1848  VLOG(1) << "Installation complete, returning: " << return_code;
1849
1850  return return_code;
1851}
1852