Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / installer / setup / setup_main.cc
blobee2601f50fcf4f65074f4a426bd15201f6a7d62a
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/installer/setup/setup_main.h"
7 #include <windows.h>
8 #include <msi.h>
9 #include <shellapi.h>
10 #include <shlobj.h>
12 #include <string>
14 #include "base/at_exit.h"
15 #include "base/basictypes.h"
16 #include "base/command_line.h"
17 #include "base/file_version_info.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/files/scoped_temp_dir.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/path_service.h"
23 #include "base/process/launch.h"
24 #include "base/strings/string16.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/values.h"
30 #include "base/version.h"
31 #include "base/win/registry.h"
32 #include "base/win/scoped_com_initializer.h"
33 #include "base/win/scoped_comptr.h"
34 #include "base/win/scoped_handle.h"
35 #include "base/win/win_util.h"
36 #include "base/win/windows_version.h"
37 #include "breakpad/src/client/windows/handler/exception_handler.h"
38 #include "chrome/common/chrome_constants.h"
39 #include "chrome/common/chrome_paths.h"
40 #include "chrome/common/chrome_switches.h"
41 #include "chrome/installer/setup/archive_patch_helper.h"
42 #include "chrome/installer/setup/install.h"
43 #include "chrome/installer/setup/install_worker.h"
44 #include "chrome/installer/setup/setup_constants.h"
45 #include "chrome/installer/setup/setup_util.h"
46 #include "chrome/installer/setup/uninstall.h"
47 #include "chrome/installer/util/browser_distribution.h"
48 #include "chrome/installer/util/channel_info.h"
49 #include "chrome/installer/util/delete_after_reboot_helper.h"
50 #include "chrome/installer/util/delete_tree_work_item.h"
51 #include "chrome/installer/util/google_update_constants.h"
52 #include "chrome/installer/util/google_update_settings.h"
53 #include "chrome/installer/util/google_update_util.h"
54 #include "chrome/installer/util/helper.h"
55 #include "chrome/installer/util/html_dialog.h"
56 #include "chrome/installer/util/install_util.h"
57 #include "chrome/installer/util/installation_state.h"
58 #include "chrome/installer/util/installation_validator.h"
59 #include "chrome/installer/util/installer_state.h"
60 #include "chrome/installer/util/installer_util_strings.h"
61 #include "chrome/installer/util/l10n_string_util.h"
62 #include "chrome/installer/util/logging_installer.h"
63 #include "chrome/installer/util/lzma_util.h"
64 #include "chrome/installer/util/master_preferences.h"
65 #include "chrome/installer/util/master_preferences_constants.h"
66 #include "chrome/installer/util/self_cleaning_temp_dir.h"
67 #include "chrome/installer/util/shell_util.h"
68 #include "chrome/installer/util/user_experiment.h"
69 #include "version.h" // NOLINT
71 #if defined(GOOGLE_CHROME_BUILD)
72 #include "chrome/installer/util/updating_app_registration_data.h"
73 #endif
75 using installer::InstallerState;
76 using installer::InstallationState;
77 using installer::InstallationValidator;
78 using installer::MasterPreferences;
79 using installer::Product;
80 using installer::ProductState;
81 using installer::Products;
83 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
84 MiniDumpWithProcessThreadData | // Get PEB and TEB.
85 MiniDumpWithUnloadedModules | // Get unloaded modules when available.
86 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
88 namespace {
90 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
91 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
92 const wchar_t kDisplayVersion[] = L"DisplayVersion";
93 const wchar_t kMsiDisplayVersionOverwriteDelay[] = L"10"; // seconds as string
95 // Overwrite an existing DisplayVersion as written by the MSI installer
96 // with the real version number of Chrome.
97 LONG OverwriteDisplayVersion(const base::string16& path,
98 const base::string16& value,
99 REGSAM wowkey) {
100 base::win::RegKey key;
101 LONG result = 0;
102 base::string16 existing;
103 if ((result = key.Open(HKEY_LOCAL_MACHINE, path.c_str(),
104 KEY_QUERY_VALUE | KEY_SET_VALUE | wowkey))
105 != ERROR_SUCCESS) {
106 LOG(ERROR) << "Failed to set DisplayVersion: " << path << " not found";
107 return result;
109 if ((result = key.ReadValue(kDisplayVersion, &existing)) != ERROR_SUCCESS) {
110 LOG(ERROR) << "Failed to set DisplayVersion: " << kDisplayVersion
111 << " not found under " << path;
112 return result;
114 if ((result = key.WriteValue(kDisplayVersion, value.c_str()))
115 != ERROR_SUCCESS) {
116 LOG(ERROR) << "Failed to set DisplayVersion: " << kDisplayVersion
117 << " could not be written under " << path;
118 return result;
120 VLOG(1) << "Set DisplayVersion at " << path << " to " << value
121 << " from " << existing;
122 return ERROR_SUCCESS;
125 LONG OverwriteDisplayVersions(const base::string16& product,
126 const base::string16& value) {
127 // The version is held in two places. Frist change it in the MSI Installer
128 // registry entry. It is held under a "squashed guid" key.
129 base::string16 reg_path = base::StringPrintf(
130 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"
131 L"%ls\\Products\\%ls\\InstallProperties", kSystemPrincipalSid,
132 installer::GuidToSquid(product).c_str());
133 LONG result1 = OverwriteDisplayVersion(reg_path, value, KEY_WOW64_64KEY);
135 // The display version also exists under the Unininstall registry key with
136 // the original guid. Check both WOW64_64 and WOW64_32.
137 reg_path = base::StringPrintf(
138 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{%ls}",
139 product.c_str());
140 LONG result2 = OverwriteDisplayVersion(reg_path, value, KEY_WOW64_64KEY);
141 if (result2 != ERROR_SUCCESS)
142 result2 = OverwriteDisplayVersion(reg_path, value, KEY_WOW64_32KEY);
144 return result1 != ERROR_SUCCESS ? result1 : result2;
147 void DelayedOverwriteDisplayVersions(const base::FilePath& setup_exe,
148 const std::string& id,
149 const Version& version) {
150 // This process has to be able to exit so we launch ourselves with
151 // instructions on what to do and then return.
152 base::CommandLine command_line(setup_exe);
153 command_line.AppendSwitchASCII(installer::switches::kSetDisplayVersionProduct,
154 id);
155 command_line.AppendSwitchASCII(installer::switches::kSetDisplayVersionValue,
156 version.GetString());
157 command_line.AppendSwitchNative(installer::switches::kDelay,
158 kMsiDisplayVersionOverwriteDelay);
160 base::LaunchOptions launch_options;
161 launch_options.force_breakaway_from_job_ = true;
162 base::Process writer = base::LaunchProcess(command_line, launch_options);
163 if (!writer.IsValid()) {
164 PLOG(ERROR) << "Failed to set DisplayVersion: "
165 << "could not launch subprocess to make desired changes."
166 << " <<" << command_line.GetCommandLineString() << ">>";
170 // Returns NULL if no compressed archive is available for processing, otherwise
171 // returns a patch helper configured to uncompress and patch.
172 scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
173 const base::FilePath& setup_exe,
174 const base::CommandLine& command_line,
175 const installer::InstallerState& installer_state,
176 const base::FilePath& working_directory) {
177 // A compressed archive is ordinarily given on the command line by the mini
178 // installer. If one was not given, look for chrome.packed.7z next to the
179 // running program.
180 base::FilePath compressed_archive(
181 command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
182 bool compressed_archive_specified = !compressed_archive.empty();
183 if (!compressed_archive_specified) {
184 compressed_archive = setup_exe.DirName().Append(
185 installer::kChromeCompressedArchive);
188 // Fail if no compressed archive is found.
189 if (!base::PathExists(compressed_archive)) {
190 if (compressed_archive_specified) {
191 LOG(ERROR) << installer::switches::kInstallArchive << "="
192 << compressed_archive.value() << " not found.";
194 return scoped_ptr<installer::ArchivePatchHelper>();
197 // chrome.7z is either extracted directly from the compressed archive into the
198 // working dir or is the target of patching in the working dir.
199 base::FilePath target(working_directory.Append(installer::kChromeArchive));
200 DCHECK(!base::PathExists(target));
202 // Specify an empty path for the patch source since it isn't yet known that
203 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
204 // is.
205 return scoped_ptr<installer::ArchivePatchHelper>(
206 new installer::ArchivePatchHelper(working_directory,
207 compressed_archive,
208 base::FilePath(),
209 target));
212 // Workhorse for producing an uncompressed archive (chrome.7z) given a
213 // chrome.packed.7z containing either a patch file based on the version of
214 // chrome being updated or the full uncompressed archive. Returns true on
215 // success, in which case |archive_type| is populated based on what was found.
216 // Returns false on failure, in which case |install_status| contains the error
217 // code and the result is written to the registry (via WriteInstallerResult).
218 bool UncompressAndPatchChromeArchive(
219 const installer::InstallationState& original_state,
220 const installer::InstallerState& installer_state,
221 installer::ArchivePatchHelper* archive_helper,
222 installer::ArchiveType* archive_type,
223 installer::InstallStatus* install_status,
224 const base::Version& previous_version) {
225 installer_state.UpdateStage(installer::UNCOMPRESSING);
226 if (!archive_helper->Uncompress(NULL)) {
227 *install_status = installer::UNCOMPRESSION_FAILED;
228 installer_state.WriteInstallerResult(*install_status,
229 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
230 NULL);
231 return false;
234 // Short-circuit if uncompression produced the uncompressed archive rather
235 // than a patch file.
236 if (base::PathExists(archive_helper->target())) {
237 *archive_type = installer::FULL_ARCHIVE_TYPE;
238 return true;
241 // Find the installed version's archive to serve as the source for patching.
242 base::FilePath patch_source(installer::FindArchiveToPatch(original_state,
243 installer_state,
244 previous_version));
245 if (patch_source.empty()) {
246 LOG(ERROR) << "Failed to find archive to patch.";
247 *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
248 installer_state.WriteInstallerResult(*install_status,
249 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
250 NULL);
251 return false;
253 archive_helper->set_patch_source(patch_source);
255 // Try courgette first. Failing that, try bspatch.
256 installer_state.UpdateStage(installer::ENSEMBLE_PATCHING);
257 if (!archive_helper->EnsemblePatch()) {
258 installer_state.UpdateStage(installer::BINARY_PATCHING);
259 if (!archive_helper->BinaryPatch()) {
260 *install_status = installer::APPLY_DIFF_PATCH_FAILED;
261 installer_state.WriteInstallerResult(
262 *install_status, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, NULL);
263 return false;
267 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
268 return true;
271 // In multi-install, adds all products to |installer_state| that are
272 // multi-installed and must be updated along with the products already present
273 // in |installer_state|.
274 void AddExistingMultiInstalls(const InstallationState& original_state,
275 InstallerState* installer_state) {
276 if (installer_state->is_multi_install()) {
277 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
278 BrowserDistribution::Type type =
279 static_cast<BrowserDistribution::Type>(i);
281 if (!installer_state->FindProduct(type)) {
282 const ProductState* state =
283 original_state.GetProductState(installer_state->system_install(),
284 type);
285 if ((state != NULL) && state->is_multi_install()) {
286 installer_state->AddProductFromState(type, *state);
287 VLOG(1) << "Product already installed and must be included: "
288 << BrowserDistribution::GetSpecificDistribution(type)->
289 GetDisplayName();
296 // This function is called when --rename-chrome-exe option is specified on
297 // setup.exe command line. This function assumes an in-use update has happened
298 // for Chrome so there should be a file called new_chrome.exe on the file
299 // system and a key called 'opv' in the registry. This function will move
300 // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
301 // This function also deletes elevation policies associated with the old version
302 // if they exist.
303 installer::InstallStatus RenameChromeExecutables(
304 const InstallationState& original_state,
305 InstallerState* installer_state) {
306 // See what products are already installed in multi mode. When we do the
307 // rename for multi installs, we must update all installations since they
308 // share the binaries.
309 AddExistingMultiInstalls(original_state, installer_state);
310 const base::FilePath &target_path = installer_state->target_path();
311 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
312 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
313 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));
315 // Create a temporary backup directory on the same volume as chrome.exe so
316 // that moving in-use files doesn't lead to trouble.
317 installer::SelfCleaningTempDir temp_path;
318 if (!temp_path.Initialize(target_path.DirName(),
319 installer::kInstallTempDir)) {
320 PLOG(ERROR) << "Failed to create Temp directory "
321 << target_path.DirName()
322 .Append(installer::kInstallTempDir).value();
323 return installer::RENAME_FAILED;
325 scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
326 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
327 install_list->AddMoveTreeWorkItem(chrome_exe.value(),
328 chrome_old_exe.value(),
329 temp_path.path().value(),
330 WorkItem::ALWAYS_MOVE);
331 install_list->AddMoveTreeWorkItem(chrome_new_exe.value(),
332 chrome_exe.value(),
333 temp_path.path().value(),
334 WorkItem::ALWAYS_MOVE);
335 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
336 // old_chrome.exe is still in use in most cases, so ignore failures here.
337 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())->
338 set_ignore_failure(true);
340 // Add work items to delete the "opv", "cpv", and "cmd" values from all
341 // products we're operating on (which including the multi-install binaries).
342 const Products& products = installer_state->products();
343 HKEY reg_root = installer_state->root_key();
344 base::string16 version_key;
345 for (Products::const_iterator it = products.begin(); it < products.end();
346 ++it) {
347 version_key = (*it)->distribution()->GetVersionKey();
348 install_list->AddDeleteRegValueWorkItem(reg_root,
349 version_key,
350 KEY_WOW64_32KEY,
351 google_update::kRegOldVersionField);
352 install_list->AddDeleteRegValueWorkItem(
353 reg_root,
354 version_key,
355 KEY_WOW64_32KEY,
356 google_update::kRegCriticalVersionField);
357 install_list->AddDeleteRegValueWorkItem(reg_root,
358 version_key,
359 KEY_WOW64_32KEY,
360 google_update::kRegRenameCmdField);
362 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
363 if (!install_list->Do()) {
364 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
365 install_list->Rollback();
366 ret = installer::RENAME_FAILED;
368 // temp_path's dtor will take care of deleting or scheduling itself for
369 // deletion at reboot when this scope closes.
370 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
372 return ret;
375 // If only the binaries are being updated, fail.
376 // If any product is being installed in single-mode that already exists in
377 // multi-mode, fail.
378 bool CheckMultiInstallConditions(const InstallationState& original_state,
379 InstallerState* installer_state,
380 installer::InstallStatus* status) {
381 const Products& products = installer_state->products();
382 DCHECK(products.size());
384 const bool system_level = installer_state->system_install();
386 if (installer_state->is_multi_install()) {
387 const Product* chrome =
388 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
389 const Product* binaries =
390 installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES);
391 const ProductState* chrome_state =
392 original_state.GetProductState(system_level,
393 BrowserDistribution::CHROME_BROWSER);
395 if (binaries) {
396 if (products.size() == 1) {
397 // There are no products aside from the binaries, so there is no update
398 // to be applied. This can happen after multi-install Chrome Frame is
399 // migrated to single-install. This is treated as an update failure
400 // unless the binaries are not in-use, in which case they will be
401 // uninstalled and success will be reported (see handling in wWinMain).
402 VLOG(1) << "No products to be updated.";
403 *status = installer::UNUSED_BINARIES;
404 installer_state->WriteInstallerResult(*status, 0, NULL);
405 return false;
407 } else {
408 // This will only be hit if --multi-install is given with no products.
409 return true;
412 if (!chrome && chrome_state) {
413 // A product other than Chrome is being installed in multi-install mode,
414 // and Chrome is already present. Add Chrome to the set of products
415 // (making it multi-install in the process) so that it is updated, too.
416 scoped_ptr<Product> multi_chrome(new Product(
417 BrowserDistribution::GetSpecificDistribution(
418 BrowserDistribution::CHROME_BROWSER)));
419 multi_chrome->SetOption(installer::kOptionMultiInstall, true);
420 chrome = installer_state->AddProduct(&multi_chrome);
421 VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
423 } // else migrate multi-install Chrome to single-install.
425 return true;
428 // Checks for compatibility between the current state of the system and the
429 // desired operation. Also applies policy that mutates the desired operation;
430 // specifically, the |installer_state| object.
431 // Also blocks simultaneous user-level and system-level installs. In the case
432 // of trying to install user-level Chrome when system-level exists, the
433 // existing system-level Chrome is launched.
434 // When the pre-install conditions are not satisfied, the result is written to
435 // the registry (via WriteInstallerResult), |status| is set appropriately, and
436 // false is returned.
437 bool CheckPreInstallConditions(const InstallationState& original_state,
438 InstallerState* installer_state,
439 installer::InstallStatus* status) {
440 // See what products are already installed in multi mode. When we do multi
441 // installs, we must upgrade all installations since they share the binaries.
442 AddExistingMultiInstalls(original_state, installer_state);
444 if (!CheckMultiInstallConditions(original_state, installer_state, status)) {
445 DCHECK_NE(*status, installer::UNKNOWN_STATUS);
446 return false;
449 const Products& products = installer_state->products();
450 if (products.empty()) {
451 // We haven't been given any products on which to operate.
452 LOG(ERROR)
453 << "Not given any products to install and no products found to update.";
454 *status = installer::CHROME_NOT_INSTALLED;
455 installer_state->WriteInstallerResult(*status,
456 IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL);
457 return false;
460 if (!installer_state->system_install()) {
461 // This is a user-level installation. Make sure that we are not installing
462 // on top of an existing system-level installation.
463 for (Products::const_iterator it = products.begin(); it < products.end();
464 ++it) {
465 const Product& product = **it;
466 BrowserDistribution* browser_dist = product.distribution();
468 // Skip over the binaries, as it's okay for them to be at both levels
469 // for different products.
470 if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES)
471 continue;
473 const ProductState* user_level_product_state =
474 original_state.GetProductState(false, browser_dist->GetType());
475 const ProductState* system_level_product_state =
476 original_state.GetProductState(true, browser_dist->GetType());
478 // Allow upgrades to proceed so that out-of-date versions are not left
479 // around.
480 if (user_level_product_state)
481 continue;
483 // This is a new user-level install...
485 if (system_level_product_state) {
486 // ... and the product already exists at system-level.
487 LOG(ERROR) << "Already installed version "
488 << system_level_product_state->version().GetString()
489 << " at system-level conflicts with this one at user-level.";
490 if (product.is_chrome()) {
491 // Instruct Google Update to launch the existing system-level Chrome.
492 // There should be no error dialog.
493 base::FilePath install_path(installer::GetChromeInstallPath(
494 true, // system
495 browser_dist));
496 if (install_path.empty()) {
497 // Give up if we failed to construct the install path.
498 *status = installer::OS_ERROR;
499 installer_state->WriteInstallerResult(*status,
500 IDS_INSTALL_OS_ERROR_BASE,
501 NULL);
502 } else {
503 *status = installer::EXISTING_VERSION_LAUNCHED;
504 base::FilePath chrome_exe =
505 install_path.Append(installer::kChromeExe);
506 base::CommandLine cmd(chrome_exe);
507 cmd.AppendSwitch(switches::kForceFirstRun);
508 installer_state->WriteInstallerResult(*status, 0, NULL);
509 VLOG(1) << "Launching existing system-level chrome instead.";
510 base::LaunchProcess(cmd, base::LaunchOptions());
512 } else {
513 // It's no longer possible for |product| to be anything other than
514 // Chrome.
515 NOTREACHED();
517 return false;
522 return true;
525 // Initializes |temp_path| to "Temp" within the target directory, and
526 // |unpack_path| to a random directory beginning with "source" within
527 // |temp_path|. Returns false on error.
528 bool CreateTemporaryAndUnpackDirectories(
529 const InstallerState& installer_state,
530 installer::SelfCleaningTempDir* temp_path,
531 base::FilePath* unpack_path) {
532 DCHECK(temp_path && unpack_path);
534 if (!temp_path->Initialize(installer_state.target_path().DirName(),
535 installer::kInstallTempDir)) {
536 PLOG(ERROR) << "Could not create temporary path.";
537 return false;
539 VLOG(1) << "Created path " << temp_path->path().value();
541 if (!base::CreateTemporaryDirInDir(temp_path->path(),
542 installer::kInstallSourceDir,
543 unpack_path)) {
544 PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
545 return false;
548 return true;
551 installer::InstallStatus UninstallProduct(
552 const InstallationState& original_state,
553 const InstallerState& installer_state,
554 const base::FilePath& setup_exe,
555 const base::CommandLine& cmd_line,
556 bool remove_all,
557 bool force_uninstall,
558 const Product& product) {
559 const ProductState* product_state =
560 original_state.GetProductState(installer_state.system_install(),
561 product.distribution()->GetType());
562 if (product_state != NULL) {
563 VLOG(1) << "version on the system: "
564 << product_state->version().GetString();
565 } else if (!force_uninstall) {
566 LOG(ERROR) << product.distribution()->GetDisplayName()
567 << " not found for uninstall.";
568 return installer::CHROME_NOT_INSTALLED;
571 return installer::UninstallProduct(
572 original_state, installer_state, setup_exe, product, remove_all,
573 force_uninstall, cmd_line);
576 installer::InstallStatus UninstallProducts(
577 const InstallationState& original_state,
578 const InstallerState& installer_state,
579 const base::FilePath& setup_exe,
580 const base::CommandLine& cmd_line) {
581 const Products& products = installer_state.products();
583 // System-level Chrome will be launched via this command if its program gets
584 // set below.
585 base::CommandLine system_level_cmd(base::CommandLine::NO_PROGRAM);
587 const Product* chrome =
588 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
589 if (chrome) {
590 // InstallerState::Initialize always puts Chrome first, and we rely on that
591 // here for this reason: if Chrome is in-use, the user will be prompted to
592 // confirm uninstallation. Upon cancel, we should not continue with the
593 // other products.
594 DCHECK(products[0]->is_chrome());
596 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
597 !installer_state.system_install()) {
598 BrowserDistribution* dist = chrome->distribution();
599 const base::FilePath system_exe_path(
600 installer::GetChromeInstallPath(true, dist)
601 .Append(installer::kChromeExe));
602 system_level_cmd.SetProgram(system_exe_path);
605 if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
606 // Chrome Binaries should be last; if something else is cancelled, they
607 // should stay.
608 DCHECK(products[products.size() - 1]->is_chrome_binaries());
611 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL;
612 installer::InstallStatus prod_status = installer::UNKNOWN_STATUS;
613 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall);
614 const bool remove_all = !cmd_line.HasSwitch(
615 installer::switches::kDoNotRemoveSharedItems);
617 for (Products::const_iterator it = products.begin();
618 install_status != installer::UNINSTALL_CANCELLED && it < products.end();
619 ++it) {
620 prod_status = UninstallProduct(original_state, installer_state, setup_exe,
621 cmd_line, remove_all, force, **it);
622 if (prod_status != installer::UNINSTALL_SUCCESSFUL)
623 install_status = prod_status;
626 installer::CleanUpInstallationDirectoryAfterUninstall(
627 original_state, installer_state, setup_exe, &install_status);
629 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
630 // delete them.
631 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path());
633 // Trigger Active Setup if it was requested for the chrome product. This needs
634 // to be done after the UninstallProduct calls as some of them might
635 // otherwise terminate the process launched by TriggerActiveSetupCommand().
636 if (chrome && cmd_line.HasSwitch(installer::switches::kTriggerActiveSetup))
637 InstallUtil::TriggerActiveSetupCommand();
639 if (!system_level_cmd.GetProgram().empty())
640 base::LaunchProcess(system_level_cmd, base::LaunchOptions());
642 // Tell Google Update that an uninstall has taken place.
643 // Ignore the return value: success or failure of Google Update
644 // has no bearing on the success or failure of Chrome's uninstallation.
645 google_update::UninstallGoogleUpdate(installer_state.system_install());
647 return install_status;
650 // Uninstall the binaries if they are the only product present and they're not
651 // in-use.
652 void UninstallBinariesIfUnused(
653 const InstallationState& original_state,
654 const InstallerState& installer_state,
655 installer::InstallStatus* install_status) {
656 // Early exit if the binaries are still in use.
657 if (*install_status != installer::UNUSED_BINARIES ||
658 installer_state.AreBinariesInUse(original_state)) {
659 return;
662 LOG(INFO) << "Uninstalling unused binaries";
663 installer_state.UpdateStage(installer::UNINSTALLING_BINARIES);
665 // Simulate the uninstall as coming from the installed version.
666 const ProductState* binaries_state =
667 original_state.GetProductState(installer_state.system_install(),
668 BrowserDistribution::CHROME_BINARIES);
669 const base::CommandLine& uninstall_cmd(binaries_state->uninstall_command());
670 MasterPreferences uninstall_prefs(uninstall_cmd);
671 InstallerState uninstall_state;
672 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, original_state);
674 *install_status = UninstallProducts(original_state, uninstall_state,
675 uninstall_cmd.GetProgram(),
676 uninstall_cmd);
678 // Report that the binaries were uninstalled if they were. This translates
679 // into a successful install return code.
680 if (IsUninstallSuccess(*install_status)) {
681 *install_status = installer::UNUSED_BINARIES_UNINSTALLED;
682 installer_state.WriteInstallerResult(*install_status, 0, NULL);
686 // This function is a short-term repair for the damage documented in
687 // http://crbug.com/456602. Briefly: canaries from 42.0.2293.0 through
688 // 42.0.2302.0 (inclusive) contained a bug that broke normal Chrome installed at
689 // user-level. This function detects the broken state during a canary update and
690 // repairs it by calling on the existing Chrome's installer to fix itself.
691 // TODO(grt): Remove this once the majority of impacted canary clients have
692 // picked it up.
693 void RepairChromeIfBroken(const InstallationState& original_state,
694 const InstallerState& installer_state) {
695 #if !defined(GOOGLE_CHROME_BUILD)
696 // Chromium does not support SxS installation, so there is no work to be done.
697 return;
698 #else // GOOGLE_CHROME_BUILD
699 // Nothing to do if not a per-user SxS install/update.
700 if (!InstallUtil::IsChromeSxSProcess() ||
701 installer_state.system_install() ||
702 installer_state.is_multi_install()) {
703 return;
706 // When running a side-by-side install, BrowserDistribution provides no way
707 // to create or access a GoogleChromeDistribution (by design).
708 static const base::char16 kChromeGuid[] =
709 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
710 static const base::char16 kChromeBinariesGuid[] =
711 L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
713 UpdatingAppRegistrationData chrome_reg_data(kChromeGuid);
714 UpdatingAppRegistrationData binaries_reg_data(kChromeBinariesGuid);
716 // Nothing to do if the binaries are installed.
717 base::win::RegKey key;
718 base::string16 version_str;
719 if (key.Open(HKEY_CURRENT_USER,
720 binaries_reg_data.GetVersionKey().c_str(),
721 KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
722 key.ReadValue(google_update::kRegVersionField,
723 &version_str) == ERROR_SUCCESS) {
724 return;
727 // Nothing to do if Chrome is not installed.
728 if (key.Open(HKEY_CURRENT_USER,
729 chrome_reg_data.GetVersionKey().c_str(),
730 KEY_QUERY_VALUE | KEY_WOW64_32KEY) != ERROR_SUCCESS ||
731 key.ReadValue(google_update::kRegVersionField,
732 &version_str) != ERROR_SUCCESS) {
733 return;
736 // Nothing to do if Chrome is not multi-install.
737 base::string16 setup_args;
738 if (key.Open(HKEY_CURRENT_USER,
739 chrome_reg_data.GetStateKey().c_str(),
740 KEY_QUERY_VALUE | KEY_WOW64_32KEY) != ERROR_SUCCESS) {
741 LOG(ERROR) << "RepairChrome: Failed to open Chrome's ClientState key.";
742 return;
744 if (key.ReadValue(installer::kUninstallArgumentsField,
745 &setup_args) != ERROR_SUCCESS) {
746 LOG(ERROR) << "RepairChrome: Failed to read Chrome's UninstallArguments.";
747 return;
749 if (setup_args.find(base::UTF8ToUTF16(installer::switches::kMultiInstall)) ==
750 base::string16::npos) {
751 LOG(INFO) << "RepairChrome: Not repairing single-install Chrome.";
752 return;
755 // Generate a command line to run Chrome's installer.
756 base::string16 setup_path;
757 if (key.ReadValue(installer::kUninstallStringField,
758 &setup_path) != ERROR_SUCCESS) {
759 LOG(ERROR) << "RepairChrome: Failed to read Chrome's UninstallString.";
760 return;
763 // Replace --uninstall with --do-not-launch-chrome to cause chrome to
764 // self-repair.
765 base::ReplaceFirstSubstringAfterOffset(
766 &setup_args, 0, base::UTF8ToUTF16(installer::switches::kUninstall),
767 base::UTF8ToUTF16(installer::switches::kDoNotLaunchChrome));
768 base::CommandLine setup_command(base::CommandLine::NO_PROGRAM);
769 InstallUtil::ComposeCommandLine(setup_path, setup_args, &setup_command);
771 // Run Chrome's installer so that it repairs itself. Break away from any job
772 // in which this operation is running so that Google Update doesn't wait
773 // around for the repair. Retry once without the attempt to break away in case
774 // this process doesn't have JOB_OBJECT_LIMIT_BREAKAWAY_OK.
775 base::LaunchOptions launch_options;
776 launch_options.force_breakaway_from_job_ = true;
777 while (true) {
778 if (base::LaunchProcess(setup_command, launch_options).IsValid()) {
779 LOG(INFO) << "RepairChrome: Launched repair command \""
780 << setup_command.GetCommandLineString() << "\"";
781 break;
782 } else {
783 PLOG(ERROR) << "RepairChrome: Failed launching repair command \""
784 << setup_command.GetCommandLineString() << "\"";
785 if (launch_options.force_breakaway_from_job_) {
786 LOG(ERROR) << "RepairChrome: Will retry without breakaway.";
787 launch_options.force_breakaway_from_job_ = false;
788 } else {
789 break;
793 #endif // GOOGLE_CHROME_BUILD
796 installer::InstallStatus InstallProducts(
797 const InstallationState& original_state,
798 const base::FilePath& setup_exe,
799 const base::CommandLine& cmd_line,
800 const MasterPreferences& prefs,
801 InstallerState* installer_state,
802 base::FilePath* installer_directory) {
803 DCHECK(installer_state);
804 const bool system_install = installer_state->system_install();
805 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
806 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
807 bool delegated_to_existing = false;
808 installer_state->UpdateStage(installer::PRECONDITIONS);
809 // The stage provides more fine-grained information than -multifail, so remove
810 // the -multifail suffix from the Google Update "ap" value.
811 BrowserDistribution::GetSpecificDistribution(installer_state->state_type())->
812 UpdateInstallStatus(system_install, archive_type, install_status);
813 if (CheckPreInstallConditions(original_state, installer_state,
814 &install_status)) {
815 VLOG(1) << "Installing to " << installer_state->target_path().value();
816 install_status = InstallProductsHelper(
817 original_state, setup_exe, cmd_line, prefs, *installer_state,
818 installer_directory, &archive_type, &delegated_to_existing);
819 } else {
820 // CheckPreInstallConditions must set the status on failure.
821 DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
824 // Delete the master preferences file if present. Note that we do not care
825 // about rollback here and we schedule for deletion on reboot if the delete
826 // fails. As such, we do not use DeleteTreeWorkItem.
827 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
828 base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
829 installer::switches::kInstallerData));
830 if (!base::DeleteFile(prefs_path, false)) {
831 LOG(ERROR) << "Failed deleting master preferences file "
832 << prefs_path.value()
833 << ", scheduling for deletion after reboot.";
834 ScheduleFileSystemEntityForDeletion(prefs_path);
838 // Early exit if this setup.exe delegated to another, since that one would
839 // have taken care of UpdateInstallStatus and UpdateStage.
840 if (delegated_to_existing)
841 return install_status;
843 const Products& products = installer_state->products();
844 for (Products::const_iterator it = products.begin(); it < products.end();
845 ++it) {
846 (*it)->distribution()->UpdateInstallStatus(
847 system_install, archive_type, install_status);
850 UninstallBinariesIfUnused(original_state, *installer_state, &install_status);
852 RepairChromeIfBroken(original_state, *installer_state);
854 installer_state->UpdateStage(installer::NO_STAGE);
855 return install_status;
858 installer::InstallStatus ShowEULADialog(const base::string16& inner_frame) {
859 VLOG(1) << "About to show EULA";
860 base::string16 eula_path = installer::GetLocalizedEulaResource();
861 if (eula_path.empty()) {
862 LOG(ERROR) << "No EULA path available";
863 return installer::EULA_REJECTED;
865 // Newer versions of the caller pass an inner frame parameter that must
866 // be given to the html page being launched.
867 installer::EulaHTMLDialog dlg(eula_path, inner_frame);
868 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal();
869 if (installer::EulaHTMLDialog::REJECTED == outcome) {
870 LOG(ERROR) << "EULA rejected or EULA failure";
871 return installer::EULA_REJECTED;
873 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) {
874 VLOG(1) << "EULA accepted (opt-in)";
875 return installer::EULA_ACCEPTED_OPT_IN;
877 VLOG(1) << "EULA accepted (no opt-in)";
878 return installer::EULA_ACCEPTED;
881 // Creates the sentinel indicating that the EULA was required and has been
882 // accepted.
883 bool CreateEULASentinel(BrowserDistribution* dist) {
884 base::FilePath eula_sentinel;
885 if (!InstallUtil::GetEULASentinelFilePath(&eula_sentinel))
886 return false;
888 return (base::CreateDirectory(eula_sentinel.DirName()) &&
889 base::WriteFile(eula_sentinel, "", 0) != -1);
892 void ActivateMetroChrome() {
893 // Check to see if we're per-user or not. Need to do this since we may
894 // not have been invoked with --system-level even for a machine install.
895 base::FilePath exe_path;
896 PathService::Get(base::FILE_EXE, &exe_path);
897 bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path);
899 base::string16 app_model_id = ShellUtil::GetBrowserModelId(
900 BrowserDistribution::GetDistribution(), is_per_user_install);
902 base::win::ScopedComPtr<IApplicationActivationManager> activator;
903 HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
904 if (SUCCEEDED(hr)) {
905 DWORD pid = 0;
906 hr = activator->ActivateApplication(
907 app_model_id.c_str(), L"open", AO_NONE, &pid);
910 LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
911 << "hr=" << std::hex << hr;
914 installer::InstallStatus RegisterDevChrome(
915 const InstallationState& original_state,
916 const InstallerState& installer_state,
917 const base::FilePath& setup_exe,
918 const base::CommandLine& cmd_line) {
919 BrowserDistribution* chrome_dist =
920 BrowserDistribution::GetSpecificDistribution(
921 BrowserDistribution::CHROME_BROWSER);
923 // Only proceed with registering a dev chrome if no real Chrome installation
924 // of the same distribution are present on this system.
925 const ProductState* existing_chrome =
926 original_state.GetProductState(false,
927 BrowserDistribution::CHROME_BROWSER);
928 if (!existing_chrome) {
929 existing_chrome =
930 original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER);
932 if (existing_chrome) {
933 static const wchar_t kPleaseUninstallYourChromeMessage[] =
934 L"You already have a full-installation (non-dev) of %1ls, please "
935 L"uninstall it first using Add/Remove Programs in the control panel.";
936 base::string16 name(chrome_dist->GetDisplayName());
937 base::string16 message(
938 base::StringPrintf(kPleaseUninstallYourChromeMessage, name.c_str()));
940 LOG(ERROR) << "Aborting operation: another installation of " << name
941 << " was found, as a last resort (if the product is not present "
942 "in Add/Remove Programs), try executing: "
943 << existing_chrome->uninstall_command().GetCommandLineString();
944 MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR);
945 return installer::INSTALL_FAILED;
948 base::FilePath chrome_exe(
949 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome));
950 if (chrome_exe.empty())
951 chrome_exe = setup_exe.DirName().Append(installer::kChromeExe);
952 if (!chrome_exe.IsAbsolute())
953 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe);
955 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS;
956 if (base::PathExists(chrome_exe)) {
957 Product chrome(chrome_dist);
959 // Create the Start menu shortcut and pin it to the Win7+ taskbar and Win10+
960 // start menu.
961 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER);
962 chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties);
963 if (InstallUtil::ShouldInstallMetroProperties())
964 shortcut_properties.set_dual_mode(true);
965 shortcut_properties.set_pin_to_taskbar(true);
966 shortcut_properties.set_pin_to_start(true);
967 ShellUtil::CreateOrUpdateShortcut(
968 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, chrome_dist,
969 shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS);
971 // Register Chrome at user-level and make it default.
972 scoped_ptr<WorkItemList> delegate_execute_list(
973 WorkItem::CreateWorkItemList());
974 installer::AddDelegateExecuteWorkItems(
975 installer_state, chrome_exe.DirName(), Version(), chrome,
976 delegate_execute_list.get());
977 delegate_execute_list->Do();
978 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
979 ShellUtil::MakeChromeDefault(chrome_dist, ShellUtil::CURRENT_USER,
980 chrome_exe, true);
981 } else {
982 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe);
984 } else {
985 LOG(ERROR) << "Path not found: " << chrome_exe.value();
986 status = installer::INSTALL_FAILED;
988 return status;
991 // This method processes any command line options that make setup.exe do
992 // various tasks other than installation (renaming chrome.exe, showing eula
993 // among others). This function returns true if any such command line option
994 // has been found and processed (so setup.exe should exit at that point).
995 bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
996 const base::FilePath& setup_exe,
997 const base::CommandLine& cmd_line,
998 InstallerState* installer_state,
999 int* exit_code) {
1000 // This option is independent of all others so doesn't belong in the if/else
1001 // block below.
1002 if (cmd_line.HasSwitch(installer::switches::kDelay)) {
1003 const std::string delay_seconds_string(
1004 cmd_line.GetSwitchValueASCII(installer::switches::kDelay));
1005 int delay_seconds;
1006 if (base::StringToInt(delay_seconds_string, &delay_seconds) &&
1007 delay_seconds > 0) {
1008 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(delay_seconds));
1012 // TODO(gab): Add a local |status| variable which each block below sets;
1013 // only determine the |exit_code| from |status| at the end (this will allow
1014 // this method to validate that
1015 // (!handled || status != installer::UNKNOWN_STATUS)).
1016 bool handled = true;
1017 // TODO(tommi): Split these checks up into functions and use a data driven
1018 // map of switch->function.
1019 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) {
1020 installer::InstallStatus status = installer::SETUP_PATCH_FAILED;
1021 // If --update-setup-exe command line option is given, we apply the given
1022 // patch to current exe, and store the resulting binary in the path
1023 // specified by --new-setup-exe. But we need to first unpack the file
1024 // given in --update-setup-exe.
1025 base::ScopedTempDir temp_path;
1026 if (!temp_path.CreateUniqueTempDir()) {
1027 PLOG(ERROR) << "Could not create temporary path.";
1028 } else {
1029 base::FilePath compressed_archive(cmd_line.GetSwitchValuePath(
1030 installer::switches::kUpdateSetupExe));
1031 VLOG(1) << "Opening archive " << compressed_archive.value();
1032 if (installer::ArchivePatchHelper::UncompressAndPatch(
1033 temp_path.path(),
1034 compressed_archive,
1035 setup_exe,
1036 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) {
1037 status = installer::NEW_VERSION_UPDATED;
1039 if (!temp_path.Delete()) {
1040 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
1041 // the Windows last-error code.
1042 LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value()
1043 << " for deletion at reboot.";
1044 ScheduleDirectoryForDeletion(temp_path.path());
1048 *exit_code = InstallUtil::GetInstallReturnCode(status);
1049 if (*exit_code) {
1050 LOG(WARNING) << "setup.exe patching failed.";
1051 installer_state->WriteInstallerResult(
1052 status, IDS_SETUP_PATCH_FAILED_BASE, NULL);
1054 // We will be exiting normally, so clear the stage indicator.
1055 installer_state->UpdateStage(installer::NO_STAGE);
1056 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) {
1057 // Check if we need to show the EULA. If it is passed as a command line
1058 // then the dialog is shown and regardless of the outcome setup exits here.
1059 base::string16 inner_frame =
1060 cmd_line.GetSwitchValueNative(installer::switches::kShowEula);
1061 *exit_code = ShowEULADialog(inner_frame);
1063 if (installer::EULA_REJECTED != *exit_code) {
1064 if (GoogleUpdateSettings::SetEULAConsent(
1065 original_state, BrowserDistribution::GetDistribution(), true)) {
1066 CreateEULASentinel(BrowserDistribution::GetDistribution());
1068 // For a metro-originated launch, we now need to launch back into metro.
1069 if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro))
1070 ActivateMetroChrome();
1072 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) {
1073 // NOTE: Should the work done here, on kConfigureUserSettings, change:
1074 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
1075 // Setup to invoke this again for all users of this install.
1076 const Product* chrome_install =
1077 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1078 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1079 if (chrome_install && installer_state->system_install()) {
1080 bool force =
1081 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
1082 installer::HandleActiveSetupForBrowser(installer_state->target_path(),
1083 *chrome_install, force);
1084 status = installer::INSTALL_REPAIRED;
1085 } else {
1086 LOG(DFATAL) << "chrome_install:" << chrome_install
1087 << ", system_install:" << installer_state->system_install();
1089 *exit_code = InstallUtil::GetInstallReturnCode(status);
1090 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) {
1091 installer::InstallStatus status = RegisterDevChrome(
1092 original_state, *installer_state, setup_exe, cmd_line);
1093 *exit_code = InstallUtil::GetInstallReturnCode(status);
1094 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) {
1095 installer::InstallStatus status = installer::UNKNOWN_STATUS;
1096 const Product* chrome_install =
1097 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1098 if (chrome_install) {
1099 // If --register-chrome-browser option is specified, register all
1100 // Chrome protocol/file associations, as well as register it as a valid
1101 // browser for Start Menu->Internet shortcut. This switch will also
1102 // register Chrome as a valid handler for a set of URL protocols that
1103 // Chrome may become the default handler for, either by the user marking
1104 // Chrome as the default browser, through the Windows Default Programs
1105 // control panel settings, or through website use of
1106 // registerProtocolHandler. These protocols are found in
1107 // ShellUtil::kPotentialProtocolAssociations.
1108 // The --register-url-protocol will additionally register Chrome as a
1109 // potential handler for the supplied protocol, and is used if a website
1110 // registers a handler for a protocol not found in
1111 // ShellUtil::kPotentialProtocolAssociations.
1112 // These options should only be used when setup.exe is launched with admin
1113 // rights. We do not make any user specific changes with this option.
1114 DCHECK(IsUserAnAdmin());
1115 base::FilePath chrome_exe(cmd_line.GetSwitchValuePath(
1116 installer::switches::kRegisterChromeBrowser));
1117 base::string16 suffix;
1118 if (cmd_line.HasSwitch(
1119 installer::switches::kRegisterChromeBrowserSuffix)) {
1120 suffix = cmd_line.GetSwitchValueNative(
1121 installer::switches::kRegisterChromeBrowserSuffix);
1123 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) {
1124 base::string16 protocol = cmd_line.GetSwitchValueNative(
1125 installer::switches::kRegisterURLProtocol);
1126 // ShellUtil::RegisterChromeForProtocol performs all registration
1127 // done by ShellUtil::RegisterChromeBrowser, as well as registering
1128 // with Windows as capable of handling the supplied protocol.
1129 if (ShellUtil::RegisterChromeForProtocol(
1130 chrome_install->distribution(), chrome_exe, suffix, protocol,
1131 false))
1132 status = installer::IN_USE_UPDATED;
1133 } else {
1134 if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(),
1135 chrome_exe, suffix, false))
1136 status = installer::IN_USE_UPDATED;
1138 } else {
1139 LOG(DFATAL) << "Can't register browser - Chrome distribution not found";
1141 *exit_code = InstallUtil::GetInstallReturnCode(status);
1142 } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) {
1143 // If --rename-chrome-exe is specified, we want to rename the executables
1144 // and exit.
1145 *exit_code = RenameChromeExecutables(original_state, installer_state);
1146 } else if (cmd_line.HasSwitch(
1147 installer::switches::kRemoveChromeRegistration)) {
1148 // This is almost reverse of --register-chrome-browser option above.
1149 // Here we delete Chrome browser registration. This option should only
1150 // be used when setup.exe is launched with admin rights. We do not
1151 // make any user specific changes in this option.
1152 base::string16 suffix;
1153 if (cmd_line.HasSwitch(
1154 installer::switches::kRegisterChromeBrowserSuffix)) {
1155 suffix = cmd_line.GetSwitchValueNative(
1156 installer::switches::kRegisterChromeBrowserSuffix);
1158 installer::InstallStatus tmp = installer::UNKNOWN_STATUS;
1159 const Product* chrome_install =
1160 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1161 DCHECK(chrome_install);
1162 if (chrome_install) {
1163 installer::DeleteChromeRegistrationKeys(*installer_state,
1164 chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp);
1166 *exit_code = tmp;
1167 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) {
1168 const Product* chrome_install =
1169 installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
1170 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
1171 if (chrome_install) {
1172 scoped_ptr<FileVersionInfo> version_info(
1173 FileVersionInfo::CreateFileVersionInfo(setup_exe));
1174 const base::Version installed_version(
1175 base::UTF16ToUTF8(version_info->product_version()));
1176 if (installed_version.IsValid()) {
1177 installer::HandleOsUpgradeForBrowser(*installer_state, *chrome_install,
1178 installed_version);
1179 status = installer::INSTALL_REPAIRED;
1180 } else {
1181 LOG(DFATAL) << "Failed to extract product version from "
1182 << setup_exe.value();
1184 } else {
1185 LOG(DFATAL) << "Chrome product not found.";
1187 *exit_code = InstallUtil::GetInstallReturnCode(status);
1188 } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) {
1189 // Launch the inactive user toast experiment.
1190 int flavor = -1;
1191 base::StringToInt(cmd_line.GetSwitchValueNative(
1192 installer::switches::kInactiveUserToast), &flavor);
1193 std::string experiment_group =
1194 cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup);
1195 DCHECK_NE(-1, flavor);
1196 if (flavor == -1) {
1197 *exit_code = installer::UNKNOWN_STATUS;
1198 } else {
1199 // This code is called (via setup.exe relaunch) only if a product is known
1200 // to run user experiments, so no check is required.
1201 const Products& products = installer_state->products();
1202 for (Products::const_iterator it = products.begin(); it < products.end();
1203 ++it) {
1204 const Product& product = **it;
1205 installer::InactiveUserToastExperiment(
1206 flavor, base::ASCIIToUTF16(experiment_group), product,
1207 installer_state->target_path());
1210 } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1211 const Products& products = installer_state->products();
1212 for (Products::const_iterator it = products.begin(); it < products.end();
1213 ++it) {
1214 const Product& product = **it;
1215 BrowserDistribution* browser_dist = product.distribution();
1216 // We started as system-level and have been re-launched as user level
1217 // to continue with the toast experiment.
1218 Version installed_version;
1219 InstallUtil::GetChromeVersion(browser_dist, true, &installed_version);
1220 if (!installed_version.IsValid()) {
1221 LOG(ERROR) << "No installation of "
1222 << browser_dist->GetDisplayName()
1223 << " found for system-level toast.";
1224 } else {
1225 product.LaunchUserExperiment(
1226 setup_exe, installer::REENTRY_SYS_UPDATE, true);
1229 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
1230 const std::string patch_type_str(
1231 cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
1232 const base::FilePath input_file(
1233 cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
1234 const base::FilePath patch_file(
1235 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
1236 const base::FilePath output_file(
1237 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));
1239 if (patch_type_str == installer::kCourgette) {
1240 *exit_code = installer::CourgettePatchFiles(input_file,
1241 patch_file,
1242 output_file);
1243 } else if (patch_type_str == installer::kBsdiff) {
1244 *exit_code = installer::BsdiffPatchFiles(input_file,
1245 patch_file,
1246 output_file);
1247 } else {
1248 *exit_code = installer::PATCH_INVALID_ARGUMENTS;
1250 } else if (cmd_line.HasSwitch(installer::switches::kReenableAutoupdates)) {
1251 // setup.exe has been asked to attempt to reenable updates for Chrome.
1252 bool updates_enabled = GoogleUpdateSettings::ReenableAutoupdates();
1253 *exit_code = updates_enabled ? installer::REENABLE_UPDATES_SUCCEEDED :
1254 installer::REENABLE_UPDATES_FAILED;
1255 } else if (cmd_line.HasSwitch(
1256 installer::switches::kSetDisplayVersionProduct)) {
1257 const base::string16 registry_product(
1258 cmd_line.GetSwitchValueNative(
1259 installer::switches::kSetDisplayVersionProduct));
1260 const base::string16 registry_value(
1261 cmd_line.GetSwitchValueNative(
1262 installer::switches::kSetDisplayVersionValue));
1263 *exit_code = OverwriteDisplayVersions(registry_product, registry_value);
1264 } else {
1265 handled = false;
1268 return handled;
1271 // Returns the Custom information for the client identified by the exe path
1272 // passed in. This information is used for crash reporting.
1273 google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) {
1274 base::string16 version;
1275 scoped_ptr<FileVersionInfo> version_info(
1276 FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path)));
1277 if (version_info.get())
1278 version = version_info->product_version();
1280 if (version.empty())
1281 version = L"0.1.0.0";
1283 // Report crashes under the same product name as the browser. This string
1284 // MUST match server-side configuration.
1285 base::string16 product(base::ASCIIToUTF16(PRODUCT_SHORTNAME_STRING));
1286 static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str());
1287 static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str());
1288 static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
1289 static google_breakpad::CustomInfoEntry type_entry(L"ptype",
1290 L"Chrome Installer");
1291 static google_breakpad::CustomInfoEntry entries[] = {
1292 ver_entry, prod_entry, plat_entry, type_entry };
1293 static google_breakpad::CustomClientInfo custom_info = {
1294 entries, arraysize(entries) };
1295 return &custom_info;
1298 // Initialize crash reporting for this process. This involves connecting to
1299 // breakpad, etc.
1300 scoped_ptr<google_breakpad::ExceptionHandler> InitializeCrashReporting(
1301 bool system_install) {
1302 // Only report crashes if the user allows it.
1303 if (!GoogleUpdateSettings::GetCollectStatsConsent())
1304 return scoped_ptr<google_breakpad::ExceptionHandler>();
1306 // Get the alternate dump directory. We use the temp path.
1307 base::FilePath temp_directory;
1308 if (!base::GetTempDir(&temp_directory) || temp_directory.empty())
1309 return scoped_ptr<google_breakpad::ExceptionHandler>();
1311 wchar_t exe_path[MAX_PATH * 2] = {0};
1312 GetModuleFileName(NULL, exe_path, arraysize(exe_path));
1314 // Build the pipe name. It can be either:
1315 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
1316 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
1317 base::string16 user_sid = kSystemPrincipalSid;
1319 if (!system_install) {
1320 if (!base::win::GetUserSidString(&user_sid)) {
1321 return scoped_ptr<google_breakpad::ExceptionHandler>();
1325 base::string16 pipe_name = kGoogleUpdatePipeName;
1326 pipe_name += user_sid;
1328 #ifdef _WIN64
1329 // The protocol for connecting to the out-of-process Breakpad crash
1330 // reporter is different for x86-32 and x86-64: the message sizes
1331 // are different because the message struct contains a pointer. As
1332 // a result, there are two different named pipes to connect to. The
1333 // 64-bit one is distinguished with an "-x64" suffix.
1334 pipe_name += L"-x64";
1335 #endif
1337 return scoped_ptr<google_breakpad::ExceptionHandler>(
1338 new google_breakpad::ExceptionHandler(
1339 temp_directory.value(), NULL, NULL, NULL,
1340 google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
1341 pipe_name.c_str(), GetCustomInfo(exe_path)));
1344 // Uninstalls multi-install Chrome Frame if the current operation is a
1345 // multi-install install or update. The operation is performed directly rather
1346 // than delegated to the existing install since there is no facility in older
1347 // versions of setup.exe to uninstall GCF without touching the binaries. The
1348 // binaries will be uninstalled during later processing if they are not in-use
1349 // (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
1350 // updated to reflect the state of the world following the operation.
1351 void UninstallMultiChromeFrameIfPresent(const base::CommandLine& cmd_line,
1352 const MasterPreferences& prefs,
1353 InstallationState* original_state,
1354 InstallerState* installer_state) {
1355 // Early exit if not installing or updating.
1356 if (installer_state->operation() == InstallerState::UNINSTALL)
1357 return;
1359 // Early exit if Chrome Frame is not present as multi-install.
1360 const ProductState* chrome_frame_state =
1361 original_state->GetProductState(installer_state->system_install(),
1362 BrowserDistribution::CHROME_FRAME);
1363 if (!chrome_frame_state || !chrome_frame_state->is_multi_install())
1364 return;
1366 LOG(INFO) << "Uninstalling multi-install Chrome Frame.";
1367 installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME);
1369 // Uninstall Chrome Frame without touching the multi-install binaries.
1370 // Simulate the uninstall as coming from the installed version.
1371 const base::CommandLine& uninstall_cmd(
1372 chrome_frame_state->uninstall_command());
1373 MasterPreferences uninstall_prefs(uninstall_cmd);
1374 InstallerState uninstall_state;
1375 uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state);
1376 // Post M32, uninstall_prefs and uninstall_state won't have Chrome Frame in
1377 // them since they've lost the power to do Chrome Frame installs.
1378 const Product* chrome_frame_product = uninstall_state.AddProductFromState(
1379 BrowserDistribution::CHROME_FRAME, *chrome_frame_state);
1380 if (chrome_frame_product) {
1381 // No shared state should be left behind.
1382 const bool remove_all = true;
1383 // Don't accept no for an answer.
1384 const bool force_uninstall = true;
1385 installer::InstallStatus uninstall_status =
1386 installer::UninstallProduct(*original_state, uninstall_state,
1387 uninstall_cmd.GetProgram(),
1388 *chrome_frame_product, remove_all,
1389 force_uninstall, cmd_line);
1391 VLOG(1) << "Uninstallation of Chrome Frame returned status "
1392 << uninstall_status;
1393 } else {
1394 LOG(ERROR) << "Chrome Frame not found for uninstall.";
1397 // Refresh state for the continuation of the original install/update.
1398 original_state->Initialize();
1399 installer_state->Initialize(cmd_line, prefs, *original_state);
1402 } // namespace
1404 namespace installer {
1406 InstallStatus InstallProductsHelper(const InstallationState& original_state,
1407 const base::FilePath& setup_exe,
1408 const base::CommandLine& cmd_line,
1409 const MasterPreferences& prefs,
1410 const InstallerState& installer_state,
1411 base::FilePath* installer_directory,
1412 ArchiveType* archive_type,
1413 bool* delegated_to_existing) {
1414 DCHECK(archive_type);
1415 DCHECK(delegated_to_existing);
1416 const bool system_install = installer_state.system_install();
1417 InstallStatus install_status = UNKNOWN_STATUS;
1419 // Drop to background processing mode if the process was started below the
1420 // normal process priority class.
1421 bool entered_background_mode = AdjustProcessPriority();
1422 VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
1424 // Create a temp folder where we will unpack Chrome archive. If it fails,
1425 // then we are doomed, so return immediately and no cleanup is required.
1426 SelfCleaningTempDir temp_path;
1427 base::FilePath unpack_path;
1428 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
1429 &unpack_path)) {
1430 installer_state.WriteInstallerResult(TEMP_DIR_FAILED,
1431 IDS_INSTALL_TEMP_DIR_FAILED_BASE,
1432 NULL);
1433 return TEMP_DIR_FAILED;
1436 // Uncompress and optionally patch the archive if an uncompressed archive was
1437 // not specified on the command line and a compressed archive is found.
1438 *archive_type = UNKNOWN_ARCHIVE_TYPE;
1439 base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
1440 switches::kUncompressedArchive));
1441 if (uncompressed_archive.empty()) {
1442 base::Version previous_version;
1443 if (cmd_line.HasSwitch(installer::switches::kPreviousVersion)) {
1444 previous_version = base::Version(cmd_line.GetSwitchValueASCII(
1445 installer::switches::kPreviousVersion));
1448 scoped_ptr<ArchivePatchHelper> archive_helper(
1449 CreateChromeArchiveHelper(setup_exe, cmd_line, installer_state,
1450 unpack_path));
1451 if (archive_helper) {
1452 VLOG(1) << "Installing Chrome from compressed archive "
1453 << archive_helper->compressed_archive().value();
1454 if (!UncompressAndPatchChromeArchive(original_state,
1455 installer_state,
1456 archive_helper.get(),
1457 archive_type,
1458 &install_status,
1459 previous_version)) {
1460 DCHECK_NE(install_status, UNKNOWN_STATUS);
1461 return install_status;
1463 uncompressed_archive = archive_helper->target();
1464 DCHECK(!uncompressed_archive.empty());
1468 // Check for an uncompressed archive alongside the current executable if one
1469 // was not given or generated.
1470 if (uncompressed_archive.empty())
1471 uncompressed_archive = setup_exe.DirName().Append(kChromeArchive);
1473 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
1474 // An archive was not uncompressed or patched above.
1475 if (uncompressed_archive.empty() ||
1476 !base::PathExists(uncompressed_archive)) {
1477 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
1478 installer_state.WriteInstallerResult(
1479 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1480 return INVALID_ARCHIVE;
1482 *archive_type = FULL_ARCHIVE_TYPE;
1485 // Unpack the uncompressed archive.
1486 if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
1487 unpack_path.value(),
1488 NULL)) {
1489 installer_state.WriteInstallerResult(
1490 UNPACKING_FAILED,
1491 IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
1492 NULL);
1493 return UNPACKING_FAILED;
1496 VLOG(1) << "unpacked to " << unpack_path.value();
1497 base::FilePath src_path(
1498 unpack_path.Append(kInstallSourceChromeDir));
1499 scoped_ptr<Version>
1500 installer_version(GetMaxVersionFromArchiveDir(src_path));
1501 if (!installer_version.get()) {
1502 LOG(ERROR) << "Did not find any valid version in installer.";
1503 install_status = INVALID_ARCHIVE;
1504 installer_state.WriteInstallerResult(install_status,
1505 IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
1506 } else {
1507 VLOG(1) << "version to install: " << installer_version->GetString();
1508 bool proceed_with_installation = true;
1510 if (installer_state.operation() == InstallerState::MULTI_INSTALL) {
1511 // This is a new install of a multi-install product. Rather than give up
1512 // in case a higher version of the binaries (including a single-install
1513 // of Chrome, which can safely be migrated to multi-install by way of
1514 // CheckMultiInstallConditions) is already installed, delegate to the
1515 // installed setup.exe to install the product at hand.
1516 base::FilePath existing_setup_exe;
1517 if (GetExistingHigherInstaller(original_state, system_install,
1518 *installer_version, &existing_setup_exe)) {
1519 VLOG(1) << "Deferring to existing installer.";
1520 installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION);
1521 if (DeferToExistingInstall(existing_setup_exe, cmd_line,
1522 installer_state, temp_path.path(),
1523 &install_status)) {
1524 *delegated_to_existing = true;
1525 return install_status;
1530 uint32 higher_products = 0;
1531 COMPILE_ASSERT(
1532 sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES,
1533 too_many_distribution_types_);
1534 const Products& products = installer_state.products();
1535 for (Products::const_iterator it = products.begin(); it < products.end();
1536 ++it) {
1537 const Product& product = **it;
1538 const ProductState* product_state =
1539 original_state.GetProductState(system_install,
1540 product.distribution()->GetType());
1541 if (product_state != NULL &&
1542 (product_state->version().CompareTo(*installer_version) > 0)) {
1543 LOG(ERROR) << "Higher version of "
1544 << product.distribution()->GetDisplayName()
1545 << " is already installed.";
1546 higher_products |= (1 << product.distribution()->GetType());
1550 if (higher_products != 0) {
1551 COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 3,
1552 add_support_for_new_products_here_);
1553 int message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
1554 proceed_with_installation = false;
1555 install_status = HIGHER_VERSION_EXISTS;
1556 installer_state.WriteInstallerResult(install_status, message_id, NULL);
1559 if (proceed_with_installation) {
1560 base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
1561 switches::kInstallerData));
1562 install_status = InstallOrUpdateProduct(
1563 original_state, installer_state, setup_exe, uncompressed_archive,
1564 temp_path.path(), src_path, prefs_source_path, prefs,
1565 *installer_version);
1567 int install_msg_base = IDS_INSTALL_FAILED_BASE;
1568 base::FilePath chrome_exe;
1569 base::string16 quoted_chrome_exe;
1570 if (install_status == SAME_VERSION_REPAIR_FAILED) {
1571 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
1572 } else if (install_status != INSTALL_FAILED) {
1573 if (installer_state.target_path().empty()) {
1574 // If we failed to construct install path, it means the OS call to
1575 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1576 install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
1577 install_status = OS_ERROR;
1578 } else {
1579 chrome_exe = installer_state.target_path().Append(kChromeExe);
1580 quoted_chrome_exe = L"\"" + chrome_exe.value() + L"\"";
1581 install_msg_base = 0;
1585 installer_state.UpdateStage(FINISHING);
1587 // Only do Chrome-specific stuff (like launching the browser) if
1588 // Chrome was specifically requested (rather than being upgraded as
1589 // part of a multi-install).
1590 const Product* chrome_install = prefs.install_chrome() ?
1591 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) :
1592 NULL;
1594 bool do_not_register_for_update_launch = false;
1595 if (chrome_install) {
1596 prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch,
1597 &do_not_register_for_update_launch);
1598 } else {
1599 do_not_register_for_update_launch = true; // Never register.
1602 bool write_chrome_launch_string =
1603 (!do_not_register_for_update_launch &&
1604 install_status != IN_USE_UPDATED);
1606 installer_state.WriteInstallerResult(install_status, install_msg_base,
1607 write_chrome_launch_string ? &quoted_chrome_exe : NULL);
1609 if (install_status == FIRST_INSTALL_SUCCESS) {
1610 VLOG(1) << "First install successful.";
1611 if (chrome_install) {
1612 // We never want to launch Chrome in system level install mode.
1613 bool do_not_launch_chrome = false;
1614 prefs.GetBool(master_preferences::kDoNotLaunchChrome,
1615 &do_not_launch_chrome);
1616 if (!system_install && !do_not_launch_chrome)
1617 chrome_install->LaunchChrome(installer_state.target_path());
1619 } else if ((install_status == NEW_VERSION_UPDATED) ||
1620 (install_status == IN_USE_UPDATED)) {
1621 const Product* chrome = installer_state.FindProduct(
1622 BrowserDistribution::CHROME_BROWSER);
1623 if (chrome != NULL) {
1624 DCHECK_NE(chrome_exe.value(), base::string16());
1625 RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe);
1631 // There might be an experiment (for upgrade usually) that needs to happen.
1632 // An experiment's outcome can include chrome's uninstallation. If that is
1633 // the case we would not do that directly at this point but in another
1634 // instance of setup.exe
1636 // There is another way to reach this same function if this is a system
1637 // level install. See HandleNonInstallCmdLineOptions().
1639 // If installation failed, use the path to the currently running setup.
1640 // If installation succeeded, use the path to setup in the installer dir.
1641 base::FilePath setup_path(setup_exe);
1642 if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1643 setup_path = installer_state.GetInstallerDirectory(*installer_version)
1644 .Append(setup_path.BaseName());
1646 const Products& products = installer_state.products();
1647 for (Products::const_iterator it = products.begin(); it < products.end();
1648 ++it) {
1649 const Product& product = **it;
1650 product.LaunchUserExperiment(setup_path, install_status, system_install);
1654 // If the installation completed successfully...
1655 if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1656 // Update the DisplayVersion created by an MSI-based install.
1657 std::string install_id;
1658 if (prefs.GetString(installer::master_preferences::kMsiProductId,
1659 &install_id)) {
1660 base::FilePath new_setup =
1661 installer_state.GetInstallerDirectory(*installer_version)
1662 .Append(kSetupExe);
1663 DelayedOverwriteDisplayVersions(
1664 new_setup, install_id, *installer_version);
1666 // Return the path to the directory containing the newly installed
1667 // setup.exe and uncompressed archive if the caller requested it.
1668 if (installer_directory) {
1669 *installer_directory =
1670 installer_state.GetInstallerDirectory(*installer_version);
1674 // temp_path's dtor will take care of deleting or scheduling itself for
1675 // deletion at reboot when this scope closes.
1676 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
1678 return install_status;
1681 } // namespace installer
1683 int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
1684 wchar_t* command_line, int show_command) {
1685 // Check to see if the CPU is supported before doing anything else. There's
1686 // very little than can safely be accomplished if the CPU isn't supported
1687 // since dependent libraries (e.g., base) may use invalid instructions.
1688 if (!installer::IsProcessorSupported())
1689 return installer::CPU_NOT_SUPPORTED;
1691 // The exit manager is in charge of calling the dtors of singletons.
1692 base::AtExitManager exit_manager;
1693 base::CommandLine::Init(0, NULL);
1695 // install_util uses chrome paths.
1696 chrome::RegisterPathProvider();
1698 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
1699 installer::InitInstallerLogging(prefs);
1701 const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
1702 VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString();
1704 VLOG(1) << "multi install is " << prefs.is_multi_install();
1705 bool system_install = false;
1706 prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install);
1707 VLOG(1) << "system install is " << system_install;
1709 scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
1710 InitializeCrashReporting(system_install));
1712 InstallationState original_state;
1713 original_state.Initialize();
1715 InstallerState installer_state;
1716 installer_state.Initialize(cmd_line, prefs, original_state);
1717 const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall);
1719 // Check to make sure current system is WinXP or later. If not, log
1720 // error message and get out.
1721 if (!InstallUtil::IsOSSupported()) {
1722 LOG(ERROR) << "Chrome only supports Windows XP or later.";
1723 installer_state.WriteInstallerResult(
1724 installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL);
1725 return installer::OS_NOT_SUPPORTED;
1728 // Initialize COM for use later.
1729 base::win::ScopedCOMInitializer com_initializer;
1730 if (!com_initializer.succeeded()) {
1731 installer_state.WriteInstallerResult(
1732 installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL);
1733 return installer::OS_ERROR;
1736 // Some command line options don't work with SxS install/uninstall
1737 if (InstallUtil::IsChromeSxSProcess()) {
1738 if (system_install ||
1739 prefs.is_multi_install() ||
1740 cmd_line.HasSwitch(installer::switches::kSelfDestruct) ||
1741 cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) ||
1742 cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) ||
1743 cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) ||
1744 cmd_line.HasSwitch(installer::switches::kInactiveUserToast) ||
1745 cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
1746 return installer::SXS_OPTION_NOT_SUPPORTED;
1750 // Some command line options are no longer supported and must error out.
1751 if (installer::ContainsUnsupportedSwitch(cmd_line))
1752 return installer::UNSUPPORTED_OPTION;
1754 // A variety of installer operations require the path to the current
1755 // executable. Get it once here for use throughout these operations. Note that
1756 // the path service is the authoritative source for this path. One might think
1757 // that CommandLine::GetProgram would suffice, but it won't since
1758 // CreateProcess may have been called with a command line that is somewhat
1759 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file
1760 // extension), in which case CommandLineToArgv will not yield an argv with the
1761 // true path to the program at position 0.
1762 base::FilePath setup_exe;
1763 PathService::Get(base::FILE_EXE, &setup_exe);
1765 int exit_code = 0;
1766 if (HandleNonInstallCmdLineOptions(
1767 original_state, setup_exe, cmd_line, &installer_state, &exit_code)) {
1768 return exit_code;
1771 if (system_install && !IsUserAnAdmin()) {
1772 if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
1773 !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1774 base::CommandLine new_cmd(base::CommandLine::NO_PROGRAM);
1775 new_cmd.AppendArguments(cmd_line, true);
1776 // Append --run-as-admin flag to let the new instance of setup.exe know
1777 // that we already tried to launch ourselves as admin.
1778 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1779 // If system_install became true due to an environment variable, append
1780 // it to the command line here since env vars may not propagate past the
1781 // elevation.
1782 if (!new_cmd.HasSwitch(installer::switches::kSystemLevel))
1783 new_cmd.AppendSwitch(installer::switches::kSystemLevel);
1785 DWORD exit_code = installer::UNKNOWN_STATUS;
1786 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1787 return exit_code;
1788 } else {
1789 LOG(ERROR) << "Non admin user can not install system level Chrome.";
1790 installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS,
1791 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL);
1792 return installer::INSUFFICIENT_RIGHTS;
1796 UninstallMultiChromeFrameIfPresent(cmd_line, prefs,
1797 &original_state, &installer_state);
1799 base::FilePath installer_directory;
1800 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
1801 // If --uninstall option is given, uninstall the identified product(s)
1802 if (is_uninstall) {
1803 install_status =
1804 UninstallProducts(original_state, installer_state, setup_exe, cmd_line);
1805 } else {
1806 // If --uninstall option is not specified, we assume it is install case.
1807 install_status =
1808 InstallProducts(original_state, setup_exe, cmd_line, prefs,
1809 &installer_state, &installer_directory);
1812 // Validate that the machine is now in a good state following the operation.
1813 // TODO(grt): change this to log at DFATAL once we're convinced that the
1814 // validator handles all cases properly.
1815 InstallationValidator::InstallationType installation_type =
1816 InstallationValidator::NO_PRODUCTS;
1817 LOG_IF(ERROR,
1818 !InstallationValidator::ValidateInstallationType(system_install,
1819 &installation_type));
1821 int return_code = 0;
1822 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1823 // rollback the action. If we're uninstalling we want to avoid this, so always
1824 // report success, squashing any more informative return codes.
1825 if (!(installer_state.is_msi() && is_uninstall)) {
1826 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1827 // to pass through, since this is only returned on uninstall which is
1828 // never invoked directly by Google Update.
1829 return_code = InstallUtil::GetInstallReturnCode(install_status);
1832 VLOG(1) << "Installation complete, returning: " << return_code;
1834 return return_code;