WindowsXP-SP1/shell/shell32/filtgrep.cpp

1195 lines
33 KiB
C++

// 10/12/99 scotthan created
#include "shellprv.h"
#include "filtgrep.h"
#include <ntquery.h>
#include <filterr.h>
class CGrepTokens // maintains an index of unicode and ansi grep tokens.
{
public:
STDMETHODIMP Initialize(UINT nCodepage, LPCWSTR pwszMatch, LPCWSTR pwszExclude, BOOL bCaseSensitive);
STDMETHODIMP_(void) Reset();
STDMETHODIMP_(BOOL) GrepW(LPCWSTR pwszText);
STDMETHODIMP_(BOOL) GrepA(LPCSTR pwszText);
STDMETHODIMP GetCodePage(UINT* pnCodepage) const;
STDMETHODIMP GetMatchTokens(OUT LPWSTR pszTokens, UINT cchTokens) const;
STDMETHODIMP GetExcludeTokens(OUT LPWSTR pszTokens, UINT cchTokens) const;
private:
UINT _nCodepage;
LPWSTR _pszMatchW, _pszExcludeW; // raw strings, unicode
LPSTR _pszMatchA, _pszExcludeA; // raw strings, ansi
LPCWSTR *_rgpszMatchW, *_rgpszExcludeW; // token index, unicode
LPCSTR *_rgpszMatchA, *_rgpszExcludeA; // token index, ansi
LONG _cMatch, _cExclude; // token counts
LPWSTR (__stdcall * _pfnStrStrW)(LPCWSTR, LPCWSTR);
LPSTR (__stdcall * _pfnStrStrA)(LPCSTR, LPCSTR);
public:
// Ctor, Dtor
CGrepTokens()
: _nCodepage(0), _cMatch(0), _cExclude(0), _pfnStrStrW(StrStrIW), _pfnStrStrA(StrStrIA),
_pszMatchW(NULL), _pszExcludeW(NULL), _rgpszMatchW(NULL), _rgpszExcludeW(NULL),
_pszMatchA(NULL), _pszExcludeA(NULL), _rgpszMatchA(NULL), _rgpszExcludeA(NULL) {}
~CGrepTokens() { Reset(); }
};
class CGrepBuffer // auxilliary class: per-thread grep buffer
{
public:
CGrepBuffer(ULONG dwThreadID) : _dwThreadID(dwThreadID), _pszBuf(NULL), _cchBuf(0) {}
virtual ~CGrepBuffer() {delete [] _pszBuf;}
STDMETHODIMP Alloc(ULONG cch);
STDMETHODIMP_(BOOL) IsThread(ULONG dwThread) const {return dwThread == _dwThreadID;}
STDMETHODIMP_(LPWSTR) Buffer() { return _pszBuf; }
#define DEFAULT_GREPBUFFERSIZE 0x00FF // +1 = 1 page.
private:
LPWSTR _pszBuf;
ULONG _cchBuf;
ULONG _dwThreadID;
};
// Makes a heap copy of a widechar string
LPWSTR _AllocAndCopyString(LPCWSTR pszSrc, UINT cch = -1)
{
if (pszSrc)
{
if ((int)cch < 0) // must cast to "int" since cch is a UINT
cch = lstrlenW(pszSrc);
LPWSTR pszRet = new WCHAR[cch + 1];
if (pszRet)
{
CopyMemory(pszRet, pszSrc, sizeof(*pszSrc) * cch);
pszRet[cch] = 0;
return pszRet;
}
}
return NULL;
}
// Makes an ansi copy of a widechar string
LPSTR _AllocAndCopyAnsiString(UINT nCodepage, LPCWSTR pszSrc, UINT cch = -1)
{
if (pszSrc)
{
if ((int)cch < 0) // must cast to "int" since cch is a UINT
cch = lstrlenW(pszSrc);
int cchBuf = WideCharToMultiByte(nCodepage, 0, pszSrc, cch, NULL, 0, NULL, NULL);
LPSTR pszRet = new CHAR[cchBuf+1];
if (pszRet)
{
int cchRet = WideCharToMultiByte(nCodepage, 0, pszSrc, cch, pszRet, cchBuf, NULL, NULL);
pszRet[cchRet] = 0;
return pszRet;
}
}
return NULL;
}
// CGrepBuffer impl
STDMETHODIMP CGrepBuffer::Alloc(ULONG cch)
{
LPWSTR pszBuf = NULL;
if (cch)
{
if (_pszBuf && _cchBuf >= cch)
return S_OK;
pszBuf = new WCHAR[cch+1];
if (NULL == pszBuf)
return E_OUTOFMEMORY;
*pszBuf = 0;
}
delete [] _pszBuf;
_pszBuf = pszBuf;
_cchBuf = cch;
return _pszBuf != NULL ? S_OK : S_FALSE ;
}
// CGrepTokens impl
// Counts the number of characters in a string containing NULL-delimited tokens ("foo\0bloke\0TheEnd\0\0")
LONG _GetTokenListLength(LPCWSTR pszList, LONG* pcTokens = NULL)
{
LONG cchRet = 0;
if (pcTokens) *pcTokens = 0;
if (pszList && *pszList)
{
LPCWSTR pszToken, pszPrev;
int i = 0;
for (pszToken = pszPrev = pszList;
pszToken && *pszToken;)
{
if (pcTokens)
(*pcTokens)++;
pszToken += lstrlenW(pszToken) + 1,
cchRet += (DWORD)(pszToken - pszPrev) ;
pszPrev = pszToken;
}
}
return cchRet;
}
// wide version: Counts and/or indexes NULL-delimited string tokens ("foo\0bloke\0TheEnd\0\0")
LONG _IndexTokensW(LPCWSTR pszList, LPCWSTR* prgszTokens = NULL)
{
LONG cRet = 0;
if (pszList && *pszList)
{
LPCWSTR psz = pszList;
int i = 0;
for (; psz && *psz; psz += (lstrlenW(psz) + 1), i++)
{
if (prgszTokens)
prgszTokens[i] = psz;
cRet++;
}
}
return cRet;
}
// ansi version: Counts and/or indexes NULL-delimited string tokens ("foo\0bloke\0TheEnd\0\0")
LONG _IndexTokensA(LPCSTR pszList, LPCSTR* prgszTokens = NULL)
{
LONG cRet = 0;
if (pszList && *pszList)
{
LPCSTR psz = pszList;
int i = 0;
for (; psz && *psz; psz += (lstrlenA(psz) + 1), i++)
{
if (prgszTokens)
prgszTokens[i] = psz;
cRet++;
}
}
return cRet;
}
// wide version: Allocates a string token index and indexes a string of NULL-delimited tokens.
STDMETHODIMP _AllocAndIndexTokensW(LONG cTokens, LPCWSTR pszList, LPCWSTR** pprgszTokens)
{
if (cTokens)
{
if (NULL == (*pprgszTokens = new LPCWSTR[cTokens]))
return E_OUTOFMEMORY;
if (cTokens != _IndexTokensW(pszList, *pprgszTokens))
{
delete [] (*pprgszTokens);
*pprgszTokens = NULL;
return E_FAIL;
}
}
return S_OK;
}
// ansi version: Allocates a string token index and indexes a string of NULL-delimited tokens.
STDMETHODIMP _AllocAndIndexTokensA(LONG cTokens, LPCSTR pszList, LPCSTR** pprgszTokens)
{
if (cTokens)
{
if (NULL == (*pprgszTokens = new LPCSTR[cTokens]))
return E_OUTOFMEMORY;
if (cTokens != _IndexTokensA(pszList, *pprgszTokens))
{
delete [] (*pprgszTokens);
*pprgszTokens = NULL;
return E_FAIL;
}
}
return S_OK;
}
// Frees unicode and ansi token lists and corresponding indices.
void _FreeUniAnsiTokenList(
OUT LPWSTR* ppszListW,
OUT LPSTR* ppszListA,
OUT LPCWSTR** pprgTokensW,
OUT LPCSTR** pprgTokensA)
{
delete [] *ppszListW; *ppszListW = NULL;
delete [] *ppszListA; *ppszListA = NULL;
delete [] *pprgTokensW; *pprgTokensW = NULL;
delete [] *pprgTokensA; *pprgTokensA = NULL;
}
// Allocates unicode and ansi token lists and corresponding indices.
STDMETHODIMP _AllocUniAnsiTokenList(
UINT nCodepage,
LPCWSTR pszList,
OUT LPWSTR* ppszListW,
OUT LPSTR* ppszListA,
OUT LONG* pcTokens,
OUT LPCWSTR** pprgTokensW,
OUT LPCSTR** pprgTokensA)
{
HRESULT hr = S_FALSE;
LONG cTokens = 0;
UINT cch = _GetTokenListLength(pszList, &cTokens);
*ppszListW = NULL;
*ppszListA = NULL;
*pprgTokensW = NULL;
*pprgTokensA = NULL;
*pcTokens = 0;
if (cTokens)
{
hr = E_OUTOFMEMORY;
if (NULL == (*ppszListW = _AllocAndCopyString(pszList, cch)))
goto failure_exit;
if (NULL == (*ppszListA = _AllocAndCopyAnsiString(nCodepage, pszList, cch)))
goto failure_exit;
if (FAILED((hr = _AllocAndIndexTokensW(cTokens, *ppszListW, pprgTokensW))))
goto failure_exit;
if (FAILED((hr = _AllocAndIndexTokensA(cTokens, *ppszListA, pprgTokensA))))
goto failure_exit;
*pcTokens = cTokens;
hr = S_OK;
}
return hr;
failure_exit:
_FreeUniAnsiTokenList(ppszListW, ppszListA, pprgTokensW, pprgTokensA);
return hr;
}
STDMETHODIMP CGrepTokens::Initialize(UINT nCodepage, LPCWSTR pszMatch, LPCWSTR pszExclude, BOOL bCaseSensitive)
{
HRESULT hr = E_INVALIDARG;
Reset();
BOOL bMatchString = (pszMatch && *pszMatch);
BOOL bExcludeString = (pszExclude && *pszExclude);
if (!(bMatchString || bExcludeString))
return E_INVALIDARG;
_nCodepage = nCodepage;
if (bCaseSensitive)
{
_pfnStrStrW = StrStrW;
_pfnStrStrA = StrStrA;
}
else
{
_pfnStrStrW = StrStrIW;
_pfnStrStrA = StrStrIA;
}
if (bMatchString)
{
if (FAILED((hr = _AllocUniAnsiTokenList(nCodepage, pszMatch,
&_pszMatchW, &_pszMatchA, &_cMatch, &_rgpszMatchW, &_rgpszMatchA))))
{
return hr;
}
}
if (bExcludeString)
{
if (FAILED((hr = _AllocUniAnsiTokenList(nCodepage, pszExclude,
&_pszExcludeW, &_pszExcludeA, &_cExclude, &_rgpszExcludeW, &_rgpszExcludeA))))
{
return hr;
}
}
return hr;
}
STDMETHODIMP CGrepTokens::GetCodePage(UINT* pnCodepage) const
{
HRESULT hr = _nCodepage ? S_OK : S_FALSE;
if (pnCodepage)
*pnCodepage = _nCodepage;
return hr;
}
// S_OK we have some match tokens, S_FALSE otherwise
STDMETHODIMP CGrepTokens::GetMatchTokens(OUT LPWSTR pszMatch, UINT cchMatch) const
{
HRESULT hr = (_pszMatchW && *_pszMatchW) ? S_OK : S_FALSE;
if (pszMatch)
lstrcpynW(pszMatch, _pszMatchW ? _pszMatchW : L"", cchMatch);
return hr;
}
// S_OK we have some exclude tokens, S_FALSE otherwise
STDMETHODIMP CGrepTokens::GetExcludeTokens(OUT LPWSTR pszExclude, UINT cchExclude) const
{
HRESULT hr = (_pszExcludeW && *_pszExcludeW) ? S_OK : S_FALSE;
if (pszExclude)
lstrcpynW(pszExclude, _pszExcludeW ? _pszExcludeW : L"", cchExclude);
return hr;
}
void CGrepTokens::Reset()
{
_FreeUniAnsiTokenList(&_pszMatchW, &_pszMatchA, &_rgpszMatchW, &_rgpszMatchA);
_FreeUniAnsiTokenList(&_pszExcludeW, &_pszExcludeA, &_rgpszExcludeW, &_rgpszExcludeA);
_cMatch = _cExclude = 0;
_nCodepage = 0;
}
STDMETHODIMP_(BOOL) CGrepTokens::GrepW(LPCWSTR pszText)
{
BOOL bMatch = FALSE;
if (pszText)
{
BOOL bExclude = FALSE;
for (int i = 0; i < _cMatch; i++)
{
if (_pfnStrStrW(pszText, _rgpszMatchW[i]))
{
bMatch = TRUE;
break;
}
}
for (i = 0; i < _cExclude; i++)
{
if (_pfnStrStrW(pszText, _rgpszExcludeW[i]))
{
bExclude = TRUE;
break;
}
}
if (_cMatch && _cExclude)
return bMatch || !_cExclude;
if (_cExclude)
return !bExclude;
}
return bMatch;
}
STDMETHODIMP_(BOOL) CGrepTokens::GrepA(LPCSTR pszText)
{
BOOL bMatch = FALSE;
if (pszText)
{
BOOL bExclude = FALSE;
for (int i = 0; i < _cMatch; i++)
{
if (_pfnStrStrA(pszText, _rgpszMatchA[i]))
{
bMatch = TRUE;
break;
}
}
for (i = 0; i < _cExclude; i++)
{
if (_pfnStrStrA(pszText, _rgpszExcludeA[i]))
{
bExclude = TRUE;
break;
}
}
if (_cMatch && _cExclude)
return bMatch || !_cExclude;
if (_cExclude)
return !bExclude;
}
return bMatch;
}
inline STDMETHODIMP_(BOOL) _IsEqualAttribute(const FULLPROPSPEC& fps, REFFMTID fmtid, PROPID propid)
{
return IsEqualGUID(fmtid, fps.guidPropSet) &&
PRSPEC_PROPID == fps.psProperty.ulKind &&
propid == fps.psProperty.propid;
}
STDMETHODIMP_(BOOL) _PropVariantGrep(PROPVARIANT* pvar, CGrepTokens* pTokens)
{
BOOL bRet = FALSE;
switch(pvar->vt)
{
case VT_LPWSTR:
bRet = pTokens->GrepW(pvar->pwszVal);
break;
case VT_BSTR:
bRet = pTokens->GrepW(pvar->bstrVal);
break;
case VT_LPSTR:
bRet = pTokens->GrepA(pvar->pszVal);
break;
case VT_VECTOR|VT_LPWSTR:
{
for (UINT i = 0; !bRet && i < pvar->calpwstr.cElems; i++)
bRet = pTokens->GrepW(pvar->calpwstr.pElems[i]);
break;
}
case VT_VECTOR|VT_BSTR:
{
for (UINT i = 0; !bRet && i < pvar->cabstr.cElems; i++)
bRet = pTokens->GrepW(pvar->cabstr.pElems[i]);
break;
}
case VT_VECTOR|VT_LPSTR:
{
for (UINT i = 0; !bRet && i < pvar->calpstr.cElems; i++)
bRet = pTokens->GrepA(pvar->calpstr.pElems[i]);
break;
}
case VT_VECTOR|VT_VARIANT:
{
for (UINT i = 0; !bRet && i < pvar->capropvar.cElems; i++)
bRet = _PropVariantGrep(pvar->capropvar.pElems + i, pTokens);
break;
}
case VT_BSTR|VT_ARRAY:
{
// Only grep 1-dimensional arrays.
UINT cDims = SafeArrayGetDim(pvar->parray);
if (cDims == 1)
{
LONG lBound, uBound;
if (SUCCEEDED(SafeArrayGetLBound(pvar->parray, 1, &lBound)) &&
SUCCEEDED(SafeArrayGetUBound(pvar->parray, 1, &uBound)) &&
uBound > lBound)
{
BSTR *rgpbstr;
if (SUCCEEDED(SafeArrayAccessData(pvar->parray, (void **)&rgpbstr)))
{
for (int i = 0; !bRet && i <= (uBound - lBound); i++)
{
bRet = pTokens->GrepW(rgpbstr[i]);
}
SafeArrayUnaccessData(pvar->parray);
}
}
}
else if (cDims > 1)
{
ASSERT(FALSE); // we didn't expect > 1 dimension on bstr arrays!
}
break;
}
}
return bRet;
}
// Retrieves grep restriction settings
STDMETHODIMP_(BOOL) _FetchRestrictionSettings(LPCWSTR pwszVal, LPWSTR* ppwszSettings, BOOL bRefresh)
{
ASSERT(ppwszSettings);
if (bRefresh)
{
delete [] *ppwszSettings;
*ppwszSettings = NULL;
}
if (NULL == *ppwszSettings)
{
HKEY hkeyPolicy = NULL;
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Search\\ExcludedFileTypes", 0,
KEY_READ, &hkeyPolicy) == ERROR_SUCCESS)
{
DWORD dwType, cbData = 0;
if (RegQueryValueExW(hkeyPolicy, pwszVal, NULL, &dwType, NULL, &cbData) == ERROR_SUCCESS &&
REG_MULTI_SZ == dwType)
{
if ((*ppwszSettings = new WCHAR[(cbData/sizeof(WCHAR))+ 1]) != NULL)
{
if (RegQueryValueExW(hkeyPolicy, pwszVal, NULL,
&dwType, (LPBYTE)*ppwszSettings, &cbData) != ERROR_SUCCESS)
{
*ppwszSettings = 0;
}
}
}
RegCloseKey(hkeyPolicy);
}
if (NULL == *ppwszSettings) // we found no restriction key or value.
{
if ((*ppwszSettings = new WCHAR[1]))
**ppwszSettings = 0;
}
}
return *ppwszSettings != NULL;
}
// Scans restriction entries for a match against the specified filename extension
STDMETHODIMP_(BOOL) _ScanRestrictionSettings(LPCWSTR pwszSettings, LPCWSTR pwszExt)
{
ASSERT(pwszSettings);
ASSERT(pwszExt);
for (LPCWSTR psz = pwszSettings; *psz; psz += (lstrlenW(psz) + 1))
{
if (0 == StrCmpIW(psz, pwszExt))
return TRUE;
}
return FALSE;
}
CFilterGrep::CFilterGrep()
: _hdpaGrepBuffers(NULL),
_pTokens(NULL),
_dwFlags(0),
_pwszContentRestricted(NULL),
_pwszPropertiesRestricted(NULL)
{
InitializeCriticalSection(&_critsec);
}
CFilterGrep::~CFilterGrep()
{
_ClearGrepBuffers();
delete [] _pwszContentRestricted;
delete [] _pwszPropertiesRestricted;
delete _pTokens;
DeleteCriticalSection(&_critsec);
}
STDMETHODIMP CFilterGrep::Initialize(UINT nCodepage, LPCWSTR pszMatch, LPCWSTR pszExclude, DWORD dwFlags)
{
Reset();
if ((0 == (dwFlags & (FGIF_BLANKETGREP|FGIF_GREPFILENAME))) ||
!((pszMatch && *pszMatch) || (pszExclude && *pszExclude)))
return E_INVALIDARG;
if (!(_pTokens || (_pTokens = new CGrepTokens) != NULL))
return E_OUTOFMEMORY;
_dwFlags = dwFlags;
return _pTokens->Initialize(nCodepage, pszMatch, pszExclude, BOOLIFY(dwFlags & FGIF_CASESENSITIVE));
}
STDMETHODIMP CFilterGrep::Reset()
{
if (_pTokens)
_pTokens->Reset();
_dwFlags = 0;
return S_OK;
}
// converts non critical errors into S_FALSE, other return as FAILED(hr)
HRESULT _MapFilterCriticalError(HRESULT hr)
{
switch (hr)
{
case FILTER_E_END_OF_CHUNKS:
case FILTER_E_NO_MORE_TEXT:
case FILTER_E_NO_MORE_VALUES:
case FILTER_W_MONIKER_CLIPPED:
case FILTER_E_NO_TEXT:
case FILTER_E_NO_VALUES:
case FILTER_E_EMBEDDING_UNAVAILABLE:
case FILTER_E_LINK_UNAVAILABLE:
hr = S_FALSE;
break;
}
return hr;
}
// returns:
// S_OK match
// S_FALSE did not match
STDMETHODIMP CFilterGrep::Grep(IShellFolder *psf, LPCITEMIDLIST pidl, LPCTSTR pszName)
{
HRESULT hr = S_FALSE;
BOOL bHit = FALSE;
ULONG ulFlags = IFILTER_FLAGS_OLE_PROPERTIES; // default to try to use pss
ULONG dwThread = GetCurrentThreadId();
if (NULL == _pTokens)
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
if (_IsRestrictedFileType(pszName))
return S_FALSE;
// Grep the filename.
if ((_dwFlags & FGIF_GREPFILENAME) && _pTokens->GrepW(pszName))
{
return S_OK;
}
IFilter *pFilter;
if (SUCCEEDED(psf->BindToStorage(pidl, NULL, IID_PPV_ARG(IFilter, &pFilter))))
{
__try
{
hr = pFilter->Init(IFILTER_INIT_CANON_PARAGRAPHS |
IFILTER_INIT_CANON_HYPHENS |
IFILTER_INIT_CANON_SPACES |
IFILTER_INIT_APPLY_INDEX_ATTRIBUTES |
IFILTER_INIT_INDEXING_ONLY,
0, 0, &ulFlags);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_ABORT;
}
while (!bHit && (S_OK == hr))
{
STAT_CHUNK stat;
__try
{
hr = pFilter->GetChunk(&stat);
while ((S_OK == hr) && (0 == (stat.flags & (CHUNK_TEXT | CHUNK_VALUE))))
{
TraceMsg(TF_WARNING, "CFilterGrep::Grep encountered bad/unknown type for chunk; skipping.");
hr = pFilter->GetChunk(&stat);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_ABORT;
}
hr = _MapFilterCriticalError(hr); // convert filter errors into S_FALSE
if (S_OK == hr)
{
ULONG grfDescriminate = (_dwFlags & FGIF_BLANKETGREP);
if (FGIF_BLANKETGREP == grfDescriminate ||
(_IsEqualAttribute(stat.attribute, FMTID_Storage, PID_STG_CONTENTS) ?
FGIF_GREPPROPERTIES == grfDescriminate : FGIF_GREPCONTENT == grfDescriminate))
{
if (((stat.flags & CHUNK_VALUE) && S_OK == _GrepValue(pFilter, &stat)) ||
((stat.flags & CHUNK_TEXT) && S_OK == _GrepText(pFilter, &stat, dwThread)))
{
bHit = TRUE;
}
}
}
}
pFilter->Release();
}
// Grep OLE/NFF properties if appropriate
if (SUCCEEDED(hr))
{
if (!bHit && (ulFlags & IFILTER_FLAGS_OLE_PROPERTIES) && (_dwFlags & FGIF_BLANKETGREP))
{
IPropertySetStorage *pps;
if (SUCCEEDED(psf->BindToStorage(pidl, NULL, IID_PPV_ARG(IPropertySetStorage, &pps))))
{
hr = _GrepProperties(pps);
bHit = (S_OK == hr);
pps->Release();
}
}
}
if (SUCCEEDED(hr))
hr = bHit ? S_OK : S_FALSE;
return hr;
}
STDMETHODIMP CFilterGrep::_GrepValue(IFilter* pFilter, STAT_CHUNK* pstat)
{
PROPVARIANT* pvar = NULL;
HRESULT hr;
__try
{
hr = pFilter->GetValue(&pvar);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_ABORT;
}
if (SUCCEEDED(hr))
{
hr = _PropVariantGrep(pvar, _pTokens) ? S_OK : S_FALSE;
PropVariantClear(pvar);
CoTaskMemFree(pvar);
}
return hr;
}
// Greps OLE/NFF properties.
STDMETHODIMP CFilterGrep::_GrepProperties(IPropertySetStorage *pss)
{
BOOL bHit = FALSE;
IEnumSTATPROPSETSTG* pEnumSet;
if (SUCCEEDED(pss->Enum(&pEnumSet)))
{
STATPROPSETSTG statSet[8];
DWORD cSets = 0;
while (!bHit &&
SUCCEEDED(pEnumSet->Next(ARRAYSIZE(statSet), statSet, &cSets)) && cSets)
{
for (UINT i = 0; !bHit && i < cSets; i++)
{
IPropertyStorage *pstg;
if (SUCCEEDED(pss->Open(statSet[i].fmtid, STGM_READ | STGM_DIRECT | STGM_SHARE_EXCLUSIVE, &pstg)))
{
bHit = (S_OK == _GrepEnumPropStg(pstg));
pstg->Release();
}
}
}
pEnumSet->Release();
}
return bHit ? S_OK : S_FALSE;
}
#define PROPGREPBUFSIZE 16
// Reads and greps a block of properties described by a
// caller-supplied array of PROPSPECs.
STDMETHODIMP CFilterGrep::_GrepPropStg(IPropertyStorage *pstg, ULONG cspec, PROPSPEC rgspec[])
{
PROPVARIANT rgvar[PROPGREPBUFSIZE] = {0}, // stack buffer
*prgvar = rgvar;
BOOL bHit = FALSE;
if (cspec > ARRAYSIZE(rgvar)) // stack buffer large enough?
{
if (NULL == (prgvar = new PROPVARIANT[cspec]))
return E_OUTOFMEMORY;
ZeroMemory(prgvar, sizeof(PROPVARIANT) * cspec);
}
// Read properties:
HRESULT hr = pstg->ReadMultiple(cspec, rgspec, prgvar);
if (SUCCEEDED(hr))
{
for (UINT i = 0; i < cspec; i++)
{
if (!bHit)
bHit = _PropVariantGrep(prgvar + i, _pTokens);
PropVariantClear(rgvar + i);
}
}
if (prgvar != rgvar)
delete [] prgvar;
if (SUCCEEDED(hr))
return bHit ? S_OK : S_FALSE;
return hr;
}
// Enumerates and greps all properties in a property set
STDMETHODIMP CFilterGrep::_GrepEnumPropStg(IPropertyStorage* pstg)
{
BOOL bHit = FALSE;
IEnumSTATPROPSTG* pEnumStg;
if (SUCCEEDED(pstg->Enum(&pEnumStg)))
{
STATPROPSTG statProp[PROPGREPBUFSIZE];
DWORD cProps;
while (!bHit &&
SUCCEEDED(pEnumStg->Next(ARRAYSIZE(statProp), statProp, &cProps)) && cProps)
{
PROPSPEC rgspec[PROPGREPBUFSIZE] = {0};
for (UINT i = 0; i < cProps; i++)
{
rgspec[i].ulKind = PRSPEC_PROPID;
rgspec[i].propid = statProp[i].propid;
CoTaskMemFree(statProp[i].lpwstrName);
}
bHit = (S_OK == _GrepPropStg(pstg, cProps, rgspec));
}
pEnumStg->Release();
}
return bHit ? S_OK : S_FALSE;
}
// Reports whether the indicated unicode character is a
// word-breaking character.
inline BOOL _IsWordBreakCharW(IN LPWSTR pszBuf, IN ULONG ich)
{
WORD wChar;
return GetStringTypeW(CT_CTYPE1, pszBuf + ich, 1, &wChar)
&& (wChar & (C1_SPACE|C1_PUNCT|C1_CNTRL|C1_BLANK));
}
// Finds the last word-breaking character.
LPWSTR _FindLastWordBreakW(IN LPWSTR pszBuf, IN ULONG cch)
{
while(--cch)
{
if (_IsWordBreakCharW(pszBuf, cch))
return pszBuf + cch;
}
return NULL;
}
// {c1243ca0-bf96-11cd-b579-08002b30bfeb}
const CLSID CLSID_PlainTextFilter = {0xc1243ca0, 0xbf96, 0x11cd, {0xb5, 0x79, 0x08, 0x00, 0x2b, 0x30, 0xbf, 0xeb}};
STDMETHODIMP CFilterGrep::_GrepText(IFilter* pFilter, STAT_CHUNK* pstat, DWORD dwThreadID)
{
ASSERT(pstat);
LPWSTR pszBuf = NULL;
ULONG cchBuf = pstat->cwcLenSource ?
pstat->cwcLenSource : DEFAULT_GREPBUFFERSIZE;
HRESULT hr = _GetThreadGrepBuffer(dwThreadID, cchBuf, &pszBuf);
if (SUCCEEDED(hr))
{
LPWSTR pszFetch = pszBuf,
pszTail = NULL;
ULONG cchFetch = cchBuf,
cchTail = 0;
// Fetch first block of text
__try
{
hr = pFilter->GetText(&cchFetch, pszFetch);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_ABORT;
}
CLSID clsid = {0};
IUnknown_GetClassID(pFilter, &clsid); // to workaround a bug in the text filter
while (SUCCEEDED(hr) && cchFetch)
{
ASSERT((cchFetch + cchTail) <= cchBuf);
pszBuf[cchFetch + cchTail] = 0; // don't trust filter to zero-terminate buffer.
// When you get the FILTER_S_LAST_TEXT, that's it, you'll get no more text, so treat the tail part as part of the text
if (hr == FILTER_S_LAST_TEXT)
{
pszTail = NULL;
cchTail = 0;
}
else if (CLSID_PlainTextFilter == clsid)
{
// CLSID_PlainText filter always returns S_OK, instead of FILTER_S_LAST_TEXT, this forces us to scan
// the entire chunk now, AND (see below) to pass it off as a tail for scanning next chunk too.
// pszTail and cchTail are set below.
}
else
{
pszTail = _FindLastWordBreakW(pszBuf, cchFetch + cchTail);
if (pszTail)
{
// Break on word boundary and leave remainder (tail) for next iteration
*pszTail = TEXT('\0');
pszTail++;
cchTail = lstrlenW(pszTail);
}
else
{
// Wow, big block, with no word break, search its entirety.
// REVIEW: cross chunk items won't be found
pszTail = NULL;
cchTail = 0;
}
}
// do the string scan
if (_pTokens->GrepW(pszBuf))
{
*pszBuf = 0;
return S_OK;
}
else if (FILTER_S_LAST_TEXT == hr)
{
*pszBuf = 0;
return S_FALSE;
}
// prepare for next fetch...
// If it is the plaintext filter, grab the tail anyway, even though we've tested it already
// WinSE 25867
if (CLSID_PlainTextFilter == clsid)
{
pszTail = _FindLastWordBreakW(pszBuf, cchFetch + cchTail);
if (pszTail)
{
*pszTail = TEXT('\0');
pszTail++;
cchTail = lstrlenW(pszTail);
}
else
{
pszTail = NULL;
cchTail = 0;
}
}
*pszBuf = 0;
pszFetch = pszBuf;
cchFetch = cchBuf;
// If there is a tail to deal with, move it to the front of
// the buffer and prepare to have the next block of incoming text
// appended to the tail..
if (pszTail && cchTail)
{
MoveMemory(pszBuf, pszTail, cchTail * sizeof(*pszTail));
pszBuf[cchTail] = 0;
pszFetch += cchTail;
cchFetch -= cchTail;
}
// Fetch next block of text.
__try
{
hr = pFilter->GetText(&cchFetch, pszFetch);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_ABORT;
}
}
}
if (SUCCEEDED(hr) || FILTER_E_NO_MORE_TEXT == hr || FILTER_E_NO_TEXT == hr)
return S_FALSE;
return hr;
}
// Returns a grep buffer of the requested size for the specified thread.
STDMETHODIMP CFilterGrep::_GetThreadGrepBuffer(
DWORD dwThreadID,
ULONG cchNeed,
LPWSTR* ppszBuf)
{
ASSERT(dwThreadID);
ASSERT(cchNeed > 0);
ASSERT(ppszBuf);
HRESULT hr = E_FAIL;
*ppszBuf = NULL;
_EnterCritical();
if (_hdpaGrepBuffers || (_hdpaGrepBuffers = DPA_Create(4)) != NULL)
{
CGrepBuffer *pgb, *pgbCached = NULL;
for (int i = 0, cnt = DPA_GetPtrCount(_hdpaGrepBuffers); i < cnt; i++)
{
pgb = (CGrepBuffer*)DPA_FastGetPtr(_hdpaGrepBuffers, i);
if (pgb->IsThread(dwThreadID))
{
pgbCached = pgb;
hr = pgbCached->Alloc(cchNeed);
if (S_OK == hr)
*ppszBuf = pgbCached->Buffer();
break;
}
}
if (NULL == pgbCached) // not cached?
{
if ((pgb = new CGrepBuffer(dwThreadID)) != NULL)
{
hr = pgb->Alloc(cchNeed);
if (S_OK == hr)
{
*ppszBuf = pgb->Buffer();
DPA_AppendPtr(_hdpaGrepBuffers, pgb);
}
else
delete pgb;
}
else
hr = E_OUTOFMEMORY;
}
}
else
hr = E_OUTOFMEMORY;
_LeaveCritical();
return hr;
}
// Frees grep buffer for the specified thread
STDMETHODIMP CFilterGrep::_FreeThreadGrepBuffer(DWORD dwThreadID)
{
HRESULT hr = S_FALSE;
_EnterCritical();
for (int i = 0, cnt = DPA_GetPtrCount(_hdpaGrepBuffers); i < cnt; i++)
{
CGrepBuffer* pgb = (CGrepBuffer*) DPA_FastGetPtr(_hdpaGrepBuffers, i);
if (pgb->IsThread(dwThreadID))
{
DPA_DeletePtr(_hdpaGrepBuffers, i);
hr = S_OK;
break;
}
}
_LeaveCritical();
return hr;
}
// Clears grep buffer for all threads
STDMETHODIMP_(void) CFilterGrep::_ClearGrepBuffers()
{
_EnterCritical();
if (_hdpaGrepBuffers)
{
while(DPA_GetPtrCount(_hdpaGrepBuffers))
{
CGrepBuffer* pgb = (CGrepBuffer*)DPA_DeletePtr(_hdpaGrepBuffers, 0);
delete pgb;
}
DPA_Destroy(_hdpaGrepBuffers);
_hdpaGrepBuffers = NULL;
}
_LeaveCritical();
}
//#define _USE_GREP_RESTRICTIONS_ // Check for registered list of excluded files types
// Reports whether the file type is restricted from full-text grep.
STDMETHODIMP_(BOOL) CFilterGrep::_IsRestrictedFileType(LPCWSTR pwszFile)
{
#ifdef _USE_GREP_RESTRICTIONS_
LPCWSTR pwszExt = PathFindExtensionW(pwszFile);
if (pwszExt && *pwszExt)
{
if (_dwFlags & FGIF_GREPCONTENT &&
_FetchRestrictionSettings(L"Content", &_pwszContentRestricted, FALSE))
{
if (_ScanRestrictionSettings(_pwszContentRestricted, pwszExt))
return TRUE;
}
if (_dwFlags & FGIF_GREPCONTENT &&
_FetchRestrictionSettings(L"Properties", &_pwszPropertiesRestricted, FALSE))
{
if (_ScanRestrictionSettings(_pwszPropertiesRestricted, pwszExt))
return TRUE;
}
}
#endif
return FALSE;
}
STDMETHODIMP CFilterGrep::GetMatchTokens(OUT LPWSTR pszTokens, UINT cchTokens) const
{
HRESULT hr = _pTokens ? _pTokens->GetMatchTokens(pszTokens, cchTokens) : S_FALSE;
if (S_OK != hr && pszTokens)
*pszTokens = 0;
return hr;
}
STDMETHODIMP CFilterGrep::GetExcludeTokens(OUT LPWSTR pszTokens, UINT cchTokens) const
{
HRESULT hr = _pTokens ? _pTokens->GetExcludeTokens(pszTokens, cchTokens) : S_FALSE;
if (S_OK != hr && pszTokens)
*pszTokens = 0;
return hr;
}
STDMETHODIMP CFilterGrep::GetCodePage(UINT* pnCodepage) const
{
HRESULT hr = _pTokens ? _pTokens->GetCodePage(pnCodepage) : S_FALSE;
if (S_OK != hr && pnCodepage)
*pnCodepage = 0;
return hr;
}
STDMETHODIMP CFilterGrep::GetFlags(DWORD* pdwFlags) const
{
if (*pdwFlags)
*pdwFlags = _dwFlags;
return S_OK;
}