| OLD | NEW |
| (Empty) |
| 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_frame/ready_mode/internal/registry_ready_mode_state.h" | |
| 6 | |
| 7 #include <windows.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/process/launch.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "base/strings/stringprintf.h" | |
| 13 #include "base/time/time.h" | |
| 14 #include "base/win/registry.h" | |
| 15 #include "base/win/scoped_comptr.h" | |
| 16 #include "base/win/scoped_handle.h" | |
| 17 #include "base/win/windows_version.h" | |
| 18 #include "chrome/installer/util/browser_distribution.h" | |
| 19 #include "chrome/installer/util/google_update_constants.h" | |
| 20 #include "chrome/installer/util/master_preferences.h" | |
| 21 #include "chrome/installer/util/util_constants.h" | |
| 22 #include "chrome_frame/chrome_launcher_utils.h" | |
| 23 #include "chrome_frame/ready_mode/ready_mode.h" | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // Looks up a command entry in the registry and attempts to execute it directly. | |
| 28 // Returns the new process handle, which the caller is responsible for closing, | |
| 29 // or NULL upon failure. | |
| 30 HANDLE LaunchCommandDirectly(const std::wstring& command_field) { | |
| 31 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); | |
| 32 std::wstring version_key_name(dist->GetVersionKey()); | |
| 33 | |
| 34 HKEY roots[] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}; | |
| 35 | |
| 36 for (int i = 0; i < arraysize(roots); i++) { | |
| 37 base::win::RegKey version_key; | |
| 38 if (version_key.Open(roots[i], version_key_name.c_str(), | |
| 39 KEY_QUERY_VALUE) == ERROR_SUCCESS) { | |
| 40 std::wstring command_line; | |
| 41 if (version_key.ReadValue(command_field.c_str(), | |
| 42 &command_line) == ERROR_SUCCESS) { | |
| 43 base::win::ScopedHandle launched_process; | |
| 44 base::LaunchOptions options; | |
| 45 options.start_hidden = true; | |
| 46 if (base::LaunchProcess(command_line, options, &launched_process)) { | |
| 47 return launched_process.Take(); | |
| 48 } | |
| 49 } | |
| 50 } | |
| 51 } | |
| 52 return NULL; | |
| 53 } | |
| 54 | |
| 55 // Attempts to launch a command using the ProcessLauncher. Returns a handle to | |
| 56 // the launched process, which the caller is responsible for closing, or NULL | |
| 57 // upon failure. | |
| 58 HANDLE LaunchCommandViaProcessLauncher(const std::wstring& command_field) { | |
| 59 HANDLE launched_process = NULL; | |
| 60 | |
| 61 scoped_ptr<CommandLine> command_line; | |
| 62 if (chrome_launcher::CreateUpdateCommandLine(command_field, &command_line)) { | |
| 63 DCHECK(command_line != NULL); | |
| 64 base::LaunchOptions options; | |
| 65 options.start_hidden = true; | |
| 66 base::LaunchProcess(*command_line, options, &launched_process); | |
| 67 } | |
| 68 | |
| 69 return launched_process; | |
| 70 } | |
| 71 | |
| 72 // Waits for the provided process to exit, and verifies that its exit code | |
| 73 // corresponds to one of the known "success" codes for the installer. If the | |
| 74 // exit code cannot be retrieved, or if it signals failure, returns false. | |
| 75 bool CheckProcessExitCode(HANDLE handle) { | |
| 76 // TODO(erikwright): Use RegisterWaitForSingleObject to wait | |
| 77 // asynchronously. | |
| 78 DWORD wait_result = WaitForSingleObject(handle, 5000); // (ms) | |
| 79 | |
| 80 if (wait_result == WAIT_OBJECT_0) { | |
| 81 DWORD exit_code = 0; | |
| 82 if (!::GetExitCodeProcess(handle, &exit_code)) { | |
| 83 DPLOG(ERROR) << "GetExitCodeProcess failed."; | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 // These are the only two success codes returned by the installer. | |
| 88 // All other codes are errors. | |
| 89 if (exit_code != 0 && exit_code != installer::UNINSTALL_REQUIRES_REBOOT) { | |
| 90 DLOG(ERROR) << "Process failed with exit code " << exit_code << "."; | |
| 91 return false; | |
| 92 } | |
| 93 | |
| 94 return true; | |
| 95 } | |
| 96 | |
| 97 if (wait_result == WAIT_FAILED) | |
| 98 DPLOG(ERROR) << "Error while waiting for elevated child process."; | |
| 99 | |
| 100 if (wait_result == WAIT_ABANDONED) | |
| 101 DLOG(ERROR) << "Unexpeced WAIT_ABANDONED while waiting on child process."; | |
| 102 | |
| 103 if (wait_result == WAIT_TIMEOUT) | |
| 104 DLOG(ERROR) << "Timeout while waiting on child process."; | |
| 105 | |
| 106 return false; | |
| 107 } | |
| 108 | |
| 109 // If we are running on XP (no protected mode) or in a high-integrity process, | |
| 110 // we can invoke the installer directly. If not, we will have to go via the | |
| 111 // ProcessLauncher. | |
| 112 bool CanLaunchDirectly() { | |
| 113 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
| 114 return true; | |
| 115 | |
| 116 base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN; | |
| 117 if (!base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(), | |
| 118 &integrity_level)) { | |
| 119 DLOG(ERROR) << "Failed to determine process integrity level."; | |
| 120 return false; | |
| 121 } | |
| 122 | |
| 123 return integrity_level == base::HIGH_INTEGRITY; | |
| 124 } | |
| 125 | |
| 126 // Attempts to launch the specified command either directly or via the | |
| 127 // ProcessLauncher. Returns true if the command is launched and returns a | |
| 128 // success code. | |
| 129 bool LaunchAndCheckCommand(const std::wstring& command_field) { | |
| 130 base::win::ScopedHandle handle; | |
| 131 | |
| 132 if (CanLaunchDirectly()) | |
| 133 handle.Set(LaunchCommandDirectly(command_field)); | |
| 134 else | |
| 135 handle.Set(LaunchCommandViaProcessLauncher(command_field)); | |
| 136 | |
| 137 if (handle.IsValid() && CheckProcessExitCode(handle)) | |
| 138 return true; | |
| 139 | |
| 140 DLOG(ERROR) << "Command " << command_field << " could not be launched."; | |
| 141 return false; | |
| 142 } | |
| 143 | |
| 144 } // namespace | |
| 145 | |
| 146 RegistryReadyModeState::RegistryReadyModeState( | |
| 147 const std::wstring& key_name, base::TimeDelta temporary_decline_duration, | |
| 148 Observer* observer) | |
| 149 : key_name_(key_name), | |
| 150 temporary_decline_duration_(temporary_decline_duration), | |
| 151 observer_(observer) { | |
| 152 } | |
| 153 | |
| 154 RegistryReadyModeState::~RegistryReadyModeState() { | |
| 155 } | |
| 156 | |
| 157 base::Time RegistryReadyModeState::GetNow() { | |
| 158 return base::Time::Now(); | |
| 159 } | |
| 160 | |
| 161 ReadyModeStatus RegistryReadyModeState::GetStatus() { | |
| 162 bool exists = false; | |
| 163 int64 value = 0; | |
| 164 | |
| 165 if (!GetStateFromRegistry(&value, &exists)) | |
| 166 return READY_MODE_ACTIVE; | |
| 167 | |
| 168 if (!exists) | |
| 169 return READY_MODE_ACCEPTED; | |
| 170 | |
| 171 if (value == 0) | |
| 172 return READY_MODE_PERMANENTLY_DECLINED; | |
| 173 | |
| 174 if (value == 1) | |
| 175 return READY_MODE_ACTIVE; | |
| 176 | |
| 177 base::Time when_declined(base::Time::FromInternalValue(value)); | |
| 178 base::Time now(GetNow()); | |
| 179 | |
| 180 // If the decline duration has passed, or is further in the future than | |
| 181 // the total timeout, consider it expired. | |
| 182 bool expired = (now - when_declined) > temporary_decline_duration_ || | |
| 183 (when_declined - now) > temporary_decline_duration_; | |
| 184 | |
| 185 if (expired) | |
| 186 return READY_MODE_TEMPORARY_DECLINE_EXPIRED; | |
| 187 else | |
| 188 return READY_MODE_TEMPORARILY_DECLINED; | |
| 189 } | |
| 190 | |
| 191 void RegistryReadyModeState::NotifyObserver() { | |
| 192 if (observer_ != NULL) | |
| 193 observer_->OnStateChange(GetStatus()); | |
| 194 } | |
| 195 | |
| 196 bool RegistryReadyModeState::GetStateFromRegistry(int64* value, bool* exists) { | |
| 197 *exists = false; | |
| 198 *value = 0; | |
| 199 | |
| 200 HKEY roots[] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }; | |
| 201 LONG result = ERROR_SUCCESS; | |
| 202 for (int i = 0; i < arraysize(roots); i++) { | |
| 203 base::win::RegKey config_key; | |
| 204 result = config_key.Open(roots[i], key_name_.c_str(), KEY_QUERY_VALUE); | |
| 205 if (result == ERROR_SUCCESS) { | |
| 206 result = config_key.ReadInt64(installer::kChromeFrameReadyModeField, | |
| 207 value); | |
| 208 if (result == ERROR_SUCCESS) { | |
| 209 *exists = true; | |
| 210 return true; | |
| 211 } | |
| 212 if (result != ERROR_FILE_NOT_FOUND) { | |
| 213 DLOG(ERROR) << "Failed to read from registry key " << key_name_ | |
| 214 << " and value " << installer::kChromeFrameReadyModeField | |
| 215 << " error: " << result; | |
| 216 return false; | |
| 217 } | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 224 void RegistryReadyModeState::RefreshStateAndNotify() { | |
| 225 HRESULT hr = UrlMkSetSessionOption(URLMON_OPTION_USERAGENT_REFRESH, | |
| 226 NULL, 0, 0); | |
| 227 if (FAILED(hr)) { | |
| 228 DLOG(ERROR) << "Failed to refresh user agent string from registry. " | |
| 229 << "UrlMkSetSessionOption returned " | |
| 230 << base::StringPrintf("0x%08x", hr); | |
| 231 } else { | |
| 232 NotifyObserver(); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 void RegistryReadyModeState::ExpireTemporaryDecline() { | |
| 237 if (LaunchAndCheckCommand(google_update::kRegCFEndTempOptOutCmdField)) | |
| 238 RefreshStateAndNotify(); | |
| 239 } | |
| 240 | |
| 241 void RegistryReadyModeState::TemporarilyDeclineChromeFrame() { | |
| 242 if (LaunchAndCheckCommand(google_update::kRegCFTempOptOutCmdField)) | |
| 243 RefreshStateAndNotify(); | |
| 244 } | |
| 245 | |
| 246 void RegistryReadyModeState::PermanentlyDeclineChromeFrame() { | |
| 247 if (LaunchAndCheckCommand(google_update::kRegCFOptOutCmdField)) | |
| 248 RefreshStateAndNotify(); | |
| 249 } | |
| 250 | |
| 251 void RegistryReadyModeState::AcceptChromeFrame() { | |
| 252 if (LaunchAndCheckCommand(google_update::kRegCFOptInCmdField)) | |
| 253 NotifyObserver(); | |
| 254 } | |
| OLD | NEW |