diff options
Diffstat (limited to 'ceee/ie/plugin/bho/http_negotiate.cc')
-rw-r--r-- | ceee/ie/plugin/bho/http_negotiate.cc | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/ceee/ie/plugin/bho/http_negotiate.cc b/ceee/ie/plugin/bho/http_negotiate.cc new file mode 100644 index 0000000..ac39ba3 --- /dev/null +++ b/ceee/ie/plugin/bho/http_negotiate.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ceee/ie/plugin/bho/http_negotiate.h" + +#include <atlbase.h> +#include <atlctl.h> +#include <htiframe.h> +#include <urlmon.h> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/time.h" + +#include "ceee/ie/plugin/bho/cookie_accountant.h" +#include "chrome_frame/vtable_patch_manager.h" +#include "chrome_frame/utils.h" +#include "base/scoped_comptr_win.h" + +static const int kHttpNegotiateBeginningTransactionIndex = 3; +static const int kHttpNegotiateOnResponseIndex = 4; + +CComAutoCriticalSection HttpNegotiatePatch::bho_instance_count_crit_; +int HttpNegotiatePatch::bho_instance_count_ = 0; + + +BEGIN_VTABLE_PATCHES(IHttpNegotiate) + VTABLE_PATCH_ENTRY(kHttpNegotiateBeginningTransactionIndex, + HttpNegotiatePatch::BeginningTransaction) + VTABLE_PATCH_ENTRY(kHttpNegotiateOnResponseIndex, + HttpNegotiatePatch::OnResponse) +END_VTABLE_PATCHES() + +namespace { + +class SimpleBindStatusCallback : public CComObjectRootEx<CComSingleThreadModel>, + public IBindStatusCallback { + public: + BEGIN_COM_MAP(SimpleBindStatusCallback) + COM_INTERFACE_ENTRY(IBindStatusCallback) + END_COM_MAP() + + // IBindStatusCallback implementation + STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding) { + return E_NOTIMPL; + } + + STDMETHOD(GetPriority)(LONG* priority) { + return E_NOTIMPL; + } + STDMETHOD(OnLowResource)(DWORD reserved) { + return E_NOTIMPL; + } + + STDMETHOD(OnProgress)(ULONG progress, ULONG max_progress, + ULONG status_code, LPCWSTR status_text) { + return E_NOTIMPL; + } + STDMETHOD(OnStopBinding)(HRESULT result, LPCWSTR error) { + return E_NOTIMPL; + } + + STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info) { + return E_NOTIMPL; + } + + STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* formatetc, + STGMEDIUM* storage) { + return E_NOTIMPL; + } + STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* object) { + return E_NOTIMPL; + } +}; + +} // end namespace + +HttpNegotiatePatch::HttpNegotiatePatch() { +} + +HttpNegotiatePatch::~HttpNegotiatePatch() { +} + +// static +bool HttpNegotiatePatch::Initialize() { + // Patch IHttpNegotiate for user-agent and cookie functionality. + { + CComCritSecLock<CComAutoCriticalSection> lock(bho_instance_count_crit_); + bho_instance_count_++; + if (bho_instance_count_ != 1) { + return true; + } + } + + if (IS_PATCHED(IHttpNegotiate)) { + LOG(WARNING) << __FUNCTION__ << ": already patched."; + return true; + } + + // Use our SimpleBindStatusCallback class as we need a temporary object that + // implements IBindStatusCallback. + CComObjectStackEx<SimpleBindStatusCallback> request; + ScopedComPtr<IBindCtx> bind_ctx; + HRESULT hr = ::CreateAsyncBindCtx(0, &request, NULL, bind_ctx.Receive()); + + DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtx"; + if (bind_ctx) { + ScopedComPtr<IUnknown> bscb_holder; + bind_ctx->GetObjectParam(L"_BSCB_Holder_", bscb_holder.Receive()); + if (bscb_holder) { + hr = PatchHttpNegotiate(bscb_holder); + } else { + NOTREACHED() << "Failed to get _BSCB_Holder_"; + hr = E_UNEXPECTED; + } + bind_ctx.Release(); + } + + return SUCCEEDED(hr); +} + +// static +void HttpNegotiatePatch::Uninitialize() { + CComCritSecLock<CComAutoCriticalSection> lock(bho_instance_count_crit_); + bho_instance_count_--; + DCHECK_GE(bho_instance_count_, 0); + if (bho_instance_count_ == 0) { + vtable_patch::UnpatchInterfaceMethods(IHttpNegotiate_PatchInfo); + } +} + +// static +HRESULT HttpNegotiatePatch::PatchHttpNegotiate(IUnknown* to_patch) { + DCHECK(to_patch); + DCHECK_IS_NOT_PATCHED(IHttpNegotiate); + + ScopedComPtr<IHttpNegotiate> http; + HRESULT hr = http.QueryFrom(to_patch); + if (FAILED(hr)) { + hr = DoQueryService(IID_IHttpNegotiate, to_patch, http.Receive()); + } + + if (http) { + hr = vtable_patch::PatchInterfaceMethods(http, IHttpNegotiate_PatchInfo); + DLOG_IF(ERROR, FAILED(hr)) + << StringPrintf("HttpNegotiate patch failed 0x%08X", hr); + } else { + DLOG(WARNING) + << StringPrintf("IHttpNegotiate not supported 0x%08X", hr); + } + + return hr; +} + + +// static +HRESULT HttpNegotiatePatch::BeginningTransaction( + IHttpNegotiate_BeginningTransaction_Fn original, IHttpNegotiate* me, + LPCWSTR url, LPCWSTR headers, DWORD reserved, LPWSTR* additional_headers) { + DLOG(INFO) << __FUNCTION__ << " " << url << " headers:\n" << headers; + + HRESULT hr = original(me, url, headers, reserved, additional_headers); + + if (FAILED(hr)) { + DLOG(WARNING) << __FUNCTION__ << " Delegate returned an error"; + return hr; + } + + // TODO(skare@google.com): Modify User-Agent here. + + return hr; +} + +// static +HRESULT HttpNegotiatePatch::OnResponse( + IHttpNegotiate_OnResponse_Fn original, IHttpNegotiate* me, + DWORD response_code, LPCWSTR response_headers, LPCWSTR request_headers, + LPWSTR* additional_request_headers) { + DLOG(INFO) << __FUNCTION__ << " response headers:\n" << response_headers; + + base::Time current_time = base::Time::Now(); + + HRESULT hr = original(me, response_code, response_headers, request_headers, + additional_request_headers); + + if (FAILED(hr)) { + DLOG(WARNING) << __FUNCTION__ << " Delegate returned an error"; + return hr; + } + + CookieAccountant::GetInstance()->RecordHttpResponseCookies( + std::string(CW2A(response_headers)), current_time); + + return hr; +} |