| Index: src/plugin/PluginWbPassThrough.cpp |
| diff --git a/src/plugin/PluginWbPassThrough.cpp b/src/plugin/PluginWbPassThrough.cpp |
| index 9069adf80a833d0bde42587ce55df2b2b75e0065..24ec671846a963fdfb74d5e3f30b1315592b951f 100644 |
| --- a/src/plugin/PluginWbPassThrough.cpp |
| +++ b/src/plugin/PluginWbPassThrough.cpp |
| @@ -15,402 +15,405 @@ |
| * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| -#include "PluginStdAfx.h" |
| - |
| -#include "PluginWbPassThrough.h" |
| -#include "PluginClient.h" |
| -#include "PluginClientFactory.h" |
| -#include "PluginFilter.h" |
| -#include "PluginSettings.h" |
| -#include "PluginClass.h" |
| -#include "PluginSystem.h" |
| -#include <WinInet.h> |
| -#include "wtypes.h" |
| -#include "../shared/Utils.h" |
| -#include "../shared/IE_version.h" |
| - |
| -namespace |
| -{ |
| - const std::string g_blockedByABPPage = "<!DOCTYPE html>" |
| - "<html>" |
| - "<body>" |
| - "<!-- blocked by AdblockPlus -->" |
| - "</body>" |
| - "</html>"; |
| - |
| - template <class T> |
| - T ExtractHttpHeader(const T& allHeaders, const T& targetHeaderNameWithColon, const T& delimiter) |
| - { |
| - auto targetHeaderBeginsAt = allHeaders.find(targetHeaderNameWithColon); |
| - if (targetHeaderBeginsAt == T::npos) |
| - { |
| - return T(); |
| - } |
| - targetHeaderBeginsAt += targetHeaderNameWithColon.length(); |
| - auto targetHeaderEndsAt = allHeaders.find(delimiter, targetHeaderBeginsAt); |
| - if (targetHeaderEndsAt == T::npos) |
| - { |
| - return T(); |
| - } |
| - return allHeaders.substr(targetHeaderBeginsAt, targetHeaderEndsAt - targetHeaderBeginsAt); |
| - } |
| - |
| - std::string ExtractHttpAcceptHeader(IInternetProtocol* internetProtocol) |
| - { |
| - // Despite there being HTTP_QUERY_ACCEPT and other query info flags, they don't work here, |
| - // only HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS does work. |
| - ATL::CComPtr<IWinInetHttpInfo> winInetHttpInfo; |
| - HRESULT hr = internetProtocol->QueryInterface(&winInetHttpInfo); |
| - if (FAILED(hr) || !winInetHttpInfo) |
| - { |
| - return ""; |
| - } |
| - DWORD size = 0; |
| - DWORD flags = 0; |
| - DWORD queryOption = HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS; |
| - hr = winInetHttpInfo->QueryInfo(queryOption, /*buffer*/ nullptr, /*get size*/ &size, &flags, /*reserved*/ 0); |
| - if (FAILED(hr)) |
| - { |
| - return ""; |
| - } |
| - std::string buf(size, '\0'); |
| - hr = winInetHttpInfo->QueryInfo(queryOption, &buf[0], &size, &flags, 0); |
| - if (FAILED(hr)) |
| - { |
| - return ""; |
| - } |
| - return ExtractHttpHeader<std::string>(buf, "Accept:", "\r\n"); |
| - } |
| - |
| - bool IsXmlHttpRequest(const std::wstring& additionalHeaders) |
| - { |
| - auto requestedWithHeader = ExtractHttpHeader<std::wstring>(additionalHeaders, L"X-Requested-With:", L"\n"); |
| - return TrimString(requestedWithHeader) == L"XMLHttpRequest"; |
| - } |
| -} |
| - |
| -WBPassthruSink::WBPassthruSink() |
| - : m_currentPositionOfSentPage(0) |
| - , m_contentType(CFilter::EContentType::contentTypeAny) |
| - , m_isCustomResponse(false) |
| -{ |
| -} |
| - |
| -int WBPassthruSink::GetContentTypeFromMimeType(const CString& mimeType) |
| -{ |
| - if (mimeType.Find(L"image/") >= 0) |
| - { |
| - return CFilter::contentTypeImage; |
| - } |
| - if (mimeType.Find(L"text/css") >= 0) |
| - { |
| - return CFilter::contentTypeStyleSheet; |
| - } |
| - if ((mimeType.Find(L"application/javascript") >= 0) || (mimeType.Find(L"application/json") >= 0)) |
| - { |
| - return CFilter::contentTypeScript; |
| - } |
| - if (mimeType.Find(L"application/x-shockwave-flash") >= 0) |
| - { |
| - return CFilter::contentTypeObject; |
| - } |
| - if (mimeType.Find(L"text/html") >= 0) |
| - { |
| - return CFilter::contentTypeSubdocument; |
| - } |
| - // It is important to have this check last, since it is rather generic, and might overlay text/html, for example |
| - if (mimeType.Find(L"xml") >= 0) |
| - { |
| - return CFilter::contentTypeXmlHttpRequest; |
| - } |
| - |
| - return CFilter::contentTypeAny; |
| -} |
| - |
| -int WBPassthruSink::GetContentTypeFromURL(const std::wstring& src) |
| -{ |
| - CString srcLegacy = ToCString(src); |
| - CString srcExt = srcLegacy; |
| - |
| - int pos = 0; |
| - if ((pos = srcLegacy.Find('?')) > 0) |
| - { |
| - srcExt = srcLegacy.Left(pos); |
| - } |
| - |
| - int lastDotIndex = srcExt.ReverseFind('.'); |
| - if (lastDotIndex < 0) |
| - return CFilter::contentTypeAny; |
| - CString ext = srcExt.Mid(lastDotIndex); |
| - if (ext == L".jpg" || ext == L".gif" || ext == L".png" || ext == L".jpeg") |
| - { |
| - return CFilter::contentTypeImage; |
| - } |
| - else if (ext == L".css") |
| - { |
| - return CFilter::contentTypeStyleSheet; |
| - } |
| - else if (ext.Right(3) == L".js") |
| - { |
| - return CFilter::contentTypeScript; |
| - } |
| - else if (ext == L".xml") |
| - { |
| - return CFilter::contentTypeXmlHttpRequest; |
| - } |
| - else if (ext == L".swf") |
| - { |
| - return CFilter::contentTypeObject; |
| - } |
| - else if (ext == L".jsp" || ext == L".php" || ext == L".html") |
| - { |
| - return CFilter::contentTypeSubdocument; |
| - } |
| - return CFilter::contentTypeAny; |
| -} |
| - |
| -int WBPassthruSink::GetContentType(const CString& mimeType, const std::wstring& domain, const std::wstring& src) |
| -{ |
| - // No referer or mime type |
| - // BINDSTRING_XDR_ORIGIN works only for IE v8+ |
| - if (mimeType.IsEmpty() && domain.empty() && AdblockPlus::IE::InstalledMajorVersion() >= 8) |
| - { |
| - return CFilter::contentTypeXmlHttpRequest; |
| - } |
| - int contentType = GetContentTypeFromMimeType(mimeType); |
| - if (contentType == CFilter::contentTypeAny) |
| - { |
| - contentType = GetContentTypeFromURL(src); |
| - } |
| - return contentType; |
| -} |
| - |
| -//////////////////////////////////////////////////////////////////////////////////////// |
| -//WBPassthruSink |
| -//Monitor and/or cancel every request and responde |
| -//WB makes, including images, sounds, scripts, etc |
| -//////////////////////////////////////////////////////////////////////////////////////// |
| -HRESULT WBPassthruSink::OnStart(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, |
| - IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved, |
| - IInternetProtocol* pTargetProtocol, bool& handled) |
| -{ |
| - m_pTargetProtocol = pTargetProtocol; |
| - return BaseClass::OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved, pTargetProtocol); |
| -} |
| - |
| -HRESULT WBPassthruSink::OnRead(void* pv, ULONG cb, ULONG* pcbRead) |
| -{ |
| - if (!pv || !pcbRead) |
| - { |
| - return E_POINTER; |
| - } |
| - *pcbRead = 0; |
| - |
| - if (PassthroughAPP::CustomSinkStartPolicy<WBPassthru, WBPassthruSink>::GetProtocol(this)->m_shouldSupplyCustomContent) |
| - { |
| - ULONG blockedByABPPageSize = static_cast<ULONG>(g_blockedByABPPage.size()); |
| - auto positionGrow = std::min<ULONG>(cb, static_cast<ULONG>(blockedByABPPageSize - m_currentPositionOfSentPage)); |
| - if (positionGrow == 0) { |
| - return S_FALSE; |
| - } |
| - std::copy(g_blockedByABPPage.begin(), g_blockedByABPPage.begin() + positionGrow, |
| - stdext::make_checked_array_iterator(static_cast<char*>(pv), cb)); |
| - *pcbRead = positionGrow; |
| - m_currentPositionOfSentPage += positionGrow; |
| - |
| - if (m_spInternetProtocolSink) |
| - { |
| - m_spInternetProtocolSink->ReportData(BSCF_INTERMEDIATEDATANOTIFICATION, |
| - static_cast<ULONG>(m_currentPositionOfSentPage), blockedByABPPageSize); |
| - } |
| - if (blockedByABPPageSize == m_currentPositionOfSentPage && m_spInternetProtocolSink) |
| - { |
| - m_spInternetProtocolSink->ReportData(BSCF_DATAFULLYAVAILABLE, blockedByABPPageSize, blockedByABPPageSize); |
| - m_spInternetProtocolSink->ReportResult(S_OK, 0, nullptr); |
| - } |
| - return S_OK; |
| - } |
| - return m_pTargetProtocol->Read(pv, cb, pcbRead); |
| -} |
| -STDMETHODIMP WBPassthruSink::Switch( |
| - /* [in] */ PROTOCOLDATA *pProtocolData) |
| -{ |
| - ATLASSERT(m_spInternetProtocolSink != 0); |
| - |
| - /* |
| - From Igor Tandetnik "itandetnik@mvps.org" |
| - " |
| - Beware multithreading. URLMon has this nasty habit of spinning worker |
| - threads, not even bothering to initialize COM on them, and calling APP |
| - methods on those threads. If you try to raise COM events directly from |
| - such a thread, bad things happen (random crashes, events being lost). |
| - You are only guaranteed to be on the main STA thread in two cases. |
| - First, in methods of interfaces that were obtained with |
| - IServiceProvider, such as IHttpNegotiage::BeginningTransaction or |
| - IAuthenticate::Authenticate. Second, you can call |
| - IInternetProtocolSink::Switch with PD_FORCE_SWITCH flag in |
| - PROTOCOLDATA::grfFlags, eventually URLMon will turn around and call |
| - IInternetProtocol::Continue on the main thread. |
| - |
| - Or, if you happen to have a window handy that was created on the main |
| - thread, you can post yourself a message. |
| - " |
| - */ |
| - return m_spInternetProtocolSink ? m_spInternetProtocolSink->Switch(pProtocolData) : E_UNEXPECTED; |
| -} |
| - |
| -// This is the heuristic which detects the requests issued by Flash.ocx. |
| -// It turned out that the implementation from ''Flash.ocx'' (tested version is 15.0.0.152) |
| -// returns quite minimal configuration in comparison with the implementation from Microsofts' |
| -// libraries (see grfBINDF and bindInfo.dwOptions). The impl from MS often includes something |
| -// else. |
| -bool WBPassthruSink::IsFlashRequest(const wchar_t* const* additionalHeaders) |
| -{ |
| - if (additionalHeaders && *additionalHeaders) |
| - { |
| - auto flashVersionHeader = ExtractHttpHeader<std::wstring>(*additionalHeaders, L"x-flash-version:", L"\n"); |
| - if (!TrimString(flashVersionHeader).empty()) |
| - { |
| - return true; |
| - } |
| - } |
| - ATL::CComPtr<IBindStatusCallback> bscb; |
| - if (SUCCEEDED(QueryServiceFromClient(&bscb)) && !!bscb) |
| - { |
| - DWORD grfBINDF = 0; |
| - BINDINFO bindInfo = {}; |
| - bindInfo.cbSize = sizeof(bindInfo); |
| - if (SUCCEEDED(bscb->GetBindInfo(&grfBINDF, &bindInfo)) && |
| - (BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE| BINDF_PULLDATA) == grfBINDF && |
| - (BINDINFO_OPTIONS_ENABLE_UTF8 | BINDINFO_OPTIONS_USE_IE_ENCODING) == bindInfo.dwOptions |
| - ) |
| - { |
| - return true; |
| - } |
| - } |
| - return false; |
| -} |
| - |
| -STDMETHODIMP WBPassthruSink::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR* pszAdditionalHeaders) |
| -{ |
| - if (!szURL) |
| - { |
| - return E_POINTER; |
| - } |
| - std::wstring src = szURL; |
| - DEBUG_GENERAL(ToCString(src)); |
| - |
| - std::string acceptHeader = ExtractHttpAcceptHeader(m_spTargetProtocol); |
| - |
| - if (pszAdditionalHeaders) |
| - { |
| - *pszAdditionalHeaders = nullptr; |
| - } |
| - |
| - CComPtr<IHttpNegotiate> httpNegotiate; |
| - QueryServiceFromClient(&httpNegotiate); |
| - // This fills the pszAdditionalHeaders with more headers. One of which is the Referer header, which we need. |
| - // There doesn't seem to be any other way to get this header before the request has been made. |
| - HRESULT nativeHr = httpNegotiate ? httpNegotiate->BeginningTransaction(szURL, szHeaders, dwReserved, pszAdditionalHeaders) : S_OK; |
| - |
| - if (pszAdditionalHeaders && *pszAdditionalHeaders) |
| - { |
| - m_boundDomain = ExtractHttpHeader<std::wstring>(*pszAdditionalHeaders, L"Referer:", L"\n"); |
| - } |
| - m_boundDomain = TrimString(m_boundDomain); |
| - m_contentType = GetContentType(ATL::CString(acceptHeader.c_str()), m_boundDomain, src); |
| - CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId()); |
| - CPluginClient* client = CPluginClient::GetInstance(); |
| - |
| - if (tab && client) |
| - { |
| - std::wstring documentUrl = tab->GetDocumentUrl(); |
| - // Page is identical to document => don't block |
| - if (documentUrl == src) |
| - { |
| - return nativeHr; |
| - } |
| - else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsWhitelistedUrl(documentUrl)) |
| - { |
| - if (tab->IsFrameCached(src)) |
| - { |
| - m_contentType = CFilter::contentTypeSubdocument; |
| - } |
| - } |
| - } |
| - |
| - if (IsFlashRequest(pszAdditionalHeaders)) |
| - { |
| - m_contentType = CFilter::EContentType::contentTypeObjectSubrequest; |
| - } |
| - |
| - if (pszAdditionalHeaders && *pszAdditionalHeaders && IsXmlHttpRequest(*pszAdditionalHeaders)) |
| - { |
| - m_contentType = CFilter::EContentType::contentTypeXmlHttpRequest; |
| - } |
| - |
| - if (client->ShouldBlock(szURL, m_contentType, m_boundDomain, /*debug flag but must be set*/true)) |
| - { |
| - // NOTE: Feeding custom HTML to Flash, instead of original object subrequest |
| - // doesn't have much sense. It also can manifest in unwanted result |
| - // like video being blocked (See https://issues.adblockplus.org/ticket/1669) |
| - // So we report blocked object subrequests as failed, not just empty HTML. |
| - m_isCustomResponse = m_contentType != CFilter::contentTypeObjectSubrequest; |
| - return E_ABORT; |
| - } |
| - return nativeHr; |
| -} |
| - |
| -STDMETHODIMP WBPassthruSink::OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) |
| -{ |
| - if (pszAdditionalRequestHeaders) |
| - { |
| - *pszAdditionalRequestHeaders = 0; |
| - } |
| - |
| - CComPtr<IHttpNegotiate> spHttpNegotiate; |
| - QueryServiceFromClient(&spHttpNegotiate); |
| - |
| - return spHttpNegotiate ? spHttpNegotiate->OnResponse(dwResponseCode, szResponseHeaders, szRequestHeaders, pszAdditionalRequestHeaders) : S_OK; |
| -} |
| - |
| -STDMETHODIMP WBPassthruSink::ReportProgress(ULONG ulStatusCode, LPCWSTR szStatusText) |
| -{ |
| - return m_spInternetProtocolSink ? m_spInternetProtocolSink->ReportProgress(ulStatusCode, szStatusText) : S_OK; |
| -} |
| - |
| -STDMETHODIMP WBPassthruSink::ReportResult(/* [in] */ HRESULT hrResult, /* [in] */ DWORD dwError, /* [in] */ LPCWSTR szResult) |
| -{ |
| - if (m_isCustomResponse) |
| - { |
| - // Don't notify the client about aborting of the operation, thus don't call BaseClass::ReportResult. |
| - // Current method is called by the original protocol implementation and we are intercepting the |
| - // call here and eating it, we will call the proper ReportResult later by ourself. |
| - return S_OK; |
| - } |
| - return BaseClass::ReportResult(hrResult, dwError, szResult); |
| -} |
| - |
| - |
| -WBPassthru::WBPassthru() |
| - : m_shouldSupplyCustomContent(false) |
| -{ |
| -} |
| - |
| -STDMETHODIMP WBPassthru::Start(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, |
| - IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved) |
| -{ |
| - ATLASSERT(m_spInternetProtocol != 0); |
| - if (!m_spInternetProtocol) |
| - { |
| - return E_UNEXPECTED; |
| - } |
| - |
| - return OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved, m_spInternetProtocol); |
| -} |
| - |
| -STDMETHODIMP WBPassthru::Read(/* [in, out] */ void *pv,/* [in] */ ULONG cb,/* [out] */ ULONG *pcbRead) |
| -{ |
| - WBPassthruSink* pSink = GetSink(); |
| - return pSink->OnRead(pv, cb, pcbRead); |
| +#include "PluginStdAfx.h" |
| + |
| +#include "PluginWbPassThrough.h" |
| +#include "PluginClient.h" |
| +#include "PluginClientFactory.h" |
| +#include "PluginFilter.h" |
| +#include "PluginSettings.h" |
| +#include "PluginClass.h" |
| +#include "PluginSystem.h" |
| +#include <WinInet.h> |
| +#include "wtypes.h" |
| +#include "../shared/Utils.h" |
| +#include "../shared/IE_version.h" |
| + |
| +namespace |
| +{ |
| + const std::string g_blockedByABPPage = "<!DOCTYPE html>" |
| + "<html>" |
| + "<body>" |
| + "<!-- blocked by AdblockPlus -->" |
| + "</body>" |
| + "</html>"; |
| + |
| + typedef AdblockPlus::FilterEngine::ContentType ContentType; |
| + |
| + template <class T> |
| + T ExtractHttpHeader(const T& allHeaders, const T& targetHeaderNameWithColon, const T& delimiter) |
| + { |
| + auto targetHeaderBeginsAt = allHeaders.find(targetHeaderNameWithColon); |
| + if (targetHeaderBeginsAt == T::npos) |
| + { |
| + return T(); |
| + } |
| + targetHeaderBeginsAt += targetHeaderNameWithColon.length(); |
| + auto targetHeaderEndsAt = allHeaders.find(delimiter, targetHeaderBeginsAt); |
| + if (targetHeaderEndsAt == T::npos) |
| + { |
| + return T(); |
| + } |
| + return allHeaders.substr(targetHeaderBeginsAt, targetHeaderEndsAt - targetHeaderBeginsAt); |
| + } |
| + |
| + std::string ExtractHttpAcceptHeader(IInternetProtocol* internetProtocol) |
| + { |
| + // Despite there being HTTP_QUERY_ACCEPT and other query info flags, they don't work here, |
| + // only HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS does work. |
| + ATL::CComPtr<IWinInetHttpInfo> winInetHttpInfo; |
| + HRESULT hr = internetProtocol->QueryInterface(&winInetHttpInfo); |
| + if (FAILED(hr) || !winInetHttpInfo) |
| + { |
| + return ""; |
| + } |
| + DWORD size = 0; |
| + DWORD flags = 0; |
| + DWORD queryOption = HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS; |
| + hr = winInetHttpInfo->QueryInfo(queryOption, /*buffer*/ nullptr, /*get size*/ &size, &flags, /*reserved*/ 0); |
| + if (FAILED(hr)) |
| + { |
| + return ""; |
| + } |
| + std::string buf(size, '\0'); |
| + hr = winInetHttpInfo->QueryInfo(queryOption, &buf[0], &size, &flags, 0); |
| + if (FAILED(hr)) |
| + { |
| + return ""; |
| + } |
| + return ExtractHttpHeader<std::string>(buf, "Accept:", "\r\n"); |
| + } |
| + |
| + bool IsXmlHttpRequest(const std::wstring& additionalHeaders) |
| + { |
| + auto requestedWithHeader = ExtractHttpHeader<std::wstring>(additionalHeaders, L"X-Requested-With:", L"\n"); |
| + return TrimString(requestedWithHeader) == L"XMLHttpRequest"; |
| + } |
| +} |
| + |
| +WBPassthruSink::WBPassthruSink() |
| + : m_currentPositionOfSentPage(0) |
| + , m_contentType(ContentType::CONTENT_TYPE_OTHER) |
| + , m_isCustomResponse(false) |
| +{ |
| +} |
| + |
| +ContentType WBPassthruSink::GetContentTypeFromMimeType(const CString& mimeType) |
| +{ |
| + if (mimeType.Find(L"image/") >= 0) |
| + { |
| + return ContentType::CONTENT_TYPE_IMAGE; |
| + } |
| + if (mimeType.Find(L"text/css") >= 0) |
| + { |
| + return ContentType::CONTENT_TYPE_STYLESHEET; |
| + } |
| + if ((mimeType.Find(L"application/javascript") >= 0) || (mimeType.Find(L"application/json") >= 0)) |
| + { |
| + return ContentType::CONTENT_TYPE_SCRIPT; |
| + } |
| + if (mimeType.Find(L"application/x-shockwave-flash") >= 0) |
| + { |
| + return ContentType::CONTENT_TYPE_OBJECT; |
| + } |
| + if (mimeType.Find(L"text/html") >= 0) |
| + { |
| + return ContentType::CONTENT_TYPE_SUBDOCUMENT; |
| + } |
| + // It is important to have this check last, since it is rather generic, and might overlay text/html, for example |
| + if (mimeType.Find(L"xml") >= 0) |
| + { |
| + return ContentType::CONTENT_TYPE_XMLHTTPREQUEST; |
| + } |
| + |
| + return ContentType::CONTENT_TYPE_OTHER; |
| +} |
| + |
| +ContentType WBPassthruSink::GetContentTypeFromURL(const std::wstring& src) |
| +{ |
| + CString srcLegacy = ToCString(src); |
| + CString srcExt = srcLegacy; |
| + |
| + int pos = 0; |
| + if ((pos = srcLegacy.Find('?')) > 0) |
| + { |
| + srcExt = srcLegacy.Left(pos); |
| + } |
| + |
| + int lastDotIndex = srcExt.ReverseFind('.'); |
| + if (lastDotIndex < 0) |
| + return ContentType::CONTENT_TYPE_OTHER; |
| + CString ext = srcExt.Mid(lastDotIndex); |
| + if (ext == L".jpg" || ext == L".gif" || ext == L".png" || ext == L".jpeg") |
| + { |
| + return ContentType::CONTENT_TYPE_IMAGE; |
| + } |
| + else if (ext == L".css") |
| + { |
| + return ContentType::CONTENT_TYPE_STYLESHEET; |
| + } |
| + else if (ext.Right(3) == L".js") |
| + { |
| + return ContentType::CONTENT_TYPE_SCRIPT; |
| + } |
| + else if (ext == L".xml") |
| + { |
| + return ContentType::CONTENT_TYPE_XMLHTTPREQUEST; |
| + } |
| + else if (ext == L".swf") |
| + { |
| + return ContentType::CONTENT_TYPE_OBJECT; |
| + } |
| + else if (ext == L".jsp" || ext == L".php" || ext == L".html") |
| + { |
| + return ContentType::CONTENT_TYPE_SUBDOCUMENT; |
| + } |
| + return ContentType::CONTENT_TYPE_OTHER; |
| +} |
| + |
| +ContentType WBPassthruSink::GetContentType(const CString& mimeType, const std::wstring& domain, const std::wstring& src) |
| +{ |
| + // No referer or mime type |
| + // BINDSTRING_XDR_ORIGIN works only for IE v8+ |
| + if (mimeType.IsEmpty() && domain.empty() && AdblockPlus::IE::InstalledMajorVersion() >= 8) |
| + { |
| + return ContentType::CONTENT_TYPE_XMLHTTPREQUEST; |
| + } |
| + ContentType contentType = GetContentTypeFromMimeType(mimeType); |
| + if (contentType == ContentType::CONTENT_TYPE_OTHER) |
| + { |
| + contentType = GetContentTypeFromURL(src); |
| + } |
| + return contentType; |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////////////// |
| +//WBPassthruSink |
| +//Monitor and/or cancel every request and responde |
| +//WB makes, including images, sounds, scripts, etc |
| +//////////////////////////////////////////////////////////////////////////////////////// |
| +HRESULT WBPassthruSink::OnStart(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, |
| + IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved, |
| + IInternetProtocol* pTargetProtocol, bool& handled) |
| +{ |
| + m_pTargetProtocol = pTargetProtocol; |
| + return BaseClass::OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved, pTargetProtocol); |
| +} |
| + |
| +HRESULT WBPassthruSink::OnRead(void* pv, ULONG cb, ULONG* pcbRead) |
| +{ |
| + if (!pv || !pcbRead) |
| + { |
| + return E_POINTER; |
| + } |
| + *pcbRead = 0; |
| + |
| + if (PassthroughAPP::CustomSinkStartPolicy<WBPassthru, WBPassthruSink>::GetProtocol(this)->m_shouldSupplyCustomContent) |
| + { |
| + ULONG blockedByABPPageSize = static_cast<ULONG>(g_blockedByABPPage.size()); |
| + auto positionGrow = std::min<ULONG>(cb, static_cast<ULONG>(blockedByABPPageSize - m_currentPositionOfSentPage)); |
| + if (positionGrow == 0) { |
| + return S_FALSE; |
| + } |
| + std::copy(g_blockedByABPPage.begin(), g_blockedByABPPage.begin() + positionGrow, |
| + stdext::make_checked_array_iterator(static_cast<char*>(pv), cb)); |
| + *pcbRead = positionGrow; |
| + m_currentPositionOfSentPage += positionGrow; |
| + |
| + if (m_spInternetProtocolSink) |
| + { |
| + m_spInternetProtocolSink->ReportData(BSCF_INTERMEDIATEDATANOTIFICATION, |
| + static_cast<ULONG>(m_currentPositionOfSentPage), blockedByABPPageSize); |
| + } |
| + if (blockedByABPPageSize == m_currentPositionOfSentPage && m_spInternetProtocolSink) |
| + { |
| + m_spInternetProtocolSink->ReportData(BSCF_DATAFULLYAVAILABLE, blockedByABPPageSize, blockedByABPPageSize); |
| + m_spInternetProtocolSink->ReportResult(S_OK, 0, nullptr); |
| + } |
| + return S_OK; |
| + } |
| + return m_pTargetProtocol->Read(pv, cb, pcbRead); |
| +} |
| +STDMETHODIMP WBPassthruSink::Switch( |
| + /* [in] */ PROTOCOLDATA *pProtocolData) |
| +{ |
| + ATLASSERT(m_spInternetProtocolSink != 0); |
| + |
| + /* |
| + From Igor Tandetnik "itandetnik@mvps.org" |
| + " |
| + Beware multithreading. URLMon has this nasty habit of spinning worker |
| + threads, not even bothering to initialize COM on them, and calling APP |
| + methods on those threads. If you try to raise COM events directly from |
| + such a thread, bad things happen (random crashes, events being lost). |
| + You are only guaranteed to be on the main STA thread in two cases. |
| + First, in methods of interfaces that were obtained with |
| + IServiceProvider, such as IHttpNegotiage::BeginningTransaction or |
| + IAuthenticate::Authenticate. Second, you can call |
| + IInternetProtocolSink::Switch with PD_FORCE_SWITCH flag in |
| + PROTOCOLDATA::grfFlags, eventually URLMon will turn around and call |
| + IInternetProtocol::Continue on the main thread. |
| + |
| + Or, if you happen to have a window handy that was created on the main |
| + thread, you can post yourself a message. |
| + " |
| + */ |
| + return m_spInternetProtocolSink ? m_spInternetProtocolSink->Switch(pProtocolData) : E_UNEXPECTED; |
| +} |
| + |
| +// This is the heuristic which detects the requests issued by Flash.ocx. |
| +// It turned out that the implementation from ''Flash.ocx'' (tested version is 15.0.0.152) |
| +// returns quite minimal configuration in comparison with the implementation from Microsofts' |
| +// libraries (see grfBINDF and bindInfo.dwOptions). The impl from MS often includes something |
| +// else. |
| +bool WBPassthruSink::IsFlashRequest(const wchar_t* const* additionalHeaders) |
| +{ |
| + if (additionalHeaders && *additionalHeaders) |
| + { |
| + auto flashVersionHeader = ExtractHttpHeader<std::wstring>(*additionalHeaders, L"x-flash-version:", L"\n"); |
| + if (!TrimString(flashVersionHeader).empty()) |
| + { |
| + return true; |
| + } |
| + } |
| + ATL::CComPtr<IBindStatusCallback> bscb; |
| + if (SUCCEEDED(QueryServiceFromClient(&bscb)) && !!bscb) |
| + { |
| + DWORD grfBINDF = 0; |
| + BINDINFO bindInfo = {}; |
| + bindInfo.cbSize = sizeof(bindInfo); |
| + if (SUCCEEDED(bscb->GetBindInfo(&grfBINDF, &bindInfo)) && |
| + (BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE| BINDF_PULLDATA) == grfBINDF && |
| + (BINDINFO_OPTIONS_ENABLE_UTF8 | BINDINFO_OPTIONS_USE_IE_ENCODING) == bindInfo.dwOptions |
| + ) |
| + { |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +STDMETHODIMP WBPassthruSink::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR* pszAdditionalHeaders) |
| +{ |
| + if (!szURL) |
| + { |
| + return E_POINTER; |
| + } |
| + std::wstring src = szURL; |
| + DEBUG_GENERAL(ToCString(src)); |
| + |
| + std::string acceptHeader = ExtractHttpAcceptHeader(m_spTargetProtocol); |
| + |
| + if (pszAdditionalHeaders) |
| + { |
| + *pszAdditionalHeaders = nullptr; |
| + } |
| + |
| + CComPtr<IHttpNegotiate> httpNegotiate; |
| + QueryServiceFromClient(&httpNegotiate); |
| + // This fills the pszAdditionalHeaders with more headers. One of which is the Referer header, which we need. |
| + // There doesn't seem to be any other way to get this header before the request has been made. |
| + HRESULT nativeHr = httpNegotiate ? httpNegotiate->BeginningTransaction(szURL, szHeaders, dwReserved, pszAdditionalHeaders) : S_OK; |
| + |
| + if (pszAdditionalHeaders && *pszAdditionalHeaders) |
| + { |
| + m_boundDomain = ExtractHttpHeader<std::wstring>(*pszAdditionalHeaders, L"Referer:", L"\n"); |
| + } |
| + m_boundDomain = TrimString(m_boundDomain); |
| + m_contentType = GetContentType(ATL::CString(acceptHeader.c_str()), m_boundDomain, src); |
| + |
| + CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId()); |
| + CPluginClient* client = CPluginClient::GetInstance(); |
| + |
| + if (tab && client) |
| + { |
| + std::wstring documentUrl = tab->GetDocumentUrl(); |
| + // Page is identical to document => don't block |
| + if (documentUrl == src) |
| + { |
| + return nativeHr; |
| + } |
| + else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsWhitelistedUrl(std::wstring(documentUrl))) |
| + { |
| + if (tab->IsFrameCached(src)) |
| + { |
| + m_contentType = ContentType::CONTENT_TYPE_SUBDOCUMENT; |
| + } |
| + } |
| + } |
| + |
| + if (IsFlashRequest(pszAdditionalHeaders)) |
| + { |
| + m_contentType = ContentType::CONTENT_TYPE_OBJECT_SUBREQUEST; |
| + } |
| + |
| + if (pszAdditionalHeaders && *pszAdditionalHeaders && IsXmlHttpRequest(*pszAdditionalHeaders)) |
| + { |
| + m_contentType = ContentType::CONTENT_TYPE_XMLHTTPREQUEST; |
| + } |
| + |
| + if (client->ShouldBlock(szURL, m_contentType, m_boundDomain, /*debug flag but must be set*/true)) |
| + { |
| + // NOTE: Feeding custom HTML to Flash, instead of original object subrequest |
| + // doesn't have much sense. It also can manifest in unwanted result |
| + // like video being blocked (See https://issues.adblockplus.org/ticket/1669) |
| + // So we report blocked object subrequests as failed, not just empty HTML. |
| + m_isCustomResponse = m_contentType != ContentType::CONTENT_TYPE_OBJECT_SUBREQUEST; |
| + return E_ABORT; |
| + } |
| + return nativeHr; |
| +} |
| + |
| +STDMETHODIMP WBPassthruSink::OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) |
| +{ |
| + if (pszAdditionalRequestHeaders) |
| + { |
| + *pszAdditionalRequestHeaders = 0; |
| + } |
| + |
| + CComPtr<IHttpNegotiate> spHttpNegotiate; |
| + QueryServiceFromClient(&spHttpNegotiate); |
| + |
| + return spHttpNegotiate ? spHttpNegotiate->OnResponse(dwResponseCode, szResponseHeaders, szRequestHeaders, pszAdditionalRequestHeaders) : S_OK; |
| +} |
| + |
| +STDMETHODIMP WBPassthruSink::ReportProgress(ULONG ulStatusCode, LPCWSTR szStatusText) |
| +{ |
| + return m_spInternetProtocolSink ? m_spInternetProtocolSink->ReportProgress(ulStatusCode, szStatusText) : S_OK; |
| +} |
| + |
| +STDMETHODIMP WBPassthruSink::ReportResult(/* [in] */ HRESULT hrResult, /* [in] */ DWORD dwError, /* [in] */ LPCWSTR szResult) |
| +{ |
| + if (m_isCustomResponse) |
| + { |
| + // Don't notify the client about aborting of the operation, thus don't call BaseClass::ReportResult. |
| + // Current method is called by the original protocol implementation and we are intercepting the |
| + // call here and eating it, we will call the proper ReportResult later by ourself. |
| + return S_OK; |
| + } |
| + return BaseClass::ReportResult(hrResult, dwError, szResult); |
| +} |
| + |
| + |
| +WBPassthru::WBPassthru() |
| + : m_shouldSupplyCustomContent(false) |
| +{ |
| +} |
| + |
| +STDMETHODIMP WBPassthru::Start(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, |
| + IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved) |
| +{ |
| + ATLASSERT(m_spInternetProtocol != 0); |
| + if (!m_spInternetProtocol) |
| + { |
| + return E_UNEXPECTED; |
| + } |
| + |
| + return OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved, m_spInternetProtocol); |
| +} |
| + |
| +STDMETHODIMP WBPassthru::Read(/* [in, out] */ void *pv,/* [in] */ ULONG cb,/* [out] */ ULONG *pcbRead) |
| +{ |
| + WBPassthruSink* pSink = GetSink(); |
| + return pSink->OnRead(pv, cb, pcbRead); |
| } |