OLD | NEW |
| (Empty) |
1 // Copyright 2007-2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 // | |
17 // There is an implicit assumption that one bits job contains only one file. | |
18 // | |
19 // TODO(omaha): assert somewhere on all the job invariants. | |
20 // | |
21 // TODO(omaha): no caching at all is implemented, as far as sharing downloads | |
22 // between different bits users downloading the same file. | |
23 // TODO(omaha): same user downloading same file in different logon session is | |
24 // not handled. | |
25 // TODO(omaha): generally speaking, impersonation scenarios are not | |
26 // yet handled by the code. This is important when creating or opening an | |
27 // existing job. | |
28 | |
29 #include "omaha/net/bits_request.h" | |
30 | |
31 #include <winhttp.h> | |
32 #include <atlbase.h> | |
33 #include <atlstr.h> | |
34 #include <functional> | |
35 #include "omaha/base/const_addresses.h" | |
36 #include "omaha/base/debug.h" | |
37 #include "omaha/base/error.h" | |
38 #include "omaha/base/logging.h" | |
39 #include "omaha/base/scoped_impersonation.h" | |
40 #include "omaha/base/utils.h" | |
41 #include "omaha/net/bits_job_callback.h" | |
42 #include "omaha/net/bits_utils.h" | |
43 #include "omaha/net/http_client.h" | |
44 #include "omaha/net/network_request.h" | |
45 #include "omaha/net/proxy_auth.h" | |
46 | |
47 namespace omaha { | |
48 | |
49 namespace { | |
50 | |
51 const TCHAR* const kJobDescription = kAppName; | |
52 | |
53 // During BITS job downloading, we setup the notification callback so that | |
54 // BITS can notify BitsRequest whenever BITS job state changes. To avoid | |
55 // potential notification loss we also use a max polling interval (1s) and | |
56 // when that amount of time passes, we'll do a poll anyway even if no BITS | |
57 // notification is received at that time. | |
58 const LONG kPollingIntervalMs = 1000; | |
59 | |
60 // Returns the job priority or -1 in case of errors. | |
61 int GetJobPriority(IBackgroundCopyJob* job) { | |
62 ASSERT1(job); | |
63 BG_JOB_PRIORITY priority = BG_JOB_PRIORITY_FOREGROUND; | |
64 return SUCCEEDED(job->GetPriority(&priority)) ? priority : -1; | |
65 } | |
66 | |
67 } // namespace | |
68 | |
69 BitsRequest::BitsRequest() | |
70 : request_buffer_(NULL), | |
71 request_buffer_length_(0), | |
72 proxy_auth_config_(NULL, CString()), | |
73 low_priority_(false), | |
74 is_canceled_(false), | |
75 callback_(NULL), | |
76 minimum_retry_delay_(-1), | |
77 no_progress_timeout_(-1), | |
78 current_auth_scheme_(0), | |
79 bits_request_callback_(NULL), | |
80 last_progress_report_tick_(0), | |
81 creds_set_scheme_unknown_(false) { | |
82 GetBitsManager(&bits_manager_); | |
83 | |
84 // Creates a auto-reset event for BITS job change notifications. | |
85 reset(bits_job_status_changed_event_, | |
86 ::CreateEvent(NULL, false, false, NULL)); | |
87 ASSERT1(valid(bits_job_status_changed_event_)); | |
88 } | |
89 | |
90 // Once this instance connects to a BITS job, it either completes the job | |
91 // or it cleans it up to avoid leaving junk in the BITS queue. | |
92 BitsRequest::~BitsRequest() { | |
93 Close(); | |
94 callback_ = NULL; | |
95 | |
96 // TODO(omaha): for unknown reasons, qmgrprxy.dll gets unloaded at some point | |
97 // during program execution and subsequent calls to BITS crash. This | |
98 // indicates a ref count problem somewhere. The work around is to not | |
99 // call the IUnknown::Release if the module is not in memory. | |
100 if (::GetModuleHandle(_T("qmgrprxy.dll")) == NULL) { | |
101 bits_manager_.Detach(); | |
102 } | |
103 } | |
104 | |
105 HRESULT BitsRequest::SetupBitsCallback() { | |
106 ASSERT1(request_state_.get()); | |
107 ASSERT1(request_state_->bits_job); | |
108 | |
109 __mutexScope(lock_); | |
110 | |
111 VERIFY1(::ResetEvent(get(bits_job_status_changed_event_))); | |
112 | |
113 RemoveBitsCallback(); | |
114 | |
115 HRESULT hr = BitsJobCallback::Create(this, &bits_request_callback_); | |
116 if (FAILED(hr)) { | |
117 return hr; | |
118 } | |
119 | |
120 // In the /ua case, the high-integrity COM server is running as SYSTEM, and | |
121 // impersonating a medium-integrity token. Calling SetNotifyInterface() with | |
122 // impersonation will give E_ACCESSDENIED. This is because the COM server only | |
123 // accepts high integrity incoming calls, as per the DACL set in | |
124 // InitializeServerSecurity(). Calling AsSelf with EOAC_DYNAMIC_CLOAKING | |
125 // sets high-integrity SYSTEM as the identity for the callback COM proxy on | |
126 // the BITS side. | |
127 hr = StdCallAsSelfAndImpersonate1( | |
128 request_state_->bits_job.p, | |
129 &IBackgroundCopyJob::SetNotifyInterface, | |
130 static_cast<IUnknown*>(bits_request_callback_)); | |
131 if (FAILED(hr)) { | |
132 return hr; | |
133 } | |
134 | |
135 hr = request_state_->bits_job->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED | | |
136 BG_NOTIFY_JOB_ERROR | | |
137 BG_NOTIFY_JOB_MODIFICATION); | |
138 return hr; | |
139 } | |
140 | |
141 void BitsRequest::RemoveBitsCallback() { | |
142 if (bits_request_callback_ != NULL) { | |
143 bits_request_callback_->RemoveReferenceToBitsRequest(); | |
144 | |
145 bits_request_callback_->Release(); | |
146 bits_request_callback_ = NULL; | |
147 } | |
148 | |
149 if (request_state_.get() && request_state_->bits_job) { | |
150 request_state_->bits_job->SetNotifyInterface(NULL); | |
151 } | |
152 } | |
153 | |
154 void BitsRequest::OnBitsJobStateChanged() { | |
155 VERIFY1(::SetEvent(get(bits_job_status_changed_event_))); | |
156 } | |
157 | |
158 HRESULT BitsRequest::Close() { | |
159 NET_LOG(L3, (_T("[BitsRequest::Close]"))); | |
160 __mutexBlock(lock_) { | |
161 RemoveBitsCallback(); | |
162 | |
163 if (request_state_.get()) { | |
164 VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job))); | |
165 } | |
166 request_state_.reset(); | |
167 } | |
168 return S_OK; | |
169 } | |
170 | |
171 HRESULT BitsRequest::Cancel() { | |
172 NET_LOG(L3, (_T("[BitsRequest::Cancel]"))); | |
173 __mutexBlock(lock_) { | |
174 RemoveBitsCallback(); | |
175 | |
176 is_canceled_ = true; | |
177 if (request_state_.get()) { | |
178 VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job))); | |
179 } | |
180 } | |
181 | |
182 OnBitsJobStateChanged(); | |
183 return S_OK; | |
184 } | |
185 | |
186 HRESULT BitsRequest::Pause() { | |
187 NET_LOG(L3, (_T("[BitsRequest::Pause]"))); | |
188 __mutexBlock(lock_) { | |
189 if (request_state_.get()) { | |
190 VERIFY1(SUCCEEDED(PauseBitsJob(request_state_->bits_job))); | |
191 } | |
192 } | |
193 return S_OK; | |
194 } | |
195 | |
196 HRESULT BitsRequest::Resume() { | |
197 NET_LOG(L3, (_T("[BitsRequest::Resume]"))); | |
198 __mutexBlock(lock_) { | |
199 if (request_state_.get()) { | |
200 VERIFY1(SUCCEEDED(ResumeBitsJob(request_state_->bits_job))); | |
201 } | |
202 } | |
203 return S_OK; | |
204 } | |
205 | |
206 HRESULT BitsRequest::Send() { | |
207 NET_LOG(L3, (_T("[BitsRequest::Send][%s]"), url_)); | |
208 | |
209 ASSERT1(!url_.IsEmpty()); | |
210 | |
211 __mutexBlock(lock_) { | |
212 if (request_state_.get()) { | |
213 VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job))); | |
214 } | |
215 request_state_.reset(new TransientRequestState); | |
216 } | |
217 | |
218 bool is_created = false; | |
219 HRESULT hr = BitsRequest::CreateOrOpenJob(filename_, | |
220 &request_state_->bits_job, | |
221 &is_created); | |
222 if (FAILED(hr)) { | |
223 return hr; | |
224 } | |
225 | |
226 // The job id is used for logging purposes only. | |
227 request_state_->bits_job->GetId(&request_state_->bits_job_id); | |
228 | |
229 NET_LOG(L3, (_T("[BITS job %s]"), GuidToString(request_state_->bits_job_id))); | |
230 | |
231 if (is_created) { | |
232 hr = SetInvariantJobProperties(); | |
233 if (FAILED(hr)) { | |
234 HRESULT hr_cancel_bits_job(CancelBitsJob(request_state_->bits_job)); | |
235 if (FAILED(hr_cancel_bits_job)) { | |
236 NET_LOG(LW, (_T("[CancelBitsJob failed][0x%08x]"), hr_cancel_bits_job)); | |
237 } | |
238 request_state_->bits_job = NULL; | |
239 return hr; | |
240 } | |
241 } | |
242 | |
243 return DoSend(); | |
244 } | |
245 | |
246 HRESULT BitsRequest::QueryHeadersString(uint32, const TCHAR*, CString*) const { | |
247 return E_NOTIMPL; | |
248 } | |
249 | |
250 CString BitsRequest::GetResponseHeaders() const { | |
251 return CString(); | |
252 } | |
253 | |
254 | |
255 HRESULT BitsRequest::CreateOrOpenJob(const TCHAR* display_name, | |
256 IBackgroundCopyJob** bits_job, | |
257 bool* is_created) { | |
258 ASSERT1(display_name); | |
259 ASSERT1(bits_job); | |
260 ASSERT1(*bits_job == NULL); | |
261 ASSERT1(is_created); | |
262 | |
263 CComPtr<IBackgroundCopyManager> bits_manager; | |
264 HRESULT hr = GetBitsManager(&bits_manager); | |
265 if (FAILED(hr)) { | |
266 return hr; | |
267 } | |
268 | |
269 // Try to find if we already have the job in the BITS queue. | |
270 // By convention, the display name of the job is the same as the file name. | |
271 CComPtr<IBackgroundCopyJob> job; | |
272 hr = FindBitsJobIf(std::bind2nd(JobDisplayNameEqual(), display_name), | |
273 bits_manager, | |
274 &job); | |
275 if (SUCCEEDED(hr)) { | |
276 NET_LOG(L3, (_T("[found BITS job][%s]"), display_name)); | |
277 *bits_job = job.Detach(); | |
278 *is_created = false; | |
279 return S_OK; | |
280 } | |
281 | |
282 GUID guid = {0}; | |
283 hr = bits_manager->CreateJob(display_name, BG_JOB_TYPE_DOWNLOAD, &guid, &job); | |
284 if (SUCCEEDED(hr)) { | |
285 *bits_job = job.Detach(); | |
286 *is_created = true; | |
287 return S_OK; | |
288 } | |
289 | |
290 *bits_job = NULL; | |
291 return hr; | |
292 } | |
293 | |
294 HRESULT BitsRequest::SetInvariantJobProperties() { | |
295 ASSERT1(request_state_.get()); | |
296 ASSERT1(request_state_->bits_job); | |
297 HRESULT hr = request_state_->bits_job->AddFile(url_, filename_); | |
298 if (FAILED(hr)) { | |
299 NET_LOG(LE, (_T("[IBackgroundCopyJob::AddFile failed][0x%08x]"), hr)); | |
300 return hr; | |
301 } | |
302 hr = request_state_->bits_job->SetDescription(kJobDescription); | |
303 if (FAILED(hr)) { | |
304 return hr; | |
305 } | |
306 | |
307 return S_OK; | |
308 } | |
309 | |
310 HRESULT BitsRequest::SetJobProperties() { | |
311 ASSERT1(request_state_.get()); | |
312 ASSERT1(request_state_->bits_job); | |
313 BG_JOB_PRIORITY priority = low_priority_ ? BG_JOB_PRIORITY_NORMAL : | |
314 BG_JOB_PRIORITY_FOREGROUND; | |
315 HRESULT hr = request_state_->bits_job->SetPriority(priority); | |
316 if (FAILED(hr)) { | |
317 return hr; | |
318 } | |
319 if (minimum_retry_delay_ != -1) { | |
320 ASSERT1(minimum_retry_delay_ >= 0); | |
321 hr = request_state_->bits_job->SetMinimumRetryDelay(minimum_retry_delay_); | |
322 if (FAILED(hr)) { | |
323 return hr; | |
324 } | |
325 } | |
326 | |
327 // Always set no_progress_timeout to 0 for foreground jobs which means the | |
328 // jobs in transient error state will be immediately moved to error state. | |
329 int no_progress_timeout = low_priority_ ? no_progress_timeout_ : 0; | |
330 | |
331 if (no_progress_timeout != -1) { | |
332 ASSERT1(no_progress_timeout >= 0); | |
333 hr = request_state_->bits_job->SetNoProgressTimeout(no_progress_timeout); | |
334 if (FAILED(hr)) { | |
335 return hr; | |
336 } | |
337 } | |
338 | |
339 SetJobCustomHeaders(); | |
340 return S_OK; | |
341 } | |
342 | |
343 HRESULT BitsRequest::SetJobCustomHeaders() { | |
344 NET_LOG(L3, (_T("[BitsRequest::SetJobCustomHeaders][%s]"), | |
345 additional_headers_)); | |
346 ASSERT1(request_state_.get()); | |
347 ASSERT1(request_state_->bits_job); | |
348 | |
349 if (additional_headers_.IsEmpty()) { | |
350 return S_OK; | |
351 } | |
352 | |
353 CComPtr<IBackgroundCopyJobHttpOptions> http_options; | |
354 HRESULT hr = request_state_->bits_job->QueryInterface(&http_options); | |
355 if (FAILED(hr)) { | |
356 NET_LOG(LW, (_T("[QI IBackgroundCopyJobHttpOptions failed][0x%x]"), hr)); | |
357 return hr; | |
358 } | |
359 | |
360 hr = http_options->SetCustomHeaders(additional_headers_); | |
361 if (FAILED(hr)) { | |
362 NET_LOG(LE, (_T("[SetCustomHeaders failed][0x%x]"), hr)); | |
363 return hr; | |
364 } | |
365 | |
366 return S_OK; | |
367 } | |
368 | |
369 HRESULT BitsRequest::DetectManualProxy() { | |
370 if (NetworkConfig::GetAccessType(proxy_config_) != | |
371 WINHTTP_ACCESS_TYPE_AUTO_DETECT) { | |
372 return S_OK; | |
373 } | |
374 | |
375 NetworkConfig* network_config = NULL; | |
376 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
377 HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); | |
378 if (FAILED(hr)) { | |
379 return hr; | |
380 } | |
381 | |
382 HttpClient::ProxyInfo proxy_info = {0}; | |
383 hr = network_config->GetProxyForUrl(url_, | |
384 proxy_config_.auto_config_url, | |
385 &proxy_info); | |
386 if (SUCCEEDED(hr) && | |
387 proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { | |
388 proxy_config_.auto_detect = false; | |
389 proxy_config_.auto_config_url.Empty(); | |
390 proxy_config_.proxy = proxy_info.proxy; | |
391 proxy_config_.proxy_bypass = proxy_info.proxy_bypass; | |
392 } | |
393 | |
394 ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy)); | |
395 ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy_bypass)); | |
396 | |
397 NET_LOG(L3, (_T("[GetProxyForUrl returned][0x%08x]"), hr)); | |
398 return hr; | |
399 } | |
400 | |
401 HRESULT BitsRequest::SetJobProxyUsage() { | |
402 ASSERT1(request_state_.get()); | |
403 ASSERT1(request_state_->bits_job); | |
404 BG_JOB_PROXY_USAGE proxy_usage = BG_JOB_PROXY_USAGE_NO_PROXY; | |
405 const TCHAR* proxy = NULL; | |
406 const TCHAR* proxy_bypass = NULL; | |
407 | |
408 DetectManualProxy(); | |
409 | |
410 int access_type = NetworkConfig::GetAccessType(proxy_config_); | |
411 if (access_type == WINHTTP_ACCESS_TYPE_AUTO_DETECT) { | |
412 proxy_usage = BG_JOB_PROXY_USAGE_AUTODETECT; | |
413 } else if (access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { | |
414 proxy_usage = BG_JOB_PROXY_USAGE_OVERRIDE; | |
415 proxy = proxy_config_.proxy; | |
416 proxy_bypass = proxy_config_.proxy_bypass; | |
417 } | |
418 HRESULT hr = request_state_->bits_job->SetProxySettings(proxy_usage, | |
419 proxy, | |
420 proxy_bypass); | |
421 if (FAILED(hr)) { | |
422 return hr; | |
423 } | |
424 if (proxy_usage == BG_JOB_PROXY_USAGE_AUTODETECT || | |
425 proxy_usage == BG_JOB_PROXY_USAGE_OVERRIDE) { | |
426 // Set implicit credentials if we are going through a proxy, just in case | |
427 // the proxy is requiring authentication. Continue on errors, maybe | |
428 // the credentials won't be needed anyway. There will be one more chance | |
429 // to set credentials when the proxy challenges and the job errors out. | |
430 creds_set_scheme_unknown_ = false; | |
431 hr = SetProxyAuthImplicitCredentials(request_state_->bits_job, | |
432 BG_AUTH_SCHEME_NEGOTIATE); | |
433 if (SUCCEEDED(hr)) { | |
434 current_auth_scheme_ = BG_AUTH_SCHEME_NEGOTIATE; | |
435 } else { | |
436 OPT_LOG(LW, (_T("[failed to set BITS proxy credentials][0x%08x]"), hr)); | |
437 } | |
438 } | |
439 return S_OK; | |
440 } | |
441 | |
442 HRESULT BitsRequest::DoSend() { | |
443 ASSERT1(request_state_.get()); | |
444 ASSERT1(request_state_->bits_job); | |
445 | |
446 NET_LOG(L3, (_T("[BitsRequest::DoSend]"))); | |
447 | |
448 if (is_canceled_) { | |
449 return GOOPDATE_E_CANCELLED; | |
450 } | |
451 | |
452 HRESULT hr = SetJobProperties(); | |
453 if (FAILED(hr)) { | |
454 return hr; | |
455 } | |
456 hr = SetJobProxyUsage(); | |
457 if (FAILED(hr)) { | |
458 return hr; | |
459 } | |
460 hr = SetupBitsCallback(); | |
461 if (FAILED(hr)) { | |
462 return hr; | |
463 } | |
464 hr = request_state_->bits_job->Resume(); | |
465 if (FAILED(hr)) { | |
466 return hr; | |
467 } | |
468 | |
469 NET_LOG(L3, (_T("[job priority %d]"), | |
470 GetJobPriority(request_state_->bits_job))); | |
471 | |
472 // Poll for state changes. The code executing on the state changes must be | |
473 // idempotent, as the same state can be seen multiple times when looping. | |
474 // There is only one important case, which is retrying the job when the | |
475 // job is in the ERROR state. We attempt to handle the error, for | |
476 // example retrying one more time or changing proxy credentials, and then | |
477 // we resume the job. There is an assumption, so far true, that calling | |
478 // Resume on a job, the state changes right away from SUSPENDED to QUEUED. | |
479 | |
480 for (;;) { | |
481 if (is_canceled_) { | |
482 return GOOPDATE_E_CANCELLED; | |
483 } | |
484 | |
485 BG_JOB_STATE job_state = BG_JOB_STATE_ERROR; | |
486 hr = request_state_->bits_job->GetState(&job_state); | |
487 if (FAILED(hr)) { | |
488 return hr; | |
489 } | |
490 | |
491 NET_LOG(L3, (_T("[job %s][state %s]"), | |
492 GuidToString(request_state_->bits_job_id), | |
493 JobStateToString(job_state))); | |
494 | |
495 switch (job_state) { | |
496 case BG_JOB_STATE_QUEUED: | |
497 break; | |
498 | |
499 case BG_JOB_STATE_CONNECTING: | |
500 if (callback_) { | |
501 callback_->OnProgress(0, | |
502 0, | |
503 WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, | |
504 NULL); | |
505 } | |
506 break; | |
507 | |
508 case BG_JOB_STATE_TRANSFERRING: | |
509 OnStateTransferring(); | |
510 break; | |
511 | |
512 case BG_JOB_STATE_TRANSIENT_ERROR: | |
513 break; | |
514 | |
515 case BG_JOB_STATE_ERROR: | |
516 hr = OnStateError(); | |
517 if (SUCCEEDED(hr)) { | |
518 // The error handler dealt with the error. Countinue the loop. | |
519 break; | |
520 } | |
521 | |
522 // Give up. | |
523 return request_state_->http_status_code ? S_OK : hr; | |
524 | |
525 case BG_JOB_STATE_TRANSFERRED: | |
526 NotifyProgress(); | |
527 hr = request_state_->bits_job->Complete(); | |
528 if (SUCCEEDED(hr) || BG_S_UNABLE_TO_DELETE_FILES == hr) { | |
529 // Assume the status code is 200 if the transfer completed. BITS does | |
530 // not provide access to the status code. | |
531 request_state_->http_status_code = HTTP_STATUS_OK; | |
532 | |
533 if (creds_set_scheme_unknown_) { | |
534 // Bits job completed successfully. If we have a valid username, we | |
535 // record the auth scheme with the NetworkConfig, so it can be used | |
536 // in the future within this process. | |
537 uint32 win_http_scheme = | |
538 BitsToWinhttpProxyAuthScheme(current_auth_scheme_); | |
539 ASSERT1(win_http_scheme != UNKNOWN_AUTH_SCHEME); | |
540 bool is_https = String_StartsWith(url_, kHttpsProtoScheme, true); | |
541 | |
542 NetworkConfig* network_config = NULL; | |
543 NetworkConfigManager& nm = NetworkConfigManager::Instance(); | |
544 HRESULT hr = nm.GetUserNetworkConfig(&network_config); | |
545 if (FAILED(hr)) { | |
546 return hr; | |
547 } | |
548 | |
549 VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme( | |
550 proxy_config_.proxy, is_https, win_http_scheme))); | |
551 } | |
552 | |
553 return S_OK; | |
554 } else { | |
555 return hr; | |
556 } | |
557 | |
558 case BG_JOB_STATE_SUSPENDED: | |
559 break; | |
560 | |
561 case BG_JOB_STATE_ACKNOWLEDGED: | |
562 ASSERT1(false); | |
563 return S_OK; | |
564 | |
565 case BG_JOB_STATE_CANCELLED: | |
566 return GOOPDATE_E_CANCELLED; | |
567 }; | |
568 | |
569 DWORD wait_result = ::WaitForSingleObject( | |
570 get(bits_job_status_changed_event_), kPollingIntervalMs); | |
571 if (wait_result == WAIT_FAILED) { | |
572 ::Sleep(kPollingIntervalMs); | |
573 } | |
574 } | |
575 } | |
576 | |
577 HRESULT BitsRequest::OnStateTransferring() { | |
578 // BITS could call JobModification very often during transfer so we | |
579 // do report meter here to avoid too many progress notifications. | |
580 uint32 now = GetTickCount(); | |
581 | |
582 if (now >= last_progress_report_tick_ && | |
583 now - last_progress_report_tick_ < kJobProgressReportMinimumIntervalMs) { | |
584 return S_OK; | |
585 } | |
586 | |
587 last_progress_report_tick_ = now; | |
588 return NotifyProgress(); | |
589 } | |
590 | |
591 HRESULT BitsRequest::OnStateError() { | |
592 CComPtr<IBackgroundCopyError> error; | |
593 HRESULT hr = request_state_->bits_job->GetError(&error); | |
594 if (FAILED(hr)) { | |
595 return hr; | |
596 } | |
597 BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE; | |
598 HRESULT error_code = E_FAIL; | |
599 hr = error->GetError(&error_context, &error_code); | |
600 if (FAILED(hr)) { | |
601 return hr; | |
602 } | |
603 ASSERT1(FAILED(error_code)); | |
604 | |
605 NET_LOG(L3, (_T("[handle bits error][0x%08x]"), error_code)); | |
606 | |
607 request_state_->http_status_code = GetHttpStatusFromBitsError(error_code); | |
608 | |
609 if (error_code == BG_E_HTTP_ERROR_407) { | |
610 hr = creds_set_scheme_unknown_ ? HandleProxyAuthenticationErrorCredsSet() : | |
611 HandleProxyAuthenticationError(); | |
612 if (SUCCEEDED(hr)) { | |
613 return S_OK; | |
614 } | |
615 } | |
616 | |
617 // We could not handle this error. The control will return to the caller. | |
618 return error_code; | |
619 } | |
620 | |
621 HRESULT BitsRequest::NotifyProgress() { | |
622 if (!callback_) { | |
623 return S_OK; | |
624 } | |
625 BG_JOB_PROGRESS progress = {0}; | |
626 HRESULT hr = request_state_->bits_job->GetProgress(&progress); | |
627 if (FAILED(hr)) { | |
628 return hr; | |
629 } | |
630 | |
631 ASSERT1(progress.FilesTotal == 1); | |
632 ASSERT1(progress.BytesTransferred <= INT_MAX); | |
633 ASSERT1(progress.BytesTotal <= INT_MAX); | |
634 callback_->OnProgress(static_cast<int>(progress.BytesTransferred), | |
635 static_cast<int>(progress.BytesTotal), | |
636 WINHTTP_CALLBACK_STATUS_READ_COMPLETE, | |
637 NULL); | |
638 return S_OK; | |
639 } | |
640 | |
641 HRESULT BitsRequest::GetProxyCredentials() { | |
642 CString username; | |
643 CString password; | |
644 uint32 auth_scheme = UNKNOWN_AUTH_SCHEME; | |
645 bool is_https = String_StartsWith(url_, kHttpsProtoScheme, true); | |
646 | |
647 NetworkConfig* network_config = NULL; | |
648 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
649 HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); | |
650 if (FAILED(hr)) { | |
651 return hr; | |
652 } | |
653 | |
654 if (!network_config->GetProxyCredentials(true, false, | |
655 proxy_config_.proxy, proxy_auth_config_, is_https, &username, | |
656 &password, &auth_scheme)) { | |
657 OPT_LOG(LE, (_T("[BitsRequest::GetProxyCredentials failed]"))); | |
658 return E_ACCESSDENIED; | |
659 } | |
660 | |
661 if (auth_scheme != UNKNOWN_AUTH_SCHEME) { | |
662 current_auth_scheme_ = WinHttpToBitsProxyAuthScheme(auth_scheme); | |
663 OPT_LOG(L3, (_T("[BitsRequest::GetProxyCredentials][%s]"), | |
664 BitsAuthSchemeToString(current_auth_scheme_))); | |
665 return SetProxyAuthCredentials(request_state_->bits_job, | |
666 CStrBuf(username), CStrBuf(password), | |
667 static_cast<BG_AUTH_SCHEME>(current_auth_scheme_)); | |
668 } | |
669 | |
670 OPT_LOG(L3, (_T("[BitsRequest::GetProxyCredentials][Auth scheme unknown]"))); | |
671 // We do not know the scheme beforehand. So we set credentials on all the | |
672 // schemes except BASIC and try them out in seqence. We could have used BASIC | |
673 // as well, however, we do not want to leak passwords by mistake. | |
674 for (int scheme = BG_AUTH_SCHEME_DIGEST; scheme <= BG_AUTH_SCHEME_NEGOTIATE; | |
675 ++scheme) { | |
676 hr = SetProxyAuthCredentials(request_state_->bits_job, | |
677 CStrBuf(username), CStrBuf(password), | |
678 static_cast<BG_AUTH_SCHEME>(scheme)); | |
679 if (FAILED(hr)) { | |
680 OPT_LOG(LE, (_T("[BitsRequest::GetProxyCredentials][0x%08x][%s]"), | |
681 hr, BitsAuthSchemeToString(scheme))); | |
682 return hr; | |
683 } | |
684 } | |
685 | |
686 current_auth_scheme_ = BG_AUTH_SCHEME_NEGOTIATE; | |
687 creds_set_scheme_unknown_ = true; | |
688 return S_OK; | |
689 } | |
690 | |
691 HRESULT BitsRequest::HandleProxyAuthenticationError() { | |
692 ASSERT1(!creds_set_scheme_unknown_); | |
693 HRESULT hr = E_ACCESSDENIED; | |
694 | |
695 if (current_auth_scheme_ == 0) { | |
696 current_auth_scheme_ = BG_AUTH_SCHEME_NEGOTIATE; | |
697 hr = SetProxyAuthImplicitCredentials(request_state_->bits_job, | |
698 BG_AUTH_SCHEME_NEGOTIATE); | |
699 } else if (current_auth_scheme_ == BG_AUTH_SCHEME_NEGOTIATE) { | |
700 current_auth_scheme_ = BG_AUTH_SCHEME_NTLM; | |
701 hr = SetProxyAuthImplicitCredentials(request_state_->bits_job, | |
702 BG_AUTH_SCHEME_NTLM); | |
703 } else { | |
704 hr = GetProxyCredentials(); | |
705 } | |
706 | |
707 OPT_LOG(L3, (_T("[BitsRequest::HandleProxyAuthenticationError][0x%08x][%s]"), | |
708 hr, BitsAuthSchemeToString(current_auth_scheme_))); | |
709 return SUCCEEDED(hr) ? request_state_->bits_job->Resume() : hr; | |
710 } | |
711 | |
712 HRESULT BitsRequest::HandleProxyAuthenticationErrorCredsSet() { | |
713 ASSERT1(creds_set_scheme_unknown_); | |
714 | |
715 if (current_auth_scheme_ == BG_AUTH_SCHEME_NEGOTIATE) { | |
716 current_auth_scheme_ = BG_AUTH_SCHEME_NTLM; | |
717 } else if (current_auth_scheme_ == BG_AUTH_SCHEME_NTLM) { | |
718 current_auth_scheme_ = BG_AUTH_SCHEME_DIGEST; | |
719 } else { | |
720 OPT_LOG(LE, (_T("[HandleProxyAuthenticationErrorCredsSet][Failure]"))); | |
721 return E_ACCESSDENIED; | |
722 } | |
723 | |
724 OPT_LOG(L3, (_T("[BitsRequest::HandleProxyAuthenticationErrorCredsSet][%s]"), | |
725 BitsAuthSchemeToString(current_auth_scheme_))); | |
726 return request_state_->bits_job->Resume(); | |
727 } | |
728 | |
729 int BitsRequest::WinHttpToBitsProxyAuthScheme(uint32 winhttp_scheme) { | |
730 if (winhttp_scheme == WINHTTP_AUTH_SCHEME_NEGOTIATE) { | |
731 return BG_AUTH_SCHEME_NEGOTIATE; | |
732 } | |
733 if (winhttp_scheme == WINHTTP_AUTH_SCHEME_NTLM) { | |
734 return BG_AUTH_SCHEME_NTLM; | |
735 } | |
736 if (winhttp_scheme == WINHTTP_AUTH_SCHEME_DIGEST) { | |
737 return BG_AUTH_SCHEME_DIGEST; | |
738 } | |
739 if (winhttp_scheme == WINHTTP_AUTH_SCHEME_BASIC) { | |
740 return BG_AUTH_SCHEME_BASIC; | |
741 } | |
742 | |
743 ASSERT1(false); | |
744 return UNKNOWN_AUTH_SCHEME; | |
745 } | |
746 | |
747 uint32 BitsRequest::BitsToWinhttpProxyAuthScheme(int bits_scheme) { | |
748 if (bits_scheme == BG_AUTH_SCHEME_NEGOTIATE) { | |
749 return WINHTTP_AUTH_SCHEME_NEGOTIATE; | |
750 } | |
751 if (bits_scheme == BG_AUTH_SCHEME_NTLM) { | |
752 return WINHTTP_AUTH_SCHEME_NTLM; | |
753 } | |
754 if (bits_scheme == BG_AUTH_SCHEME_DIGEST) { | |
755 return WINHTTP_AUTH_SCHEME_DIGEST; | |
756 } | |
757 if (bits_scheme == BG_AUTH_SCHEME_BASIC) { | |
758 return WINHTTP_AUTH_SCHEME_BASIC; | |
759 } | |
760 | |
761 ASSERT1(false); | |
762 return UNKNOWN_AUTH_SCHEME; | |
763 } | |
764 | |
765 } // namespace omaha | |
766 | |
OLD | NEW |