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 "chrome/browser/google/google_update.h" | |
6 | |
7 #include <atlbase.h> | |
8 #include <atlcom.h> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/file_path.h" | |
12 #include "base/message_loop.h" | |
13 #include "base/path_service.h" | |
14 #include "base/string_util.h" | |
15 #include "base/stringprintf.h" | |
16 #include "base/threading/thread.h" | |
17 #include "base/win/scoped_comptr.h" | |
18 #include "base/win/windows_version.h" | |
19 #include "chrome/installer/util/browser_distribution.h" | |
20 #include "chrome/installer/util/google_update_settings.h" | |
21 #include "chrome/installer/util/helper.h" | |
22 #include "chrome/installer/util/install_util.h" | |
23 #include "content/public/browser/browser_thread.h" | |
24 #include "google_update/google_update_idl.h" | |
25 #include "grit/generated_resources.h" | |
26 #include "ui/base/l10n/l10n_util.h" | |
27 #include "ui/views/widget/widget.h" | |
28 | |
29 using content::BrowserThread; | |
30 | |
31 namespace { | |
32 | |
33 // Check if the currently running instance can be updated by Google Update. | |
34 // Returns GOOGLE_UPDATE_NO_ERROR only if the instance running is a Google | |
35 // Chrome distribution installed in a standard location. | |
36 GoogleUpdateErrorCode CanUpdateCurrentChrome( | |
37 const FilePath& chrome_exe_path) { | |
38 #if !defined(GOOGLE_CHROME_BUILD) | |
39 return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY; | |
40 #else | |
41 // TODO(tommi): Check if using the default distribution is always the right | |
42 // thing to do. | |
43 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); | |
44 FilePath user_exe_path = installer::GetChromeInstallPath(false, dist); | |
45 FilePath machine_exe_path = installer::GetChromeInstallPath(true, dist); | |
46 if (!FilePath::CompareEqualIgnoreCase(chrome_exe_path.value(), | |
47 user_exe_path.value()) && | |
48 !FilePath::CompareEqualIgnoreCase(chrome_exe_path.value(), | |
49 machine_exe_path.value())) { | |
50 LOG(ERROR) << L"Google Update cannot update Chrome installed in a " | |
51 << L"non-standard location: " << chrome_exe_path.value().c_str() | |
52 << L". The standard location is: " | |
53 << user_exe_path.value().c_str() | |
54 << L" or " << machine_exe_path.value().c_str() << L"."; | |
55 return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY; | |
56 } | |
57 | |
58 string16 app_guid = installer::GetAppGuidForUpdates( | |
59 !InstallUtil::IsPerUserInstall(chrome_exe_path.value().c_str())); | |
60 DCHECK(!app_guid.empty()); | |
61 | |
62 if (GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, NULL) == | |
63 GoogleUpdateSettings::UPDATES_DISABLED) | |
64 return GOOGLE_UPDATE_DISABLED_BY_POLICY; | |
65 | |
66 return GOOGLE_UPDATE_NO_ERROR; | |
67 #endif | |
68 } | |
69 | |
70 // Creates an instance of a COM Local Server class using either plain vanilla | |
71 // CoCreateInstance, or using the Elevation moniker if running on Vista. | |
72 // hwnd must refer to a foregound window in order to get the UAC prompt | |
73 // showing up in the foreground if running on Vista. It can also be NULL if | |
74 // background UAC prompts are desired. | |
75 HRESULT CoCreateInstanceAsAdmin(REFCLSID class_id, REFIID interface_id, | |
76 HWND hwnd, void** interface_ptr) { | |
77 if (!interface_ptr) | |
78 return E_POINTER; | |
79 | |
80 // For Vista we need to instantiate the COM server via the elevation | |
81 // moniker. This ensures that the UAC dialog shows up. | |
82 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
83 wchar_t class_id_as_string[MAX_PATH] = {0}; | |
84 StringFromGUID2(class_id, class_id_as_string, | |
85 arraysize(class_id_as_string)); | |
86 | |
87 string16 elevation_moniker_name = | |
88 base::StringPrintf(L"Elevation:Administrator!new:%ls", | |
89 class_id_as_string); | |
90 | |
91 BIND_OPTS3 bind_opts; | |
92 memset(&bind_opts, 0, sizeof(bind_opts)); | |
93 bind_opts.cbStruct = sizeof(bind_opts); | |
94 bind_opts.dwClassContext = CLSCTX_LOCAL_SERVER; | |
95 bind_opts.hwnd = hwnd; | |
96 | |
97 return CoGetObject(elevation_moniker_name.c_str(), &bind_opts, | |
98 interface_id, reinterpret_cast<void**>(interface_ptr)); | |
99 } | |
100 | |
101 return CoCreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER, | |
102 interface_id, | |
103 reinterpret_cast<void**>(interface_ptr)); | |
104 } | |
105 | |
106 | |
107 } // namespace | |
108 | |
109 //////////////////////////////////////////////////////////////////////////////// | |
110 // | |
111 // The GoogleUpdateJobObserver COM class is responsible for receiving status | |
112 // reports from google Update. It keeps track of the progress as Google Update | |
113 // notifies us and ends the message loop we are spinning in once Google Update | |
114 // reports that it is done. | |
115 // | |
116 //////////////////////////////////////////////////////////////////////////////// | |
117 class GoogleUpdateJobObserver | |
118 : public CComObjectRootEx<CComSingleThreadModel>, | |
119 public IJobObserver { | |
120 public: | |
121 BEGIN_COM_MAP(GoogleUpdateJobObserver) | |
122 COM_INTERFACE_ENTRY(IJobObserver) | |
123 END_COM_MAP() | |
124 | |
125 GoogleUpdateJobObserver() | |
126 : result_(UPGRADE_ERROR) { | |
127 } | |
128 virtual ~GoogleUpdateJobObserver() {} | |
129 | |
130 // Notifications from Google Update: | |
131 STDMETHOD(OnShow)() { | |
132 return S_OK; | |
133 } | |
134 STDMETHOD(OnCheckingForUpdate)() { | |
135 result_ = UPGRADE_CHECK_STARTED; | |
136 return S_OK; | |
137 } | |
138 STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) { | |
139 result_ = UPGRADE_IS_AVAILABLE; | |
140 new_version_ = version_string; | |
141 return S_OK; | |
142 } | |
143 STDMETHOD(OnWaitingToDownload)() { | |
144 return S_OK; | |
145 } | |
146 STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) { | |
147 return S_OK; | |
148 } | |
149 STDMETHOD(OnWaitingToInstall)() { | |
150 return S_OK; | |
151 } | |
152 STDMETHOD(OnInstalling)() { | |
153 result_ = UPGRADE_STARTED; | |
154 return S_OK; | |
155 } | |
156 STDMETHOD(OnPause)() { | |
157 return S_OK; | |
158 } | |
159 STDMETHOD(OnComplete)(LegacyCompletionCodes code, const TCHAR* text) { | |
160 switch (code) { | |
161 case COMPLETION_CODE_SUCCESS_CLOSE_UI: | |
162 case COMPLETION_CODE_SUCCESS: { | |
163 if (result_ == UPGRADE_STARTED) | |
164 result_ = UPGRADE_SUCCESSFUL; | |
165 else if (result_ == UPGRADE_CHECK_STARTED) | |
166 result_ = UPGRADE_ALREADY_UP_TO_DATE; | |
167 break; | |
168 } | |
169 case COMPLETION_CODE_ERROR: | |
170 error_message_ = text; | |
171 default: { | |
172 NOTREACHED(); | |
173 result_ = UPGRADE_ERROR; | |
174 break; | |
175 } | |
176 } | |
177 | |
178 event_sink_ = NULL; | |
179 | |
180 // We no longer need to spin the message loop that we started spinning in | |
181 // InitiateGoogleUpdateCheck. | |
182 MessageLoop::current()->Quit(); | |
183 return S_OK; | |
184 } | |
185 STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) { | |
186 event_sink_ = event_sink; | |
187 return S_OK; | |
188 } | |
189 | |
190 // Returns the results of the update operation. | |
191 STDMETHOD(GetResult)(GoogleUpdateUpgradeResult* result) { | |
192 // Intermediary steps should never be reported to the client. | |
193 DCHECK(result_ != UPGRADE_STARTED && result_ != UPGRADE_CHECK_STARTED); | |
194 | |
195 *result = result_; | |
196 return S_OK; | |
197 } | |
198 | |
199 // Returns which version Google Update found on the server (if a more | |
200 // recent version was found). Otherwise, this will be blank. | |
201 STDMETHOD(GetVersionInfo)(string16* version_string) { | |
202 *version_string = new_version_; | |
203 return S_OK; | |
204 } | |
205 | |
206 // Returns the Google Update supplied error string that describes the error | |
207 // that occurred during the update check/upgrade. | |
208 STDMETHOD(GetErrorMessage)(string16* error_message) { | |
209 *error_message = error_message_; | |
210 return S_OK; | |
211 } | |
212 | |
213 private: | |
214 // The status/result of the Google Update operation. | |
215 GoogleUpdateUpgradeResult result_; | |
216 | |
217 // The version string Google Update found. | |
218 string16 new_version_; | |
219 | |
220 // An error message, if any. | |
221 string16 error_message_; | |
222 | |
223 // Allows us control the upgrade process to a small degree. After OnComplete | |
224 // has been called, this object can not be used. | |
225 base::win::ScopedComPtr<IProgressWndEvents> event_sink_; | |
226 }; | |
227 | |
228 //////////////////////////////////////////////////////////////////////////////// | |
229 // GoogleUpdate, public: | |
230 | |
231 GoogleUpdate::GoogleUpdate() | |
232 : listener_(NULL) { | |
233 } | |
234 | |
235 GoogleUpdate::~GoogleUpdate() { | |
236 } | |
237 | |
238 void GoogleUpdate::CheckForUpdate(bool install_if_newer, | |
239 views::Widget* window) { | |
240 // We need to shunt this request over to InitiateGoogleUpdateCheck and have | |
241 // it run in the file thread. | |
242 BrowserThread::PostTask( | |
243 BrowserThread::FILE, FROM_HERE, | |
244 base::Bind(&GoogleUpdate::InitiateGoogleUpdateCheck, this, | |
245 install_if_newer, window, MessageLoop::current())); | |
246 } | |
247 | |
248 //////////////////////////////////////////////////////////////////////////////// | |
249 // GoogleUpdate, private: | |
250 | |
251 void GoogleUpdate::InitiateGoogleUpdateCheck(bool install_if_newer, | |
252 views::Widget* window, | |
253 MessageLoop* main_loop) { | |
254 FilePath chrome_exe; | |
255 if (!PathService::Get(base::DIR_EXE, &chrome_exe)) | |
256 NOTREACHED(); | |
257 | |
258 GoogleUpdateErrorCode error_code = CanUpdateCurrentChrome(chrome_exe); | |
259 if (error_code != GOOGLE_UPDATE_NO_ERROR) { | |
260 main_loop->PostTask( | |
261 FROM_HERE, | |
262 base::Bind(&GoogleUpdate::ReportResults, this, | |
263 UPGRADE_ERROR, error_code, string16())); | |
264 return; | |
265 } | |
266 | |
267 CComObject<GoogleUpdateJobObserver>* job_observer; | |
268 HRESULT hr = | |
269 CComObject<GoogleUpdateJobObserver>::CreateInstance(&job_observer); | |
270 if (hr != S_OK) { | |
271 // Most of the error messages come straight from Google Update. This one is | |
272 // deemed worthy enough to also warrant its own error. | |
273 GoogleUpdateErrorCode error = GOOGLE_UPDATE_JOB_SERVER_CREATION_FAILED; | |
274 string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr); | |
275 ReportFailure( | |
276 hr, error, | |
277 l10n_util::GetStringFUTF16(IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, | |
278 error_code), | |
279 main_loop); | |
280 return; | |
281 } | |
282 | |
283 base::win::ScopedComPtr<IJobObserver> job_holder(job_observer); | |
284 | |
285 base::win::ScopedComPtr<IGoogleUpdate> on_demand; | |
286 | |
287 bool system_level = false; | |
288 | |
289 if (InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) { | |
290 hr = on_demand.CreateInstance(CLSID_OnDemandUserAppsClass); | |
291 } else { | |
292 // The Update operation needs Admin privileges for writing | |
293 // to %ProgramFiles%. On Vista we need to elevate before instantiating | |
294 // the updater instance. | |
295 if (!install_if_newer) { | |
296 hr = on_demand.CreateInstance(CLSID_OnDemandMachineAppsClass); | |
297 } else { | |
298 HWND foreground_hwnd = NULL; | |
299 if (window != NULL) { | |
300 foreground_hwnd = window->GetNativeWindow(); | |
301 } | |
302 | |
303 hr = CoCreateInstanceAsAdmin(CLSID_OnDemandMachineAppsClass, | |
304 IID_IGoogleUpdate, foreground_hwnd, | |
305 reinterpret_cast<void**>(on_demand.Receive())); | |
306 } | |
307 system_level = true; | |
308 } | |
309 | |
310 if (hr != S_OK) { | |
311 GoogleUpdateErrorCode error = GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND; | |
312 string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr); | |
313 if (system_level) | |
314 error_code += L" -- system level"; | |
315 ReportFailure(hr, error, | |
316 l10n_util::GetStringFUTF16( | |
317 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, | |
318 error_code), | |
319 main_loop); | |
320 return; | |
321 } | |
322 | |
323 string16 app_guid = installer::GetAppGuidForUpdates(system_level); | |
324 DCHECK(!app_guid.empty()); | |
325 | |
326 if (!install_if_newer) | |
327 hr = on_demand->CheckForUpdate(app_guid.c_str(), job_observer); | |
328 else | |
329 hr = on_demand->Update(app_guid.c_str(), job_observer); | |
330 | |
331 if (hr != S_OK) { | |
332 GoogleUpdateErrorCode error = GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR; | |
333 string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr); | |
334 ReportFailure(hr, error, | |
335 l10n_util::GetStringFUTF16( | |
336 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, | |
337 error_code), | |
338 main_loop); | |
339 return; | |
340 } | |
341 | |
342 // We need to spin the message loop while Google Update is running so that it | |
343 // can report back to us through GoogleUpdateJobObserver. This message loop | |
344 // will terminate once Google Update sends us the completion status | |
345 // (success/error). See OnComplete(). | |
346 MessageLoop::current()->Run(); | |
347 | |
348 GoogleUpdateUpgradeResult results; | |
349 hr = job_observer->GetResult(&results); | |
350 | |
351 if (hr != S_OK) { | |
352 GoogleUpdateErrorCode error = GOOGLE_UPDATE_GET_RESULT_CALL_FAILED; | |
353 string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr); | |
354 ReportFailure(hr, error, | |
355 l10n_util::GetStringFUTF16( | |
356 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, | |
357 error_code), | |
358 main_loop); | |
359 return; | |
360 } | |
361 | |
362 if (results == UPGRADE_ERROR) { | |
363 string16 error_message; | |
364 job_observer->GetErrorMessage(&error_message); | |
365 ReportFailure(hr, GOOGLE_UPDATE_ERROR_UPDATING, error_message, main_loop); | |
366 return; | |
367 } | |
368 | |
369 hr = job_observer->GetVersionInfo(&version_available_); | |
370 if (hr != S_OK) { | |
371 GoogleUpdateErrorCode error = GOOGLE_UPDATE_GET_VERSION_INFO_FAILED; | |
372 string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr); | |
373 ReportFailure(hr, error, | |
374 l10n_util::GetStringFUTF16( | |
375 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, | |
376 error_code), | |
377 main_loop); | |
378 return; | |
379 } | |
380 | |
381 main_loop->PostTask( | |
382 FROM_HERE, | |
383 base::Bind(&GoogleUpdate::ReportResults, this, | |
384 results, GOOGLE_UPDATE_NO_ERROR, string16())); | |
385 job_holder = NULL; | |
386 on_demand = NULL; | |
387 } | |
388 | |
389 void GoogleUpdate::ReportResults(GoogleUpdateUpgradeResult results, | |
390 GoogleUpdateErrorCode error_code, | |
391 const string16& error_message) { | |
392 // If we get an error, then error code must not be blank, and vice versa. | |
393 DCHECK(results == UPGRADE_ERROR ? error_code != GOOGLE_UPDATE_NO_ERROR : | |
394 error_code == GOOGLE_UPDATE_NO_ERROR); | |
395 if (listener_) { | |
396 listener_->OnReportResults( | |
397 results, error_code, error_message, version_available_); | |
398 } | |
399 } | |
400 | |
401 bool GoogleUpdate::ReportFailure(HRESULT hr, | |
402 GoogleUpdateErrorCode error_code, | |
403 const string16& error_message, | |
404 MessageLoop* main_loop) { | |
405 NOTREACHED() << "Communication with Google Update failed: " << hr | |
406 << " error: " << error_code | |
407 << ", message: " << error_message.c_str(); | |
408 main_loop->PostTask( | |
409 FROM_HERE, | |
410 base::Bind(&GoogleUpdate::ReportResults, this, | |
411 UPGRADE_ERROR, error_code, error_message)); | |
412 return false; | |
413 } | |
OLD | NEW |