// 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 #ifdef _ATL_MIN_CRT #error "_ATL_MIN_CRT cannot be used with atlstencil.h" #endif #include #include #include #include #ifdef ATL_DEBUG_STENCILS #include #ifndef ATL_STENCIL_MAX_ERROR_LEN #define ATL_STENCIL_MAX_ERROR_LEN 256 #endif #endif // ATL_DEBUG_STENCILS #ifndef ATL_NO_MLANG #include #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 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 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 struct CTagReplacerMethodEntry { int nType; // REPLACEMENT_ENTRY_* LPCSTR szMethodName; CTagReplacerMethods Methods; }; #define BEGIN_REPLACEMENT_METHOD_MAP(className)\ public:\ void GetReplacementMethodMap(const ATL::CTagReplacerMethodEntry ** ppOut) const\ {\ typedef className __className;\ static const ATL::CTagReplacerMethodEntry 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 ** ppOut) const\ {\ typedef className __className;\ static const ATL::CTagReplacerMethodEntry methods[] = { #define END_ATTR_REPLACEMENT_METHOD_MAP()\ { NULL, NULL, NULL } };\ *ppOut = methods;\ } template 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(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 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 *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 *pEntry) throw() { if (pEntry == NULL) return HTTP_FAIL; const CTagReplacerMethodEntry *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(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 *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 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 ** ppOut) const { static const CTagReplacerMethodEntry methods[] = { { NULL, NULL } }; *ppOut = methods; } void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry **ppOut) const { static const CTagReplacerMethodEntry 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(*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(*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 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 m_Errors; HINSTANCE m_hResInst; class CParseErrorProvider : public ITagReplacerImpl, public CComObjectRootEx { public: BEGIN_COM_MAP(CParseErrorProvider) COM_INTERFACE_ENTRY(ITagReplacer) END_COM_MAP() CSimpleArray *m_pErrors; int m_nCurrentError; CParseErrorProvider() throw() : m_pErrors(NULL), m_nCurrentError(-1) { } void Initialize(CSimpleArray *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((*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(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 Errors; Errors.Initialize(&m_Errors); CStencil ErrorStencil; if (m_hResInst != NULL) { CFixedStringT 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(&Errors)) != false) { ErrorStencil.FinishParseReplacements(); if (ErrorStencil.ParseSuccessful() != false) { hcRet = ErrorStencil.Render(static_cast(&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 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 strRes; if (strRes.LoadString(m_hResInst, IDS_STENCIL_UNRESOLVED_REPLACEMENT) != FALSE) { CFixedStringT 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 _SECURE_ATL if(0 != strcpy_s(szDllPath, nPathLen, m_szDllPath)) { return false; } #else strcpy(szDllPath, m_szDllPath); #endif #if _SECURE_ATL if(0 != strcpy_s(szHandlerName, nHandlerNameLen, m_szHandlerName)) { return false; } #else strcpy(szHandlerName, m_szHandlerName); #endif 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(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 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(szstart[0]))) { locale = (LCID) atoi(szstart); } else { HRESULT hr; CComPtr 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 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(szstart[0]))) { nCodePage = (WORD) atoi(szstart); } else { HRESULT hr; CComPtr 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 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(this); return S_OK; } 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(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, 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, 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 PathStrType; typedef CFixedStringT 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::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 spServerContext; CComPtr 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 spContext; HRESULT hr = pReplacer->GetContext(__uuidof(IHttpServerContext), (void **)&spContext); if (hr == S_OK && spContext) { CComQIPtr 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* pNewServerContext = NULL; ATLTRY(pNewServerContext = new CComObjectNoLock); 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 serverContext; serverContext.Initialize(pWriteStream, pServerContext, pIncludeInfo); CAllocIncludeAsyncContext AsyncAllocObj(&serverContext); return _AtlRenderInclude(static_cast(&serverContext), pIncludeInfo->m_szFileName, pIncludeInfo->m_szQueryString, GetCodePage(), &AsyncAllocObj, pServiceProvider, pLookup, pState); } protected: CAtlMap, CStringPairElementTraits > m_arrExtraHandlers; CHAR m_szBaseDir[MAX_PATH]; CComPtr m_spServiceProvider; CComPtr m_spExtension; CComPtr m_spStencilCache; CComPtr m_spDllCache; public: typedef CAtlMap, 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 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 strFileNameRelative; CFixedStringT strFileName; _ATLTRY { strFileNameRelative.SetString(szStart, (int)(szEnd-szStart + 1)); if (!IsFullPathA(strFileNameRelative)) { CFixedStringT 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 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(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 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(*szStart)) && *szStart != '}') { if (!isalnum(static_cast(*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 CHtmlTagReplacer : public ITagReplacerImpl { protected: typedef StencilType StencilType; CSimpleArray m_hInstHandlers; typedef CAtlMap > mapType; mapType m_Handlers; StencilType *m_pLoadedStencil; WORD m_nCodePage; CComPtr 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 spServiceProvider; THandler *pT = static_cast(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(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]; #if _SECURE_ATL int nResult = sprintf_s(szName, sizeof(szName), "%p/%u", hInstResource, nID); #else int nResult = _snprintf(szName, sizeof(szName), "%p/%u", hInstResource, nID); #endif 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(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(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(this); CComPtr spDllCache; pT->m_spServiceProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void **)&spDllCache); for (int i=0; iFree(m_hInstHandlers[i]); } m_hInstHandlers.RemoveAll(); } } StencilType* GetNewCacheStencil() throw(...) { StencilType *pStencil = NULL; THandler *pT = static_cast(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(this); HRESULT hr = E_FAIL; HCACHEITEM hCacheItem = NULL; hr = m_spStencilCache->CacheStencil(szName, pStencilData, sizeof(StencilType*), &hCacheItem, pT->m_hInstHandler, static_cast(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(&pStencilData)); return pStencilData; } void FreeCacheStencil(StencilType* pStencilData) { ATLASSERT( pStencilData != NULL ); if(!pStencilData) { return; } IMemoryCacheClient *pMemCacheClient = static_cast(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(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 > class CRequestHandlerT : public TagReplacerType, public CComObjectRootEx, public IRequestHandlerImpl { protected: CStencilState m_state; CComObjectStackEx m_SafeSrvCtx; typedef CRequestHandlerT _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(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(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(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(&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(&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(&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(&m_HttpResponse), static_cast(&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(&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 HRESULT GetContext(Interface** ppInterface) throw(...) { return GetContext(__uuidof(Interface), reinterpret_cast(ppInterface)); } }; // class CRequestHandlerT } // namespace ATL #pragma pack(pop) #pragma warning( pop ) #endif // __ATLSTENCIL_H__