- add sources.
[platform/framework/web/crosswalk.git] / src / chrome_frame / ready_mode / internal / registry_ready_mode_state.cc
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         HANDLE launched_process = NULL;
44         base::LaunchOptions options;
45         options.start_hidden = true;
46         if (base::LaunchProcess(command_line, options, &launched_process)) {
47           return launched_process;
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 }