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