Files
Slnkdwf/ATL90/include/atlstencil.h
Jos Groot Lipman 0e37d774d2 Merge SLNKDWF64 branch
svn path=/Slnkdwf/trunk/; revision=23911
2015-01-21 12:09:31 +00:00

4153 lines
107 KiB
C++

// This is a part of the Active Template Library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.
#ifndef __ATLSTENCIL_H__
#define __ATLSTENCIL_H__
#pragma once
#include <atlisapi.h>
#include <atlfile.h>
#include <atlutil.h>
#include <math.h>
#ifdef ATL_DEBUG_STENCILS
#include <atlsrvres.h>
#ifndef ATL_STENCIL_MAX_ERROR_LEN
#define ATL_STENCIL_MAX_ERROR_LEN 256
#endif
#endif // ATL_DEBUG_STENCILS
#ifndef ATL_NO_MLANG
#include <mlang.h>
#endif
#ifndef _ATL_NO_DEFAULT_LIBS
#pragma comment(lib, "shlwapi.lib")
#endif // !_ATL_NO_DEFAULT_LIBS
#pragma warning( push )
#pragma warning(disable: 4127) // conditional expression is constant
#pragma warning(disable: 4511) // copy constructor could not be generated
#pragma warning(disable: 4512) // assignment operator could not be generated
#pragma warning(disable: 4702) // assignment operator could not be generated
#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
#pragma warning(disable: 4191) // unsafe conversion from 'functionptr1' to 'functionptr2'
#pragma pack(push,_ATL_PACKING)
namespace ATL {
// Token types
// These tags are token tags for the standard tag replacer implementation
extern __declspec(selectany) const DWORD STENCIL_TEXTTAG = 0x00000000;
extern __declspec(selectany) const DWORD STENCIL_REPLACEMENT = 0x00000001;
extern __declspec(selectany) const DWORD STENCIL_ITERATORSTART = 0x00000002;
extern __declspec(selectany) const DWORD STENCIL_ITERATOREND = 0x00000003;
extern __declspec(selectany) const DWORD STENCIL_CONDITIONALSTART = 0x00000004;
extern __declspec(selectany) const DWORD STENCIL_CONDITIONALELSE = 0x00000005;
extern __declspec(selectany) const DWORD STENCIL_CONDITIONALEND = 0x00000006;
extern __declspec(selectany) const DWORD STENCIL_STENCILINCLUDE = 0x00000007;
extern __declspec(selectany) const DWORD STENCIL_STATICINCLUDE = 0x00000008;
extern __declspec(selectany) const DWORD STENCIL_LOCALE = 0x00000009;
extern __declspec(selectany) const DWORD STENCIL_CODEPAGE = 0x0000000a;
// The base for user defined token types
extern __declspec(selectany) const DWORD STENCIL_USER_TOKEN_BASE = 0x00001000;
// Symbols to use in error handling in the stencil processor
#define STENCIL_INVALIDINDEX 0xFFFFFFFF
#define STENCIL_INVALIDOFFSET 0xFFFFFFFF
// error codes
#define STENCIL_SUCCESS HTTP_SUCCESS
#define STENCL_FAIL HTTP_FAIL
#define STENCIL_BASIC_MAP 0
#define STENCIL_ATTR_MAP 1
#ifndef ATL_MAX_METHOD_NAME_LEN
#define ATL_MAX_METHOD_NAME_LEN 64
#endif
#ifndef ATL_MAX_BLOCK_STACK
#define ATL_MAX_BLOCK_STACK 128
#endif
template <class TBase, typename T>
struct CTagReplacerMethodsEx
{
typedef HTTP_CODE (TBase::*REPLACE_FUNC)();
typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX)(T*);
typedef HTTP_CODE (TBase::*PARSE_FUNC)(IAtlMemMgr *, LPCSTR, T**);
typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX_V)(void *);
typedef HTTP_CODE (TBase::*PARSE_FUNC_V)(IAtlMemMgr *, LPCSTR, void**);
static REPLACE_FUNC_EX_V CheckRepl(REPLACE_FUNC p) throw()
{
return (REPLACE_FUNC_EX_V) p;
}
static REPLACE_FUNC_EX_V CheckReplEx(REPLACE_FUNC_EX p) throw()
{
return (REPLACE_FUNC_EX_V) p;
}
static PARSE_FUNC_V CheckParse(PARSE_FUNC p) throw()
{
return (PARSE_FUNC_V) p;
}
};
template <class TBase>
struct CTagReplacerMethods
{
union
{
HTTP_CODE (TBase::*pfnMethodEx)(void *);
HTTP_CODE (TBase::*pfnMethod)();
};
HTTP_CODE (TBase::*pfnParse)(IAtlMemMgr *pMemMgr, LPCSTR, void **);
};
#define REPLACEMENT_ENTRY_DEFAULT 0
#define REPLACEMENT_ENTRY_ARGS 1
template <class TBase>
struct CTagReplacerMethodEntry
{
int nType; // REPLACEMENT_ENTRY_*
LPCSTR szMethodName;
CTagReplacerMethods<TBase> Methods;
};
#define BEGIN_REPLACEMENT_METHOD_MAP(className)\
public:\
void GetReplacementMethodMap(const ATL::CTagReplacerMethodEntry<className> ** ppOut) const\
{\
typedef className __className;\
static const ATL::CTagReplacerMethodEntry<className> methods[] = {
#define REPLACEMENT_METHOD_ENTRY(methodName, methodFunc)\
{ 0, methodName, { ATL::CTagReplacerMethodsEx<__className, void>::CheckRepl(&__className::methodFunc), NULL } },
#define REPLACEMENT_METHOD_ENTRY_EX(methodName, methodFunc, paramType, parseFunc)\
{ 1, methodName, { ATL::CTagReplacerMethodsEx<__className, paramType>::CheckReplEx(&__className::methodFunc), ATL::CTagReplacerMethodsEx<__className, paramType>::CheckParse(&__className::parseFunc) } },
#define REPLACEMENT_METHOD_ENTRY_EX_STR(methodName, methodFunc) \
{ 1, methodName, { ATL::CTagReplacerMethodsEx<__className, char>::CheckReplEx(&__className::methodFunc), ATL::CTagReplacerMethodsEx<__className, char>::CheckParse(&__className::DefaultParseString) } },
#define END_REPLACEMENT_METHOD_MAP()\
{ 0, NULL, NULL } };\
*ppOut = methods;\
}
#define BEGIN_ATTR_REPLACEMENT_METHOD_MAP(className)\
public:\
void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<className> ** ppOut) const\
{\
typedef className __className;\
static const ATL::CTagReplacerMethodEntry<className> methods[] = {
#define END_ATTR_REPLACEMENT_METHOD_MAP()\
{ NULL, NULL, NULL } };\
*ppOut = methods;\
}
template <class T>
class ITagReplacerImpl : public ITagReplacer
{
protected:
IWriteStream *m_pStream;
public:
typedef HTTP_CODE (T::*REPLACEMENT_METHOD)();
typedef HTTP_CODE (T::*REPLACEMENT_METHOD_EX)(void *pvParam);
ITagReplacerImpl() throw()
:m_pStream(NULL)
{
}
IWriteStream *SetStream(IWriteStream *pStream)
{
IWriteStream *pRetStream = m_pStream;
m_pStream = pStream;
return pRetStream;
}
// Looks up the replacement method offset. Optionally, it will
// look up the replacement method and object offset of an alternate
// tag replacer.
HTTP_CODE FindReplacementOffset(
LPCSTR szMethodName,
DWORD *pdwMethodOffset,
LPCSTR szHandlerName,
DWORD *pdwHandlerOffset,
DWORD *pdwMap, void **ppvParam, IAtlMemMgr *pMemMgr)
{
ATLENSURE(szMethodName != NULL);
ATLENSURE(pdwMethodOffset != NULL);
ATLENSURE((szHandlerName == NULL && pdwHandlerOffset == NULL) ||
(szHandlerName != NULL && pdwHandlerOffset != NULL));
ATLENSURE(pdwMap != NULL);
ATLENSURE(ppvParam != NULL);
ATLENSURE(pMemMgr != NULL);
// we at least have to be looking up a method offset
if (!pdwMethodOffset || !szMethodName)
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
*pdwMethodOffset = STENCIL_INVALIDOFFSET;
HTTP_CODE hcErr = HTTP_FAIL;
T *pT = static_cast<T *>(this);
char szName[ATL_MAX_METHOD_NAME_LEN+1];
// if a handler name was supplied, we will try to
// find a different object to handle the method
if (szHandlerName && *szHandlerName)
{
if (!pdwHandlerOffset)
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
hcErr = pT->GetHandlerOffset(szHandlerName, pdwHandlerOffset);
// got the alternate handler, now look up the method offset on
// the handler.
if (!hcErr)
{
CComPtr<ITagReplacer> spAltTagReplacer;
hcErr = pT->GetReplacementObject(*pdwHandlerOffset, &spAltTagReplacer);
if (!hcErr)
hcErr = spAltTagReplacer->FindReplacementOffset(szMethodName, pdwMethodOffset,
NULL, NULL, pdwMap, ppvParam, pMemMgr);
return hcErr;
}
else
return hcErr;
}
if (!SafeStringCopy(szName, szMethodName))
{
return AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
}
// check for params
char *szLeftPar = strchr(szName, '(');
if (szLeftPar)
{
*szLeftPar = '\0';
szLeftPar++;
char *szRightPar = strchr(szLeftPar, ')');
if (!szRightPar)
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
*szRightPar = '\0';
szMethodName = szName;
}
// No handler name is specified, so we look up the method name in
// T's replacement method map
const CTagReplacerMethodEntry<T> *pEntry = NULL;
pT->GetReplacementMethodMap(&pEntry);
hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
if (hcErr != HTTP_SUCCESS)
{
pT->GetAttrReplacementMethodMap(&pEntry);
hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
if (hcErr == HTTP_SUCCESS)
*pdwMap = STENCIL_ATTR_MAP;
}
else
{
*pdwMap = STENCIL_BASIC_MAP;
}
// This assert will be triggered if arguments are passed to a replacement method that doesn't handle them
ATLASSERT( szLeftPar == NULL || (szLeftPar != NULL && (pEntry != NULL && pEntry[*pdwMethodOffset].Methods.pfnParse != NULL)) );
if (hcErr == HTTP_SUCCESS && pEntry && pEntry[*pdwMethodOffset].Methods.pfnParse)
hcErr = (pT->*pEntry[*pdwMethodOffset].Methods.pfnParse)(pMemMgr, szLeftPar, ppvParam);
return hcErr;
}
HTTP_CODE FindReplacementOffsetInMap(
LPCSTR szMethodName,
LPDWORD pdwMethodOffset,
const CTagReplacerMethodEntry<T> *pEntry) throw()
{
if (pEntry == NULL)
return HTTP_FAIL;
const CTagReplacerMethodEntry<T> *pEntryHead = pEntry;
while (pEntry->szMethodName)
{
if (strcmp(pEntry->szMethodName, szMethodName) == 0)
{
if (pEntry->Methods.pfnMethod)
{
*pdwMethodOffset = (DWORD)(pEntry-pEntryHead);
return HTTP_SUCCESS;
}
}
pEntry++;
}
return HTTP_FAIL;
}
// Used to render a single replacement tag into a stream.
// Looks up a pointer to a member function in user code by offseting into the users
// replacement map. Much faster than the other overload of this function since
// no string compares are performed.
HTTP_CODE RenderReplacement(DWORD dwFnOffset, DWORD dwObjOffset, DWORD dwMap, void *pvParam)
{
HTTP_CODE hcErr = HTTP_FAIL;
T *pT = static_cast<T *>(this);
// if we were not passed an object offset, then we assume
// that the function at dwFnOffset is in T's replacement
// map
if (dwObjOffset == STENCIL_INVALIDOFFSET)
{
// call a function in T's replacement map
ATLASSERT(dwFnOffset != STENCIL_INVALIDOFFSET);
const CTagReplacerMethodEntry<T> *pEntry = NULL;
if (dwMap == STENCIL_BASIC_MAP)
pT->GetReplacementMethodMap(&pEntry);
else
pT->GetAttrReplacementMethodMap(&pEntry);
if (pEntry)
{
if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_DEFAULT)
{
REPLACEMENT_METHOD pfn = NULL;
pfn = pEntry[dwFnOffset].Methods.pfnMethod;
ATLASSERT(pfn);
if (pfn)
{
hcErr = (pT->*pfn)();
}
}
else if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_ARGS)
{
REPLACEMENT_METHOD_EX pfn = NULL;
pfn = pEntry[dwFnOffset].Methods.pfnMethodEx;
ATLASSERT(pfn);
if (pfn)
{
hcErr = (pT->*pfn)(pvParam);
}
}
else
{
// unknown entry type
ATLASSERT(FALSE);
}
}
}
else
{
// otherwise, we were passed an object offset. The object
// offset is a dword ID that T can use to look up the
// ITagReplacer* of a tag replacer that will render this
// replacement.
CComPtr<ITagReplacer> spAltReplacer = NULL;
if (!pT->GetReplacementObject(dwObjOffset, &spAltReplacer))
{
spAltReplacer->SetStream(m_pStream);
hcErr = spAltReplacer->RenderReplacement(dwFnOffset, STENCIL_INVALIDOFFSET, dwMap, pvParam);
}
}
return hcErr;
}
// Default GetHandlerOffset, does nothing
HTTP_CODE GetHandlerOffset(LPCSTR /*szHandlerName*/, DWORD* pdwOffset)
{
if (pdwOffset)
*pdwOffset = 0;
return HTTP_FAIL;
}
// Default GetReplacementObject, does nothing
HTTP_CODE GetReplacementObject(DWORD /*dwObjOffset*/, ITagReplacer **ppReplacer)
{
if (ppReplacer)
*ppReplacer = NULL;
return HTTP_FAIL;
}
void GetReplacementMethodMap(const CTagReplacerMethodEntry<T> ** ppOut) const
{
static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
*ppOut = methods;
}
void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<T> **ppOut) const
{
static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
*ppOut = methods;
}
HRESULT GetContext(REFIID, void**)
{
return E_NOINTERFACE;
}
virtual HINSTANCE GetResourceInstance()
{
return GetModuleHandle(NULL);
}
HTTP_CODE DefaultParseString(IAtlMemMgr *pMemMgr, LPCSTR szParams, char **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
size_t nLen = strlen(szParams);
if (nLen)
{
nLen++;
*ppParam = (char *) pMemMgr->Allocate(nLen);
if (*ppParam)
Checked::memcpy_s(*ppParam, nLen, szParams, nLen);
else
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseUChar(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned char **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (unsigned char *) pMemMgr->Allocate(sizeof(unsigned char));
if (*ppParam)
{
char *szEnd;
**ppParam = (unsigned char) strtoul(szParams, &szEnd, 10);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, short **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (short *) pMemMgr->Allocate(sizeof(short));
if (*ppParam)
{
**ppParam = (short)atoi(szParams);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseUShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned short **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (unsigned short *) pMemMgr->Allocate(sizeof(short));
if (*ppParam)
{
char *szEnd;
**ppParam = (unsigned short) strtoul(szParams, &szEnd, 10);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, int **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (int *) pMemMgr->Allocate(sizeof(int));
if (*ppParam)
{
**ppParam = atoi(szParams);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseUInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned int **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (unsigned int *) pMemMgr->Allocate(sizeof(unsigned int));
if (*ppParam)
{
char *szEnd;
**ppParam = strtoul(szParams, &szEnd, 10);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, __int64 **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (__int64 *) pMemMgr->Allocate(sizeof(__int64));
if (*ppParam)
{
**ppParam = _atoi64(szParams);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseUInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned __int64 **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (unsigned __int64 *) pMemMgr->Allocate(sizeof(unsigned __int64));
if (*ppParam)
{
char *szEnd;
**ppParam = _strtoui64(szParams, &szEnd, 10);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseBool(IAtlMemMgr *pMemMgr, LPCSTR szParams, bool **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (bool *) pMemMgr->Allocate(sizeof(bool));
if (*ppParam)
{
if (!_strnicmp(szParams, "true", sizeof("true")-sizeof('\0')))
**ppParam = true;
else
**ppParam = false;
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseDouble(IAtlMemMgr *pMemMgr, LPCSTR szParams, double **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
*ppParam = (double *) pMemMgr->Allocate(sizeof(double));
if (*ppParam)
{
**ppParam = atof(szParams);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
HTTP_CODE DefaultParseFloat(IAtlMemMgr *pMemMgr, LPCSTR szParams, float **ppParam) throw(...)
{
ATLENSURE( pMemMgr != NULL );
ATLENSURE( szParams != NULL );
ATLENSURE( ppParam != NULL );
errno_t errnoValue = 0;
*ppParam = (float *) pMemMgr->Allocate(sizeof(float));
if (*ppParam)
{
errno_t saveErrno = Checked::get_errno();
Checked::set_errno(0);
**ppParam = (float) atof(szParams);
errnoValue = Checked::get_errno();
Checked::set_errno(saveErrno);
}
else
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
if ((**ppParam == -HUGE_VAL) || (**ppParam == HUGE_VAL) || (errnoValue == ERANGE))
{
return HTTP_FAIL;
}
return HTTP_SUCCESS;
}
};
inline LPCSTR SkipSpace(LPCSTR sz, WORD nCodePage) throw()
{
if (sz == NULL)
return NULL;
while (isspace(static_cast<unsigned char>(*sz)))
sz = CharNextExA(nCodePage, sz, 0);
return sz;
}
inline LPCSTR RSkipSpace(LPCSTR pStart, LPCSTR sz, WORD nCodePage) throw()
{
if (sz == NULL || pStart == NULL)
return NULL;
while (isspace(static_cast<unsigned char>(*sz)) && sz != pStart)
sz = CharPrevExA(nCodePage, pStart, sz, 0);
return sz;
}
//
// StencilToken
// The stencil class will create an array of these tokens during the parse
// phase and use them during rendering to render the stencil
struct StencilToken
{
LPCSTR pStart; // Start of fragment to be rendered
LPCSTR pEnd; // End of fragment to be rendered
DWORD type; // Type of token
DWORD dwFnOffset; // Offset into the replacement map for the handler function.
DWORD dwMap;
DWORD dwObjOffset; // An identifier for the caller to use in identifiying the
// object that will render this token.
CHAR szHandlerName[ATL_MAX_HANDLER_NAME_LEN + 1]; // Name of handler object.
CHAR szMethodName[ATL_MAX_METHOD_NAME_LEN + 1]; // Name of handler method.
DWORD dwLoopIndex; // Offset into array of StencilTokens of the other loop tag
DWORD_PTR dwData;
BOOL bDynamicAlloc;
};
//
// Class CStencil
// The CStencil class is used to map in a stencil from a file or resource
// and parse the stencil into an array of StencilTokens. We then render
// the stencil from the array of tokens. This class's parse and render
// functions depend on an IReplacementHandlerLookup interface pointer to be
// passed so it can retrieve the IReplacementHandler interface pointer of the
// handler object that will be called to render replacement tags
class CStencil :
public IMemoryCacheClient
{
private:
LPCSTR m_pBufferStart; // Beginning of CHAR buffer that holds the stencil.
// For mapped files this is the beginning of the mapping.
LPCSTR m_pBufferEnd; // End of CHAR buffer that holds the stencil.
CAtlArray<StencilToken> m_arrTokens; //An array of tokens.
FILETIME m_ftLastModified; // Last modified time (0 for resource)
FILETIME m_ftLastChecked; // Last time we retrieved last modified time (0 for resource)
HCACHEITEM m_hCacheItem;
WORD m_nCodePage;
BOOL m_bUseLocaleACP;
char m_szDllPath[MAX_PATH];
char m_szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1]; // Room for the path, the handler
// the '/' and the '\0'
#ifdef ATL_DEBUG_STENCILS
struct ParseError
{
char m_szError[ATL_STENCIL_MAX_ERROR_LEN];
LPCSTR m_szPosition;
LPCSTR m_szStartLine;
LPCSTR m_szEndLine;
int m_nLineNumber;
bool operator==(const ParseError& that) const throw()
{
return (m_nLineNumber == that.m_nLineNumber);
}
};
CSimpleArray<ParseError> m_Errors;
HINSTANCE m_hResInst;
class CParseErrorProvider : public ITagReplacerImpl<CParseErrorProvider>,
public CComObjectRootEx<CComSingleThreadModel>
{
public:
BEGIN_COM_MAP(CParseErrorProvider)
COM_INTERFACE_ENTRY(ITagReplacer)
END_COM_MAP()
CSimpleArray<ParseError> *m_pErrors;
int m_nCurrentError;
CParseErrorProvider() throw() :
m_pErrors(NULL),
m_nCurrentError(-1)
{
}
void Initialize(CSimpleArray<ParseError> *pErrors) throw()
{
m_pErrors = pErrors;
}
HTTP_CODE OnGetNextError() throw()
{
m_nCurrentError++;
if (m_nCurrentError >= m_pErrors->GetSize() ||
m_nCurrentError < 0 )
{
m_nCurrentError = -1;
return HTTP_S_FALSE;
}
else
return HTTP_SUCCESS;
}
HTTP_CODE OnGetErrorLineNumber() throw(...)
{
if (m_pErrors->GetSize() == 0)
return HTTP_SUCCESS;
if (m_nCurrentError > m_pErrors->GetSize() ||
m_nCurrentError < 0)
m_nCurrentError = 0;
CWriteStreamHelper c(m_pStream);
if (!c.Write((*m_pErrors)[m_nCurrentError].m_nLineNumber))
return HTTP_FAIL;
return HTTP_SUCCESS;
}
HTTP_CODE OnGetErrorText() throw(...)
{
if (m_pErrors->GetSize() == 0)
return HTTP_SUCCESS;
if (m_nCurrentError > m_pErrors->GetSize() ||
m_nCurrentError < 0)
m_nCurrentError = 0;
CWriteStreamHelper c(m_pStream);
if (!c.Write(static_cast<LPCSTR>((*m_pErrors)[m_nCurrentError].m_szError)))
return HTTP_FAIL;
return HTTP_SUCCESS;
}
HTTP_CODE OnGetErrorLine() throw(...)
{
ATLASSUME(m_pStream != NULL);
if (m_pErrors->GetSize() == 0)
return HTTP_SUCCESS;
if (m_nCurrentError > m_pErrors->GetSize() ||
m_nCurrentError < 0)
m_nCurrentError = 0;
m_pStream->WriteStream((*m_pErrors)[m_nCurrentError].m_szStartLine,
(int)((*m_pErrors)[m_nCurrentError].m_szEndLine - (*m_pErrors)[m_nCurrentError].m_szStartLine), NULL);
return HTTP_SUCCESS;
}
BEGIN_REPLACEMENT_METHOD_MAP(CParseErrorProvider)
REPLACEMENT_METHOD_ENTRY("GetNextError", OnGetNextError)
REPLACEMENT_METHOD_ENTRY("GetErrorText", OnGetErrorText)
REPLACEMENT_METHOD_ENTRY("GetErrorLine", OnGetErrorLine)
REPLACEMENT_METHOD_ENTRY("GetErrorLineNumber", OnGetErrorLineNumber)
END_REPLACEMENT_METHOD_MAP()
};
#else
bool m_bErrorsOccurred;
#endif
class CSaveThreadLocale
{
LCID m_locale;
public:
CSaveThreadLocale() throw()
{
m_locale = GetThreadLocale();
}
~CSaveThreadLocale() throw()
{
SetThreadLocale(m_locale);
}
};
HTTP_CODE LoadFromResourceInternal(HINSTANCE hInstRes, HRSRC hRsrc) throw()
{
ATLASSERT( hRsrc != NULL );
HGLOBAL hgResource = NULL;
hgResource = LoadResource(hInstRes, hRsrc);
if (!hgResource)
{
return HTTP_FAIL;
}
DWORD dwSize = SizeofResource(hInstRes, hRsrc);
if (dwSize != 0)
{
m_pBufferStart = (LPSTR)LockResource(hgResource);
if (m_pBufferStart != NULL)
{
m_pBufferEnd = m_pBufferStart+dwSize;
return HTTP_SUCCESS;
}
}
// failed to load resource
return HTTP_FAIL;
}
protected:
ITagReplacer *m_pReplacer;
IAtlMemMgr *m_pMemMgr;
static CCRTHeap m_crtHeap;
inline BOOL CheckTag(LPCSTR szTag, DWORD dwTagLen, LPCSTR szStart, DWORD dwLen) throw()
{
if (dwLen < dwTagLen)
return FALSE;
if (memcmp(szStart, szTag, dwTagLen))
return FALSE;
if (isspace(static_cast<unsigned char>(szStart[dwTagLen])) || szStart[dwTagLen] == '}')
return TRUE;
return FALSE;
}
inline void FindTagArgs(LPCSTR& szstart, LPCSTR& szend, int nKeywordChars) throw()
{
// this function should only be called after finding a valid tag
// the first two characters of szstart should be {{
ATLASSERT(szstart[0] == '{' && szstart[1] == '{');
if (*szstart == '{')
szstart += 2; // move past {{
szstart = SkipSpace(szstart, m_nCodePage); // move past whitespace
szstart += nKeywordChars; // move past keyword
szstart = SkipSpace(szstart, m_nCodePage); // move past whitespace after keyword
if (*szend == '}')
szend -=2; // chop off }}
szend = RSkipSpace(szstart, szend, m_nCodePage); // chop of trailing whitespace
}
DWORD CheckTopAndPop(DWORD *pBlockStack, DWORD *pdwTop, DWORD dwToken) throw()
{
if (*pdwTop == 0)
return STENCIL_INVALIDINDEX;
if (m_arrTokens[pBlockStack[*pdwTop]].type == dwToken)
{
*pdwTop = (*pdwTop) - 1;
return pBlockStack[(*pdwTop)+1];
}
return STENCIL_INVALIDINDEX;
}
DWORD PushToken(DWORD *pBlockStack, DWORD *pdwTop, DWORD dwIndex) throw()
{
if (*pdwTop < (ATL_MAX_BLOCK_STACK-1))
{
*pdwTop = (*pdwTop) + 1;
pBlockStack[*pdwTop] = dwIndex;
}
else
{
dwIndex = STENCIL_INVALIDINDEX;
}
return dwIndex;
}
public:
enum PARSE_TOKEN_RESULT { INVALID_TOKEN, NORMAL_TOKEN, RESERVED_TOKEN };
CStencil(IAtlMemMgr *pMemMgr=NULL) throw()
{
m_pBufferStart = NULL;
m_pBufferEnd = NULL;
m_hCacheItem = NULL;
m_ftLastModified.dwLowDateTime = 0;
m_ftLastModified.dwHighDateTime = 0;
m_ftLastChecked.dwLowDateTime = 0;
m_ftLastChecked.dwHighDateTime = 0;
m_arrTokens.SetCount(0, 128);
m_nCodePage = CP_ACP;
m_bUseLocaleACP = TRUE;
m_szHandlerName[0] = '\0';
m_szDllPath[0] = '\0';
m_pMemMgr = pMemMgr;
if (!pMemMgr)
m_pMemMgr = &m_crtHeap;
#ifdef ATL_DEBUG_STENCILS
m_hResInst = NULL;
#else
m_bErrorsOccurred = false;
#endif
}
virtual ~CStencil() throw()
{
Uninitialize();
}
#ifdef ATL_DEBUG_STENCILS
bool RenderErrors(IWriteStream *pStream) throw(...)
{
if (pStream == NULL)
{
return false;
}
CComObjectStackEx<CParseErrorProvider> Errors;
Errors.Initialize(&m_Errors);
CStencil ErrorStencil;
if (m_hResInst != NULL)
{
CFixedStringT<CStringA, 256> strErrorStencil;
_ATLTRY
{
if (strErrorStencil.LoadString(m_hResInst, IDS_STENCIL_ERROR_STENCIL) == FALSE)
{
return false;
}
}
_ATLCATCHALL()
{
return false;
}
HTTP_CODE hcRet = ErrorStencil.LoadFromString(strErrorStencil, strErrorStencil.GetLength());
if (hcRet == HTTP_SUCCESS)
{
if (ErrorStencil.ParseReplacements(static_cast<ITagReplacer *>(&Errors)) != false)
{
ErrorStencil.FinishParseReplacements();
if (ErrorStencil.ParseSuccessful() != false)
{
hcRet = ErrorStencil.Render(static_cast<ITagReplacer *>(&Errors), pStream);
if (HTTP_ERROR_CODE(hcRet) < 400)
{
return true;
}
}
}
}
}
return false;
}
void SetErrorResource(HINSTANCE hResInst)
{
m_hResInst = hResInst;
}
bool ParseSuccessful()
{
return (m_Errors.GetSize() == 0);
}
bool AddErrorInternal(LPCSTR szErrorText, LPCSTR szPosition) throw()
{
int nLineNum = 0;
LPCSTR szStartLine = NULL;
LPCSTR szPtr = m_pBufferStart;
while (szPtr < szPosition)
{
if (*szPtr == '\n')
{
szStartLine = szPtr + 1;
nLineNum++;
}
LPSTR szNext = CharNextExA(m_nCodePage, szPtr, 0);
if (szNext == szPtr)
{
break;
}
szPtr = szNext;
}
LPCSTR szEndLine = szPtr;
while (*szPtr)
{
if (*szPtr == '\n')
break;
szEndLine = szPtr;
LPSTR szNext = CharNextExA(m_nCodePage, szPtr, 0);
if (szNext == szPtr)
{
break;
}
szPtr = szNext;
}
ParseError p;
SafeStringCopy(p.m_szError, szErrorText);
p.m_szPosition = szPosition;
p.m_nLineNumber = nLineNum;
p.m_szStartLine = szStartLine;
p.m_szEndLine = szEndLine;
return (m_Errors.Add(p) == TRUE);
}
bool AddError(UINT uID, LPCSTR szPosition) throw()
{
if (m_hResInst != NULL)
{
_ATLTRY
{
CFixedStringT<CStringA, 256> strRes;
if (strRes.LoadString(m_hResInst, uID) != FALSE)
{
return AddErrorInternal(strRes, szPosition);
}
}
_ATLCATCHALL()
{
}
}
return AddErrorInternal("Could not load resource for error string", szPosition);
}
bool AddReplacementError(LPCSTR szReplacement, LPCSTR szPosition) throw()
{
if (m_hResInst != NULL)
{
_ATLTRY
{
CFixedStringT<CStringA, 256> strRes;
if (strRes.LoadString(m_hResInst, IDS_STENCIL_UNRESOLVED_REPLACEMENT) != FALSE)
{
CFixedStringT<CStringA, 256> strErrorText;
strErrorText.Format(strRes, szReplacement);
return AddErrorInternal(strErrorText, szPosition);
}
else
{
return AddErrorInternal("Could not load resource for error string", szPosition);
}
}
_ATLCATCHALL()
{
return false;
}
}
return false;
}
#else
bool ParseSuccessful()
{
return !m_bErrorsOccurred;
}
bool AddError(UINT /*uID*/, LPCSTR /*szPosition*/) throw()
{
m_bErrorsOccurred = true;
return true;
}
bool AddReplacementError(LPCSTR /*szReplacement*/, LPCSTR /*szPosition*/) throw()
{
m_bErrorsOccurred = true;
return true;
}
void SetErrorResource(HINSTANCE) throw()
{
}
#endif
// Call Uninitialize if you want to re-use an already initialized CStencil
void Uninitialize() throw()
{
int nSize = (int) m_arrTokens.GetCount();
for (int nIndex = 0; nIndex < nSize; nIndex++)
{
if (m_arrTokens[nIndex].bDynamicAlloc)
delete [] m_arrTokens[nIndex].pStart;
if (m_arrTokens[nIndex].dwData != 0 && m_arrTokens[nIndex].type != STENCIL_LOCALE)
m_pMemMgr->Free((void *) m_arrTokens[nIndex].dwData);
}
m_arrTokens.RemoveAll();
if ((m_ftLastModified.dwLowDateTime || m_ftLastModified.dwHighDateTime) && m_pBufferStart)
{
delete [] m_pBufferStart;
}
m_pBufferStart = NULL;
m_pBufferEnd = NULL;
}
void GetLastModified(FILETIME *pftLastModified)
{
ATLENSURE(pftLastModified);
*pftLastModified = m_ftLastModified;
}
void GetLastChecked(FILETIME *pftLastChecked)
{
ATLENSURE(pftLastChecked);
*pftLastChecked = m_ftLastChecked;
}
void SetLastChecked(FILETIME *pftLastChecked)
{
ATLENSURE(pftLastChecked);
m_ftLastChecked = *pftLastChecked;
}
HCACHEITEM GetCacheItem()
{
return m_hCacheItem;
}
void SetCacheItem(HCACHEITEM hCacheItem)
{
ATLASSUME(m_hCacheItem == NULL);
m_hCacheItem = hCacheItem;
}
bool GetHandlerName(__out_ecount_z(nPathLen) LPSTR szDllPath, __in size_t nPathLen, __out_ecount_z(nHandlerNameLen) LPSTR szHandlerName, __in size_t nHandlerNameLen) throw()
{
if(strlen(m_szDllPath) >= nPathLen)
{
return false;
}
if(strlen(m_szHandlerName) >= nHandlerNameLen)
{
return false;
}
if(0 != strcpy_s(szDllPath, nPathLen, m_szDllPath))
{
return false;
}
if(0 != strcpy_s(szHandlerName, nHandlerNameLen, m_szHandlerName))
{
return false;
}
return true;
}
// Adds a token to the token array, handler name, method name
// and handler function offset are optional
ATL_NOINLINE DWORD AddToken(
LPCSTR pStart,
LPCSTR pEnd,
DWORD dwType,
LPCSTR szHandlerName = NULL,
LPCSTR szMethodName = NULL,
DWORD dwFnOffset = STENCIL_INVALIDOFFSET,
DWORD dwObjOffset = STENCIL_INVALIDOFFSET,
DWORD_PTR dwData = 0,
DWORD dwMap = 0,
BOOL bDynamicAlloc = 0) throw()
{
StencilToken t;
memset(&t, 0x00, sizeof(t));
t.pStart = pStart;
t.pEnd = pEnd;
t.type = dwType;
t.dwLoopIndex = STENCIL_INVALIDINDEX;
t.dwFnOffset = dwFnOffset;
t.dwObjOffset = dwObjOffset;
t.dwData = dwData;
t.dwMap = dwMap;
t.bDynamicAlloc = bDynamicAlloc;
// this should never assert unless the user has overriden something incorrectly
if ((szHandlerName != NULL) && (*szHandlerName))
{
ATLVERIFY( SafeStringCopy(t.szHandlerName, szHandlerName) );
}
if ((szMethodName != NULL) && (*szMethodName))
{
ATLVERIFY( SafeStringCopy(t.szMethodName, szMethodName) );
}
_ATLTRY
{
return (DWORD) m_arrTokens.Add(t);
}
_ATLCATCHALL()
{
return STENCIL_INVALIDINDEX;
}
}
HTTP_CODE LoadFromFile(LPCSTR szFileName) throw()
{
HRESULT hr = E_FAIL;
ULONGLONG dwLen = 0;
CAtlFile file;
_ATLTRY
{
hr = file.Create(CA2CTEX<MAX_PATH>(szFileName), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
return AtlsHttpError(500, ISE_SUBERR_STENCIL_LOAD_FAIL); // couldn't load SRF!
if (GetFileTime(file, NULL, NULL, &m_ftLastModified))
{
if (SUCCEEDED(file.GetSize(dwLen)))
{
ATLASSERT(!m_pBufferStart);
GetSystemTimeAsFileTime(&m_ftLastChecked);
m_pBufferStart = NULL;
CAutoVectorPtr<char> buffer;
if (!buffer.Allocate((size_t) dwLen))
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // out of memory
DWORD dwRead = 0;
hr = file.Read(buffer, (DWORD) dwLen, dwRead);
if (FAILED(hr))
return AtlsHttpError(500, ISE_SUBERR_READFILEFAIL); // ReadFile failed
m_pBufferStart = buffer.Detach();
m_pBufferEnd = m_pBufferStart + dwRead;
}
}
}
_ATLCATCHALL()
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
}
return HTTP_SUCCESS;
}
// loads a stencil from the specified resource.
HTTP_CODE LoadFromResource(HINSTANCE hInstRes, LPCSTR szID, LPCSTR szType = NULL) throw()
{
if (szType == NULL)
{
szType = (LPCSTR) RT_HTML;
}
HRSRC hRsrc = FindResourceA(hInstRes, szID, szType);
if (hRsrc != NULL)
{
return LoadFromResourceInternal(hInstRes, hRsrc);
}
return HTTP_FAIL;
}
HTTP_CODE LoadFromResourceEx(HINSTANCE hInstRes, LPCSTR szID, WORD wLanguage, LPCSTR szType = NULL) throw()
{
if (szType == NULL)
{
szType = (LPCSTR) RT_HTML;
}
HRSRC hRsrc = FindResourceExA(hInstRes, szType, szID, wLanguage);
if (hRsrc != NULL)
{
return LoadFromResourceInternal(hInstRes, hRsrc);
}
return HTTP_FAIL;
}
// loads a stencil from the specified resource
HTTP_CODE LoadFromResource(HINSTANCE hInstRes, UINT nId, LPCSTR szType = NULL) throw()
{
return LoadFromResource(hInstRes, MAKEINTRESOURCEA(nId), szType);
}
HTTP_CODE LoadFromResourceEx(HINSTANCE hInstRes, UINT nId, WORD wLanguage, LPCSTR szType = NULL) throw()
{
return LoadFromResourceEx(hInstRes, MAKEINTRESOURCEA(nId), wLanguage, szType);
}
// loads a stencil from a string
HTTP_CODE LoadFromString(LPCSTR szString, DWORD dwSize) throw()
{
m_pBufferStart = szString;
m_pBufferEnd = m_pBufferStart+dwSize;
return HTTP_SUCCESS;
}
// Cracks the loaded stencil into an array of StencilTokens in preparation for
// rendering. LoadStencil must be called prior to calling this function.
virtual bool ParseReplacements(ITagReplacer* pReplacer) throw(...)
{
return ParseReplacementsFromBuffer(pReplacer, GetBufferStart(), GetBufferEnd());
}
virtual bool FinishParseReplacements() throw(...)
{
DWORD dwSize = (DWORD) m_arrTokens.GetCount();
for (DWORD dwIndex = 0; dwIndex < dwSize; dwIndex++)
{
StencilToken& token = m_arrTokens[dwIndex];
bool bUnclosedBlock = ((token.type == STENCIL_CONDITIONALSTART ||
token.type == STENCIL_CONDITIONALELSE ||
token.type == STENCIL_ITERATORSTART) &&
token.dwLoopIndex == STENCIL_INVALIDINDEX);
if ((token.szMethodName[0] && token.dwFnOffset == STENCIL_INVALIDOFFSET) || bUnclosedBlock)
{
if (bUnclosedBlock ||
m_pReplacer->FindReplacementOffset(
token.szMethodName, &token.dwFnOffset,
token.szHandlerName, &token.dwObjOffset,
&token.dwMap, (void **)(&token.dwData), m_pMemMgr) != HTTP_SUCCESS)
{
if (bUnclosedBlock && token.type == STENCIL_CONDITIONALSTART)
{
AddError(IDS_STENCIL_UNCLOSEDBLOCK_IF, token.pStart);
}
else if (bUnclosedBlock && token.type == STENCIL_CONDITIONALELSE)
{
AddError(IDS_STENCIL_UNCLOSEDBLOCK_ELSE, token.pStart);
}
else if (bUnclosedBlock && token.type == STENCIL_ITERATORSTART)
{
AddError(IDS_STENCIL_UNCLOSEDBLOCK_WHILE, token.pStart);
}
else
{
AddReplacementError(token.szMethodName, token.pStart);
}
// unresolved replacement, convert it to a text token
token.type = STENCIL_TEXTTAG;
// convert all linked tokens to text tokens as well
// this includes: endif, else, endwhile
DWORD dwLoopIndex = token.dwLoopIndex;
while (dwLoopIndex != dwIndex && dwLoopIndex != STENCIL_INVALIDINDEX)
{
m_arrTokens[dwLoopIndex].type = STENCIL_TEXTTAG;
dwLoopIndex = m_arrTokens[dwLoopIndex].dwLoopIndex;
}
}
}
}
return ParseSuccessful();
}
virtual bool Parse(ITagReplacer *pReplacer) throw( ... )
{
if (ParseReplacements(pReplacer))
{
return FinishParseReplacements();
}
return false;
}
DWORD ParseReplacement( LPCSTR szTokenStart,
LPCSTR szTokenEnd,
DWORD dwTokenType = STENCIL_REPLACEMENT,
DWORD dwKeywordLen = 0) throw()
{
// hold on to the start and end pointers (before removing curlies and whitespace)
// this is needed so that we can convert the token to a text token if the method
// is not resolved (in FinishParseReplacements)
LPCSTR szStart = szTokenStart;
LPCSTR szEnd = szTokenEnd;
FindTagArgs(szTokenStart, szTokenEnd, dwKeywordLen);
char szMethodName[ATL_MAX_METHOD_NAME_LEN+1];
char szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1];
DWORD dwIndex;
//look up the handler name, method name and handler interface
if (HTTP_SUCCESS == GetHandlerAndMethodNames(szTokenStart, szTokenEnd,
szMethodName, ATL_MAX_METHOD_NAME_LEN+1,
szHandlerName, ATL_MAX_HANDLER_NAME_LEN+1))
dwIndex = AddToken(szStart, szEnd, dwTokenType,
szHandlerName, szMethodName,
STENCIL_INVALIDINDEX, STENCIL_INVALIDINDEX,
0, STENCIL_BASIC_MAP);
else
dwIndex = STENCIL_INVALIDINDEX;
return dwIndex;
}
DWORD ParseWhile(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
{
DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_ITERATORSTART, sizeof("while")-1);
if (dwIndex == STENCIL_INVALIDINDEX)
return dwIndex;
return PushToken(pBlockStack, pdwTop, dwIndex);
}
DWORD ParseEndWhile(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
{
DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_ITERATORSTART);
if (dwTopIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE, szTokenStart);
return dwTopIndex;
}
DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_ITERATOREND);
if (dwIndex != STENCIL_INVALIDINDEX)
{
m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
m_arrTokens[dwIndex].dwLoopIndex = dwTopIndex;
}
return dwIndex;
}
DWORD ParseIf(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
{
DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_CONDITIONALSTART, sizeof("if")-1);
if (dwIndex == STENCIL_INVALIDINDEX)
return dwIndex;
return PushToken(pBlockStack, pdwTop, dwIndex);
}
DWORD ParseElse(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
{
DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
if (dwTopIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_UNOPENEDBLOCK_ELSE, szTokenStart);
return dwTopIndex;
}
DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALELSE);
if (dwIndex != STENCIL_INVALIDINDEX)
{
m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
return PushToken(pBlockStack, pdwTop, dwIndex);
}
return dwIndex;
}
DWORD ParseEndIf(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
{
DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
if (dwTopIndex == STENCIL_INVALIDINDEX)
{
dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALELSE);
if (dwTopIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_UNOPENEDBLOCK_ENDIF, szTokenStart);
return dwTopIndex;
}
}
DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALEND);
if (dwIndex != STENCIL_INVALIDINDEX)
{
m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
}
return dwIndex;
}
DWORD ParseLocale(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
{
LPCSTR szstart = szTokenStart;
LPCSTR szend = szTokenEnd;
LCID locale = 0xFFFFFFFF;
FindTagArgs(szstart, szend, 6);
#ifndef ATL_NO_MLANG
if (isdigit(static_cast<unsigned char>(szstart[0])))
{
locale = (LCID) atoi(szstart);
}
else
{
HRESULT hr;
CComPtr<IMultiLanguage> pML;
hr = pML.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
if (FAILED(hr))
{
ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
AddError(IDS_STENCIL_MLANG_COCREATE, szTokenStart);
}
else
{
CStringW str(szstart, (int)((szend-szstart)+1));
#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
// use IMultiLanguage2 if possible
CComPtr<IMultiLanguage2> spML2;
hr = pML.QueryInterface(&spML2);
if (FAILED(hr) || !spML2.p)
hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
else
hr = spML2->GetLcidFromRfc1766(&locale, CComBSTR(str));
#else // __IMultiLanguage2_INTERFACE_DEFINED__
hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
#endif // __IMultiLanguage2_INTERFACE_DEFINED__
if (FAILED(hr))
{
AddError(IDS_STENCIL_MLANG_LCID, szTokenStart);
}
}
if (FAILED(hr))
locale = 0xFFFFFFFF;
}
#else
locale = (LCID) atoi(szstart);
#endif
if (m_bUseLocaleACP)
{
TCHAR szACP[7];
if (GetLocaleInfo(locale, LOCALE_IDEFAULTANSICODEPAGE, szACP, 7) != 0)
{
m_nCodePage = (WORD) _ttoi(szACP);
}
else
{
AddError(IDS_STENCIL_MLANG_GETLOCALE, szTokenStart);
}
}
DWORD dwCurrentTokenIndex = STENCIL_INVALIDINDEX;
if (locale != 0xFFFFFFFF)
dwCurrentTokenIndex = AddToken(NULL, NULL, STENCIL_LOCALE,
NULL, NULL, STENCIL_INVALIDOFFSET, STENCIL_INVALIDOFFSET, locale);
else
return STENCIL_INVALIDINDEX;
return dwCurrentTokenIndex;
}
DWORD ParseCodepage(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
{
LPCSTR szstart = szTokenStart;
LPCSTR szend = szTokenEnd;
WORD nCodePage = 0xFFFF;
FindTagArgs(szstart, szend, 8);
#ifndef ATL_NO_MLANG
if (isdigit(static_cast<unsigned char>(szstart[0])))
{
nCodePage = (WORD) atoi(szstart);
}
else
{
HRESULT hr;
CComPtr<IMultiLanguage> pML;
hr = pML.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
if (FAILED(hr))
{
ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
AddError(IDS_STENCIL_MLANG_COCREATE, szTokenStart);
}
else
{
CStringW str(szstart, (int)((szend-szstart)+1));
MIMECSETINFO info;
#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
// use IMultiLanguage2 if possible
CComPtr<IMultiLanguage2> spML2;
hr = pML.QueryInterface(&spML2);
if (FAILED(hr) || !spML2.p)
hr = pML->GetCharsetInfo(CComBSTR(str), &info);
else
hr = spML2->GetCharsetInfo(CComBSTR(str), &info);
#else // __IMultiLanguage2_INTERFACE_DEFINED__
hr = pML->GetCharsetInfo(CComBSTR(str), &info);
#endif // __IMultiLanguage2_INTERFACE_DEFINED__
// for most character sets, uiCodePage and uiInternetEncoding
// are the same. UTF-8 is the exception that we're concerned about.
// for that character set, we want uiInternetEncoding (65001 - UTF-8)
// instead of uiCodePage (1200 - UCS-2)
if (SUCCEEDED(hr))
{
nCodePage = (WORD) info.uiInternetEncoding;
}
else
{
AddError(IDS_STENCIL_MLANG_GETCHARSET, szTokenStart);
}
}
if (FAILED(hr))
nCodePage = 0xFFFF;
}
#else
nCodePage = (WORD) atoi(szstart);
#endif
if (nCodePage != 0xFFFF)
m_nCodePage = nCodePage;
m_bUseLocaleACP = FALSE;
return STENCIL_INVALIDINDEX;
}
PARSE_TOKEN_RESULT ParseHandler(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
{
LPCSTR szstart = szTokenStart;
LPCSTR szend = szTokenEnd;
if (m_szHandlerName[0] && m_szDllPath[0])
return RESERVED_TOKEN; // already found the handler and path dll names
FindTagArgs(szstart, szend, 7);
size_t nlen = (szend-szstart)+1;
char szHandlerDllName[MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1];
if (nlen < MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1)
{
Checked::memcpy_s(szHandlerDllName, MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1, szstart, szend-szstart+1);
szHandlerDllName[szend-szstart+1] = '\0';
DWORD dwDllPathLen = MAX_PATH;
DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
if (!_AtlCrackHandler(szHandlerDllName, m_szDllPath, &dwDllPathLen, m_szHandlerName, &dwHandlerNameLen))
{
AddError(IDS_STENCIL_INVALID_HANDLER, szTokenStart);
return INVALID_TOKEN;
}
}
return RESERVED_TOKEN;
}
virtual PARSE_TOKEN_RESULT ParseToken(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop)
{
LPCSTR pStart = szTokenStart;
pStart += 2; //skip curlies
pStart = SkipSpace(pStart, m_nCodePage);
DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
DWORD dwIndex = STENCIL_INVALIDINDEX;
PARSE_TOKEN_RESULT ret = RESERVED_TOKEN;
if (CheckTag("endwhile", 8, pStart, dwLen))
dwIndex = ParseEndWhile(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
else if (CheckTag("while", 5, pStart, dwLen))
dwIndex = ParseWhile(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
else if (CheckTag("endif", 5, pStart, dwLen))
dwIndex = ParseEndIf(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
else if (CheckTag("else", 4, pStart, dwLen))
dwIndex = ParseElse(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
else if (CheckTag("if", 2, pStart, dwLen))
dwIndex = ParseIf(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
else if (CheckTag("locale", 6, pStart, dwLen))
dwIndex = ParseLocale(szTokenStart, szTokenEnd);
else if (CheckTag("handler", 7, pStart, dwLen))
{
return ParseHandler(szTokenStart, szTokenEnd);
}
else if (CheckTag("codepage", 8, pStart, dwLen))
{
ParseCodepage(szTokenStart, szTokenEnd);
return RESERVED_TOKEN;
}
else
{
dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_REPLACEMENT);
if (dwIndex == STENCIL_INVALIDINDEX)
return INVALID_TOKEN;
ret = NORMAL_TOKEN;
}
if (dwIndex == STENCIL_INVALIDINDEX)
return INVALID_TOKEN;
return ret;
}
virtual bool ParseReplacementsFromBuffer(ITagReplacer* pReplacer, LPCSTR pStart, LPCSTR pEnd)
{
LPCSTR szCurr = pStart;
DWORD BlockStack[ATL_MAX_BLOCK_STACK];
DWORD dwTop = 0;
m_pReplacer = pReplacer;
DWORD dwCurrentTokenIndex = 0;
if (!szCurr)
{
ATLASSERT(FALSE);
AddError(IDS_STENCIL_NULLPARAM, NULL);
return false;
}
LPCSTR szEnd = pEnd;
if (szEnd <= szCurr)
{
ATLASSERT(FALSE);
AddError(IDS_STENCIL_INVALIDSTRING, NULL);
return true;
}
while(szCurr < szEnd)
{
//mark the start of this block, then find the end of the block
//the end is denoted by an opening curly
LPCSTR szStart = szCurr;
while (szCurr < szEnd && (*szCurr != '{' || szCurr[1] != '{'))
{
LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
if (szNext == szCurr)
{
// embedded null
AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
return true;
}
szCurr = szNext;
}
//special case for the last text block, if there is one
if (szCurr >= szEnd)
{
// add the last token. This is everything after the last
// double curly block, which is text.
dwCurrentTokenIndex = AddToken(szStart, szEnd-1, STENCIL_TEXTTAG);
break;
}
//if there are any characters between szStart and szCurr inclusive,
//copy them to a text token.
if (szCurr-1 >= szStart)
dwCurrentTokenIndex = AddToken(szStart, szCurr-1, STENCIL_TEXTTAG);
if (dwCurrentTokenIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
return false;
}
//find the end of the tag
LPSTR szEndTag;
szStart = szCurr;
szCurr += 2; // Skip over the two '{' s
while (szCurr < szEnd)
{
if (szCurr[0] == '}' && szCurr[1] == '}')
break;
else if (szCurr[0] == '{')
break;
LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
if (szNext == szCurr)
{
// embedded null
AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
return true;
}
szCurr = szNext;
}
if (szCurr >= szEnd)
{
AddError(IDS_STENCIL_UNMATCHED_TAG_START, szStart);
if (AddToken(szStart, szCurr-1, STENCIL_TEXTTAG) == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
return false;
}
break;
}
if (szCurr[0] == '{')
{
if (szCurr[1] != '{')
{
szCurr--;
}
AddError(IDS_STENCIL_MISMATCHED_TAG_START, szStart);
if (AddToken(szStart, szCurr-1, STENCIL_TEXTTAG) == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
return false;
}
continue;
}
szEndTag = CharNextExA(m_nCodePage, szCurr, 0);
if (szEndTag == szCurr)
{
// embedded null
AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
return true;
}
PARSE_TOKEN_RESULT ret = ParseToken(szStart, szEndTag, BlockStack, &dwTop);
if (ret == INVALID_TOKEN)
{
dwCurrentTokenIndex = AddToken(szStart, szEndTag, STENCIL_TEXTTAG);
if (dwCurrentTokenIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
return false;
}
szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
continue;
}
szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
if (szEndTag == szCurr)
{
// embedded null
AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
return true;
}
if (ret == RESERVED_TOKEN)
{
if (szCurr < szEnd && *szCurr == '\n')
szCurr++;
else if ((szCurr+1 < szEnd && *szCurr == '\r' && *(szCurr+1) == '\n'))
szCurr += 2;
}
}
return true;
}
HTTP_CODE GetHandlerAndMethodNames(
__in LPCSTR pStart,
__in LPCSTR pEnd,
__out_ecount_z(nMethodNameLen) LPSTR pszMethodName,
__in size_t nMethodNameLen,
__out_ecount_z(nHandlerNameLen) LPSTR pszHandlerName,
__in size_t nHandlerNameLen) throw()
{
ATLASSERT(pStart);
ATLASSERT(pEnd);
ATLASSERT(pEnd > pStart);
if (!pszMethodName || !pszHandlerName || nMethodNameLen < 1 || nHandlerNameLen < 1)
{
ATLASSERT(FALSE);
AddError(IDS_STENCIL_BAD_PARAMETER, pStart);
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
}
*pszMethodName = '\0';
*pszHandlerName = '\0';
CHAR szMethodString[ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1];
HTTP_CODE hcErr = HTTP_SUCCESS;
//
// copy the method string
//
size_t nMethodLen = (pEnd-pStart)+1;
if (nMethodLen >= (ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1))
{
AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart);
return AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
}
Checked::memcpy_s(szMethodString, ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1, pStart, nMethodLen);
szMethodString[nMethodLen] = '\0';
//
// now crack the method string and get the handler
// id and function name
//
LPSTR szParen = strchr(szMethodString, '(');
LPSTR szDot = strchr(szMethodString, '.');
if (szDot && (!szParen || (szDot < szParen)))
{
*szDot = '\0';
szDot++;
// copy method name
if (strlen(szDot) < nMethodNameLen)
Checked::strcpy_s(pszMethodName, nMethodNameLen, szDot);
else
{
AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart + (szDot - szMethodString));
hcErr = AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
}
// copy handler name
if (!hcErr)
{
if (strlen(szMethodString) < nHandlerNameLen)
Checked::strcpy_s(pszHandlerName, nHandlerNameLen, szMethodString);
else
{
AddError(IDS_STENCIL_HANDLERNAME_TOO_LONG, pStart);
hcErr = AtlsHttpError(500, ISE_SUBERR_LONGHANDLERNAME);
}
}
}
else
{
// only a method name so just copy it.
if (strlen(szMethodString) < nMethodNameLen)
Checked::strcpy_s(pszMethodName, nMethodNameLen, szMethodString);
else
{
AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart);
hcErr = AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
}
}
return hcErr;
}
virtual HTTP_CODE Render(
ITagReplacer *pReplacer,
IWriteStream *pWriteStream,
CStencilState* pState = NULL) const throw(...)
{
ATLENSURE(pReplacer != NULL);
ATLENSURE(pWriteStream != NULL);
HTTP_CODE hcErrorCode = HTTP_SUCCESS;
DWORD dwIndex = 0;
DWORD dwArraySize = GetTokenCount();
// set up locale info
CSaveThreadLocale lcidSave;
if (pState)
{
dwIndex = pState->dwIndex;
// restore the locale if we're restarting rendering
if (pState->locale != CP_ACP)
SetThreadLocale(pState->locale);
}
pReplacer->SetStream(pWriteStream);
while (dwIndex < dwArraySize)
{
// RenderToken advances dwIndex appropriately for us.
dwIndex = RenderToken(dwIndex, pReplacer, pWriteStream, &hcErrorCode, pState);
if (dwIndex == STENCIL_INVALIDINDEX ||
hcErrorCode != HTTP_SUCCESS)
break;
}
if (IsAsyncStatus(hcErrorCode))
{
ATLASSERT( pState != NULL ); // state is required for async
if (pState)
pState->dwIndex = dwIndex;
}
// lcidSave destructor will restore the locale info in case it was changed
return hcErrorCode;
}
inline BOOL IsValidIndex(DWORD dwIndex) const throw()
{
if (dwIndex == STENCIL_INVALIDINDEX)
return FALSE;
if (dwIndex < GetTokenCount())
return TRUE;
else
return FALSE;
}
virtual DWORD RenderToken(
DWORD dwIndex,
ITagReplacer *pReplacer,
IWriteStream *pWriteStream,
HTTP_CODE *phcErrorCode,
CStencilState* pState = NULL) const throw(...)
{
ATLENSURE(pReplacer != NULL);
ATLENSURE(pWriteStream != NULL);
const StencilToken* pToken = GetToken(dwIndex);
DWORD dwNextToken = 0;
HTTP_CODE hcErrorCode = HTTP_SUCCESS;
if (!pToken)
return STENCIL_INVALIDINDEX;
switch (pToken->type)
{
case STENCIL_TEXTTAG:
{
pWriteStream->WriteStream(pToken->pStart,
(int)((pToken->pEnd-pToken->pStart)+1), NULL);
dwNextToken = dwIndex+1;
}
break;
case STENCIL_ITERATORSTART:
{
HTTP_CODE hcErr = STENCIL_SUCCESS;
#ifdef ATL_DEBUG_STENCILS
// A 'while' token has to at least be followed by an endwhile!
if (!IsValidIndex(dwIndex+1))
{
// This should have been caught at parse time
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDINDEX);
ATLASSERT(FALSE);
break;
}
// End of loop should be valid
if (!IsValidIndex(pToken->dwLoopIndex))
{
// This should have been caught at parse time
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHWHILE);
ATLASSERT(FALSE);
break;
}
if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
{
// This should have been caught at parse time
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
ATLASSERT(FALSE);
break;
}
#endif // ATL_DEBUG_STENCILS
DWORD dwLoopIndex = pToken->dwLoopIndex; // points to the end of the loop
// Call the replacement method
// if it returns HTTP_SUCCESS, enter the loop
// if it returns HTTP_S_FALSE, terminate the loop
hcErr = pReplacer->RenderReplacement(pToken->dwFnOffset,
pToken->dwObjOffset, pToken->dwMap, (void *) pToken->dwData);
if (hcErr == HTTP_SUCCESS)
{
dwNextToken = dwIndex+1;
hcErrorCode = HTTP_SUCCESS;
}
else if (hcErr == HTTP_S_FALSE)
{
dwNextToken = dwLoopIndex+1;
hcErrorCode = HTTP_SUCCESS;
}
else
{
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = hcErr;
break;
}
}
break;
case STENCIL_REPLACEMENT:
{
#ifdef ATL_DEBUG_STENCILS
if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
{
// This should have been caught at parse time
ATLASSERT(FALSE);
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
break;
}
#endif // ATL_DEBUG_STENCILS
hcErrorCode = pReplacer->RenderReplacement(pToken->dwFnOffset,
pToken->dwObjOffset, pToken->dwMap, (void *)pToken->dwData);
if (IsAsyncContinueStatus(hcErrorCode))
dwNextToken = dwIndex; // call the tag again after we get back
else
{
dwNextToken = dwIndex + 1;
// when returned from a handler, these indicate that the handler is done
// and that we should move on to the next handler when called back
if (hcErrorCode == HTTP_SUCCESS_ASYNC_DONE)
hcErrorCode = HTTP_SUCCESS_ASYNC;
else if (hcErrorCode == HTTP_SUCCESS_ASYNC_NOFLUSH_DONE)
hcErrorCode = HTTP_SUCCESS_ASYNC_NOFLUSH;
}
}
break;
case STENCIL_ITERATOREND:
{
dwNextToken = pToken->dwLoopIndex;
hcErrorCode = HTTP_SUCCESS;
ATLASSERT(GetToken(dwNextToken)->type == STENCIL_ITERATORSTART);
}
break;
case STENCIL_CONDITIONALSTART:
{
#ifdef ATL_DEBUG_STENCILS
if (pToken->type == STENCIL_CONDITIONALSTART && pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
{
// This should have been caught at parse time
ATLASSERT(FALSE);
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
break;
}
if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
{
// This should have been caught at parse time
ATLASSERT(FALSE);
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHIF);
break;
}
#endif // ATL_DEBUG_STENCILS
DWORD dwLoopIndex = pToken->dwLoopIndex; // points to the end of the loop
HTTP_CODE hcErr;
// Call the replacement method.
// If it returns HTTP_SUCCESS, we render everything up to
// the end of the conditional.
// if it returns HTTP_S_FALSE, the condition is not met and we
// render the else part if it exists or jump past the endif otherwise
hcErr = pReplacer->RenderReplacement(pToken->dwFnOffset,
pToken->dwObjOffset, pToken->dwMap, (void *)pToken->dwData);
if (hcErr == HTTP_SUCCESS)
{
dwNextToken = dwIndex+1;
hcErrorCode = HTTP_SUCCESS;
}
else if (hcErr == HTTP_S_FALSE)
{
dwNextToken = dwLoopIndex+1;
hcErrorCode = HTTP_SUCCESS;
}
else
{
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = hcErr;
break;
}
}
break;
case STENCIL_CONDITIONALELSE:
{
#ifdef ATL_DEBUG_STENCILS
if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
{
// This should have been caught at parse time
ATLASSERT(FALSE);
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHIF);
break;
}
#endif // ATL_DEBUG_STENCILS
dwNextToken = pToken->dwLoopIndex+1;
hcErrorCode = HTTP_SUCCESS;
}
break;
case STENCIL_CONDITIONALEND:
{
dwNextToken = dwIndex+1;
hcErrorCode = HTTP_SUCCESS;
}
break;
case STENCIL_LOCALE:
{
if (pState)
{
pState->locale = (LCID) pToken->dwData;
}
SetThreadLocale((LCID) pToken->dwData);
dwNextToken = dwIndex + 1;
}
break;
default:
{
ATLASSERT(FALSE);
dwNextToken = STENCIL_INVALIDINDEX;
hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_UNEXPECTEDTYPE);
break;
}
}
ATLASSERT(dwNextToken != dwIndex || IsAsyncContinueStatus(hcErrorCode));
if (phcErrorCode)
*phcErrorCode = hcErrorCode;
return dwNextToken;
}
DWORD GetTokenCount() const throw()
{
return (DWORD) m_arrTokens.GetCount();
}
const StencilToken* GetToken(DWORD dwIndex) const throw()
{
return &(m_arrTokens[dwIndex]);
}
StencilToken* GetToken(DWORD dwIndex) throw()
{
return &(m_arrTokens[dwIndex]);
}
LPCSTR GetBufferStart() const throw()
{
return m_pBufferStart;
}
LPCSTR GetBufferEnd() const throw()
{
return m_pBufferEnd;
}
WORD GetCodePage() const throw()
{
return m_nCodePage;
}
// IMemoryCacheClient
STDMETHOD(QueryInterface)(REFIID riid, void **ppv)
{
if (!ppv)
return E_POINTER;
if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
InlineIsEqualGUID(riid, __uuidof(IMemoryCacheClient)))
{
*ppv = static_cast<IMemoryCacheClient*>(this);
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHOD_(ULONG, AddRef)()
{
return 1;
}
STDMETHOD_(ULONG, Release)()
{
return 1;
}
STDMETHOD(Free)(const void *pData)
{
if (!pData)
return E_POINTER;
ATLASSERT(*((void **) pData) == static_cast<void*>(this));
delete this;
return S_OK;
}
}; // class CStencil
struct StencilIncludeInfo
{
public:
CHAR m_szQueryString[ATL_URL_MAX_URL_LENGTH+1];
CHAR m_szFileName[MAX_PATH];
};
class CIncludeServerContext :
public CComObjectRootEx<CComMultiThreadModel>,
public CWrappedServerContext
{
public:
BEGIN_COM_MAP(CIncludeServerContext)
COM_INTERFACE_ENTRY(IHttpServerContext)
END_COM_MAP()
IWriteStream * m_pStream;
const StencilIncludeInfo * m_pIncludeInfo;
CIncludeServerContext() throw()
{
m_pStream = NULL;
m_pIncludeInfo = NULL;
}
void Initialize(
IWriteStream *pStream,
IHttpServerContext* pServerContext,
const StencilIncludeInfo * pIncludeInfo) throw()
{
ATLASSERT(pStream != NULL);
ATLASSERT(pServerContext != NULL);
ATLASSERT(pIncludeInfo != NULL);
m_pStream = pStream;
m_spParent = pServerContext;
m_pIncludeInfo = pIncludeInfo;
}
void Initialize(CIncludeServerContext *pOtherContext)
{
ATLENSURE(pOtherContext != NULL);
m_pStream = pOtherContext->m_pStream;
m_spParent = pOtherContext->m_spParent;
m_pIncludeInfo = pOtherContext->m_pIncludeInfo;
}
LPCSTR GetRequestMethod()
{
return "GET";
}
LPCSTR GetQueryString()
{
ATLASSUME(m_pIncludeInfo != NULL);
return m_pIncludeInfo->m_szQueryString;
}
LPCSTR GetPathTranslated()
{
ATLASSUME(m_pIncludeInfo != NULL);
return m_pIncludeInfo->m_szFileName;
}
LPCSTR GetScriptPathTranslated()
{
ATLASSUME(m_pIncludeInfo != NULL);
return m_pIncludeInfo->m_szFileName;
}
DWORD GetTotalBytes()
{
return 0;
}
DWORD GetAvailableBytes()
{
return 0;
}
BYTE *GetAvailableData()
{
return NULL;
}
LPCSTR GetContentType()
{
return 0;
}
BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes)
{
ATLASSUME(m_pStream != NULL);
ATLENSURE(pvBuffer != NULL);
ATLENSURE(pdwBytes != NULL);
HRESULT hr = S_OK;
_ATLTRY
{
hr = m_pStream->WriteStream((LPCSTR) pvBuffer, *pdwBytes, pdwBytes);
}
_ATLCATCHALL()
{
hr = E_FAIL;
}
return SUCCEEDED(hr);
}
BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
{
return FALSE;
}
BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
{
return FALSE;
}
BOOL SendRedirectResponse(LPCSTR /*pszRedirectURL*/)
{
return FALSE;
}
BOOL SendResponseHeader(
LPCSTR /*pszHeader*/,
LPCSTR /*pszStatusCode*/,
BOOL /*fKeepConn*/)
{
return TRUE;
}
BOOL DoneWithSession(DWORD /*dwHttpStatusCode*/)
{
return TRUE;
}
BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/)
{
return FALSE;
}
}; // class CIncludeServerContext
class CIDServerContext :
public CComObjectRootEx<CComMultiThreadModel>,
public CWrappedServerContext
{
public:
CHttpResponse *m_pResponse;
CHttpRequest *m_pRequest;
BEGIN_COM_MAP(CIDServerContext)
COM_INTERFACE_ENTRY(IHttpServerContext)
END_COM_MAP()
CIDServerContext() throw()
: m_pResponse(NULL), m_pRequest(NULL)
{
}
BOOL Initialize(
CHttpResponse *pResponse,
CHttpRequest *pRequest) throw()
{
ATLASSERT(pResponse != NULL);
ATLASSERT(pRequest != NULL);
m_pResponse = pResponse;
m_pRequest = pRequest;
if(!m_pRequest)
{
return FALSE;
}
HRESULT hr = m_pRequest->GetServerContext(&m_spParent);
return (SUCCEEDED(hr));
}
LPCSTR GetRequestMethod()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetMethodString();
}
LPCSTR GetQueryString()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetQueryString();
}
LPCSTR GetPathInfo()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetPathInfo();
}
LPCSTR GetPathTranslated()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetPathTranslated();
}
DWORD GetTotalBytes()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetTotalBytes();
}
DWORD GetAvailableBytes()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetAvailableBytes();
}
BYTE *GetAvailableData()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetAvailableData();
}
LPCSTR GetContentType()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetContentType();
}
LPCSTR GetScriptPathTranslated()
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->GetScriptPathTranslated();
}
BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes)
{
ATLASSUME(m_pResponse != NULL);
return m_pResponse->WriteLen((LPCSTR)pvBuffer, *pdwBytes);
}
BOOL ReadClient(void *pvBuffer, DWORD *pdwSize)
{
ATLASSUME(m_pRequest != NULL);
return m_pRequest->ReadData((LPSTR)pvBuffer, pdwSize);
}
BOOL SendRedirectResponse(LPCSTR pszRedirectURL)
{
ATLASSUME(m_pResponse != NULL);
return m_pResponse->Redirect(pszRedirectURL);
}
BOOL TransmitFile(
HANDLE hFile,
PFN_HSE_IO_COMPLETION pfn,
void *pContext,
LPCSTR szStatusCode,
DWORD dwBytesToWrite,
DWORD dwOffset,
void *pvHead,
DWORD dwHeadLen,
void *pvTail,
DWORD dwTailLen,
DWORD dwFlags)
{
ATLASSUME(m_pResponse != NULL);
ATLASSUME(m_spParent != NULL);
m_pResponse->Flush();
return m_spParent->TransmitFile(hFile, pfn, pContext, szStatusCode,
dwBytesToWrite, dwOffset, pvHead, dwHeadLen, pvTail, dwTailLen, dwFlags);
}
}; // class CIDServerContext
//
// CHtmlStencil
// CHtmlStencil is a specialization of CStencil. CHtmlStencil adds the following
// capabilities to CStencil:
//
// Support for rendering {{include }} tags
// The {{include }} tags specify another stencil to be included in-place during
// stencil rendering. The {{include }} tag takes a single parameter which is the
// URL of the stencil to include. That URL can optionally include parameters.
// An example:
// {{include mystencil.srf?param1=value1}}
//
// We also grab the handler name and the name of any subhandlers. The syntax for the
// handler specification is:
// {{handler MyDynamicHandler.dll/Default}}
// which would cause the MyDynamicHandler.dll to be loaded. Once loaded, the stencil
// processor will ask for the IReplacementHandler interface of the object named "Default".
//
// Additional handlers can be specified after the default handler. An example of an
// additional handler would be:
// {{subhandler OtherHandler MyOtherHandler.dll/Default}}
// would cause the MyOtherHandler.dll to be loaded. Once loaded, the stencil processor will
// ask for the IReplacementHandler interface of the object named "Default" and use it in
// processing the stencil anywhere it sees a stencil tag of the form
// {{OtherHandler.RenderReplacement}}
struct CStringPair
{
typedef CFixedStringT<CStringA, MAX_PATH> PathStrType;
typedef CFixedStringT<CStringA, ATL_MAX_HANDLER_NAME_LEN+1> HdlrNameStrType;
PathStrType strDllPath;
HdlrNameStrType strHandlerName;
CStringPair()throw()
{
}
CStringPair(PathStrType &strDllPath_, HdlrNameStrType &strHandlerName_) throw(...)
:strDllPath(strDllPath_), strHandlerName(strHandlerName_)
{
}
CStringPair(CStringA &strDllPath_, CStringA &strHandlerName_) throw(...)
:strDllPath(strDllPath_), strHandlerName(strHandlerName_)
{
}
};
class CStringPairElementTraits :
public CElementTraitsBase< CStringPair >
{
private:
static ULONG HashStr( ULONG nHash, CStringElementTraits<CStringA>::INARGTYPE str )
{
ATLENSURE( str != NULL );
const CStringA::XCHAR* pch = str;
while( *pch != 0 )
{
nHash = (nHash<<5)+nHash+(*pch);
pch++;
}
return( nHash );
}
public:
static ULONG Hash( INARGTYPE pair ) throw()
{
ULONG nHash = HashStr(0, pair.strDllPath);
return HashStr(nHash, pair.strHandlerName);
}
static bool CompareElements( INARGTYPE pair1, INARGTYPE pair2 ) throw()
{
return( (pair1.strDllPath == pair2.strDllPath) && (pair1.strHandlerName == pair2.strHandlerName) );
}
static int CompareElementsOrdered( INARGTYPE pair1, INARGTYPE pair2 ) throw()
{
return( pair1.strDllPath.Compare( pair2.strDllPath ) );
}
};
class CHtmlStencil : public CStencil
{
private:
ATL_NOINLINE HTTP_CODE RenderInclude(
ITagReplacer *pReplacer,
const StencilToken *pToken,
IWriteStream *pWriteStream,
CStencilState *pState) const
{
ATLASSUME(m_spServiceProvider);
CComPtr<IHttpServerContext> spServerContext;
CComPtr<IHttpRequestLookup> spLookup;
if (FAILED(pReplacer->GetContext(__uuidof(IHttpServerContext), (VOID**) &spServerContext)))
{
return AtlsHttpError(500, 0);
}
if (FAILED(pReplacer->GetContext(__uuidof(IHttpRequestLookup), (VOID**) &spLookup)))
{
return AtlsHttpError(500, 0);
}
return RenderInclude(m_spServiceProvider, pWriteStream,
(StencilIncludeInfo *)pToken->dwData, spServerContext, spLookup,
pState);
}
ATL_NOINLINE HTTP_CODE NoCachePage(ITagReplacer *pReplacer) const
{
CComPtr<IHttpServerContext> spContext;
HRESULT hr = pReplacer->GetContext(__uuidof(IHttpServerContext), (void **)&spContext);
if (hr == S_OK && spContext)
{
CComQIPtr<IPageCacheControl> spControl;
spControl = spContext;
if (spControl)
spControl->Cache(FALSE);
}
return HTTP_SUCCESS;
}
// CAllocIncludeAsyncContext is an unsupported implementation detail of RenderInclude
class CAllocIncludeAsyncContext :
public CAllocContextBase
{
public:
CAllocIncludeAsyncContext(CIncludeServerContext *pBase) :
m_pBase(pBase)
{
}
HTTP_CODE Alloc(IHttpServerContext **ppNewContext)
{
ATLASSUME(m_pBase);
if (!ppNewContext)
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
*ppNewContext = NULL;
CComObjectNoLock<CIncludeServerContext>* pNewServerContext = NULL;
ATLTRY(pNewServerContext = new CComObjectNoLock<CIncludeServerContext>);
if (pNewServerContext == NULL)
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
pNewServerContext->Initialize(m_pBase);
pNewServerContext->AddRef();
*ppNewContext = pNewServerContext;
return HTTP_SUCCESS;
}
private:
CIncludeServerContext *m_pBase;
}; // CAllocIncludeAsyncContext
ATL_NOINLINE HTTP_CODE RenderInclude(
IServiceProvider *pServiceProvider,
IWriteStream *pWriteStream,
const StencilIncludeInfo *pIncludeInfo,
IHttpServerContext *pServerContext,
IHttpRequestLookup *pLookup,
CStencilState* pState = NULL) const throw(...)
{
CComObjectStackEx<CIncludeServerContext> serverContext;
serverContext.Initialize(pWriteStream, pServerContext, pIncludeInfo);
CAllocIncludeAsyncContext AsyncAllocObj(&serverContext);
return _AtlRenderInclude(static_cast<IHttpServerContext*>(&serverContext),
pIncludeInfo->m_szFileName,
pIncludeInfo->m_szQueryString,
GetCodePage(),
&AsyncAllocObj,
pServiceProvider,
pLookup,
pState);
}
protected:
CAtlMap<CStringA, CStringPair,
CStringElementTraits<CStringA>, CStringPairElementTraits > m_arrExtraHandlers;
CHAR m_szBaseDir[MAX_PATH];
CComPtr<IServiceProvider> m_spServiceProvider;
CComPtr<IIsapiExtension> m_spExtension;
CComPtr<IStencilCache> m_spStencilCache;
CComPtr<IDllCache> m_spDllCache;
public:
typedef CAtlMap<CStringA, CStringPair,
CStringElementTraits<CStringA>, CStringPairElementTraits > mapType;
typedef CStencil baseType;
CHtmlStencil(IAtlMemMgr *pMemMgr=NULL) throw() :
CStencil(pMemMgr)
{
}
void Initialize(IServiceProvider *pProvider) throw(...)
{
ATLENSURE(pProvider);
if (m_spServiceProvider)
m_spServiceProvider.Release();
m_spServiceProvider = pProvider;
if (!m_spDllCache)
pProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void **) &m_spDllCache);
if (!m_spExtension)
pProvider->QueryInterface(__uuidof(IIsapiExtension), (void **) &m_spExtension);
}
BOOL GetIncludeInfo(LPCSTR szParamBegin, LPCSTR szParamEnd, StencilIncludeInfo *pInfo) const
{
ATLENSURE(szParamBegin != NULL);
ATLENSURE(szParamEnd != NULL);
ATLENSURE(pInfo != NULL);
LPCSTR szQueryBegin = szParamBegin;
while (*szQueryBegin && *szQueryBegin != '?' && *szQueryBegin != '}')
{
LPSTR szNext = CharNextExA(GetCodePage(), szQueryBegin, 0);
if (szNext == szQueryBegin)
{
return FALSE;
}
szQueryBegin = szNext;
}
CFixedStringT<CStringA, MAX_PATH> strPath;
_ATLTRY
{
DWORD dwPrefixLen = 0;
if (*szParamBegin == '"')
{
szParamBegin++;
}
if (!IsFullPathA(szParamBegin))
{
if (*szParamBegin != '\\')
{
strPath = m_szBaseDir;
}
else
{
LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
if (szBackslash)
{
#pragma warning(push)
#pragma warning(disable: 6204)
/* prefast noise VSW 492749 */
strPath.SetString(m_szBaseDir, (int)(szBackslash-m_szBaseDir));
#pragma warning(pop)
}
else
{
strPath = m_szBaseDir;
}
}
dwPrefixLen = strPath.GetLength();
}
if (*szQueryBegin=='?')
{
size_t nMinus = (*(szQueryBegin-1) == '"') ? 1 : 0;
strPath.Append(szParamBegin, (int)(szQueryBegin-szParamBegin-nMinus));
if ((szParamEnd-szQueryBegin) > ATL_URL_MAX_PATH_LENGTH || ((szParamEnd - szQueryBegin) < 0))
{
// query string is too long
return FALSE;
}
Checked::memcpy_s(pInfo->m_szQueryString, ATL_URL_MAX_URL_LENGTH+1, szQueryBegin + 1, szParamEnd - szQueryBegin);
pInfo->m_szQueryString[szParamEnd - szQueryBegin] = '\0';
}
else
{
pInfo->m_szQueryString[0] = '\0';
size_t nAdd = (*szParamEnd == '"') ? 0 : 1;
strPath.Append(szParamBegin, (int)(szParamEnd - szParamBegin + nAdd));
}
}
_ATLCATCHALL()
{
// out of memory
return FALSE;
}
if (strPath.GetLength() > MAX_PATH-1)
{
// path is too long
return FALSE;
}
// strPath is <= MAX_PATH-1
return PathCanonicalizeA(pInfo->m_szFileName, strPath);
}
DWORD ParseInclude(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw(...)
{
ATLENSURE(szTokenStart != NULL);
ATLENSURE(szTokenEnd != NULL);
LPCSTR szStart = szTokenStart;
LPCSTR szEnd = szTokenEnd;
FindTagArgs(szStart, szEnd, 7);
CFixedStringT<CStringA, MAX_PATH> strFileNameRelative;
CFixedStringT<CString, MAX_PATH> strFileName;
_ATLTRY
{
strFileNameRelative.SetString(szStart, (int)(szEnd-szStart + 1));
if (!IsFullPathA(strFileNameRelative))
{
CFixedStringT<CStringA, MAX_PATH> strTemp;
if (*((LPCSTR)strFileNameRelative) != '\\')
{
strTemp = m_szBaseDir;
}
else
{
LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
if (szBackslash)
{
#pragma warning(push)
#pragma warning(disable: 6204)
#pragma warning(disable: 6535)
/* prefast noise VSW 492749 */
/* prefast noise VSW 493256 */
strTemp.SetString(m_szBaseDir, (int)(szBackslash-m_szBaseDir));
#pragma warning(pop)
}
else
{
strTemp = m_szBaseDir;
}
}
strTemp.Append(strFileNameRelative, strFileNameRelative.GetLength());
CFixedStringT<CString, MAX_PATH> strConv = (LPCTSTR) CA2CT(strTemp);
LPTSTR szFileBuf = strFileName.GetBuffer(strConv.GetLength()+1);
if (szFileBuf == NULL)
{
AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
if (!PathCanonicalize(szFileBuf, strConv))
{
return STENCIL_INVALIDINDEX;
}
strFileName.ReleaseBuffer();
}
else
{
strFileName = CA2CTEX<MAX_PATH>(strFileNameRelative);
}
}
_ATLCATCHALL()
{
AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
LPCTSTR szFileName = strFileName;
LPCTSTR szDot = NULL;
LPCTSTR szExtra = _tcschr(szFileName, '?');
if (!szExtra)
{
szExtra = _tcschr(szFileName, '#');
if (!szExtra)
{
szDot = _tcsrchr(szFileName, '.');
}
}
if (szExtra != NULL)
{
// there is some extra information
LPCTSTR szDotTmp = szFileName;
do
{
szDot = szDotTmp;
szDotTmp = _tcschr(szDotTmp+1, '.');
} while (szDotTmp && szDotTmp < szExtra);
}
if (!szDot || *szDot != '.')
{
AddError(IDS_STENCIL_UNEXPECTED, szTokenStart);
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
LPCTSTR szExtEnd = szDot;
while (true)
{
szExtEnd++;
if (!*szExtEnd || *szExtEnd == '/' || *szExtEnd == '\\' || *szExtEnd == '?' || *szExtEnd == '#' || *szExtEnd == '"')
break;
}
if (szDot && (size_t)(szExtEnd-szDot) == _tcslen(c_tAtlDLLExtension) &&
!_tcsnicmp(szDot, c_tAtlDLLExtension, _tcslen(c_tAtlDLLExtension)))
{
// Do .dll stuff
DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
if (dwIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
if (!pInfo)
{
return STENCIL_INVALIDINDEX;
}
if (!GetIncludeInfo(szStart, szEnd, pInfo))
{
return STENCIL_INVALIDINDEX;
}
GetToken(dwIndex)->dwData = (DWORD_PTR) pInfo;
return dwIndex;
}
else if (szDot && (size_t)(szExtEnd-szDot) == _tcslen(c_tAtlSRFExtension) &&
!_tcsnicmp(szDot, c_tAtlSRFExtension, _tcslen(c_tAtlSRFExtension)))
{
// Do .srf stuff
DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
if (dwIndex == STENCIL_INVALIDINDEX)
{
AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
if (!pInfo)
{
return STENCIL_INVALIDINDEX;
}
if (!GetIncludeInfo(szStart, szEnd, pInfo))
{
return STENCIL_INVALIDINDEX;
}
GetToken(dwIndex)->dwData = (DWORD_PTR) pInfo;
return dwIndex;
}
else
{
// Assume static content
CAtlFile file;
HRESULT hr = file.Create(szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
{
if (FAILED(hr))
{
AddError(IDS_STENCIL_INCLUDE_ERROR, szTokenStart);
}
else
{
AddError(IDS_STENCIL_INCLUDE_INVALID, szTokenStart);
}
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
CAutoVectorPtr<CHAR> szBufferStart;
LPSTR szBufferEnd = NULL;
ULONGLONG dwLen = 0;
if (FAILED(file.GetSize(dwLen)))
{
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
if (!szBufferStart.Allocate((size_t) dwLen))
{
AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
DWORD dwRead;
if (FAILED(file.Read(szBufferStart, (DWORD) dwLen, dwRead)))
{
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
}
szBufferEnd = szBufferStart + dwRead-1;
DWORD dwIndex = AddToken(szBufferStart, szBufferEnd, STENCIL_STATICINCLUDE);
if (dwIndex != STENCIL_INVALIDINDEX)
{
GetToken(dwIndex)->bDynamicAlloc = TRUE;
szBufferStart.Detach();
}
return dwIndex;
}
}
PARSE_TOKEN_RESULT ParseSubhandler(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
{
ATLASSERT(szTokenStart != NULL);
ATLASSERT(szTokenEnd != NULL);
LPCSTR szStart = szTokenStart;
LPCSTR szEnd = szTokenEnd;
// move to the start of the arguments
// (the first char past 'subhandler'
FindTagArgs(szStart, szEnd, 10);
// skip any space to bring us to the start
// of the id for the subhandler.
szStart = SkipSpace(szStart, GetCodePage());
// id names cannot contain spaces. Mark the
// beginning and end if the subhandler id
LPCSTR szIdStart = szStart;
while (!isspace(static_cast<unsigned char>(*szStart)) && *szStart != '}')
{
if (!isalnum(static_cast<unsigned char>(*szStart)))
{
// id names can only contain alphanumeric characters
return INVALID_TOKEN;
}
LPSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
if (szNext == szStart)
{
// embedded null
AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
return INVALID_TOKEN;
}
szStart = szNext;
}
LPCSTR szIdEnd = szStart;
// skip space to bring us to the beginning of the
// the dllpath/handlername
szStart = SkipSpace(szStart, GetCodePage());
// everything up to the end if the tag is
// part of the dllpath/handlername
LPCSTR szHandlerStart = szStart;
while (*szStart != '}')
{
LPCSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
if (szNext == szStart)
{
// embedded null
AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
return INVALID_TOKEN;
}
szStart = szNext;
}
LPCSTR szHandlerEnd = szStart;
_ATLTRY
{
CStringPair::HdlrNameStrType strName(szIdStart, (int)(szIdEnd-szIdStart));
CStringPair::PathStrType strPath(szHandlerStart, (int)(szHandlerEnd-szHandlerStart));
CStringPair::PathStrType strDllPath;
CStringPair::HdlrNameStrType strHandlerName;
DWORD dwDllPathLen = MAX_PATH;
DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
LPSTR szDllPath = strDllPath.GetBuffer(dwDllPathLen);
LPSTR szHandlerName = strHandlerName.GetBuffer(dwHandlerNameLen);
if (!_AtlCrackHandler(strPath, szDllPath, &dwDllPathLen, szHandlerName, &dwHandlerNameLen))
{
strDllPath.ReleaseBuffer();
strHandlerName.ReleaseBuffer();
AddError(IDS_STENCIL_INVALID_SUBHANDLER, szTokenStart);
return INVALID_TOKEN;
}
strDllPath.ReleaseBuffer(dwDllPathLen);
strHandlerName.ReleaseBuffer(dwHandlerNameLen);
m_arrExtraHandlers.SetAt(strName, CStringPair(strDllPath, strHandlerName));
}
_ATLCATCHALL()
{
AddError(IDS_STENCIL_OUTOFMEMORY, NULL);
return INVALID_TOKEN;
}
return RESERVED_TOKEN;
}
virtual PARSE_TOKEN_RESULT ParseToken(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop)
{
ATLASSERT(szTokenStart != NULL);
ATLASSERT(szTokenEnd != NULL);
LPCSTR pStart = szTokenStart;
pStart += 2; //skip curlies
pStart = SkipSpace(pStart, GetCodePage());
DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
DWORD dwIndex = STENCIL_INVALIDINDEX;
if (CheckTag("include", sizeof("include")-1, pStart, dwLen))
{
dwIndex = ParseInclude(szTokenStart, szTokenEnd);
}
else if (dwLen > 3 && !memcmp("!--", pStart, 3))
{
return RESERVED_TOKEN;
}
else if (dwLen > 2 && !memcmp("//", pStart, 2))
{
return RESERVED_TOKEN;
}
else if (CheckTag("subhandler", sizeof("subhandler")-1, pStart, dwLen))
{
return ParseSubhandler(szTokenStart, szTokenEnd);
}
else
{
return CStencil::ParseToken(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
}
if (dwIndex == STENCIL_INVALIDINDEX)
{
return INVALID_TOKEN;
}
return RESERVED_TOKEN;
}
mapType* GetExtraHandlers() throw()
{
return &m_arrExtraHandlers;
}
BOOL SetBaseDirFromFile(LPCSTR szBaseDir)
{
if (!SafeStringCopy(m_szBaseDir, szBaseDir))
{
return FALSE;
}
LPSTR szSlash = strrchr(m_szBaseDir, '\\');
if (szSlash)
{
szSlash++;
*szSlash = '\0';
}
else
{
*m_szBaseDir = '\0';
}
return TRUE;
}
LPCSTR GetBaseDir()
{
return m_szBaseDir;
}
DWORD RenderToken(
DWORD dwIndex,
ITagReplacer* pReplacer,
IWriteStream *pWriteStream,
HTTP_CODE *phcErrorCode,
CStencilState* pState = NULL) const throw(...)
{
DWORD dwNextToken = STENCIL_INVALIDINDEX;
HTTP_CODE hcErrorCode = HTTP_SUCCESS;
const StencilToken* pToken = GetToken(dwIndex);
if (pToken)
{
if (pToken->type == STENCIL_STENCILINCLUDE)
{
hcErrorCode = RenderInclude(pReplacer, pToken, pWriteStream, pState);
if (hcErrorCode == HTTP_SUCCESS || IsAsyncDoneStatus(hcErrorCode))
{
dwNextToken = dwIndex+1;
}
else if (IsAsyncContinueStatus(hcErrorCode))
{
dwNextToken = dwIndex;
}
}
else if (pToken->type == STENCIL_STATICINCLUDE)
{
pWriteStream->WriteStream(pToken->pStart,
(int)((pToken->pEnd-pToken->pStart)+1), NULL);
dwNextToken = dwIndex+1;
}
else
{
dwNextToken = baseType::RenderToken(dwIndex, pReplacer,
pWriteStream, &hcErrorCode, pState);
}
}
if (hcErrorCode == HTTP_SUCCESS_NO_CACHE)
{
hcErrorCode = NoCachePage(pReplacer);
}
if (phcErrorCode)
{
*phcErrorCode = hcErrorCode;
}
return dwNextToken;
}
}; // class CHtmlStencil
__declspec(selectany) CCRTHeap CStencil::m_crtHeap;
//
// CHtmlTagReplacer
// This class manages CStencil based objects for HTTP requests. This class will retrieve
// CStencil based objects from the stencil cache, store CStencil based objects in the
// stencil cache and allocate and initialize CStencil based objects on a per reqeust
// basis. Typically, one instance of this class is created for each HTTP request. The
// instance is destroyed once the request has been completed.
template <class THandler, class StencilType=CHtmlStencil>
class CHtmlTagReplacer :
public ITagReplacerImpl<THandler>
{
protected:
typedef StencilType StencilType;
CSimpleArray<HINSTANCE> m_hInstHandlers;
typedef CAtlMap<CStringA, IRequestHandler*, CStringElementTraits<CStringA> > mapType;
mapType m_Handlers;
StencilType *m_pLoadedStencil;
WORD m_nCodePage;
CComPtr<IStencilCache> m_spStencilCache;
AtlServerRequest m_RequestInfo;
public:
// public members
CHtmlTagReplacer() throw() :
m_pLoadedStencil(NULL)
{
memset(&m_RequestInfo, 0x00, sizeof(m_RequestInfo));
m_nCodePage = CP_THREAD_ACP;
}
~CHtmlTagReplacer() throw()
{
// you should call FreeHandlers before
// the object is destructed
ATLASSUME(m_hInstHandlers.GetSize() == 0);
}
HTTP_CODE Initialize(AtlServerRequest *pRequestInfo, IHttpServerContext *pSafeSrvCtx=NULL) throw(...)
{
ATLASSERT(pRequestInfo != NULL);
CComPtr<IServiceProvider> spServiceProvider;
THandler *pT = static_cast<THandler*>(this);
HRESULT hr = pT->GetContext(__uuidof(IServiceProvider), (void **)&spServiceProvider);
if (FAILED(hr))
return HTTP_FAIL;
spServiceProvider->QueryService(__uuidof(IStencilCache), __uuidof(IStencilCache), (void **) &m_spStencilCache);
if (!m_spStencilCache)
{
ATLASSERT(FALSE);
return HTTP_FAIL;
}
// copy the AtlServerRequest into the safe version
Checked::memcpy_s(&m_RequestInfo, sizeof(m_RequestInfo), pRequestInfo, sizeof(m_RequestInfo));
// override appropriate fields
m_RequestInfo.cbSize = sizeof(m_RequestInfo);
m_RequestInfo.pServerContext = pSafeSrvCtx;
return HTTP_SUCCESS;
}
HTTP_CODE LoadStencilResource(
HINSTANCE hInstResource,
LPCSTR szResourceID,
LPCSTR szResourceType = NULL, LPCSTR szStencilName=NULL) throw(...)
{
if (!szResourceType)
szResourceType = (LPCSTR) (RT_HTML);
// look up stencil in cache
HTTP_CODE hcErr = HTTP_SUCCESS;
// check the cache first
StencilType *pStencil = FindCacheStencil(szStencilName ? szStencilName : szResourceID);
if (!pStencil)
{
// create a new stencil
pStencil = GetNewCacheStencil();
if (!pStencil)
{
return AtlsHttpError(500,ISE_SUBERR_OUTOFMEM);
}
THandler *pT = static_cast<THandler*>(this);
LPCSTR szFileName = pT->m_spServerContext->GetScriptPathTranslated();
if (!szFileName)
return HTTP_FAIL;
if (!pStencil->SetBaseDirFromFile(szFileName))
{
return HTTP_FAIL;
}
pStencil->SetErrorResource(GetResourceInstance());
// load the stencil and parse its replacements
if (HTTP_SUCCESS == pStencil->LoadFromResource(hInstResource,
szResourceID, szResourceType))
{
_ATLTRY
{
if (!pStencil->ParseReplacements(this))
{
return AtlsHttpError(500, ISE_SUBERR_BADSRF);
}
hcErr = FinishLoadStencil(pStencil, NULL);
if (!hcErr)
{
#ifdef ATL_DEBUG_STENCILS
pStencil->FinishParseReplacements();
#else
if (!pStencil->FinishParseReplacements())
{
return AtlsHttpError(500, ISE_SUBERR_BADSRF);
}
#endif // ATL_DEBUG_STENCILS
}
}
_ATLCATCHALL()
{
return HTTP_FAIL;
}
}
else
{
hcErr = HTTP_FAIL;
}
// if everything went OK, put the stencil in the stencil cache.
if (!hcErr)
{
hcErr = CacheStencil(szStencilName ? szStencilName : szResourceID, pStencil);
}
if (pStencil && hcErr) // something went wrong, free the stencil data
{
FreeCacheStencil(pStencil);
}
}
else
{
hcErr = FinishLoadStencil(pStencil);
}
return hcErr;
}
HTTP_CODE LoadStencilResource(HINSTANCE hInstResource, UINT nID, LPCSTR szResourceType = NULL) throw(...)
{
if (!szResourceType)
szResourceType = (LPCSTR) RT_HTML;
char szName[80];
int nResult = sprintf_s(szName, sizeof(szName), "%p/%u", hInstResource, nID);
if ((nResult < 0) || (nResult == sizeof(szName)))
{
return HTTP_FAIL;
}
return LoadStencilResource(hInstResource, MAKEINTRESOURCEA(nID), szResourceType, szName);
}
HTTP_CODE LoadStencil(LPCSTR szFileName, IHttpRequestLookup * pLookup = NULL) throw(...)
{
if (!szFileName)
{
return HTTP_FAIL;
}
HTTP_CODE hcErr = HTTP_FAIL;
// try to find the stencil in the cache
StencilType *pStencil = FindCacheStencil(szFileName);
if (!pStencil)
{
// not in cache. Create a new one
pStencil = GetNewCacheStencil();
if (!pStencil)
{
return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // out of memory!
}
if (!pStencil->SetBaseDirFromFile(szFileName))
{
return HTTP_FAIL;
}
pStencil->SetErrorResource(GetResourceInstance());
// finish loading
hcErr = pStencil->LoadFromFile(szFileName);
if (!hcErr)
{
_ATLTRY
{
if (!pStencil->ParseReplacements(static_cast<ITagReplacer*>(this)))
{
return AtlsHttpError(500, ISE_SUBERR_BADSRF);
}
hcErr = FinishLoadStencil(pStencil, pLookup);
if (!hcErr)
{
#ifdef ATL_DEBUG_STENCILS
pStencil->FinishParseReplacements();
#else
if (!pStencil->FinishParseReplacements())
{
return AtlsHttpError(500, ISE_SUBERR_BADSRF);
}
#endif // ATL_DEBUG_STENCILS
}
}
_ATLCATCHALL()
{
return HTTP_FAIL;
}
}
// if everything is OK, cache the stencil
if (!hcErr)
{
hcErr = CacheStencil(szFileName, pStencil);
}
if (pStencil && hcErr) // something went wrong, free stencil data
FreeCacheStencil(pStencil);
}
else
{
hcErr = FinishLoadStencil(pStencil, pLookup);
}
return hcErr;
}
HTTP_CODE RenderStencil(IWriteStream* pStream, CStencilState* pState = NULL) throw(...)
{
if (!m_pLoadedStencil)
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
WORD nCodePage = m_pLoadedStencil->GetCodePage();
if (nCodePage != CP_ACP)
m_nCodePage = nCodePage;
HTTP_CODE hcErr = HTTP_FAIL;
hcErr = m_pLoadedStencil->Render(static_cast<ITagReplacer*>(this),
pStream, pState);
if (!IsAsyncStatus(hcErr) && m_pLoadedStencil->GetCacheItem())
m_spStencilCache->ReleaseStencil(m_pLoadedStencil->GetCacheItem());
return hcErr;
}
//Implementation
void FreeHandlers() throw(...)
{
POSITION pos = m_Handlers.GetStartPosition();
while (pos)
{
m_Handlers.GetValueAt(pos)->UninitializeHandler();
m_Handlers.GetNextValue(pos)->Release();
}
m_Handlers.RemoveAll();
int nLen = m_hInstHandlers.GetSize();
if (nLen != 0)
{
THandler *pT = static_cast<THandler *>(this);
CComPtr<IDllCache> spDllCache;
pT->m_spServiceProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache),
(void **)&spDllCache);
for (int i=0; i<nLen; i++)
{
spDllCache->Free(m_hInstHandlers[i]);
}
m_hInstHandlers.RemoveAll();
}
}
StencilType* GetNewCacheStencil() throw(...)
{
StencilType *pStencil = NULL;
THandler *pT = static_cast<THandler *>(this);
IAtlMemMgr *pMemMgr;
if (FAILED(pT->m_spServiceProvider->QueryService(__uuidof(IAtlMemMgr), __uuidof(IAtlMemMgr), (void **)&pMemMgr)))
pMemMgr = NULL;
ATLTRY(pStencil = new StencilType(pMemMgr));
if (pStencil != NULL)
{
pStencil->Initialize(pT->m_spServiceProvider);
}
return pStencil;
}
HTTP_CODE CacheStencil(
LPCSTR szName,
StencilType* pStencilData) throw()
{
THandler *pT = static_cast<THandler *>(this);
HRESULT hr = E_FAIL;
HCACHEITEM hCacheItem = NULL;
hr = m_spStencilCache->CacheStencil(szName,
pStencilData,
sizeof(StencilType*),
&hCacheItem,
pT->m_hInstHandler,
static_cast<IMemoryCacheClient*>(pStencilData));
if (hr == S_OK && hCacheItem)
{
_ATLTRY
{
pStencilData->SetCacheItem(hCacheItem);
}
_ATLCATCHALL()
{
hr = E_FAIL;
}
}
return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
}
StencilType *FindCacheStencil(LPCSTR szName) throw()
{
if (!szName || !m_spStencilCache)
return NULL;
StencilType *pStencilData = NULL;
HCACHEITEM hStencil;
if (m_spStencilCache->LookupStencil(szName, &hStencil) != S_OK)
return NULL;
m_spStencilCache->GetStencil(hStencil, reinterpret_cast<void **>(&pStencilData));
return pStencilData;
}
void FreeCacheStencil(StencilType* pStencilData)
{
ATLASSERT( pStencilData != NULL );
if(!pStencilData)
{
return;
}
IMemoryCacheClient *pMemCacheClient = static_cast<IMemoryCacheClient *>(pStencilData);
if(!pMemCacheClient)
{
return;
}
_ATLTRY
{
pMemCacheClient->Free(pStencilData);
}
_ATLCATCHALL()
{
}
}
HTTP_CODE GetHandlerOffset(LPCSTR szHandlerName, DWORD* pdwOffset)
{
if (!pdwOffset)
return HTTP_FAIL;
mapType::CPair *p = m_Handlers.Lookup(szHandlerName);
if (p)
{
DWORD dwIndex = 0;
POSITION pos = m_Handlers.GetStartPosition();
while (pos)
{
const mapType::CPair *p1 = m_Handlers.GetNext(pos);
if (p1 == p)
{
*pdwOffset = dwIndex;
return HTTP_SUCCESS;
}
dwIndex++;
}
ATLASSERT(FALSE);
}
*pdwOffset = 0;
return HTTP_FAIL;
}
HTTP_CODE GetReplacementObject(DWORD dwObjOffset, ITagReplacer **ppReplacer)
{
HRESULT hr = E_FAIL;
POSITION pos = m_Handlers.GetStartPosition();
for (DWORD dwIndex=0; dwIndex < dwObjOffset; dwIndex++)
m_Handlers.GetNext(pos);
ATLASSERT(pos != NULL);
IRequestHandler *pHandler = NULL;
pHandler = m_Handlers.GetValueAt(pos);
ATLENSURE(pHandler != NULL);
hr = pHandler->QueryInterface(__uuidof(ITagReplacer), (void**)ppReplacer);
if (hr != S_OK)
return HTTP_FAIL;
return HTTP_SUCCESS;
}
// This is where we would actually load any extra request
// handlers the HTML stencil might have parsed for us.
HTTP_CODE FinishLoadStencil(StencilType *pStencil, IHttpRequestLookup * pLookup = NULL) throw(...)
{
THandler *pT = static_cast<THandler *>(this);
ATLASSERT(pStencil);
if (!pStencil)
return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED); // unexpected condition
m_pLoadedStencil = pStencil;
//load extra handlers if there are any
StencilType::mapType *pExtraHandlersMap =
pStencil->GetExtraHandlers();
if (pExtraHandlersMap)
{
POSITION pos = pExtraHandlersMap->GetStartPosition();
CStringA name;
CStringPair path;
IRequestHandler *pHandler;
HINSTANCE hInstHandler;
while(pos)
{
pExtraHandlersMap->GetNextAssoc(pos, name, path);
pHandler = NULL;
hInstHandler = NULL;
HTTP_CODE hcErr = pT->m_spExtension->LoadRequestHandler(path.strDllPath, path.strHandlerName,
pT->m_spServerContext,
&hInstHandler,
&pHandler);
if (!hcErr)
{
_ATLTRY
{
//map the name to the pointer to request handler
m_Handlers.SetAt(name, pHandler);
//store HINSTANCE of handler
m_hInstHandlers.Add(hInstHandler);
}
_ATLCATCHALL()
{
return HTTP_FAIL;
}
if (pLookup)
{
hcErr = pHandler->InitializeChild(&m_RequestInfo, pT->m_spServiceProvider, pLookup);
if (hcErr != HTTP_SUCCESS)
return hcErr;
}
}
else
return hcErr;
}
}
return HTTP_SUCCESS;
}
}; // class CHtmlTagReplacer
// CRequestHandlerT
// This is the base class for all user request handlers. This class implements
// the IReplacementHandler interface whose methods will be called to render HTML
// into a stream. The stream will be returned as the HTTP response upon completion
// of the HTTP request.
template < class THandler,
class ThreadModel=CComSingleThreadModel,
class TagReplacerType=CHtmlTagReplacer<THandler>
>
class CRequestHandlerT :
public TagReplacerType,
public CComObjectRootEx<ThreadModel>,
public IRequestHandlerImpl<THandler>
{
protected:
CStencilState m_state;
CComObjectStackEx<CIDServerContext> m_SafeSrvCtx;
typedef CRequestHandlerT<THandler, ThreadModel, TagReplacerType> _requestHandler;
public:
BEGIN_COM_MAP(_requestHandler)
COM_INTERFACE_ENTRY(IRequestHandler)
COM_INTERFACE_ENTRY(ITagReplacer)
END_COM_MAP()
// public CRequestHandlerT members
CHttpResponse m_HttpResponse;
CHttpRequest m_HttpRequest;
ATLSRV_REQUESTTYPE m_dwRequestType;
AtlServerRequest* m_pRequestInfo;
CRequestHandlerT() throw()
{
m_hInstHandler = NULL;
m_dwAsyncFlags = 0;
m_pRequestInfo = NULL;
}
~CRequestHandlerT() throw()
{
_ATLTRY
{
FreeHandlers(); // free handlers held by CTagReplacer
}
_ATLCATCHALL()
{
}
}
void ClearResponse() throw()
{
m_HttpResponse.ClearResponse();
}
// Where user initialization should take place
HTTP_CODE ValidateAndExchange()
{
return HTTP_SUCCESS; // continue processing request
}
// Where user Uninitialization should take place
HTTP_CODE Uninitialize(HTTP_CODE hcError)
{
return hcError;
}
HTTP_CODE InitializeInternal(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
{
// Initialize our internal references to required services
m_pRequestInfo = pRequestInfo;
m_state.pParentInfo = pRequestInfo;
m_hInstHandler = pRequestInfo->hInstDll;
m_spServerContext = pRequestInfo->pServerContext;
m_spServiceProvider = pProvider;
return HTTP_SUCCESS;
}
HTTP_CODE InitializeHandler(
AtlServerRequest *pRequestInfo,
IServiceProvider *pProvider)
{
HTTP_CODE hcErr = HTTP_FAIL;
ATLASSERT(pRequestInfo);
ATLASSERT(pProvider);
THandler* pT = static_cast<THandler*>(this);
hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
if (!hcErr)
{
m_HttpResponse.Initialize(m_spServerContext);
hcErr = pT->CheckValidRequest();
if (!hcErr)
{
hcErr = HTTP_FAIL;
if (m_HttpRequest.Initialize(m_spServerContext,
pT->MaxFormSize(),
pT->FormFlags()))
{
if (m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
{
hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
if (!hcErr)
{
hcErr = pT->ValidateAndExchange();
}
}
}
}
}
return hcErr;
}
HTTP_CODE InitializeChild(
AtlServerRequest *pRequestInfo,
IServiceProvider *pProvider,
IHttpRequestLookup *pRequestLookup)
{
ATLASSERT(pRequestInfo);
ATLASSERT(pProvider);
THandler *pT = static_cast<THandler*>(this);
HTTP_CODE hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
if (hcErr)
return hcErr;
if (pRequestLookup)
{
// initialize with the pRequestLookup
if(!m_HttpResponse.Initialize(pRequestLookup))
{
return HTTP_FAIL;
}
// Initialize with the IHttpServerContext if it exists
// the only time this is different than the previous call to
// initialize is if the user passes a different IHttpServerContext
// in pRequestInfo than the one extracted from pRequestLookup.
if (m_spServerContext)
{
if(!m_HttpResponse.Initialize(m_spServerContext))
{
return HTTP_FAIL;
}
}
hcErr = pT->CheckValidRequest();
if (hcErr)
{
return hcErr;
}
// initialize with the pRequestLookup to chain query parameters
m_HttpRequest.Initialize(pRequestLookup);
// initialize with the m_spServerContext to get additional query params
// if they exist.
if (m_spServerContext)
{
m_HttpRequest.Initialize(m_spServerContext);
}
}
m_HttpResponse.SetBufferOutput(false); // child cannot buffer
// initialize the safe server context
if (!m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
{
return HTTP_FAIL;
}
hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
if (hcErr)
{
return hcErr;
}
return pT->ValidateAndExchange();
}
// HandleRequest is called to perform default processing of HTTP requests. Users
// can override this function in their derived classes if they need to perform
// specific initialization prior to processing this request or want to change the
// way the request is processed.
HTTP_CODE HandleRequest(
AtlServerRequest *pRequestInfo,
IServiceProvider* /*pServiceProvider*/)
{
ATLENSURE(pRequestInfo);
THandler *pT = static_cast<THandler *>(this);
HTTP_CODE hcErr = HTTP_SUCCESS;
if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
{
m_dwRequestType = pRequestInfo->dwRequestType;
if (pRequestInfo->dwRequestType==ATLSRV_REQUEST_STENCIL)
{
LPCSTR szFileName = pRequestInfo->pServerContext->GetScriptPathTranslated();
hcErr = HTTP_FAIL;
if (szFileName)
hcErr = pT->LoadStencil(szFileName, static_cast<IHttpRequestLookup *>(&m_HttpRequest));
}
}
else if (pRequestInfo->dwRequestState == ATLSRV_STATE_CONTINUE)
m_HttpResponse.ClearContent();
#ifdef ATL_DEBUG_STENCILS
if (m_pLoadedStencil && !m_pLoadedStencil->ParseSuccessful())
{
// An error or series of errors occurred in parsing the stencil
_ATLTRY
{
m_pLoadedStencil->RenderErrors(static_cast<IWriteStream*>(&m_HttpResponse));
}
_ATLCATCHALL()
{
return HTTP_FAIL;
}
}
#endif
if (hcErr == HTTP_SUCCESS && m_pLoadedStencil)
{
// if anything other than HTTP_SUCCESS is returned during
// the rendering of replacement tags, we return that value
// here.
hcErr = pT->RenderStencil(static_cast<IWriteStream*>(&m_HttpResponse), &m_state);
if (hcErr == HTTP_SUCCESS && !m_HttpResponse.Flush(TRUE))
hcErr = HTTP_FAIL;
}
if (IsAsyncFlushStatus(hcErr))
{
pRequestInfo->pszBuffer = LPCSTR(m_HttpResponse.m_strContent);
pRequestInfo->dwBufferLen = m_HttpResponse.m_strContent.GetLength();
}
if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN || IsAsyncDoneStatus(hcErr))
return pT->Uninitialize(hcErr);
else if (!IsAsyncStatus(hcErr))
m_HttpResponse.ClearContent();
return hcErr;
}
HTTP_CODE ServerTransferRequest(LPCSTR szRequest, bool bContinueAfterTransfer=false,
WORD nCodePage = 0, CStencilState *pState = NULL) throw(...)
{
return m_spExtension->TransferRequest(
m_pRequestInfo,
m_spServiceProvider,
static_cast<IWriteStream*>(&m_HttpResponse),
static_cast<IHttpRequestLookup*>(&m_HttpRequest),
szRequest,
nCodePage == 0 ? m_nCodePage : nCodePage,
bContinueAfterTransfer,
pState);
}
inline DWORD MaxFormSize()
{
return DEFAULT_MAX_FORM_SIZE;
}
inline DWORD FormFlags()
{
return ATL_FORM_FLAG_IGNORE_FILES;
}
// Override this function to check if the request
// is valid. This function is called after m_HttpResponse
// has been initialized, so you can use it if you need
// to return an error to the client. This is also a
// good place to initialize any internal class data needed
// to handle the request. CRequestHandlerT::CheckValidRequest
// is called after CRequestHandlerT::InitializeInternal is
// called, so your override of this method will have access to
// m_pRequestInfo (this request's AtlServerRequest structure),
// m_hInstHandler (the HINSTANCE of this handler dll),
// m_spServerContext (the IHttpServerContext interface for this request),
// m_spServiceProvider (the IServiceProvider interface for this request).
// You should call CRequestHandlerT::CheckValidRequest in your override
// if you override this function.
//
// Note that m_HttpRequest has not been initialized, so
// you cannot use it. This function is intended to
// do simple checking throught IHttpServerContext to avoid
// expensive initialization of m_HttpRequest.
HTTP_CODE CheckValidRequest()
{
LPCSTR szMethod = NULL;
ATLASSUME(m_pRequestInfo);
szMethod = m_pRequestInfo->pServerContext->GetRequestMethod();
if (strcmp(szMethod, "GET") && strcmp(szMethod, "POST") && strcmp(szMethod, "HEAD"))
return HTTP_NOT_IMPLEMENTED;
return HTTP_SUCCESS;
}
HRESULT GetContext(REFIID riid, void** ppv)
{
if (!ppv)
return E_POINTER;
if (InlineIsEqualGUID(riid, __uuidof(IHttpServerContext)))
{
return m_spServerContext.CopyTo((IHttpServerContext **)ppv);
}
if (InlineIsEqualGUID(riid, __uuidof(IHttpRequestLookup)))
{
*ppv = static_cast<IHttpRequestLookup*>(&m_HttpRequest);
m_HttpRequest.AddRef();
return S_OK;
}
if (InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
{
*ppv = m_spServiceProvider;
m_spServiceProvider.p->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
HINSTANCE GetResourceInstance()
{
if (m_pRequestInfo != NULL)
{
return m_pRequestInfo->hInstDll;
}
return NULL;
}
template <typename Interface>
HRESULT GetContext(Interface** ppInterface) throw(...)
{
return GetContext(__uuidof(Interface), reinterpret_cast<void**>(ppInterface));
}
}; // class CRequestHandlerT
} // namespace ATL
#pragma pack(pop)
#pragma warning( pop )
#endif // __ATLSTENCIL_H__