| Index: net/bits_request.cc
|
| diff --git a/net/bits_request.cc b/net/bits_request.cc
|
| deleted file mode 100644
|
| index 10c29f2ef2a36387ad713c5621d3b1f00a2754b9..0000000000000000000000000000000000000000
|
| --- a/net/bits_request.cc
|
| +++ /dev/null
|
| @@ -1,766 +0,0 @@
|
| -// Copyright 2007-2010 Google Inc.
|
| -//
|
| -// Licensed under the Apache License, Version 2.0 (the "License");
|
| -// you may not use this file except in compliance with the License.
|
| -// You may obtain a copy of the License at
|
| -//
|
| -// http://www.apache.org/licenses/LICENSE-2.0
|
| -//
|
| -// Unless required by applicable law or agreed to in writing, software
|
| -// distributed under the License is distributed on an "AS IS" BASIS,
|
| -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -// See the License for the specific language governing permissions and
|
| -// limitations under the License.
|
| -// ========================================================================
|
| -
|
| -//
|
| -// There is an implicit assumption that one bits job contains only one file.
|
| -//
|
| -// TODO(omaha): assert somewhere on all the job invariants.
|
| -//
|
| -// TODO(omaha): no caching at all is implemented, as far as sharing downloads
|
| -// between different bits users downloading the same file.
|
| -// TODO(omaha): same user downloading same file in different logon session is
|
| -// not handled.
|
| -// TODO(omaha): generally speaking, impersonation scenarios are not
|
| -// yet handled by the code. This is important when creating or opening an
|
| -// existing job.
|
| -
|
| -#include "omaha/net/bits_request.h"
|
| -
|
| -#include <winhttp.h>
|
| -#include <atlbase.h>
|
| -#include <atlstr.h>
|
| -#include <functional>
|
| -#include "omaha/base/const_addresses.h"
|
| -#include "omaha/base/debug.h"
|
| -#include "omaha/base/error.h"
|
| -#include "omaha/base/logging.h"
|
| -#include "omaha/base/scoped_impersonation.h"
|
| -#include "omaha/base/utils.h"
|
| -#include "omaha/net/bits_job_callback.h"
|
| -#include "omaha/net/bits_utils.h"
|
| -#include "omaha/net/http_client.h"
|
| -#include "omaha/net/network_request.h"
|
| -#include "omaha/net/proxy_auth.h"
|
| -
|
| -namespace omaha {
|
| -
|
| -namespace {
|
| -
|
| -const TCHAR* const kJobDescription = kAppName;
|
| -
|
| -// During BITS job downloading, we setup the notification callback so that
|
| -// BITS can notify BitsRequest whenever BITS job state changes. To avoid
|
| -// potential notification loss we also use a max polling interval (1s) and
|
| -// when that amount of time passes, we'll do a poll anyway even if no BITS
|
| -// notification is received at that time.
|
| -const LONG kPollingIntervalMs = 1000;
|
| -
|
| -// Returns the job priority or -1 in case of errors.
|
| -int GetJobPriority(IBackgroundCopyJob* job) {
|
| - ASSERT1(job);
|
| - BG_JOB_PRIORITY priority = BG_JOB_PRIORITY_FOREGROUND;
|
| - return SUCCEEDED(job->GetPriority(&priority)) ? priority : -1;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -BitsRequest::BitsRequest()
|
| - : request_buffer_(NULL),
|
| - request_buffer_length_(0),
|
| - proxy_auth_config_(NULL, CString()),
|
| - low_priority_(false),
|
| - is_canceled_(false),
|
| - callback_(NULL),
|
| - minimum_retry_delay_(-1),
|
| - no_progress_timeout_(-1),
|
| - current_auth_scheme_(0),
|
| - bits_request_callback_(NULL),
|
| - last_progress_report_tick_(0),
|
| - creds_set_scheme_unknown_(false) {
|
| - GetBitsManager(&bits_manager_);
|
| -
|
| - // Creates a auto-reset event for BITS job change notifications.
|
| - reset(bits_job_status_changed_event_,
|
| - ::CreateEvent(NULL, false, false, NULL));
|
| - ASSERT1(valid(bits_job_status_changed_event_));
|
| -}
|
| -
|
| -// Once this instance connects to a BITS job, it either completes the job
|
| -// or it cleans it up to avoid leaving junk in the BITS queue.
|
| -BitsRequest::~BitsRequest() {
|
| - Close();
|
| - callback_ = NULL;
|
| -
|
| - // TODO(omaha): for unknown reasons, qmgrprxy.dll gets unloaded at some point
|
| - // during program execution and subsequent calls to BITS crash. This
|
| - // indicates a ref count problem somewhere. The work around is to not
|
| - // call the IUnknown::Release if the module is not in memory.
|
| - if (::GetModuleHandle(_T("qmgrprxy.dll")) == NULL) {
|
| - bits_manager_.Detach();
|
| - }
|
| -}
|
| -
|
| -HRESULT BitsRequest::SetupBitsCallback() {
|
| - ASSERT1(request_state_.get());
|
| - ASSERT1(request_state_->bits_job);
|
| -
|
| - __mutexScope(lock_);
|
| -
|
| - VERIFY1(::ResetEvent(get(bits_job_status_changed_event_)));
|
| -
|
| - RemoveBitsCallback();
|
| -
|
| - HRESULT hr = BitsJobCallback::Create(this, &bits_request_callback_);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - // In the /ua case, the high-integrity COM server is running as SYSTEM, and
|
| - // impersonating a medium-integrity token. Calling SetNotifyInterface() with
|
| - // impersonation will give E_ACCESSDENIED. This is because the COM server only
|
| - // accepts high integrity incoming calls, as per the DACL set in
|
| - // InitializeServerSecurity(). Calling AsSelf with EOAC_DYNAMIC_CLOAKING
|
| - // sets high-integrity SYSTEM as the identity for the callback COM proxy on
|
| - // the BITS side.
|
| - hr = StdCallAsSelfAndImpersonate1(
|
| - request_state_->bits_job.p,
|
| - &IBackgroundCopyJob::SetNotifyInterface,
|
| - static_cast<IUnknown*>(bits_request_callback_));
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - hr = request_state_->bits_job->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED |
|
| - BG_NOTIFY_JOB_ERROR |
|
| - BG_NOTIFY_JOB_MODIFICATION);
|
| - return hr;
|
| -}
|
| -
|
| -void BitsRequest::RemoveBitsCallback() {
|
| - if (bits_request_callback_ != NULL) {
|
| - bits_request_callback_->RemoveReferenceToBitsRequest();
|
| -
|
| - bits_request_callback_->Release();
|
| - bits_request_callback_ = NULL;
|
| - }
|
| -
|
| - if (request_state_.get() && request_state_->bits_job) {
|
| - request_state_->bits_job->SetNotifyInterface(NULL);
|
| - }
|
| -}
|
| -
|
| -void BitsRequest::OnBitsJobStateChanged() {
|
| - VERIFY1(::SetEvent(get(bits_job_status_changed_event_)));
|
| -}
|
| -
|
| -HRESULT BitsRequest::Close() {
|
| - NET_LOG(L3, (_T("[BitsRequest::Close]")));
|
| - __mutexBlock(lock_) {
|
| - RemoveBitsCallback();
|
| -
|
| - if (request_state_.get()) {
|
| - VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job)));
|
| - }
|
| - request_state_.reset();
|
| - }
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::Cancel() {
|
| - NET_LOG(L3, (_T("[BitsRequest::Cancel]")));
|
| - __mutexBlock(lock_) {
|
| - RemoveBitsCallback();
|
| -
|
| - is_canceled_ = true;
|
| - if (request_state_.get()) {
|
| - VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job)));
|
| - }
|
| - }
|
| -
|
| - OnBitsJobStateChanged();
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::Pause() {
|
| - NET_LOG(L3, (_T("[BitsRequest::Pause]")));
|
| - __mutexBlock(lock_) {
|
| - if (request_state_.get()) {
|
| - VERIFY1(SUCCEEDED(PauseBitsJob(request_state_->bits_job)));
|
| - }
|
| - }
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::Resume() {
|
| - NET_LOG(L3, (_T("[BitsRequest::Resume]")));
|
| - __mutexBlock(lock_) {
|
| - if (request_state_.get()) {
|
| - VERIFY1(SUCCEEDED(ResumeBitsJob(request_state_->bits_job)));
|
| - }
|
| - }
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::Send() {
|
| - NET_LOG(L3, (_T("[BitsRequest::Send][%s]"), url_));
|
| -
|
| - ASSERT1(!url_.IsEmpty());
|
| -
|
| - __mutexBlock(lock_) {
|
| - if (request_state_.get()) {
|
| - VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job)));
|
| - }
|
| - request_state_.reset(new TransientRequestState);
|
| - }
|
| -
|
| - bool is_created = false;
|
| - HRESULT hr = BitsRequest::CreateOrOpenJob(filename_,
|
| - &request_state_->bits_job,
|
| - &is_created);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - // The job id is used for logging purposes only.
|
| - request_state_->bits_job->GetId(&request_state_->bits_job_id);
|
| -
|
| - NET_LOG(L3, (_T("[BITS job %s]"), GuidToString(request_state_->bits_job_id)));
|
| -
|
| - if (is_created) {
|
| - hr = SetInvariantJobProperties();
|
| - if (FAILED(hr)) {
|
| - HRESULT hr_cancel_bits_job(CancelBitsJob(request_state_->bits_job));
|
| - if (FAILED(hr_cancel_bits_job)) {
|
| - NET_LOG(LW, (_T("[CancelBitsJob failed][0x%08x]"), hr_cancel_bits_job));
|
| - }
|
| - request_state_->bits_job = NULL;
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - return DoSend();
|
| -}
|
| -
|
| -HRESULT BitsRequest::QueryHeadersString(uint32, const TCHAR*, CString*) const {
|
| - return E_NOTIMPL;
|
| -}
|
| -
|
| -CString BitsRequest::GetResponseHeaders() const {
|
| - return CString();
|
| -}
|
| -
|
| -
|
| -HRESULT BitsRequest::CreateOrOpenJob(const TCHAR* display_name,
|
| - IBackgroundCopyJob** bits_job,
|
| - bool* is_created) {
|
| - ASSERT1(display_name);
|
| - ASSERT1(bits_job);
|
| - ASSERT1(*bits_job == NULL);
|
| - ASSERT1(is_created);
|
| -
|
| - CComPtr<IBackgroundCopyManager> bits_manager;
|
| - HRESULT hr = GetBitsManager(&bits_manager);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - // Try to find if we already have the job in the BITS queue.
|
| - // By convention, the display name of the job is the same as the file name.
|
| - CComPtr<IBackgroundCopyJob> job;
|
| - hr = FindBitsJobIf(std::bind2nd(JobDisplayNameEqual(), display_name),
|
| - bits_manager,
|
| - &job);
|
| - if (SUCCEEDED(hr)) {
|
| - NET_LOG(L3, (_T("[found BITS job][%s]"), display_name));
|
| - *bits_job = job.Detach();
|
| - *is_created = false;
|
| - return S_OK;
|
| - }
|
| -
|
| - GUID guid = {0};
|
| - hr = bits_manager->CreateJob(display_name, BG_JOB_TYPE_DOWNLOAD, &guid, &job);
|
| - if (SUCCEEDED(hr)) {
|
| - *bits_job = job.Detach();
|
| - *is_created = true;
|
| - return S_OK;
|
| - }
|
| -
|
| - *bits_job = NULL;
|
| - return hr;
|
| -}
|
| -
|
| -HRESULT BitsRequest::SetInvariantJobProperties() {
|
| - ASSERT1(request_state_.get());
|
| - ASSERT1(request_state_->bits_job);
|
| - HRESULT hr = request_state_->bits_job->AddFile(url_, filename_);
|
| - if (FAILED(hr)) {
|
| - NET_LOG(LE, (_T("[IBackgroundCopyJob::AddFile failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| - hr = request_state_->bits_job->SetDescription(kJobDescription);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::SetJobProperties() {
|
| - ASSERT1(request_state_.get());
|
| - ASSERT1(request_state_->bits_job);
|
| - BG_JOB_PRIORITY priority = low_priority_ ? BG_JOB_PRIORITY_NORMAL :
|
| - BG_JOB_PRIORITY_FOREGROUND;
|
| - HRESULT hr = request_state_->bits_job->SetPriority(priority);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - if (minimum_retry_delay_ != -1) {
|
| - ASSERT1(minimum_retry_delay_ >= 0);
|
| - hr = request_state_->bits_job->SetMinimumRetryDelay(minimum_retry_delay_);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - // Always set no_progress_timeout to 0 for foreground jobs which means the
|
| - // jobs in transient error state will be immediately moved to error state.
|
| - int no_progress_timeout = low_priority_ ? no_progress_timeout_ : 0;
|
| -
|
| - if (no_progress_timeout != -1) {
|
| - ASSERT1(no_progress_timeout >= 0);
|
| - hr = request_state_->bits_job->SetNoProgressTimeout(no_progress_timeout);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - SetJobCustomHeaders();
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::SetJobCustomHeaders() {
|
| - NET_LOG(L3, (_T("[BitsRequest::SetJobCustomHeaders][%s]"),
|
| - additional_headers_));
|
| - ASSERT1(request_state_.get());
|
| - ASSERT1(request_state_->bits_job);
|
| -
|
| - if (additional_headers_.IsEmpty()) {
|
| - return S_OK;
|
| - }
|
| -
|
| - CComPtr<IBackgroundCopyJobHttpOptions> http_options;
|
| - HRESULT hr = request_state_->bits_job->QueryInterface(&http_options);
|
| - if (FAILED(hr)) {
|
| - NET_LOG(LW, (_T("[QI IBackgroundCopyJobHttpOptions failed][0x%x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - hr = http_options->SetCustomHeaders(additional_headers_);
|
| - if (FAILED(hr)) {
|
| - NET_LOG(LE, (_T("[SetCustomHeaders failed][0x%x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::DetectManualProxy() {
|
| - if (NetworkConfig::GetAccessType(proxy_config_) !=
|
| - WINHTTP_ACCESS_TYPE_AUTO_DETECT) {
|
| - return S_OK;
|
| - }
|
| -
|
| - NetworkConfig* network_config = NULL;
|
| - NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
|
| - HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - HttpClient::ProxyInfo proxy_info = {0};
|
| - hr = network_config->GetProxyForUrl(url_,
|
| - proxy_config_.auto_config_url,
|
| - &proxy_info);
|
| - if (SUCCEEDED(hr) &&
|
| - proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
|
| - proxy_config_.auto_detect = false;
|
| - proxy_config_.auto_config_url.Empty();
|
| - proxy_config_.proxy = proxy_info.proxy;
|
| - proxy_config_.proxy_bypass = proxy_info.proxy_bypass;
|
| - }
|
| -
|
| - ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy));
|
| - ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy_bypass));
|
| -
|
| - NET_LOG(L3, (_T("[GetProxyForUrl returned][0x%08x]"), hr));
|
| - return hr;
|
| -}
|
| -
|
| -HRESULT BitsRequest::SetJobProxyUsage() {
|
| - ASSERT1(request_state_.get());
|
| - ASSERT1(request_state_->bits_job);
|
| - BG_JOB_PROXY_USAGE proxy_usage = BG_JOB_PROXY_USAGE_NO_PROXY;
|
| - const TCHAR* proxy = NULL;
|
| - const TCHAR* proxy_bypass = NULL;
|
| -
|
| - DetectManualProxy();
|
| -
|
| - int access_type = NetworkConfig::GetAccessType(proxy_config_);
|
| - if (access_type == WINHTTP_ACCESS_TYPE_AUTO_DETECT) {
|
| - proxy_usage = BG_JOB_PROXY_USAGE_AUTODETECT;
|
| - } else if (access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
|
| - proxy_usage = BG_JOB_PROXY_USAGE_OVERRIDE;
|
| - proxy = proxy_config_.proxy;
|
| - proxy_bypass = proxy_config_.proxy_bypass;
|
| - }
|
| - HRESULT hr = request_state_->bits_job->SetProxySettings(proxy_usage,
|
| - proxy,
|
| - proxy_bypass);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - if (proxy_usage == BG_JOB_PROXY_USAGE_AUTODETECT ||
|
| - proxy_usage == BG_JOB_PROXY_USAGE_OVERRIDE) {
|
| - // Set implicit credentials if we are going through a proxy, just in case
|
| - // the proxy is requiring authentication. Continue on errors, maybe
|
| - // the credentials won't be needed anyway. There will be one more chance
|
| - // to set credentials when the proxy challenges and the job errors out.
|
| - creds_set_scheme_unknown_ = false;
|
| - hr = SetProxyAuthImplicitCredentials(request_state_->bits_job,
|
| - BG_AUTH_SCHEME_NEGOTIATE);
|
| - if (SUCCEEDED(hr)) {
|
| - current_auth_scheme_ = BG_AUTH_SCHEME_NEGOTIATE;
|
| - } else {
|
| - OPT_LOG(LW, (_T("[failed to set BITS proxy credentials][0x%08x]"), hr));
|
| - }
|
| - }
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::DoSend() {
|
| - ASSERT1(request_state_.get());
|
| - ASSERT1(request_state_->bits_job);
|
| -
|
| - NET_LOG(L3, (_T("[BitsRequest::DoSend]")));
|
| -
|
| - if (is_canceled_) {
|
| - return GOOPDATE_E_CANCELLED;
|
| - }
|
| -
|
| - HRESULT hr = SetJobProperties();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - hr = SetJobProxyUsage();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - hr = SetupBitsCallback();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - hr = request_state_->bits_job->Resume();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - NET_LOG(L3, (_T("[job priority %d]"),
|
| - GetJobPriority(request_state_->bits_job)));
|
| -
|
| - // Poll for state changes. The code executing on the state changes must be
|
| - // idempotent, as the same state can be seen multiple times when looping.
|
| - // There is only one important case, which is retrying the job when the
|
| - // job is in the ERROR state. We attempt to handle the error, for
|
| - // example retrying one more time or changing proxy credentials, and then
|
| - // we resume the job. There is an assumption, so far true, that calling
|
| - // Resume on a job, the state changes right away from SUSPENDED to QUEUED.
|
| -
|
| - for (;;) {
|
| - if (is_canceled_) {
|
| - return GOOPDATE_E_CANCELLED;
|
| - }
|
| -
|
| - BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
|
| - hr = request_state_->bits_job->GetState(&job_state);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - NET_LOG(L3, (_T("[job %s][state %s]"),
|
| - GuidToString(request_state_->bits_job_id),
|
| - JobStateToString(job_state)));
|
| -
|
| - switch (job_state) {
|
| - case BG_JOB_STATE_QUEUED:
|
| - break;
|
| -
|
| - case BG_JOB_STATE_CONNECTING:
|
| - if (callback_) {
|
| - callback_->OnProgress(0,
|
| - 0,
|
| - WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER,
|
| - NULL);
|
| - }
|
| - break;
|
| -
|
| - case BG_JOB_STATE_TRANSFERRING:
|
| - OnStateTransferring();
|
| - break;
|
| -
|
| - case BG_JOB_STATE_TRANSIENT_ERROR:
|
| - break;
|
| -
|
| - case BG_JOB_STATE_ERROR:
|
| - hr = OnStateError();
|
| - if (SUCCEEDED(hr)) {
|
| - // The error handler dealt with the error. Countinue the loop.
|
| - break;
|
| - }
|
| -
|
| - // Give up.
|
| - return request_state_->http_status_code ? S_OK : hr;
|
| -
|
| - case BG_JOB_STATE_TRANSFERRED:
|
| - NotifyProgress();
|
| - hr = request_state_->bits_job->Complete();
|
| - if (SUCCEEDED(hr) || BG_S_UNABLE_TO_DELETE_FILES == hr) {
|
| - // Assume the status code is 200 if the transfer completed. BITS does
|
| - // not provide access to the status code.
|
| - request_state_->http_status_code = HTTP_STATUS_OK;
|
| -
|
| - if (creds_set_scheme_unknown_) {
|
| - // Bits job completed successfully. If we have a valid username, we
|
| - // record the auth scheme with the NetworkConfig, so it can be used
|
| - // in the future within this process.
|
| - uint32 win_http_scheme =
|
| - BitsToWinhttpProxyAuthScheme(current_auth_scheme_);
|
| - ASSERT1(win_http_scheme != UNKNOWN_AUTH_SCHEME);
|
| - bool is_https = String_StartsWith(url_, kHttpsProtoScheme, true);
|
| -
|
| - NetworkConfig* network_config = NULL;
|
| - NetworkConfigManager& nm = NetworkConfigManager::Instance();
|
| - HRESULT hr = nm.GetUserNetworkConfig(&network_config);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme(
|
| - proxy_config_.proxy, is_https, win_http_scheme)));
|
| - }
|
| -
|
| - return S_OK;
|
| - } else {
|
| - return hr;
|
| - }
|
| -
|
| - case BG_JOB_STATE_SUSPENDED:
|
| - break;
|
| -
|
| - case BG_JOB_STATE_ACKNOWLEDGED:
|
| - ASSERT1(false);
|
| - return S_OK;
|
| -
|
| - case BG_JOB_STATE_CANCELLED:
|
| - return GOOPDATE_E_CANCELLED;
|
| - };
|
| -
|
| - DWORD wait_result = ::WaitForSingleObject(
|
| - get(bits_job_status_changed_event_), kPollingIntervalMs);
|
| - if (wait_result == WAIT_FAILED) {
|
| - ::Sleep(kPollingIntervalMs);
|
| - }
|
| - }
|
| -}
|
| -
|
| -HRESULT BitsRequest::OnStateTransferring() {
|
| - // BITS could call JobModification very often during transfer so we
|
| - // do report meter here to avoid too many progress notifications.
|
| - uint32 now = GetTickCount();
|
| -
|
| - if (now >= last_progress_report_tick_ &&
|
| - now - last_progress_report_tick_ < kJobProgressReportMinimumIntervalMs) {
|
| - return S_OK;
|
| - }
|
| -
|
| - last_progress_report_tick_ = now;
|
| - return NotifyProgress();
|
| -}
|
| -
|
| -HRESULT BitsRequest::OnStateError() {
|
| - CComPtr<IBackgroundCopyError> error;
|
| - HRESULT hr = request_state_->bits_job->GetError(&error);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE;
|
| - HRESULT error_code = E_FAIL;
|
| - hr = error->GetError(&error_context, &error_code);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - ASSERT1(FAILED(error_code));
|
| -
|
| - NET_LOG(L3, (_T("[handle bits error][0x%08x]"), error_code));
|
| -
|
| - request_state_->http_status_code = GetHttpStatusFromBitsError(error_code);
|
| -
|
| - if (error_code == BG_E_HTTP_ERROR_407) {
|
| - hr = creds_set_scheme_unknown_ ? HandleProxyAuthenticationErrorCredsSet() :
|
| - HandleProxyAuthenticationError();
|
| - if (SUCCEEDED(hr)) {
|
| - return S_OK;
|
| - }
|
| - }
|
| -
|
| - // We could not handle this error. The control will return to the caller.
|
| - return error_code;
|
| -}
|
| -
|
| -HRESULT BitsRequest::NotifyProgress() {
|
| - if (!callback_) {
|
| - return S_OK;
|
| - }
|
| - BG_JOB_PROGRESS progress = {0};
|
| - HRESULT hr = request_state_->bits_job->GetProgress(&progress);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - ASSERT1(progress.FilesTotal == 1);
|
| - ASSERT1(progress.BytesTransferred <= INT_MAX);
|
| - ASSERT1(progress.BytesTotal <= INT_MAX);
|
| - callback_->OnProgress(static_cast<int>(progress.BytesTransferred),
|
| - static_cast<int>(progress.BytesTotal),
|
| - WINHTTP_CALLBACK_STATUS_READ_COMPLETE,
|
| - NULL);
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::GetProxyCredentials() {
|
| - CString username;
|
| - CString password;
|
| - uint32 auth_scheme = UNKNOWN_AUTH_SCHEME;
|
| - bool is_https = String_StartsWith(url_, kHttpsProtoScheme, true);
|
| -
|
| - NetworkConfig* network_config = NULL;
|
| - NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
|
| - HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - if (!network_config->GetProxyCredentials(true, false,
|
| - proxy_config_.proxy, proxy_auth_config_, is_https, &username,
|
| - &password, &auth_scheme)) {
|
| - OPT_LOG(LE, (_T("[BitsRequest::GetProxyCredentials failed]")));
|
| - return E_ACCESSDENIED;
|
| - }
|
| -
|
| - if (auth_scheme != UNKNOWN_AUTH_SCHEME) {
|
| - current_auth_scheme_ = WinHttpToBitsProxyAuthScheme(auth_scheme);
|
| - OPT_LOG(L3, (_T("[BitsRequest::GetProxyCredentials][%s]"),
|
| - BitsAuthSchemeToString(current_auth_scheme_)));
|
| - return SetProxyAuthCredentials(request_state_->bits_job,
|
| - CStrBuf(username), CStrBuf(password),
|
| - static_cast<BG_AUTH_SCHEME>(current_auth_scheme_));
|
| - }
|
| -
|
| - OPT_LOG(L3, (_T("[BitsRequest::GetProxyCredentials][Auth scheme unknown]")));
|
| - // We do not know the scheme beforehand. So we set credentials on all the
|
| - // schemes except BASIC and try them out in seqence. We could have used BASIC
|
| - // as well, however, we do not want to leak passwords by mistake.
|
| - for (int scheme = BG_AUTH_SCHEME_DIGEST; scheme <= BG_AUTH_SCHEME_NEGOTIATE;
|
| - ++scheme) {
|
| - hr = SetProxyAuthCredentials(request_state_->bits_job,
|
| - CStrBuf(username), CStrBuf(password),
|
| - static_cast<BG_AUTH_SCHEME>(scheme));
|
| - if (FAILED(hr)) {
|
| - OPT_LOG(LE, (_T("[BitsRequest::GetProxyCredentials][0x%08x][%s]"),
|
| - hr, BitsAuthSchemeToString(scheme)));
|
| - return hr;
|
| - }
|
| - }
|
| -
|
| - current_auth_scheme_ = BG_AUTH_SCHEME_NEGOTIATE;
|
| - creds_set_scheme_unknown_ = true;
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BitsRequest::HandleProxyAuthenticationError() {
|
| - ASSERT1(!creds_set_scheme_unknown_);
|
| - HRESULT hr = E_ACCESSDENIED;
|
| -
|
| - if (current_auth_scheme_ == 0) {
|
| - current_auth_scheme_ = BG_AUTH_SCHEME_NEGOTIATE;
|
| - hr = SetProxyAuthImplicitCredentials(request_state_->bits_job,
|
| - BG_AUTH_SCHEME_NEGOTIATE);
|
| - } else if (current_auth_scheme_ == BG_AUTH_SCHEME_NEGOTIATE) {
|
| - current_auth_scheme_ = BG_AUTH_SCHEME_NTLM;
|
| - hr = SetProxyAuthImplicitCredentials(request_state_->bits_job,
|
| - BG_AUTH_SCHEME_NTLM);
|
| - } else {
|
| - hr = GetProxyCredentials();
|
| - }
|
| -
|
| - OPT_LOG(L3, (_T("[BitsRequest::HandleProxyAuthenticationError][0x%08x][%s]"),
|
| - hr, BitsAuthSchemeToString(current_auth_scheme_)));
|
| - return SUCCEEDED(hr) ? request_state_->bits_job->Resume() : hr;
|
| -}
|
| -
|
| -HRESULT BitsRequest::HandleProxyAuthenticationErrorCredsSet() {
|
| - ASSERT1(creds_set_scheme_unknown_);
|
| -
|
| - if (current_auth_scheme_ == BG_AUTH_SCHEME_NEGOTIATE) {
|
| - current_auth_scheme_ = BG_AUTH_SCHEME_NTLM;
|
| - } else if (current_auth_scheme_ == BG_AUTH_SCHEME_NTLM) {
|
| - current_auth_scheme_ = BG_AUTH_SCHEME_DIGEST;
|
| - } else {
|
| - OPT_LOG(LE, (_T("[HandleProxyAuthenticationErrorCredsSet][Failure]")));
|
| - return E_ACCESSDENIED;
|
| - }
|
| -
|
| - OPT_LOG(L3, (_T("[BitsRequest::HandleProxyAuthenticationErrorCredsSet][%s]"),
|
| - BitsAuthSchemeToString(current_auth_scheme_)));
|
| - return request_state_->bits_job->Resume();
|
| -}
|
| -
|
| -int BitsRequest::WinHttpToBitsProxyAuthScheme(uint32 winhttp_scheme) {
|
| - if (winhttp_scheme == WINHTTP_AUTH_SCHEME_NEGOTIATE) {
|
| - return BG_AUTH_SCHEME_NEGOTIATE;
|
| - }
|
| - if (winhttp_scheme == WINHTTP_AUTH_SCHEME_NTLM) {
|
| - return BG_AUTH_SCHEME_NTLM;
|
| - }
|
| - if (winhttp_scheme == WINHTTP_AUTH_SCHEME_DIGEST) {
|
| - return BG_AUTH_SCHEME_DIGEST;
|
| - }
|
| - if (winhttp_scheme == WINHTTP_AUTH_SCHEME_BASIC) {
|
| - return BG_AUTH_SCHEME_BASIC;
|
| - }
|
| -
|
| - ASSERT1(false);
|
| - return UNKNOWN_AUTH_SCHEME;
|
| -}
|
| -
|
| -uint32 BitsRequest::BitsToWinhttpProxyAuthScheme(int bits_scheme) {
|
| - if (bits_scheme == BG_AUTH_SCHEME_NEGOTIATE) {
|
| - return WINHTTP_AUTH_SCHEME_NEGOTIATE;
|
| - }
|
| - if (bits_scheme == BG_AUTH_SCHEME_NTLM) {
|
| - return WINHTTP_AUTH_SCHEME_NTLM;
|
| - }
|
| - if (bits_scheme == BG_AUTH_SCHEME_DIGEST) {
|
| - return WINHTTP_AUTH_SCHEME_DIGEST;
|
| - }
|
| - if (bits_scheme == BG_AUTH_SCHEME_BASIC) {
|
| - return WINHTTP_AUTH_SCHEME_BASIC;
|
| - }
|
| -
|
| - ASSERT1(false);
|
| - return UNKNOWN_AUTH_SCHEME;
|
| -}
|
| -
|
| -} // namespace omaha
|
| -
|
|
|