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); |
} |