You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
907 lines
26 KiB
907 lines
26 KiB
// CLSID_CWebViewMimeFilter
|
|
//
|
|
// Mime filter for Web View (.htt) content. Does substitutions on:
|
|
//
|
|
// %TEMPLATEDIR%
|
|
// %THISDIRPATH%
|
|
// %THISDIRNAME%
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#pragma hdrstop
|
|
|
|
#define MAX_VARIABLE_NAME_SIZE 15 // see _Expand
|
|
|
|
// urlmon uses a 2K buffer size, so match that in retail. To force
|
|
// extra iterations and reallocations, use a smaller buffer size
|
|
// in debug. To further save on reallocations, we don't read the
|
|
// entire buffer to leave room for growth.
|
|
#ifdef DEBUG
|
|
#define BUFFER_SIZE 512
|
|
#define BUFFER_ALLOC_SIZE BUFFER_SIZE
|
|
#else
|
|
#define BUFFER_SIZE 0x2000
|
|
#define BUFFER_ALLOC_SIZE (BUFFER_SIZE+2*MAX_PATH)
|
|
#endif
|
|
#define BUFFER_SIZE_INC MAX_VARIABLE_NAME_SIZE*2 // must be > MAX_VARIABLE_NAME_SIZE
|
|
|
|
#define TF_EXPAND 0 // show strings as they are expanded in our mime filter?
|
|
|
|
#define MAX_HTML_ESCAPE_SEQUENCE 8 // longest string representation of a 16 bit integer is 65535. So, entire composite escape string has:
|
|
// 2 for "&#" + maximum of 5 digits + 1 for ";" = 8 characters
|
|
|
|
|
|
class CWebViewMimeFilter : public IInternetProtocol
|
|
, public IInternetProtocolSink
|
|
, public IServiceProvider
|
|
{
|
|
public:
|
|
virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
|
|
virtual STDMETHODIMP_(ULONG) AddRef(void);
|
|
virtual STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IInternetProtocol methods
|
|
STDMETHOD(Start)(
|
|
LPCWSTR szUrl,
|
|
IInternetProtocolSink *pProtSink,
|
|
IInternetBindInfo *pOIBindInfo,
|
|
DWORD grfSTI,
|
|
HANDLE_PTR dwReserved);
|
|
STDMETHOD(Continue)(PROTOCOLDATA *pStateInfo);
|
|
STDMETHOD(Abort)(HRESULT hrReason,DWORD dwOptions);
|
|
STDMETHOD(Terminate)(DWORD dwOptions);
|
|
STDMETHOD(Suspend)();
|
|
STDMETHOD(Resume)();
|
|
STDMETHOD(Read)(void *pv,ULONG cb,ULONG *pcbRead);
|
|
STDMETHOD(Seek)(
|
|
LARGE_INTEGER dlibMove,
|
|
DWORD dwOrigin,
|
|
ULARGE_INTEGER *plibNewPosition);
|
|
STDMETHOD(LockRequest)(DWORD dwOptions);
|
|
STDMETHOD(UnlockRequest)();
|
|
|
|
// IInternetProtocolSink methods
|
|
STDMETHOD(Switch)(PROTOCOLDATA * pProtocolData);
|
|
STDMETHOD(ReportProgress)(ULONG ulStatusCode, LPCWSTR pwszStatusText);
|
|
STDMETHOD(ReportData)(DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax);
|
|
STDMETHOD(ReportResult)(HRESULT hrResult, DWORD dwError, LPCWSTR pwszResult);
|
|
|
|
// IServiceProvider methods
|
|
STDMETHOD(QueryService)(REFGUID rsid, REFIID riid, void ** ppvObj);
|
|
|
|
private:
|
|
CWebViewMimeFilter();
|
|
~CWebViewMimeFilter();
|
|
friend HRESULT CWebViewMimeFilter_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut);
|
|
|
|
int _StrCmp(LPBYTE pSrc, LPCSTR pAnsi, LPWSTR pUnicode);
|
|
LPBYTE _StrChr(LPBYTE pSrc, char chA, WCHAR chW);
|
|
int _StrLen(LPBYTE pStr);
|
|
|
|
void _QueryForDVCMDID(int dvcmdid, LPBYTE pDst, int cbDst);
|
|
void _EncodeHtml(LPBYTE psz, size_t cbByte);
|
|
HRESULT _IncreaseBuffer(ULONG cbIncrement, LPBYTE * pp1, LPBYTE * pp2);
|
|
int _Expand(LPBYTE pszVar, LPBYTE * ppszExp);
|
|
HRESULT _ReadAndExpandBuffer();
|
|
|
|
int _cRef;
|
|
|
|
LPBYTE _pBuf; // our buffer
|
|
ULONG _cbBufSize; // size of the buffer
|
|
ULONG _nCharSize; // sizeof(char) or sizeof(WCHAR) depending on data type
|
|
ULONG _cbBuf; // count of bytes read into the buffer
|
|
ULONG _cbSeek; // offset to seek position
|
|
BYTE _szTemplateDirPath[2*MAX_PATH];
|
|
BYTE _szThisDirPath[MAX_HTML_ESCAPE_SEQUENCE*MAX_PATH];
|
|
BYTE _szThisDirName[MAX_HTML_ESCAPE_SEQUENCE*MAX_PATH];
|
|
BYTE _szExpansion[2*MAX_PATH];
|
|
|
|
IInternetProtocol* _pProt; // incoming
|
|
IInternetProtocolSink* _pProtSink; // outgoing
|
|
};
|
|
|
|
CWebViewMimeFilter::CWebViewMimeFilter()
|
|
{
|
|
_cRef = 1;
|
|
}
|
|
|
|
CWebViewMimeFilter::~CWebViewMimeFilter()
|
|
{
|
|
ATOMICRELEASE(_pProt);
|
|
|
|
if (_pBuf)
|
|
{
|
|
LocalFree(_pBuf);
|
|
_pBuf = NULL;
|
|
_cbBufSize = 0;
|
|
}
|
|
|
|
ASSERT(NULL == _pProtSink);
|
|
}
|
|
|
|
HRESULT CWebViewMimeFilter_CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void **ppvOut)
|
|
{
|
|
// aggregation checking is handled in class factory
|
|
|
|
HRESULT hres;
|
|
CWebViewMimeFilter* pObj;
|
|
|
|
pObj = new CWebViewMimeFilter();
|
|
if (pObj)
|
|
{
|
|
hres = pObj->QueryInterface(riid, ppvOut);
|
|
pObj->Release();
|
|
}
|
|
else
|
|
{
|
|
*ppvOut = NULL;
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
ULONG CWebViewMimeFilter::AddRef(void)
|
|
{
|
|
_cRef++;
|
|
return _cRef;
|
|
}
|
|
|
|
|
|
ULONG CWebViewMimeFilter::Release(void)
|
|
{
|
|
_cRef--;
|
|
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CWebViewMimeFilter::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CWebViewMimeFilter, IInternetProtocol),
|
|
QITABENTMULTI(CWebViewMimeFilter, IInternetProtocolRoot, IInternetProtocol),
|
|
QITABENT(CWebViewMimeFilter, IInternetProtocolSink),
|
|
QITABENT(CWebViewMimeFilter, IServiceProvider),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
// IInternetProtocol methods
|
|
HRESULT CWebViewMimeFilter::Start(
|
|
LPCWSTR szUrl,
|
|
IInternetProtocolSink *pProtSink,
|
|
IInternetBindInfo *pOIBindInfo,
|
|
DWORD grfSTI,
|
|
HANDLE_PTR dwReserved)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!(EVAL(grfSTI & PI_FILTER_MODE)))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// get the Prot pointer here
|
|
PROTOCOLFILTERDATA* FiltData = (PROTOCOLFILTERDATA*) dwReserved;
|
|
ASSERT(NULL == _pProt);
|
|
_pProt = FiltData->pProtocol;
|
|
_pProt->AddRef();
|
|
|
|
// hold onto the sink as well
|
|
ASSERT(NULL == _pProtSink);
|
|
_pProtSink = pProtSink;
|
|
_pProtSink->AddRef();
|
|
|
|
// this filter converts text/webviewhtml to text/html
|
|
_pProtSink->ReportProgress(BINDSTATUS_FILTERREPORTMIMETYPE, L"text/html");
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
HRESULT CWebViewMimeFilter::Continue(PROTOCOLDATA *pStateInfo)
|
|
{
|
|
ASSERT(_pProt);
|
|
return _pProt->Continue(pStateInfo);
|
|
}
|
|
HRESULT CWebViewMimeFilter::Abort(HRESULT hrReason,DWORD dwOptions)
|
|
{
|
|
ATOMICRELEASE(_pProtSink); // probably to remove ref cycle
|
|
|
|
ASSERT(_pProt);
|
|
return _pProt->Abort(hrReason, dwOptions);
|
|
}
|
|
HRESULT CWebViewMimeFilter::Terminate(DWORD dwOptions)
|
|
{
|
|
ATOMICRELEASE(_pProtSink); // probably to remove ref cycle
|
|
|
|
return _pProt->Terminate(dwOptions);
|
|
}
|
|
HRESULT CWebViewMimeFilter::Suspend()
|
|
{
|
|
return _pProt->Suspend();
|
|
}
|
|
HRESULT CWebViewMimeFilter::Resume()
|
|
{
|
|
return _pProt->Resume();
|
|
}
|
|
|
|
int CWebViewMimeFilter::_StrCmp(LPBYTE pSrc, LPCSTR pAnsi, LPWSTR pUnicode)
|
|
{
|
|
if (sizeof(char) == _nCharSize)
|
|
{
|
|
return lstrcmpA(pAnsi, (LPSTR)pSrc);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(_nCharSize == sizeof(WCHAR));
|
|
|
|
return StrCmpW(pUnicode, (LPWSTR)pSrc);
|
|
}
|
|
}
|
|
|
|
LPBYTE CWebViewMimeFilter::_StrChr(LPBYTE pSrc, char chA, WCHAR chW)
|
|
{
|
|
if (sizeof(char) == _nCharSize)
|
|
{
|
|
return (LPBYTE)StrChrA((LPSTR)pSrc, chA);
|
|
}
|
|
else
|
|
{
|
|
return (LPBYTE)StrChrW((LPWSTR)pSrc, chW);
|
|
}
|
|
}
|
|
|
|
int CWebViewMimeFilter::_StrLen(LPBYTE pStr)
|
|
{
|
|
if (sizeof(char) == _nCharSize)
|
|
{
|
|
return lstrlenA((LPSTR)pStr);
|
|
}
|
|
else
|
|
{
|
|
return lstrlenW((LPWSTR)pStr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* UnicodeToHTMLEscapeStringAnsi
|
|
*
|
|
* Takes a unicode string as the input source and translates it into an ansi string that mshtml can process. Characters > 127 will be
|
|
* translated into an html escape sequence that has the following syntax: "&#xxxxx;" where xxxxx is the string representation of the decimal
|
|
* integer which is the value for the unicode character. In this manner we are able to generate HTML text which represent UNICODE characters.
|
|
*/
|
|
void UnicodeToHTMLEscapeStringAnsi(LPWSTR pstrSrc, LPSTR pstrDest, int cbDest)
|
|
{
|
|
LPSTR pstrDestOriginal = pstrDest;
|
|
|
|
while (*pstrSrc && (cbDest > MAX_HTML_ESCAPE_SEQUENCE))
|
|
{
|
|
int iLen;
|
|
ULONG ul = MAKELONG(*pstrSrc, 0);
|
|
|
|
// We can optimize the common ansi characters to avoid generating the long escape sequence. This allows us to fit
|
|
// longer paths in the buffer.
|
|
if (ul < 128)
|
|
{
|
|
*pstrDest = (CHAR)*pstrSrc;
|
|
iLen = 1;
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr = StringCchPrintfA(pstrDest, cbDest, "&#%lu;", ul);
|
|
if (FAILED(hr))
|
|
{
|
|
*pstrDestOriginal = '\0';
|
|
return;
|
|
}
|
|
iLen = lstrlenA(pstrDest);
|
|
}
|
|
pstrDest += iLen;
|
|
cbDest -= iLen;
|
|
pstrSrc++;
|
|
}
|
|
*pstrDest = 0;
|
|
}
|
|
|
|
void CWebViewMimeFilter::_QueryForDVCMDID(int dvcmdid, LPBYTE pDst, int cbDst)
|
|
{
|
|
IOleCommandTarget * pct;
|
|
if (SUCCEEDED(QueryService(SID_DefView, IID_IOleCommandTarget, (LPVOID*)&pct)))
|
|
{
|
|
VARIANT v = {0};
|
|
|
|
if (S_OK == pct->Exec(&CGID_DefView, dvcmdid, 0, NULL, &v))
|
|
{
|
|
if (v.vt == VT_BSTR)
|
|
{
|
|
if (sizeof(char) == _nCharSize)
|
|
{
|
|
UnicodeToHTMLEscapeStringAnsi(v.bstrVal, (LPSTR)pDst, cbDst);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(_nCharSize == sizeof(WCHAR));
|
|
|
|
HRESULT hr = StringCbCopy((LPWSTR)pDst, cbDst, v.bstrVal);
|
|
if (FAILED(hr))
|
|
{
|
|
*((LPWSTR)pDst) = L'\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
VariantClear(&v);
|
|
}
|
|
pct->Release();
|
|
}
|
|
}
|
|
|
|
void ConvertTCharToBytes(LPCTSTR psz, UINT nCharSize, LPBYTE pBuf, int nBytes)
|
|
{
|
|
if (sizeof(char) == nCharSize)
|
|
{
|
|
SHTCharToAnsi(psz, (LPSTR)pBuf, nBytes/nCharSize);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(nCharSize == sizeof(WCHAR));
|
|
SHTCharToUnicode(psz, (LPWSTR)pBuf, nBytes/nCharSize);
|
|
}
|
|
}
|
|
|
|
void GetMachineTemplateDir(LPBYTE pszTemplateDirPath, int nBytes, UINT nCharSize)
|
|
{
|
|
TCHAR szTemplateDir[MAX_PATH];
|
|
szTemplateDir[0] = TEXT('\0');
|
|
SHGetWebFolderFilePath(TEXT(""), szTemplateDir, ARRAYSIZE(szTemplateDir));
|
|
// Remove the trailing back slash, if any
|
|
int len = lstrlen(szTemplateDir);
|
|
if ((len > 0) && (szTemplateDir[len - 1] == TEXT('\\')))
|
|
{
|
|
szTemplateDir[len - 1] = TEXT('\0');
|
|
}
|
|
|
|
ConvertTCharToBytes(szTemplateDir, nCharSize, pszTemplateDirPath, nBytes);
|
|
}
|
|
|
|
#define REG_WEBVIEW_TEMPLATE_MACROS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\WebView\\TemplateMacros")
|
|
|
|
void ConvertBytesToTChar(LPCBYTE pBuf, UINT nCharSize, LPTSTR psz, int cch)
|
|
{
|
|
if (sizeof(char) == nCharSize)
|
|
{
|
|
SHAnsiToTChar((LPCSTR)pBuf, psz, cch);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(nCharSize == sizeof(WCHAR));
|
|
SHUnicodeToTChar((LPCWSTR)pBuf, psz, cch);
|
|
}
|
|
}
|
|
|
|
void ExpandMacro(LPBYTE pszMacro, LPBYTE pszExpansion, int nBytes, UINT nCharSize)
|
|
{
|
|
TCHAR szExpansion[MAX_PATH];
|
|
szExpansion[0] = TEXT('\0');
|
|
|
|
TCHAR szTCharMacro[MAX_PATH];
|
|
ConvertBytesToTChar(pszMacro, nCharSize, szTCharMacro, ARRAYSIZE(szTCharMacro));
|
|
|
|
TCHAR szKey[MAX_PATH];
|
|
HRESULT hr = StringCchPrintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\%s"), REG_WEBVIEW_TEMPLATE_MACROS, szTCharMacro);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
HKEY hkMacros;
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER, szKey, 0, KEY_QUERY_VALUE, &hkMacros) == ERROR_SUCCESS
|
|
|| RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_QUERY_VALUE, &hkMacros) == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof(szExpansion);
|
|
SHQueryValueEx(hkMacros, NULL, NULL, &dwType, (LPBYTE)szExpansion, &cbData);
|
|
RegCloseKey(hkMacros);
|
|
}
|
|
}
|
|
ConvertTCharToBytes(szExpansion, nCharSize, pszExpansion, nBytes);
|
|
}
|
|
|
|
// Replace the first character of pszDst with the string pszIns.
|
|
//
|
|
HRESULT StringCchReplaceFirstCharWithStringA(LPSTR psz, size_t cch, LPCSTR pszIns)
|
|
{
|
|
HRESULT hr;
|
|
|
|
size_t cchIns = lstrlenA(pszIns);
|
|
|
|
if (cchIns < cch )
|
|
{
|
|
MoveMemory(psz + cchIns, psz + 1, cch - cchIns);
|
|
*(psz + cch - 1) = '\0';
|
|
MoveMemory(psz, pszIns, cchIns);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT StringCchReplaceFirstCharWithStringW(LPWSTR psz, size_t cch, LPCWSTR pszIns)
|
|
{
|
|
HRESULT hr;
|
|
|
|
size_t cchIns = lstrlenW(pszIns);
|
|
|
|
if (cchIns < cch)
|
|
{
|
|
MoveMemory(psz + cchIns, psz + 1, (cch - cchIns) * sizeof(WCHAR));
|
|
*(psz + cch - 1) = L'\0';
|
|
MoveMemory(psz, pszIns, cchIns * sizeof(WCHAR));
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CWebViewMimeFilter::_EncodeHtml(LPBYTE psz, size_t cb)
|
|
{
|
|
HRESULT hr;
|
|
size_t length;
|
|
|
|
if (sizeof(char) == _nCharSize)
|
|
{
|
|
while (*psz)
|
|
{
|
|
switch (*psz)
|
|
{
|
|
case '<':
|
|
hr = StringCchReplaceFirstCharWithStringA((LPSTR)psz, cb, "<");
|
|
length = 3;
|
|
break;
|
|
|
|
case '>':
|
|
hr = StringCchReplaceFirstCharWithStringA((LPSTR)psz, cb, ">");
|
|
length = 3;
|
|
break;
|
|
|
|
case '"':
|
|
hr = StringCchReplaceFirstCharWithStringA((LPSTR)psz, cb, """);
|
|
length = 5;
|
|
break;
|
|
|
|
default:
|
|
hr = S_OK;
|
|
length = 1;
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
psz += length;
|
|
cb -= length;
|
|
}
|
|
else
|
|
{
|
|
*psz = '\0';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(sizeof(WCHAR) == _nCharSize);
|
|
|
|
WCHAR* pszW = (WCHAR*)psz;
|
|
size_t cchW = cb / sizeof(WCHAR);
|
|
|
|
while (*pszW)
|
|
{
|
|
switch (*pszW)
|
|
{
|
|
case L'<':
|
|
hr = StringCchReplaceFirstCharWithStringW(pszW, cchW, L"<");
|
|
length = 3;
|
|
break;
|
|
|
|
case L'>':
|
|
hr = StringCchReplaceFirstCharWithStringW(pszW, cchW, L">");
|
|
length = 3;
|
|
break;
|
|
|
|
case L'"':
|
|
hr = StringCchReplaceFirstCharWithStringW(pszW, cchW, L""");
|
|
length = 5;
|
|
break;
|
|
|
|
default:
|
|
hr = S_OK;
|
|
length = 1;
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pszW += length;
|
|
cchW -= length;
|
|
}
|
|
else
|
|
{
|
|
*pszW = L'\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int CWebViewMimeFilter::_Expand(LPBYTE pszVar, LPBYTE * ppszExp)
|
|
{
|
|
if (!_StrCmp(pszVar, "TEMPLATEDIR", L"TEMPLATEDIR"))
|
|
{
|
|
if (!_szTemplateDirPath[0])
|
|
{
|
|
GetMachineTemplateDir(_szTemplateDirPath, sizeof(_szTemplateDirPath), _nCharSize);
|
|
_EncodeHtml(_szTemplateDirPath, sizeof(_szTemplateDirPath));
|
|
}
|
|
*ppszExp = _szTemplateDirPath;
|
|
}
|
|
else if (!_StrCmp(pszVar, "THISDIRPATH", L"THISDIRPATH"))
|
|
{
|
|
if (!_szThisDirPath[0])
|
|
{
|
|
_QueryForDVCMDID(DVCMDID_GETTHISDIRPATH, _szThisDirPath, sizeof(_szThisDirPath));
|
|
_EncodeHtml(_szThisDirPath, sizeof(_szThisDirPath));
|
|
}
|
|
*ppszExp = _szThisDirPath;
|
|
}
|
|
else if (!_StrCmp(pszVar, "THISDIRNAME", L"THISDIRNAME"))
|
|
{
|
|
if (!_szThisDirName[0])
|
|
{
|
|
_QueryForDVCMDID(DVCMDID_GETTHISDIRNAME, _szThisDirName, sizeof(_szThisDirName));
|
|
_EncodeHtml(_szThisDirName, sizeof(_szThisDirName));
|
|
}
|
|
*ppszExp = _szThisDirName;
|
|
}
|
|
else
|
|
{
|
|
ExpandMacro(pszVar, _szExpansion, sizeof(_szExpansion), _nCharSize);
|
|
_EncodeHtml(_szExpansion, sizeof(_szExpansion));
|
|
*ppszExp = _szExpansion;
|
|
}
|
|
|
|
return _StrLen(*ppszExp);
|
|
}
|
|
|
|
//
|
|
// Ensure room for at least cbIncrement more bytes at the end of the buffer.
|
|
// If the memory gets moved or realloced, *pp1 and *pp2 are adjusted to
|
|
// point to the corresponding bytes at their new location(s).
|
|
//
|
|
HRESULT CWebViewMimeFilter::_IncreaseBuffer(ULONG cbIncrement, LPBYTE * pp1, LPBYTE * pp2)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// first check if there's room at the beginning of the buffer
|
|
if (_cbSeek >= cbIncrement)
|
|
{
|
|
MoveMemory(_pBuf, _pBuf + _cbSeek, _cbBuf - _cbSeek);
|
|
_cbBuf -= _cbSeek;
|
|
|
|
if (pp1)
|
|
*pp1 = *pp1 - _cbSeek;
|
|
if (pp2)
|
|
*pp2 = *pp2 - _cbSeek;
|
|
|
|
_cbSeek = 0;
|
|
}
|
|
else
|
|
{
|
|
// not enough room, so allocate more memory
|
|
LPBYTE p = (LPBYTE)LocalReAlloc(_pBuf, _cbBufSize + cbIncrement, LMEM_MOVEABLE);
|
|
if (!p)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
if (pp1)
|
|
*pp1 = p + (int)(*pp1 - _pBuf);
|
|
if (pp2)
|
|
*pp2 = p + (int)(*pp2 - _pBuf);
|
|
|
|
_pBuf = p;
|
|
_cbBufSize += cbIncrement;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CWebViewMimeFilter::_ReadAndExpandBuffer()
|
|
{
|
|
HRESULT hr;
|
|
|
|
_cbBuf = 0;
|
|
_cbSeek = 0;
|
|
|
|
if (!_pBuf)
|
|
{
|
|
_pBuf = (LPBYTE)LocalAlloc(LPTR, BUFFER_ALLOC_SIZE);
|
|
if (!_pBuf)
|
|
return E_OUTOFMEMORY;
|
|
|
|
_cbBufSize = BUFFER_ALLOC_SIZE;
|
|
}
|
|
|
|
// As strings expand, our buffer grows. If we keep reading in the
|
|
// max amount, we'll keep reallocating the more variable expansions
|
|
// we do. By only reading in BUFFER_SIZE, our _pBuf will grow only
|
|
// a few times and then all the variable expansions should fit
|
|
// in the extra room generated. NOTE: for debug builds, always
|
|
// read the most we can, so we reallocate more often.
|
|
#ifdef DEBUG
|
|
#define BUFFER_READ_SIZE (_cbBufSize)
|
|
#else
|
|
#define BUFFER_READ_SIZE BUFFER_SIZE
|
|
#endif
|
|
hr = _pProt->Read(_pBuf, BUFFER_READ_SIZE - sizeof(WCHAR), &_cbBuf); // make sure we have room for NULL
|
|
if (SUCCEEDED(hr) && _cbBuf > 0)
|
|
{
|
|
LPBYTE pchSeek = _pBuf;
|
|
LPBYTE pchEnd;
|
|
|
|
if (!_nCharSize)
|
|
{
|
|
// scan buffer and figure out if it's unicode or ansi
|
|
//
|
|
// since we'll always be looking at html and the html header
|
|
// is standard ansi chars, every other byte will be null if
|
|
// we have a unicode buffer. i'm sure 3 checks are enough,
|
|
// so we'll require 8 characters...
|
|
if (_cbBuf > 6 &&
|
|
0 == _pBuf[1] &&
|
|
0 == _pBuf[3] &&
|
|
0 == _pBuf[5])
|
|
{
|
|
TraceMsg(TF_EXPAND, "WebView MIME filter - buffer is UNICODE");
|
|
_nCharSize = sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(TF_EXPAND, "WebView MIME filter - buffer is ANSI");
|
|
_nCharSize = sizeof(char);
|
|
}
|
|
}
|
|
|
|
// The string had better be null-terminated, for not only are we
|
|
// going to do a StrChr, but our loop control relies on it!
|
|
// The buffer might have leftover goo from a previous go-round, so
|
|
// ensure that the nulls are there.
|
|
_pBuf[_cbBuf] = _pBuf[_cbBuf+1] = 0;
|
|
|
|
#ifdef DEBUG
|
|
if (sizeof(char)==_nCharSize)
|
|
TraceMsg(TF_EXPAND, "Read A[%hs]", _pBuf);
|
|
else
|
|
TraceMsg(TF_EXPAND, "Read W[%ls]", _pBuf);
|
|
#endif
|
|
|
|
do {
|
|
LPBYTE pchStart = pchSeek;
|
|
|
|
// Assert that the string is still properly null-terminated
|
|
// because we're going to be doing StrChr soon.
|
|
ASSERT(_pBuf[_cbBuf] == 0);
|
|
ASSERT(_pBuf[_cbBuf+1] == 0);
|
|
|
|
pchSeek = _StrChr(pchSeek, '%', L'%');
|
|
if (!pchSeek)
|
|
break;
|
|
|
|
pchEnd = _StrChr(pchSeek+_nCharSize, '%', L'%');
|
|
if (!pchEnd)
|
|
{
|
|
// no terminator. if there's plenty of space to the end of
|
|
// this buffer then there can't be a clipped variable
|
|
// name to expand.
|
|
if (_cbBuf - (pchSeek - _pBuf) > MAX_VARIABLE_NAME_SIZE*_nCharSize)
|
|
break;
|
|
|
|
// there may be a real variable here we need to expand,
|
|
// so increase our buffer size and read some more data.
|
|
//
|
|
// we may get re-allocated, so update pchStart!
|
|
hr = _IncreaseBuffer(BUFFER_SIZE_INC, &pchStart, NULL);
|
|
if (FAILED(hr))
|
|
break;
|
|
pchSeek = pchStart;
|
|
|
|
// read in more info -- this will be enough to complete
|
|
// any partial variable name expansions
|
|
DWORD dwTmp;
|
|
ASSERT(_cbBufSize - _cbBuf - sizeof(WCHAR) > 0);
|
|
hr = _pProt->Read(_pBuf + _cbBuf, _cbBufSize- _cbBuf - sizeof(WCHAR), &dwTmp);
|
|
if (FAILED(hr) || dwTmp == 0)
|
|
break;
|
|
_cbBuf += dwTmp;
|
|
// Ensure proper null termination
|
|
_pBuf[_cbBuf] = _pBuf[_cbBuf+1] = 0;
|
|
continue;
|
|
}
|
|
|
|
|
|
// figure out what to expand to
|
|
LPBYTE pszExp;
|
|
BYTE b[2];
|
|
|
|
b[0] = pchEnd[0];
|
|
b[1] = pchEnd[1];
|
|
pchEnd[0] = 0;
|
|
pchEnd[1] = 0;
|
|
int cbExp = _Expand(pchSeek + _nCharSize, &pszExp);
|
|
pchEnd[0] = b[0];
|
|
pchEnd[1] = b[1];
|
|
|
|
if (!cbExp)
|
|
{
|
|
// if it's not a recognized variable, use the bytes as they are
|
|
pchSeek = pchEnd;
|
|
continue;
|
|
}
|
|
|
|
// cbVar = number of bytes being replaced (sizeof("%VARNAME%"))
|
|
// pchSeek points to the starting percent sign and pchEnd to
|
|
// the trailing percent sign, so we need to add one more
|
|
// _nCharSize to include the trailing percent sign itself.
|
|
int cbVar = (int)(pchEnd - pchSeek) + _nCharSize;
|
|
|
|
if (_cbBuf - cbVar + cbExp > _cbBufSize - sizeof(WCHAR))
|
|
{
|
|
hr = _IncreaseBuffer((_cbBuf - cbVar + cbExp) - (_cbBufSize - sizeof(WCHAR)), &pchSeek, &pchEnd);
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
// move the bytes around!
|
|
// cbSeek = the number of bytes before the first percent sign
|
|
int cbSeek = (int)(pchSeek - _pBuf);
|
|
ASSERT(_cbBuf - cbVar + cbExp <= _cbBufSize - sizeof(WCHAR));
|
|
// Move the stuff after the %VARNAME% to its final home
|
|
// Don't forget to move the artificial trailing NULLs too!
|
|
MoveMemory(pchSeek + cbExp, pchEnd + _nCharSize, _cbBuf - cbSeek - cbVar + sizeof(WCHAR));
|
|
|
|
// Insert the expansion
|
|
MoveMemory(pchSeek, pszExp, cbExp);
|
|
|
|
// on to the rest of the buffer...
|
|
pchSeek = pchEnd + _nCharSize;
|
|
_cbBuf = _cbBuf - cbVar + cbExp;
|
|
|
|
} while (*pchSeek);
|
|
|
|
#ifdef DEBUG
|
|
if (sizeof(char)==_nCharSize)
|
|
TraceMsg(TF_EXPAND, "---> A[%s]", _pBuf);
|
|
else
|
|
TraceMsg(TF_EXPAND, "---> W[%hs]", _pBuf);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// we're at end of stream
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CWebViewMimeFilter::Read(void *pv,ULONG cb,ULONG *pcbRead)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!_pProt)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
*pcbRead = 0;
|
|
|
|
while (cb)
|
|
{
|
|
// if our buffer is empty, fill it
|
|
if (_cbSeek == _cbBuf)
|
|
{
|
|
hr = _ReadAndExpandBuffer();
|
|
}
|
|
|
|
// do we have any data to copy?
|
|
int cbLeft = _cbBuf - _cbSeek;
|
|
if (SUCCEEDED(hr) && cbLeft > 0)
|
|
{
|
|
ULONG cbCopy = min(cb, (ULONG)cbLeft);
|
|
|
|
memcpy(pv, &_pBuf[_cbSeek], cbCopy);
|
|
|
|
pv = (LPVOID)(((LPBYTE)pv) + cbCopy);
|
|
cb -= cbCopy;
|
|
*pcbRead += cbCopy;
|
|
_cbSeek += cbCopy;
|
|
|
|
// do not return S_FALSE if some bytes were left unread
|
|
if (cbCopy < (ULONG)cbLeft)
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FAILED(hr) || hr == S_FALSE);
|
|
|
|
// nothing left to copy
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
HRESULT CWebViewMimeFilter::Seek(
|
|
LARGE_INTEGER dlibMove,
|
|
DWORD dwOrigin,
|
|
ULARGE_INTEGER *plibNewPosition)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
HRESULT CWebViewMimeFilter::LockRequest(DWORD dwOptions)
|
|
{
|
|
return S_OK;
|
|
}
|
|
HRESULT CWebViewMimeFilter::UnlockRequest()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// IInternetProtocolSink methods
|
|
HRESULT CWebViewMimeFilter::Switch(PROTOCOLDATA * pProtocolData)
|
|
{
|
|
if (_pProtSink)
|
|
return _pProtSink->Switch(pProtocolData);
|
|
return E_FAIL;
|
|
}
|
|
HRESULT CWebViewMimeFilter::ReportProgress(ULONG ulStatusCode, LPCWSTR pwszStatusText)
|
|
{
|
|
if (_pProtSink)
|
|
return _pProtSink->ReportProgress(ulStatusCode, pwszStatusText);
|
|
return E_FAIL;
|
|
}
|
|
HRESULT CWebViewMimeFilter::ReportData(DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
|
|
{
|
|
if (_pProtSink)
|
|
return _pProtSink->ReportData(grfBSCF, ulProgress, ulProgressMax);
|
|
return E_FAIL;
|
|
}
|
|
HRESULT CWebViewMimeFilter::ReportResult(HRESULT hrResult, DWORD dwError, LPCWSTR pwszResult)
|
|
{
|
|
if (_pProtSink)
|
|
return _pProtSink->ReportResult(hrResult, dwError, pwszResult);
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
//IServiceProvider methods
|
|
HRESULT CWebViewMimeFilter::QueryService(REFGUID rsid, REFIID riid, void ** ppvObj)
|
|
{
|
|
return IUnknown_QueryService(_pProtSink, rsid, riid, ppvObj);
|
|
}
|