1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/first_run/upgrade_util.h"
6
7#include <algorithm>
8#include <string>
9
10#include "base/base_paths.h"
11#include "base/command_line.h"
12#include "base/environment.h"
13#include "base/file_path.h"
14#include "base/file_util.h"
15#include "base/logging.h"
16#include "base/path_service.h"
17#include "base/process_util.h"
18#include "base/win/registry.h"
19#include "base/win/scoped_comptr.h"
20#include "chrome/browser/first_run/upgrade_util_win.h"
21#include "chrome/common/chrome_constants.h"
22#include "chrome/installer/util/browser_distribution.h"
23#include "chrome/installer/util/google_update_constants.h"
24#include "chrome/installer/util/install_util.h"
25#include "chrome/installer/util/shell_util.h"
26#include "chrome/installer/util/util_constants.h"
27#include "google_update_idl.h"
28
29namespace {
30
31bool GetNewerChromeFile(FilePath* path) {
32  if (!PathService::Get(base::DIR_EXE, path))
33    return false;
34  *path = path->Append(installer::kChromeNewExe);
35  return true;
36}
37
38bool InvokeGoogleUpdateForRename() {
39  base::win::ScopedComPtr<IProcessLauncher> ipl;
40  if (!FAILED(ipl.CreateInstance(__uuidof(ProcessLauncherClass)))) {
41    ULONG_PTR phandle = NULL;
42    DWORD id = GetCurrentProcessId();
43    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
44    if (!FAILED(ipl->LaunchCmdElevated(dist->GetAppGuid().c_str(),
45                                       google_update::kRegRenameCmdField,
46                                       id,
47                                       &phandle))) {
48      HANDLE handle = HANDLE(phandle);
49      WaitForSingleObject(handle, INFINITE);
50      DWORD exit_code;
51      ::GetExitCodeProcess(handle, &exit_code);
52      ::CloseHandle(handle);
53      if (exit_code == installer::RENAME_SUCCESSFUL)
54        return true;
55    }
56  }
57  return false;
58}
59
60}  // namespace
61
62namespace upgrade_util {
63
64bool RelaunchChromeBrowser(const CommandLine& command_line) {
65  scoped_ptr<base::Environment> env(base::Environment::Create());
66  env->UnSetVar(chrome::kChromeVersionEnvVar);
67  return base::LaunchApp(
68      command_line.command_line_string(), false, false, NULL);
69}
70
71bool IsUpdatePendingRestart() {
72  FilePath new_chrome_exe;
73  if (!GetNewerChromeFile(&new_chrome_exe))
74    return false;
75  return file_util::PathExists(new_chrome_exe);
76}
77
78bool SwapNewChromeExeIfPresent() {
79  FilePath new_chrome_exe;
80  if (!GetNewerChromeFile(&new_chrome_exe))
81    return false;
82  if (!file_util::PathExists(new_chrome_exe))
83    return false;
84  FilePath cur_chrome_exe;
85  if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe))
86    return false;
87
88  // First try to rename exe by launching rename command ourselves.
89  bool user_install =
90      InstallUtil::IsPerUserInstall(cur_chrome_exe.value().c_str());
91  HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
92  BrowserDistribution *dist = BrowserDistribution::GetDistribution();
93  base::win::RegKey key;
94  std::wstring rename_cmd;
95  if ((key.Open(reg_root, dist->GetVersionKey().c_str(),
96                KEY_READ) == ERROR_SUCCESS) &&
97      (key.ReadValue(google_update::kRegRenameCmdField,
98                     &rename_cmd) == ERROR_SUCCESS)) {
99    base::ProcessHandle handle;
100    if (base::LaunchApp(rename_cmd, true, true, &handle)) {
101      DWORD exit_code;
102      ::GetExitCodeProcess(handle, &exit_code);
103      ::CloseHandle(handle);
104      if (exit_code == installer::RENAME_SUCCESSFUL)
105        return true;
106    }
107  }
108
109  // Rename didn't work so try to rename by calling Google Update
110  return InvokeGoogleUpdateForRename();
111}
112
113bool DoUpgradeTasks(const CommandLine& command_line) {
114  if (!SwapNewChromeExeIfPresent())
115    return false;
116  // At this point the chrome.exe has been swapped with the new one.
117  if (!RelaunchChromeBrowser(command_line)) {
118    // The re-launch fails. Feel free to panic now.
119    NOTREACHED();
120  }
121  return true;
122}
123
124}  // namespace upgrade_util
125