windows-nt/Source/XPSP1/NT/shell/shell32/unicpp/mimeflt.cpp
2020-09-26 16:20:57 +08:00

896 lines
25 KiB
C++

// 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)
{
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
{
iLen = wsprintfA(pstrDest, "&#%lu;", ul);
}
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));
StrCpyNW((LPWSTR)pDst, v.bstrVal, cbDst/sizeof(WCHAR));
}
}
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];
lstrcpyn(szKey, REG_WEBVIEW_TEMPLATE_MACROS, ARRAYSIZE(szKey));
StrCatBuff(szKey, TEXT("\\"), ARRAYSIZE(szKey));
StrCatBuff(szKey, szTCharMacro, ARRAYSIZE(szKey));
HKEY hkMacros;
if (RegOpenKey(HKEY_CURRENT_USER, szKey, &hkMacros) == ERROR_SUCCESS
|| RegOpenKey(HKEY_LOCAL_MACHINE, szKey, &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, "&lt");
length = 3;
break;
case '>':
hr = StringCchReplaceFirstCharWithStringA((LPSTR)psz, cb, "&gt");
length = 3;
break;
case '"':
hr = StringCchReplaceFirstCharWithStringA((LPSTR)psz, cb, "&quot");
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"&lt");
length = 3;
break;
case L'>':
hr = StringCchReplaceFirstCharWithStringW(pszW, cchW, L"&gt");
length = 3;
break;
case L'"':
hr = StringCchReplaceFirstCharWithStringW(pszW, cchW, L"&quot");
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);
}