OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "win8/delegate_execute/chrome_util.h" | 5 #include "win8/delegate_execute/chrome_util.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <atlbase.h> | 8 #include <atlbase.h> |
9 #include <shlobj.h> | 9 #include <shlobj.h> |
10 | 10 |
11 #include <algorithm> | 11 #include <algorithm> |
12 #include <limits> | 12 #include <limits> |
13 #include <string> | 13 #include <string> |
14 | 14 |
15 #include "base/file_util.h" | 15 #include "base/file_util.h" |
16 #include "base/files/file_path.h" | 16 #include "base/files/file_path.h" |
17 #include "base/md5.h" | 17 #include "base/md5.h" |
18 #include "base/process/kill.h" | 18 #include "base/process/kill.h" |
19 #include "base/process/launch.h" | 19 #include "base/process/launch.h" |
20 #include "base/process/process_handle.h" | 20 #include "base/process/process_handle.h" |
21 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
22 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
23 #include "base/win/registry.h" | 23 #include "base/win/registry.h" |
24 #include "base/win/scoped_comptr.h" | 24 #include "base/win/scoped_comptr.h" |
25 #include "base/win/scoped_handle.h" | 25 #include "base/win/scoped_handle.h" |
26 #include "base/win/win_util.h" | 26 #include "base/win/win_util.h" |
| 27 #include "chrome/installer/util/browser_distribution.h" |
| 28 #include "chrome/installer/util/install_util.h" |
| 29 #include "chrome/installer/util/util_constants.h" |
27 #include "google_update/google_update_idl.h" | 30 #include "google_update/google_update_idl.h" |
28 | 31 |
29 namespace { | 32 namespace { |
30 | 33 |
31 #if defined(GOOGLE_CHROME_BUILD) | 34 #if defined(GOOGLE_CHROME_BUILD) |
32 const wchar_t kAppUserModelId[] = L"Chrome"; | |
33 #else // GOOGLE_CHROME_BUILD | |
34 const wchar_t kAppUserModelId[] = L"Chromium"; | |
35 #endif // GOOGLE_CHROME_BUILD | |
36 | |
37 #if defined(GOOGLE_CHROME_BUILD) | |
38 | 35 |
39 // TODO(grt): These constants live in installer_util. Consider moving them | 36 // TODO(grt): These constants live in installer_util. Consider moving them |
40 // into common_constants to allow for reuse. | 37 // into common_constants to allow for reuse. |
41 const base::FilePath::CharType kNewChromeExe[] = | 38 const base::FilePath::CharType kNewChromeExe[] = |
42 FILE_PATH_LITERAL("new_chrome.exe"); | 39 FILE_PATH_LITERAL("new_chrome.exe"); |
43 const wchar_t kRenameCommandValue[] = L"cmd"; | 40 const wchar_t kRenameCommandValue[] = L"cmd"; |
44 const wchar_t kChromeAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; | 41 const wchar_t kRegPathChromeClientBase[] = |
45 const wchar_t kRegPathChromeClient[] = | 42 L"Software\\Google\\Update\\Clients\\"; |
46 L"Software\\Google\\Update\\Clients\\" | |
47 L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; | |
48 const int kExitCodeRenameSuccessful = 23; | |
49 | 43 |
50 // Returns the name of the global event used to detect if |chrome_exe| is in | 44 // Returns the name of the global event used to detect if |chrome_exe| is in |
51 // use by a browser process. | 45 // use by a browser process. |
52 // TODO(grt): Move this somewhere central so it can be used by both this | 46 // TODO(grt): Move this somewhere central so it can be used by both this |
53 // IsBrowserRunning (below) and IsBrowserAlreadyRunning (browser_util_win.cc). | 47 // IsBrowserRunning (below) and IsBrowserAlreadyRunning (browser_util_win.cc). |
54 string16 GetEventName(const base::FilePath& chrome_exe) { | 48 string16 GetEventName(const base::FilePath& chrome_exe) { |
55 static wchar_t const kEventPrefix[] = L"Global\\"; | 49 static wchar_t const kEventPrefix[] = L"Global\\"; |
56 const size_t prefix_len = arraysize(kEventPrefix) - 1; | 50 const size_t prefix_len = arraysize(kEventPrefix) - 1; |
57 string16 name; | 51 string16 name; |
58 name.reserve(prefix_len + chrome_exe.value().size()); | 52 name.reserve(prefix_len + chrome_exe.value().size()); |
(...skipping 22 matching lines...) Expand all Loading... |
81 | 75 |
82 // Returns true if the file new_chrome.exe exists in the same directory as | 76 // Returns true if the file new_chrome.exe exists in the same directory as |
83 // |chrome_exe|. | 77 // |chrome_exe|. |
84 bool NewChromeExeExists(const base::FilePath& chrome_exe) { | 78 bool NewChromeExeExists(const base::FilePath& chrome_exe) { |
85 base::FilePath new_chrome_exe(chrome_exe.DirName().Append(kNewChromeExe)); | 79 base::FilePath new_chrome_exe(chrome_exe.DirName().Append(kNewChromeExe)); |
86 return base::PathExists(new_chrome_exe); | 80 return base::PathExists(new_chrome_exe); |
87 } | 81 } |
88 | 82 |
89 bool GetUpdateCommand(bool is_per_user, string16* update_command) { | 83 bool GetUpdateCommand(bool is_per_user, string16* update_command) { |
90 const HKEY root = is_per_user ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; | 84 const HKEY root = is_per_user ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; |
91 base::win::RegKey key(root, kRegPathChromeClient, KEY_QUERY_VALUE); | 85 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| 86 string16 reg_path_chrome_client = kRegPathChromeClientBase; |
| 87 reg_path_chrome_client.append(dist->GetAppGuid()); |
| 88 base::win::RegKey key(root, reg_path_chrome_client.c_str(), KEY_QUERY_VALUE); |
92 | 89 |
93 return key.ReadValue(kRenameCommandValue, update_command) == ERROR_SUCCESS; | 90 return key.ReadValue(kRenameCommandValue, update_command) == ERROR_SUCCESS; |
94 } | 91 } |
95 | 92 |
96 #endif // GOOGLE_CHROME_BUILD | 93 #endif // GOOGLE_CHROME_BUILD |
97 | 94 |
98 // TODO(grt): This code also lives in installer_util. Refactor for reuse. | |
99 bool IsPerUserInstall(const base::FilePath& chrome_exe) { | |
100 wchar_t program_files_path[MAX_PATH] = {0}; | |
101 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, | |
102 SHGFP_TYPE_CURRENT, program_files_path))) { | |
103 return !StartsWith(chrome_exe.value().c_str(), program_files_path, false); | |
104 } else { | |
105 NOTREACHED(); | |
106 } | |
107 return true; | |
108 } | |
109 | |
110 // TODO(gab): This code also lives in shell_util. Refactor for reuse. | |
111 string16 ByteArrayToBase32(const uint8* bytes, size_t size) { | |
112 static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; | |
113 | |
114 // Eliminate special cases first. | |
115 if (size == 0) { | |
116 return string16(); | |
117 } else if (size == 1) { | |
118 string16 ret; | |
119 ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]); | |
120 ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]); | |
121 return ret; | |
122 } else if (size >= std::numeric_limits<size_t>::max() / 8) { | |
123 // If |size| is too big, the calculation of |encoded_length| below will | |
124 // overflow. | |
125 AtlTrace("%hs. Byte array is too long.\n", __FUNCTION__); | |
126 return string16(); | |
127 } | |
128 | |
129 // Overestimate the number of bits in the string by 4 so that dividing by 5 | |
130 // is the equivalent of rounding up the actual number of bits divided by 5. | |
131 const size_t encoded_length = (size * 8 + 4) / 5; | |
132 | |
133 string16 ret; | |
134 ret.reserve(encoded_length); | |
135 | |
136 // A bit stream which will be read from the left and appended to from the | |
137 // right as it's emptied. | |
138 uint16 bit_stream = (bytes[0] << 8) + bytes[1]; | |
139 size_t next_byte_index = 2; | |
140 int free_bits = 0; | |
141 while (free_bits < 16) { | |
142 // Extract the 5 leftmost bits in the stream | |
143 ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]); | |
144 bit_stream <<= 5; | |
145 free_bits += 5; | |
146 | |
147 // If there is enough room in the bit stream, inject another byte (if there | |
148 // are any left...). | |
149 if (free_bits >= 8 && next_byte_index < size) { | |
150 free_bits -= 8; | |
151 bit_stream += bytes[next_byte_index++] << free_bits; | |
152 } | |
153 } | |
154 | |
155 if (ret.length() != encoded_length) { | |
156 AtlTrace("%hs. Encoding doesn't match expected length.\n", __FUNCTION__); | |
157 return string16(); | |
158 } | |
159 return ret; | |
160 } | |
161 | |
162 // TODO(gab): This code also lives in shell_util. Refactor for reuse. | |
163 bool GetUserSpecificRegistrySuffix(string16* suffix) { | |
164 string16 user_sid; | |
165 if (!base::win::GetUserSidString(&user_sid)) { | |
166 AtlTrace("%hs. GetUserSidString failed.\n", __FUNCTION__); | |
167 return false; | |
168 } | |
169 COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_); | |
170 base::MD5Digest md5_digest; | |
171 std::string user_sid_ascii(UTF16ToASCII(user_sid)); | |
172 base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest); | |
173 const string16 base32_md5( | |
174 ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a))); | |
175 // The value returned by the base32 algorithm above must never change and must | |
176 // always be 26 characters long (i.e. if someone ever moves this to base and | |
177 // implements the full base32 algorithm (i.e. with appended '=' signs in the | |
178 // output), they must provide a flag to allow this method to still request | |
179 // the output with no appended '=' signs). | |
180 if (base32_md5.length() != 26U) { | |
181 AtlTrace("%hs. Base32 encoding of md5 hash is incorrect.\n", __FUNCTION__); | |
182 return false; | |
183 } | |
184 suffix->reserve(base32_md5.length() + 1); | |
185 suffix->assign(1, L'.'); | |
186 suffix->append(base32_md5); | |
187 return true; | |
188 } | |
189 | |
190 } // namespace | 95 } // namespace |
191 | 96 |
192 namespace delegate_execute { | 97 namespace delegate_execute { |
193 | 98 |
194 void UpdateChromeIfNeeded(const base::FilePath& chrome_exe) { | 99 void UpdateChromeIfNeeded(const base::FilePath& chrome_exe) { |
195 #if defined(GOOGLE_CHROME_BUILD) | 100 #if defined(GOOGLE_CHROME_BUILD) |
196 // Nothing to do if a browser is already running or if there's no | 101 // Nothing to do if a browser is already running or if there's no |
197 // new_chrome.exe. | 102 // new_chrome.exe. |
198 if (IsBrowserRunning(chrome_exe) || !NewChromeExeExists(chrome_exe)) | 103 if (IsBrowserRunning(chrome_exe) || !NewChromeExeExists(chrome_exe)) |
199 return; | 104 return; |
200 | 105 |
201 base::ProcessHandle process_handle = base::kNullProcessHandle; | 106 base::ProcessHandle process_handle = base::kNullProcessHandle; |
202 | 107 |
203 if (IsPerUserInstall(chrome_exe)) { | 108 if (InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) { |
204 // Read the update command from the registry. | 109 // Read the update command from the registry. |
205 string16 update_command; | 110 string16 update_command; |
206 if (!GetUpdateCommand(true, &update_command)) { | 111 if (!GetUpdateCommand(true, &update_command)) { |
207 AtlTrace("%hs. Failed to read update command from registry.\n", | 112 AtlTrace("%hs. Failed to read update command from registry.\n", |
208 __FUNCTION__); | 113 __FUNCTION__); |
209 } else { | 114 } else { |
210 // Run the update command. | 115 // Run the update command. |
211 base::LaunchOptions launch_options; | 116 base::LaunchOptions launch_options; |
212 launch_options.start_hidden = true; | 117 launch_options.start_hidden = true; |
213 if (!base::LaunchProcess(update_command, launch_options, | 118 if (!base::LaunchProcess(update_command, launch_options, |
214 &process_handle)) { | 119 &process_handle)) { |
215 AtlTrace("%hs. Failed to launch command to finalize update; " | 120 AtlTrace("%hs. Failed to launch command to finalize update; " |
216 "error %u.\n", __FUNCTION__, ::GetLastError()); | 121 "error %u.\n", __FUNCTION__, ::GetLastError()); |
217 process_handle = base::kNullProcessHandle; | 122 process_handle = base::kNullProcessHandle; |
218 } | 123 } |
219 } | 124 } |
220 } else { | 125 } else { |
221 // Run the update command via Google Update. | 126 // Run the update command via Google Update. |
222 HRESULT hr = S_OK; | 127 HRESULT hr = S_OK; |
223 base::win::ScopedComPtr<IProcessLauncher> process_launcher; | 128 base::win::ScopedComPtr<IProcessLauncher> process_launcher; |
224 hr = process_launcher.CreateInstance(__uuidof(ProcessLauncherClass)); | 129 hr = process_launcher.CreateInstance(__uuidof(ProcessLauncherClass)); |
225 if (FAILED(hr)) { | 130 if (FAILED(hr)) { |
226 AtlTrace("%hs. Failed to Create ProcessLauncher; hr=0x%X.\n", | 131 AtlTrace("%hs. Failed to Create ProcessLauncher; hr=0x%X.\n", |
227 __FUNCTION__, hr); | 132 __FUNCTION__, hr); |
228 } else { | 133 } else { |
229 ULONG_PTR handle = 0; | 134 ULONG_PTR handle = 0; |
| 135 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
230 hr = process_launcher->LaunchCmdElevated( | 136 hr = process_launcher->LaunchCmdElevated( |
231 kChromeAppGuid, kRenameCommandValue, GetCurrentProcessId(), &handle); | 137 dist->GetAppGuid().c_str(), kRenameCommandValue, |
| 138 GetCurrentProcessId(), &handle); |
232 if (FAILED(hr)) { | 139 if (FAILED(hr)) { |
233 AtlTrace("%hs. Failed to launch command to finalize update; " | 140 AtlTrace("%hs. Failed to launch command to finalize update; " |
234 "hr=0x%X.\n", __FUNCTION__, hr); | 141 "hr=0x%X.\n", __FUNCTION__, hr); |
235 } else { | 142 } else { |
236 process_handle = reinterpret_cast<base::ProcessHandle>(handle); | 143 process_handle = reinterpret_cast<base::ProcessHandle>(handle); |
237 } | 144 } |
238 } | 145 } |
239 } | 146 } |
240 | 147 |
241 // Wait for the update to complete and report the results. | 148 // Wait for the update to complete and report the results. |
242 if (process_handle != base::kNullProcessHandle) { | 149 if (process_handle != base::kNullProcessHandle) { |
243 int exit_code = 0; | 150 int exit_code = 0; |
244 // WaitForExitCode will close the handle in all cases. | 151 // WaitForExitCode will close the handle in all cases. |
245 if (!base::WaitForExitCode(process_handle, &exit_code)) { | 152 if (!base::WaitForExitCode(process_handle, &exit_code)) { |
246 AtlTrace("%hs. Failed to get result when finalizing update.\n", | 153 AtlTrace("%hs. Failed to get result when finalizing update.\n", |
247 __FUNCTION__); | 154 __FUNCTION__); |
248 } else if (exit_code != kExitCodeRenameSuccessful) { | 155 } else if (exit_code != installer::RENAME_SUCCESSFUL) { |
249 AtlTrace("%hs. Failed to finalize update with exit code %d.\n", | 156 AtlTrace("%hs. Failed to finalize update with exit code %d.\n", |
250 __FUNCTION__, exit_code); | 157 __FUNCTION__, exit_code); |
251 } else { | 158 } else { |
252 AtlTrace("%hs. Finalized pending update.\n", __FUNCTION__); | 159 AtlTrace("%hs. Finalized pending update.\n", __FUNCTION__); |
253 } | 160 } |
254 } | 161 } |
255 #endif | 162 #endif |
256 } | 163 } |
257 | 164 |
258 // TODO(gab): This code also lives in shell_util. Refactor for reuse. | |
259 string16 GetAppId(const base::FilePath& chrome_exe) { | |
260 string16 app_id(kAppUserModelId); | |
261 string16 suffix; | |
262 if (IsPerUserInstall(chrome_exe) && | |
263 !GetUserSpecificRegistrySuffix(&suffix)) { | |
264 AtlTrace("%hs. GetUserSpecificRegistrySuffix failed.\n", | |
265 __FUNCTION__); | |
266 } | |
267 return app_id.append(suffix); | |
268 } | |
269 | |
270 } // delegate_execute | 165 } // delegate_execute |
OLD | NEW |