3812 lines
120 KiB
C++
3812 lines
120 KiB
C++
// This is a part of the Active Template Library.
|
|
// Copyright (C) 1996-2001 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 <atlisapi.h>
|
|
#include <atlfile.h>
|
|
#include <atlutil.h>
|
|
|
|
#ifndef ATL_NO_MLANG
|
|
#include <mlang.h>
|
|
#endif
|
|
|
|
#ifndef _ATL_NO_DEFAULT_LIBS
|
|
#pragma comment(lib, "shlwapi.lib")
|
|
#endif // !_ATL_NO_DEFAULT_LIBS
|
|
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4127 )
|
|
#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
|
|
|
|
namespace ATL {
|
|
|
|
// Token types
|
|
// These tags are token tags for the standard tag replacer implementation
|
|
extern __declspec(selectany) const DWORD STENCIL_TEXTTAG = 0x00000000;
|
|
extern __declspec(selectany) const DWORD STENCIL_REPLACEMENT = 0x00000001;
|
|
extern __declspec(selectany) const DWORD STENCIL_ITERATORSTART = 0x00000002;
|
|
extern __declspec(selectany) const DWORD STENCIL_ITERATOREND = 0x00000003;
|
|
extern __declspec(selectany) const DWORD STENCIL_CONDITIONALSTART = 0x00000004;
|
|
extern __declspec(selectany) const DWORD STENCIL_CONDITIONALELSE = 0x00000005;
|
|
extern __declspec(selectany) const DWORD STENCIL_CONDITIONALEND = 0x00000006;
|
|
extern __declspec(selectany) const DWORD STENCIL_STENCILINCLUDE = 0x00000007;
|
|
extern __declspec(selectany) const DWORD STENCIL_STATICINCLUDE = 0x00000008;
|
|
extern __declspec(selectany) const DWORD STENCIL_LOCALE = 0x00000009;
|
|
extern __declspec(selectany) const DWORD STENCIL_CODEPAGE = 0x0000000a;
|
|
|
|
// The base for user defined token types
|
|
extern __declspec(selectany) const DWORD STENCIL_USER_TOKEN_BASE = 0x00001000;
|
|
|
|
// Symbols to use in error handling in the stencil processor
|
|
#define STENCIL_INVALIDINDEX 0xFFFFFFFF
|
|
#define STENCIL_INVALIDOFFSET 0xFFFFFFFF
|
|
|
|
// error codes
|
|
#define STENCIL_SUCCESS HTTP_SUCCESS
|
|
#define STENCL_FAIL HTTP_FAIL
|
|
|
|
#define STENCIL_BASIC_MAP 0
|
|
#define STENCIL_ATTR_MAP 1
|
|
|
|
#ifndef ATL_MAX_METHOD_NAME_LEN
|
|
#define ATL_MAX_METHOD_NAME_LEN 64
|
|
#endif
|
|
|
|
#ifndef ATL_MAX_BLOCK_STACK
|
|
#define ATL_MAX_BLOCK_STACK 128
|
|
#endif
|
|
|
|
template <class TBase, typename T>
|
|
struct CTagReplacerMethodsEx
|
|
{
|
|
typedef HTTP_CODE (TBase::*REPLACE_FUNC)();
|
|
typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX)(T*);
|
|
typedef HTTP_CODE (TBase::*PARSE_FUNC)(IAtlMemMgr *, LPCSTR, T**);
|
|
typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX_V)(void *);
|
|
typedef HTTP_CODE (TBase::*PARSE_FUNC_V)(IAtlMemMgr *, LPCSTR, void**);
|
|
|
|
static REPLACE_FUNC_EX_V CheckRepl(REPLACE_FUNC p) throw()
|
|
{
|
|
return (REPLACE_FUNC_EX_V) p;
|
|
}
|
|
|
|
static REPLACE_FUNC_EX_V CheckReplEx(REPLACE_FUNC_EX p) throw()
|
|
{
|
|
return (REPLACE_FUNC_EX_V) p;
|
|
}
|
|
|
|
static PARSE_FUNC_V CheckParse(PARSE_FUNC p) throw()
|
|
{
|
|
return (PARSE_FUNC_V) p;
|
|
}
|
|
};
|
|
|
|
template <class TBase>
|
|
struct CTagReplacerMethods
|
|
{
|
|
union
|
|
{
|
|
HTTP_CODE (TBase::*pfnMethodEx)(void *);
|
|
HTTP_CODE (TBase::*pfnMethod)();
|
|
};
|
|
HTTP_CODE (TBase::*pfnParse)(IAtlMemMgr *pMemMgr, LPCSTR, void **);
|
|
};
|
|
|
|
#define REPLACEMENT_ENTRY_DEFAULT 0
|
|
#define REPLACEMENT_ENTRY_ARGS 1
|
|
|
|
template <class TBase>
|
|
struct CTagReplacerMethodEntry
|
|
{
|
|
int nType; // REPLACEMENT_ENTRY_*
|
|
LPCSTR szMethodName;
|
|
CTagReplacerMethods<TBase> Methods;
|
|
};
|
|
|
|
|
|
#define BEGIN_REPLACEMENT_METHOD_MAP(className)\
|
|
public:\
|
|
void GetReplacementMethodMap(const ATL::CTagReplacerMethodEntry<className> ** ppOut) const throw()\
|
|
{\
|
|
typedef className __className;\
|
|
static const ATL::CTagReplacerMethodEntry<className> methods[] = {
|
|
|
|
#define REPLACEMENT_METHOD_ENTRY(methodName, methodFunc)\
|
|
{ 0, methodName, { ATL::CTagReplacerMethodsEx<__className, void>::CheckRepl(methodFunc), NULL } },
|
|
|
|
#define REPLACEMENT_METHOD_ENTRY_EX(methodName, methodFunc, paramType, parseFunc)\
|
|
{ 1, methodName, { ATL::CTagReplacerMethodsEx<__className, paramType>::CheckReplEx(methodFunc), ATL::CTagReplacerMethodsEx<__className, paramType>::CheckParse(parseFunc) } },
|
|
|
|
#define REPLACEMENT_METHOD_ENTRY_EX_STR(methodName, methodFunc) \
|
|
{ 1, methodName, { ATL::CTagReplacerMethodsEx<__className, char>::CheckReplEx(methodFunc), ATL::CTagReplacerMethodsEx<__className, char>::CheckParse(DefaultParseString) } },
|
|
|
|
#define END_REPLACEMENT_METHOD_MAP()\
|
|
{ 0, NULL, NULL } };\
|
|
*ppOut = methods;\
|
|
}
|
|
|
|
#define BEGIN_ATTR_REPLACEMENT_METHOD_MAP(className)\
|
|
public:\
|
|
void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<className> ** ppOut) const throw()\
|
|
{\
|
|
typedef className __className;\
|
|
static const ATL::CTagReplacerMethodEntry<className> methods[] = {
|
|
|
|
#define END_ATTR_REPLACEMENT_METHOD_MAP()\
|
|
{ NULL, NULL, NULL } };\
|
|
*ppOut = methods;\
|
|
}
|
|
|
|
template <class T>
|
|
class ITagReplacerImpl : public ITagReplacer
|
|
{
|
|
protected:
|
|
IWriteStream *m_pStream;
|
|
|
|
public:
|
|
typedef HTTP_CODE (T::*REPLACEMENT_METHOD)();
|
|
typedef HTTP_CODE (T::*REPLACEMENT_METHOD_EX)(void *pvParam);
|
|
|
|
ITagReplacerImpl() throw()
|
|
:m_pStream(NULL)
|
|
{
|
|
}
|
|
|
|
IWriteStream *SetStream(IWriteStream *pStream) throw()
|
|
{
|
|
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) throw(...)
|
|
{
|
|
// we at least have to be looking up a method offset
|
|
if (!pdwMethodOffset || !szMethodName)
|
|
return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
|
|
*pdwMethodOffset = STENCIL_INVALIDOFFSET;
|
|
HTTP_CODE hcErr = HTTP_FAIL;
|
|
T *pT = static_cast<T *>(this);
|
|
|
|
char szName[ATL_MAX_METHOD_NAME_LEN];
|
|
|
|
// if a handler name was supplied, we will try to
|
|
// find a different object to handle the method
|
|
if (szHandlerName && *szHandlerName)
|
|
{
|
|
if (!pdwHandlerOffset)
|
|
return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
|
|
hcErr = pT->GetHandlerOffset(szHandlerName, pdwHandlerOffset);
|
|
// got the alternate handler, now look up the method offset on
|
|
// the handler.
|
|
if (!hcErr)
|
|
{
|
|
CComPtr<ITagReplacer> spAltTagReplacer;
|
|
hcErr = pT->GetReplacementObject(*pdwHandlerOffset, &spAltTagReplacer);
|
|
if (!hcErr)
|
|
hcErr = spAltTagReplacer->FindReplacementOffset(szMethodName, pdwMethodOffset,
|
|
NULL, NULL, pdwMap, ppvParam, pMemMgr);
|
|
return hcErr;
|
|
}
|
|
else
|
|
return hcErr;
|
|
}
|
|
strcpy(szName, szMethodName);
|
|
|
|
// check for params
|
|
char *szLeftPar = strchr(szName, '(');
|
|
if (szLeftPar)
|
|
{
|
|
*szLeftPar = '\0';
|
|
szLeftPar++;
|
|
|
|
char *szRightPar = strchr(szLeftPar, ')');
|
|
if (!szRightPar)
|
|
return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
|
|
*szRightPar = '\0';
|
|
|
|
szMethodName = szName;
|
|
|
|
}
|
|
|
|
// No handler name is specified, so we look up the method name in
|
|
// T's replacement method map
|
|
const CTagReplacerMethodEntry<T> *pEntry;
|
|
pT->GetReplacementMethodMap(&pEntry);
|
|
|
|
hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
|
|
if (hcErr != HTTP_SUCCESS)
|
|
{
|
|
pT->GetAttrReplacementMethodMap(&pEntry);
|
|
hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
|
|
if (hcErr == HTTP_SUCCESS)
|
|
*pdwMap = STENCIL_ATTR_MAP;
|
|
}
|
|
else
|
|
{
|
|
*pdwMap = STENCIL_BASIC_MAP;
|
|
}
|
|
|
|
// This assert will be triggered if arguments are passed to a replacement method that doesn't handle them
|
|
ATLASSERT( szLeftPar == NULL || (szLeftPar != NULL && (pEntry != NULL && pEntry[*pdwMethodOffset].Methods.pfnParse) != NULL) );
|
|
|
|
if (hcErr == HTTP_SUCCESS && pEntry && pEntry[*pdwMethodOffset].Methods.pfnParse)
|
|
hcErr = (pT->*pEntry[*pdwMethodOffset].Methods.pfnParse)(pMemMgr, szLeftPar, ppvParam);
|
|
return hcErr;
|
|
|
|
}
|
|
|
|
HTTP_CODE FindReplacementOffsetInMap(
|
|
LPCSTR szMethodName,
|
|
LPDWORD pdwMethodOffset,
|
|
const CTagReplacerMethodEntry<T> *pEntry) throw()
|
|
{
|
|
if (pEntry == NULL)
|
|
return HTTP_FAIL;
|
|
|
|
const CTagReplacerMethodEntry<T> *pEntryHead = pEntry;
|
|
|
|
while (pEntry->szMethodName)
|
|
{
|
|
if (strcmp(pEntry->szMethodName, szMethodName) == 0)
|
|
{
|
|
if (pEntry->Methods.pfnMethod)
|
|
{
|
|
*pdwMethodOffset = (DWORD)(pEntry-pEntryHead);
|
|
return HTTP_SUCCESS;
|
|
}
|
|
}
|
|
pEntry++;
|
|
}
|
|
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
|
|
// Used to render a single replacement tag into a stream.
|
|
// Looks up a pointer to a member function in user code by offseting into the users
|
|
// replacement map. Much faster than the other overload of this function since
|
|
// no string compares are performed.
|
|
HTTP_CODE RenderReplacement(DWORD dwFnOffset, DWORD dwObjOffset, DWORD dwMap, void *pvParam) throw(...)
|
|
{
|
|
HTTP_CODE hcErr = HTTP_FAIL;
|
|
T *pT = static_cast<T *>(this);
|
|
|
|
// if we were not passed an object offset, then we assume
|
|
// that the function at dwFnOffset is in T's replacement
|
|
// map
|
|
if (dwObjOffset == STENCIL_INVALIDOFFSET)
|
|
{
|
|
// call a function in T's replacement map
|
|
ATLASSERT(dwFnOffset != STENCIL_INVALIDOFFSET);
|
|
const CTagReplacerMethodEntry<T> *pEntry;
|
|
if (dwMap == STENCIL_BASIC_MAP)
|
|
pT->GetReplacementMethodMap(&pEntry);
|
|
else
|
|
pT->GetAttrReplacementMethodMap(&pEntry);
|
|
if (pEntry)
|
|
{
|
|
if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_DEFAULT)
|
|
{
|
|
REPLACEMENT_METHOD pfn = NULL;
|
|
pfn = pEntry[dwFnOffset].Methods.pfnMethod;
|
|
ATLASSERT(pfn);
|
|
if (pfn)
|
|
{
|
|
hcErr = (pT->*pfn)();
|
|
}
|
|
}
|
|
else if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_ARGS)
|
|
{
|
|
REPLACEMENT_METHOD_EX pfn = NULL;
|
|
pfn = pEntry[dwFnOffset].Methods.pfnMethodEx;
|
|
ATLASSERT(pfn);
|
|
if (pfn)
|
|
{
|
|
hcErr = (pT->*pfn)(pvParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unknown entry type
|
|
ATLASSERT(FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// otherwise, we were passed an object offset. The object
|
|
// offset is a dword ID that T can use to look up the
|
|
// ITagReplacer* of a tag replacer that will render this
|
|
// replacement.
|
|
CComPtr<ITagReplacer> spAltReplacer = NULL;
|
|
if (!pT->GetReplacementObject(dwObjOffset, &spAltReplacer))
|
|
{
|
|
spAltReplacer->SetStream(m_pStream);
|
|
hcErr = spAltReplacer->RenderReplacement(dwFnOffset, STENCIL_INVALIDOFFSET, dwMap, pvParam);
|
|
}
|
|
}
|
|
return hcErr;
|
|
}
|
|
|
|
// Default GetHandlerOffset, does nothing
|
|
HTTP_CODE GetHandlerOffset(LPCSTR /*szHandlerName*/, DWORD* pdwOffset) throw()
|
|
{
|
|
if (pdwOffset)
|
|
*pdwOffset = 0;
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
// Default GetReplacementObject, does nothing
|
|
HTTP_CODE GetReplacementObject(DWORD /*dwObjOffset*/, ITagReplacer **ppReplacer) throw()
|
|
{
|
|
if (ppReplacer)
|
|
*ppReplacer = NULL;
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
void GetReplacementMethodMap(const CTagReplacerMethodEntry<T> ** ppOut) const throw()
|
|
{
|
|
static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
|
|
*ppOut = methods;
|
|
}
|
|
|
|
void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<T> **ppOut) const throw()
|
|
{
|
|
static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
|
|
*ppOut = methods;
|
|
}
|
|
|
|
HRESULT GetContext(REFIID, void**) throw()
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HTTP_CODE FreeParam(void * /*pvParam*/) throw()
|
|
{
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseString(IAtlMemMgr *pMemMgr, LPCSTR szParams, char **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
size_t nLen = strlen(szParams);
|
|
if (nLen)
|
|
{
|
|
nLen++;
|
|
*ppParam = (char *) pMemMgr->Allocate(nLen);
|
|
if (*ppParam)
|
|
memcpy(*ppParam, szParams, nLen);
|
|
else
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseUChar(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned char **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (unsigned char *) pMemMgr->Allocate(sizeof(unsigned char));
|
|
if (*ppParam)
|
|
{
|
|
char *szEnd;
|
|
**ppParam = (unsigned char) strtoul(szParams, &szEnd, 10);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, short **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (short *) pMemMgr->Allocate(sizeof(short));
|
|
if (*ppParam)
|
|
{
|
|
**ppParam = (short)atoi(szParams);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseUShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned short **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (unsigned short *) pMemMgr->Allocate(sizeof(short));
|
|
if (*ppParam)
|
|
{
|
|
char *szEnd;
|
|
**ppParam = (unsigned short) strtoul(szParams, &szEnd, 10);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, int **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (int *) pMemMgr->Allocate(sizeof(int));
|
|
if (*ppParam)
|
|
{
|
|
**ppParam = atoi(szParams);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseUInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned int **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (unsigned int *) pMemMgr->Allocate(sizeof(unsigned int));
|
|
if (*ppParam)
|
|
{
|
|
char *szEnd;
|
|
**ppParam = strtoul(szParams, &szEnd, 10);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, __int64 **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (__int64 *) pMemMgr->Allocate(sizeof(__int64));
|
|
if (*ppParam)
|
|
{
|
|
**ppParam = _atoi64(szParams);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseUInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned __int64 **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (unsigned __int64 *) pMemMgr->Allocate(sizeof(unsigned __int64));
|
|
if (*ppParam)
|
|
{
|
|
char *szEnd;
|
|
**ppParam = _strtoui64(szParams, &szEnd, 10);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseBool(IAtlMemMgr *pMemMgr, LPCSTR szParams, bool **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (bool *) pMemMgr->Allocate(sizeof(bool));
|
|
if (*ppParam)
|
|
{
|
|
if (!_strnicmp(szParams, "true", sizeof("true")-sizeof('\0')))
|
|
**ppParam = true;
|
|
else
|
|
**ppParam = false;
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseDouble(IAtlMemMgr *pMemMgr, LPCSTR szParams, double **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (double *) pMemMgr->Allocate(sizeof(double));
|
|
if (*ppParam)
|
|
{
|
|
**ppParam = atof(szParams);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE DefaultParseFloat(IAtlMemMgr *pMemMgr, LPCSTR szParams, float **ppParam) throw(...)
|
|
{
|
|
ATLASSERT(szParams);
|
|
|
|
*ppParam = (float *) pMemMgr->Allocate(sizeof(float));
|
|
if (*ppParam)
|
|
{
|
|
**ppParam = (float) atof(szParams);
|
|
}
|
|
else
|
|
{
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
};
|
|
|
|
inline LPSTR SkipSpace(LPSTR sz, WORD nCodePage)
|
|
{
|
|
while (_istspace(*sz))
|
|
sz = CharNextExA(nCodePage, sz, 0);
|
|
return sz;
|
|
}
|
|
|
|
inline LPSTR RSkipSpace(LPSTR pStart, LPSTR sz, WORD nCodePage) throw()
|
|
{
|
|
while (_istspace(*sz))
|
|
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
|
|
{
|
|
LPSTR pStart; // Start of fragment to be rendered
|
|
LPSTR 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;
|
|
};
|
|
|
|
|
|
// Specialization of CElementTraitsBase so you can put a StencilToken safely in
|
|
// a collection object.
|
|
template<>
|
|
class CElementTraits< StencilToken > :
|
|
public CElementTraitsBase< StencilToken >
|
|
{
|
|
public:
|
|
static ULONG Hash( INARGTYPE t ) throw()
|
|
{
|
|
return( ULONG( ULONG_PTR( &t ) ) );
|
|
}
|
|
|
|
static bool CompareElements( INARGTYPE element1, INARGTYPE element2 ) throw()
|
|
{
|
|
return( (element1.pStart == element2.pStart) && (element1.pEnd == element2.pEnd) );
|
|
}
|
|
|
|
static int CompareElementsOrdered( INARGTYPE element1, INARGTYPE element2 ) throw()
|
|
{
|
|
if( element1.pStart < element2.pStart )
|
|
{
|
|
return( -1 );
|
|
}
|
|
else if( CompareElements(element1,element2) )
|
|
{
|
|
return( 0 );
|
|
}
|
|
else
|
|
{
|
|
ATLASSERT( element1.pStart > element2.pStart );
|
|
return( 1 );
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
// 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:
|
|
LPSTR m_pBufferStart; // Beginning of CHAR buffer that holds the stencil.
|
|
// For mapped files this is the beginning of the mapping.
|
|
LPSTR m_pBufferEnd; // End of CHAR buffer that holds the stencil.
|
|
CAtlArray<StencilToken> m_arrTokens; //An array of tokens.
|
|
FILETIME m_ftLastModified; // Last modified time (0 for resource)
|
|
FILETIME m_ftLastChecked; // Last time we retrieved last modified time (0 for resource)
|
|
HCACHEITEM m_hCacheItem;
|
|
WORD m_nCodePage;
|
|
BOOL m_bUseLocaleACP;
|
|
char m_szDllPath[MAX_PATH+1];
|
|
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[256];
|
|
LPCSTR m_szPosition;
|
|
LPCSTR m_szStartLine;
|
|
LPCSTR m_szEndLine;
|
|
int m_nLineNumber;
|
|
|
|
bool operator==(const ParseError& that) const throw()
|
|
{
|
|
return (m_nLineNumber == that.m_nLineNumber);
|
|
}
|
|
};
|
|
|
|
CSimpleArray<ParseError> m_Errors;
|
|
|
|
class CParseErrorProvider : public ITagReplacerImpl<CParseErrorProvider>,
|
|
public CComObjectRootEx<CComSingleThreadModel>
|
|
{
|
|
public:
|
|
BEGIN_COM_MAP(CParseErrorProvider)
|
|
COM_INTERFACE_ENTRY(ITagReplacer)
|
|
END_COM_MAP()
|
|
CSimpleArray<ParseError> *m_pErrors;
|
|
int m_nCurrentError;
|
|
|
|
CParseErrorProvider() throw() :
|
|
m_pErrors(NULL),
|
|
m_nCurrentError(-1)
|
|
{
|
|
}
|
|
|
|
void Initialize(CSimpleArray<ParseError> *pErrors) throw()
|
|
{
|
|
m_pErrors = pErrors;
|
|
}
|
|
|
|
HTTP_CODE OnGetNextError() throw()
|
|
{
|
|
m_nCurrentError++;
|
|
if (m_nCurrentError >= m_pErrors->GetSize() ||
|
|
m_nCurrentError < 0 )
|
|
{
|
|
m_nCurrentError = -1;
|
|
return HTTP_S_FALSE;
|
|
}
|
|
else
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE OnGetErrorLineNumber() throw(...)
|
|
{
|
|
if (m_pErrors->GetSize() == 0)
|
|
return HTTP_SUCCESS;
|
|
if (m_nCurrentError > m_pErrors->GetSize() ||
|
|
m_nCurrentError < 0)
|
|
m_nCurrentError = 0;
|
|
|
|
CWriteStreamHelper c(m_pStream);
|
|
if (!c.Write((*m_pErrors)[m_nCurrentError].m_nLineNumber))
|
|
return HTTP_FAIL;
|
|
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE OnGetErrorText() throw(...)
|
|
{
|
|
if (m_pErrors->GetSize() == 0)
|
|
return HTTP_SUCCESS;
|
|
if (m_nCurrentError > m_pErrors->GetSize() ||
|
|
m_nCurrentError < 0)
|
|
m_nCurrentError = 0;
|
|
|
|
CWriteStreamHelper c(m_pStream);
|
|
|
|
if (!c.Write((*m_pErrors)[m_nCurrentError].m_szError))
|
|
return HTTP_FAIL;
|
|
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
HTTP_CODE OnGetErrorLine() throw(...)
|
|
{
|
|
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);
|
|
}
|
|
};
|
|
|
|
protected:
|
|
ITagReplacer *m_pReplacer;
|
|
IAtlMemMgr *m_pMemMgr;
|
|
static CCRTHeap m_crtHeap;
|
|
|
|
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_pErrorStencil = NULL;
|
|
#else
|
|
m_bErrorsOccurred = false;
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef ATL_DEBUG_STENCILS
|
|
|
|
virtual LPCSTR GetErrorStencil() throw()
|
|
{
|
|
static LPCSTR s_szString =
|
|
"<h1><font color=#ff0000> While trying to parse a stencil file, "
|
|
"the following errors occurred:</font></h1>\r\n"
|
|
"{{while GetNextError}}"
|
|
"<table border=1 width=50%>\r\n"
|
|
"<tr><td width=25%>Error type</td><td>{{GetErrorText}}</td></tr>\r\n"
|
|
"<tr><td>Line number</td><td>{{GetErrorLineNumber}}</td></tr>\r\n"
|
|
"<tr><td>Error text</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n"
|
|
"</table>\r\n"
|
|
"{{endwhile}}"
|
|
"<br>Stencil output follows:\r\n"
|
|
"<hr>";
|
|
|
|
return s_szString;
|
|
}
|
|
|
|
bool RenderErrors(IWriteStream *pStream) throw(...)
|
|
{
|
|
if (!pStream)
|
|
return false;
|
|
|
|
LPCSTR szString = GetErrorStencil();
|
|
if (szString == NULL)
|
|
{
|
|
// assume user doesn't want errors rendered
|
|
return true;
|
|
}
|
|
|
|
CComObjectStackEx<CParseErrorProvider> Errors;
|
|
|
|
Errors.Initialize(&m_Errors);
|
|
CStencil ErrorStencil;
|
|
|
|
HTTP_CODE hcRet = ErrorStencil.LoadFromString(szString, (DWORD)strlen(szString));
|
|
|
|
if (hcRet != HTTP_SUCCESS)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ErrorStencil.ParseReplacements(static_cast<ITagReplacer *>(&Errors)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ErrorStencil.FinishParseReplacements();
|
|
|
|
HTTP_CODE hcErrorCode = ErrorStencil.Render(static_cast<ITagReplacer *>(&Errors), pStream);
|
|
|
|
if (HTTP_ERROR_CODE(hcErrorCode) >= 400)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ParseSuccessful() throw()
|
|
{
|
|
return (m_Errors.GetSize() == 0);
|
|
}
|
|
|
|
bool AddError(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;
|
|
strcpy(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);
|
|
}
|
|
#else
|
|
|
|
bool ParseSuccessful() throw()
|
|
{
|
|
return m_bErrorsOccurred;
|
|
}
|
|
|
|
void AddError(LPCSTR /*szErrorText*/, LPCSTR /*szPosition*/) throw()
|
|
{
|
|
m_bErrorsOccurred = true;
|
|
}
|
|
#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) throw()
|
|
{
|
|
ATLASSERT(pftLastModified);
|
|
*pftLastModified = m_ftLastModified;
|
|
}
|
|
|
|
void GetLastChecked(FILETIME *pftLastChecked) throw()
|
|
{
|
|
ATLASSERT(pftLastChecked);
|
|
*pftLastChecked = m_ftLastChecked;
|
|
}
|
|
|
|
void SetLastChecked(FILETIME *pftLastChecked) throw()
|
|
{
|
|
ATLASSERT(pftLastChecked);
|
|
m_ftLastChecked = *pftLastChecked;
|
|
}
|
|
|
|
HCACHEITEM GetCacheItem() throw()
|
|
{
|
|
return m_hCacheItem;
|
|
}
|
|
|
|
void SetCacheItem(HCACHEITEM hCacheItem) throw()
|
|
{
|
|
ATLASSERT(m_hCacheItem == NULL);
|
|
m_hCacheItem = hCacheItem;
|
|
}
|
|
|
|
void GetHandlerName(LPSTR szDllPath, LPSTR szHandlerName) throw()
|
|
{
|
|
strcpy(szDllPath, m_szDllPath);
|
|
strcpy(szHandlerName, m_szHandlerName);
|
|
}
|
|
|
|
// Adds a token to the token array, handler name, method name
|
|
// and handler function offset are optional
|
|
ATL_NOINLINE DWORD AddToken(
|
|
LPSTR pStart,
|
|
LPSTR 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;
|
|
|
|
if (szHandlerName && (sizeof(t.szHandlerName) > strlen(szHandlerName)))
|
|
strcpy(t.szHandlerName, szHandlerName);
|
|
else
|
|
t.szHandlerName[0] = '\0';
|
|
|
|
if (szMethodName && (sizeof(t.szMethodName) > strlen(szMethodName)))
|
|
strcpy(t.szMethodName, szMethodName);
|
|
else
|
|
t.szMethodName[0] = '\0';
|
|
|
|
_ATLTRY
|
|
{
|
|
return (DWORD) m_arrTokens.Add(t);
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return STENCIL_INVALIDINDEX;
|
|
}
|
|
}
|
|
|
|
// maps a stencil file into memory
|
|
HTTP_CODE LoadFile(LPCSTR szFileName) throw()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
ULONGLONG dwLen = 0;
|
|
CAtlFile file;
|
|
|
|
hr = file.Create(CA2CTEX<MAX_PATH+1>(szFileName), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
|
|
if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
|
|
return HTTP_ERROR(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;
|
|
ATLTRY(m_pBufferStart = new CHAR[(UINT) dwLen]);
|
|
if (m_pBufferStart == NULL)
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM); // out of memory
|
|
|
|
DWORD dwRead;
|
|
hr = file.Read(m_pBufferStart, (DWORD) dwLen, dwRead);
|
|
if (FAILED(hr))
|
|
{
|
|
delete [] m_pBufferStart;
|
|
m_pBufferStart = NULL;
|
|
return HTTP_ERROR(500, ISE_SUBERR_READFILEFAIL); // ReadFile failed
|
|
}
|
|
|
|
m_pBufferEnd = m_pBufferStart + dwRead;
|
|
}
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
// loads a stencil from the specified resource.
|
|
HTTP_CODE LoadFromResource(HINSTANCE hInstRes, LPCSTR szID, LPCSTR szType = NULL) throw()
|
|
{
|
|
if (!szType)
|
|
szType = (LPCSTR) RT_HTML;
|
|
|
|
HRSRC hRsrc = FindResourceA(hInstRes, szID, szType);
|
|
HGLOBAL hgResource = NULL;
|
|
if (hRsrc)
|
|
{
|
|
hgResource = LoadResource(hInstRes, hRsrc);
|
|
if (!hgResource)
|
|
return HTTP_FAIL;
|
|
}
|
|
else
|
|
return HTTP_FAIL;
|
|
|
|
DWORD dwSize = SizeofResource(hInstRes, hRsrc);
|
|
m_pBufferStart = (LPSTR)LockResource(hgResource);
|
|
m_pBufferEnd = m_pBufferStart+dwSize;
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
// loads a stencil from the specified resource
|
|
HTTP_CODE LoadFromResource(HINSTANCE hInstRes, UINT nId, LPCSTR szType = NULL) throw()
|
|
{
|
|
return LoadFromResource(hInstRes, MAKEINTRESOURCEA(nId), szType);
|
|
}
|
|
|
|
// loads a stencil from a string
|
|
HTTP_CODE LoadFromString(LPCSTR szString, DWORD dwSize) throw()
|
|
{
|
|
m_pBufferStart = (LPSTR) szString;
|
|
m_pBufferEnd = m_pBufferStart+dwSize;
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
inline BOOL CheckTag(LPCSTR szTag, DWORD dwTagLen, LPSTR szStart, DWORD dwLen) throw()
|
|
{
|
|
if (dwLen < dwTagLen)
|
|
return FALSE;
|
|
|
|
if (memcmp(szStart, szTag, dwTagLen))
|
|
return FALSE;
|
|
|
|
if (_istspace(szStart[dwTagLen]) || szStart[dwTagLen] == '}')
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
inline void FindTagArgs(LPSTR& szstart, LPSTR& szend, int nKeywordChars) throw()
|
|
{
|
|
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
|
|
}
|
|
|
|
// 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 ParseReplacements(pReplacer, GetBufferStart(), GetBufferEnd());
|
|
}
|
|
|
|
virtual void 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("{{if}} without {{endif}}", token.pStart);
|
|
else if (bUnclosedBlock && token.type == STENCIL_CONDITIONALELSE)
|
|
AddError("{{else}} without {{endif}}", token.pStart);
|
|
else if (bUnclosedBlock && token.type == STENCIL_ITERATORSTART)
|
|
AddError("{{while}} without {{endwhile}}", token.pStart);
|
|
else
|
|
{
|
|
char szBuf[ATL_MAX_METHOD_NAME_LEN+sizeof("Unresolved replacement : ''")];
|
|
_snprintf(szBuf,
|
|
ATL_MAX_METHOD_NAME_LEN+sizeof("Unresolved replacement : ''")-1,
|
|
"Unresolved replacement : '%s'", token.szMethodName);
|
|
|
|
AddError(szBuf, 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual bool Parse(ITagReplacer *pReplacer) throw( ... )
|
|
{
|
|
if (ParseReplacements(pReplacer))
|
|
{
|
|
FinishParseReplacements();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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 ParseReplacement( LPSTR szTokenStart,
|
|
LPSTR szTokenEnd,
|
|
DWORD * /*pBlockStack*/,
|
|
DWORD * /*pdwTop*/,
|
|
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)
|
|
LPSTR szStart = szTokenStart;
|
|
LPSTR 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,
|
|
szHandlerName))
|
|
dwIndex = AddToken(szStart, szEnd, dwTokenType,
|
|
szHandlerName, szMethodName,
|
|
STENCIL_INVALIDINDEX, STENCIL_INVALIDINDEX,
|
|
0, STENCIL_BASIC_MAP);
|
|
else
|
|
dwIndex = STENCIL_INVALIDINDEX;
|
|
return dwIndex;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
DWORD ParseWhile(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
|
|
{
|
|
DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, pBlockStack, pdwTop, STENCIL_ITERATORSTART, 5);
|
|
if (dwIndex == STENCIL_INVALIDINDEX)
|
|
return dwIndex;
|
|
return PushToken(pBlockStack, pdwTop, dwIndex);
|
|
}
|
|
|
|
DWORD ParseEndWhile(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
|
|
{
|
|
DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_ITERATORSTART);
|
|
if (dwTopIndex == STENCIL_INVALIDINDEX)
|
|
{
|
|
AddError("{{endwhile}} without {{while}}", szTokenStart);
|
|
return dwTopIndex;
|
|
}
|
|
|
|
DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_ITERATOREND);
|
|
m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
|
|
m_arrTokens[dwIndex].dwLoopIndex = dwTopIndex;
|
|
return dwIndex;
|
|
}
|
|
|
|
DWORD ParseIf(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
|
|
{
|
|
DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, pBlockStack, pdwTop, STENCIL_CONDITIONALSTART, 2);
|
|
if (dwIndex == STENCIL_INVALIDINDEX)
|
|
return dwIndex;
|
|
return PushToken(pBlockStack, pdwTop, dwIndex);
|
|
}
|
|
|
|
DWORD ParseElse(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
|
|
{
|
|
DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
|
|
if (dwTopIndex == STENCIL_INVALIDINDEX)
|
|
{
|
|
AddError("{{else}} without {{if}}", szTokenStart);
|
|
return dwTopIndex;
|
|
}
|
|
|
|
DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALELSE);
|
|
m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
|
|
|
|
|
|
return PushToken(pBlockStack, pdwTop, dwIndex);
|
|
}
|
|
|
|
DWORD ParseEndIf(LPSTR szTokenStart, LPSTR 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("{{endif}} without {{if}}", szTokenStart);
|
|
return dwTopIndex;
|
|
}
|
|
}
|
|
DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALEND);
|
|
m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
|
|
return dwIndex;
|
|
}
|
|
|
|
DWORD ParseLocale(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD * /*pBlockStack*/, DWORD * /*pdwTop*/) throw()
|
|
{
|
|
LPSTR szstart = szTokenStart;
|
|
LPSTR szend = szTokenEnd;
|
|
LCID locale = 0xFFFFFFFF;
|
|
FindTagArgs(szstart, szend, 6);
|
|
#ifndef ATL_NO_MLANG
|
|
if (isdigit(szstart[0]))
|
|
{
|
|
locale = (LCID) atoi(szstart);
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr;
|
|
|
|
CComPtr<IMultiLanguage> pML;
|
|
hr = pML.CoCreateInstance(__uuidof(CMultiLanguage));
|
|
if (FAILED(hr))
|
|
{
|
|
ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
|
|
AddError("Couldn't create CMultiLanguage", szTokenStart);
|
|
}
|
|
else
|
|
{
|
|
CStringW str(szstart, (int)((szend-szstart)+1));
|
|
|
|
#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
|
|
|
|
// use IMultiLanguage2 if possible
|
|
CComPtr<IMultiLanguage2> spML2;
|
|
hr = pML.QueryInterface(&spML2);
|
|
if (FAILED(hr) || !spML2.p)
|
|
hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
|
|
else
|
|
hr = spML2->GetLcidFromRfc1766(&locale, CComBSTR(str));
|
|
|
|
#else // __IMultiLanguage2_INTERFACE_DEFINED__
|
|
|
|
hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
|
|
|
|
#endif // __IMultiLanguage2_INTERFACE_DEFINED__
|
|
|
|
if (FAILED(hr))
|
|
AddError("Error getting 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("GetLocaleInfo failed", 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(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD * /*pBlockStack*/, DWORD * /*pdwTop*/) throw()
|
|
{
|
|
LPSTR szstart = szTokenStart;
|
|
LPSTR szend = szTokenEnd;
|
|
WORD nCodePage = 0xFFFF;
|
|
|
|
FindTagArgs(szstart, szend, 8);
|
|
#ifndef ATL_NO_MLANG
|
|
if (isdigit(szstart[0]))
|
|
{
|
|
nCodePage = (WORD) atoi(szstart);
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr;
|
|
|
|
CComPtr<IMultiLanguage> pML;
|
|
hr = pML.CoCreateInstance(__uuidof(CMultiLanguage));
|
|
if (FAILED(hr))
|
|
{
|
|
ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
|
|
AddError("Couldn't CoCreate CMultiLanguage object", szTokenStart);
|
|
}
|
|
else
|
|
{
|
|
CStringW str(szstart, (int)((szend-szstart)+1));
|
|
MIMECSETINFO info;
|
|
|
|
#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
|
|
|
|
// use IMultiLanguage2 if possible
|
|
CComPtr<IMultiLanguage2> spML2;
|
|
hr = pML.QueryInterface(&spML2);
|
|
if (FAILED(hr) || !spML2.p)
|
|
hr = pML->GetCharsetInfo(CComBSTR(str), &info);
|
|
else
|
|
hr = spML2->GetCharsetInfo(CComBSTR(str), &info);
|
|
|
|
#else // __IMultiLanguage2_INTERFACE_DEFINED__
|
|
|
|
hr = pML->GetCharsetInfo(CComBSTR(str), &info);
|
|
|
|
#endif // __IMultiLanguage2_INTERFACE_DEFINED__
|
|
|
|
// for most character sets, uiCodePage and uiInternetEncoding
|
|
// are the same. UTF-8 is the exception that we're concerned about.
|
|
// for that character set, we want uiInternetEncoding (65001 - UTF-8)
|
|
// instead of uiCodePage (1200 - UCS-2)
|
|
if (SUCCEEDED(hr))
|
|
nCodePage = (WORD) info.uiInternetEncoding;
|
|
else
|
|
AddError("GetCharsetInfo failed", 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(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD * /*pBlockStack*/, DWORD * /*pdwTop*/) throw()
|
|
{
|
|
LPSTR szstart = szTokenStart;
|
|
LPSTR 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 + 2];
|
|
if (nlen < MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 2)
|
|
{
|
|
memcpy(szHandlerDllName, szstart, szend-szstart+1);
|
|
szHandlerDllName[szend-szstart+1] = '\0';
|
|
|
|
DWORD dwDllPathLen = MAX_PATH+1;
|
|
DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
|
|
|
|
if (!_AtlCrackHandler(szHandlerDllName, m_szDllPath, &dwDllPathLen, m_szHandlerName, &dwHandlerNameLen))
|
|
{
|
|
return INVALID_TOKEN;
|
|
}
|
|
}
|
|
|
|
return RESERVED_TOKEN;
|
|
}
|
|
|
|
virtual PARSE_TOKEN_RESULT ParseToken(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
|
|
{
|
|
LPSTR 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, pBlockStack, pdwTop);
|
|
else if (CheckTag("handler", 7, pStart, dwLen))
|
|
{
|
|
return ParseHandler(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
|
|
}
|
|
else if (CheckTag("codepage", 8, pStart, dwLen))
|
|
{
|
|
ParseCodepage(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
|
|
return RESERVED_TOKEN;
|
|
}
|
|
else
|
|
{
|
|
dwIndex = ParseReplacement(szTokenStart, szTokenEnd, pBlockStack, pdwTop, STENCIL_REPLACEMENT);
|
|
if (dwIndex == STENCIL_INVALIDINDEX)
|
|
return INVALID_TOKEN;
|
|
ret = NORMAL_TOKEN;
|
|
}
|
|
if (dwIndex == STENCIL_INVALIDINDEX)
|
|
return INVALID_TOKEN;
|
|
return ret;
|
|
}
|
|
|
|
virtual bool ParseReplacements(ITagReplacer* pReplacer, LPSTR pStart, LPSTR pEnd) throw()
|
|
{
|
|
LPSTR szCurr = pStart;
|
|
DWORD BlockStack[ATL_MAX_BLOCK_STACK];
|
|
DWORD dwTop = 0;
|
|
|
|
m_pReplacer = pReplacer;
|
|
|
|
DWORD dwCurrentTokenIndex = 0;
|
|
|
|
if (!szCurr)
|
|
{
|
|
ATLASSERT(FALSE);
|
|
AddError("NULL parameter to ParseReplacements", NULL);
|
|
return false;
|
|
}
|
|
|
|
LPSTR szEnd = pEnd;
|
|
if (szEnd <= szCurr)
|
|
{
|
|
ATLASSERT(FALSE);
|
|
AddError("Empty or negative string passed to ParseReplacements", 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
|
|
LPSTR szStart = szCurr;
|
|
while (szCurr < szEnd && (*szCurr != '{' || szCurr[1] != '{'))
|
|
{
|
|
LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
|
|
if (szNext == szCurr)
|
|
{
|
|
// embedded null
|
|
AddError("Embedded null character in stencil", 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);
|
|
|
|
//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("Embedded null character in stencil", NULL);
|
|
return true;
|
|
}
|
|
szCurr = szNext;
|
|
}
|
|
|
|
if (szCurr >= szEnd)
|
|
{
|
|
AddError("Unmatched {{", szStart);
|
|
AddToken(szStart, szCurr-1, STENCIL_TEXTTAG);
|
|
break;
|
|
}
|
|
|
|
if (szCurr[0] == '{')
|
|
{
|
|
if (szCurr[1] != '{')
|
|
{
|
|
szCurr--;
|
|
}
|
|
AddError("Mismatched {{", szStart);
|
|
AddToken(szStart, szCurr-1, STENCIL_TEXTTAG);
|
|
continue;
|
|
}
|
|
|
|
szEndTag = CharNextExA(m_nCodePage, szCurr, 0);
|
|
if (szEndTag == szCurr)
|
|
{
|
|
// embedded null
|
|
AddError("Embedded null character in stencil", NULL);
|
|
return true;
|
|
}
|
|
|
|
PARSE_TOKEN_RESULT ret = ParseToken(szStart, szEndTag, BlockStack, &dwTop);
|
|
if (ret == INVALID_TOKEN)
|
|
{
|
|
dwCurrentTokenIndex = AddToken(szStart, szEndTag, STENCIL_TEXTTAG);
|
|
szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
|
|
continue;
|
|
}
|
|
|
|
szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
|
|
if (szEndTag == szCurr)
|
|
{
|
|
// embedded null
|
|
AddError("Embedded null character in stencil", NULL);
|
|
return true;
|
|
}
|
|
|
|
if (ret == RESERVED_TOKEN)
|
|
{
|
|
// REVIEW: should these be using CharNextExA
|
|
if (szCurr < szEnd && *szCurr == '\n')
|
|
szCurr++;
|
|
else if ((szCurr+1 < szEnd && *szCurr == '\r' && *(szCurr+1) == '\n'))
|
|
szCurr += 2;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
HTTP_CODE GetHandlerAndMethodNames(
|
|
LPSTR pStart,
|
|
LPSTR pEnd,
|
|
LPSTR pszMethodName,
|
|
LPSTR pszHandlerName) throw()
|
|
{
|
|
if (!pszMethodName || !pszHandlerName)
|
|
{
|
|
ATLASSERT(FALSE);
|
|
AddError("Bad parameter", pStart);
|
|
return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
}
|
|
|
|
*pszMethodName = '\0';
|
|
*pszHandlerName = '\0';
|
|
CHAR szMethodString[ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1]; //*2 in case the tag is an id.tag and +1 for the dot
|
|
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("Method name too long", pStart);
|
|
return HTTP_ERROR(500, ISE_SUBERR_LONGMETHODNAME);
|
|
}
|
|
|
|
memcpy(szMethodString, 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)<ATL_MAX_METHOD_NAME_LEN)
|
|
strcpy(pszMethodName, szDot);
|
|
else
|
|
{
|
|
AddError("Method name too long", pStart + (szDot - szMethodString));
|
|
hcErr = HTTP_ERROR(500, ISE_SUBERR_LONGMETHODNAME);
|
|
}
|
|
// copy handler name
|
|
if (!hcErr)
|
|
{
|
|
if (strlen(szMethodString) < ATL_MAX_METHOD_NAME_LEN)
|
|
strcpy(pszHandlerName, szMethodString);
|
|
else
|
|
{
|
|
AddError("Handler name too long", pStart);
|
|
hcErr = HTTP_ERROR(500, ISE_SUBERR_LONGHANDLERNAME);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// only a method name so just copy it.
|
|
if (strlen(szMethodString) < ATL_MAX_METHOD_NAME_LEN)
|
|
strcpy(pszMethodName, szMethodString);
|
|
else
|
|
{
|
|
AddError("Method name too long", pStart);
|
|
hcErr = HTTP_ERROR(500, ISE_SUBERR_LONGMETHODNAME);
|
|
}
|
|
}
|
|
return hcErr;
|
|
}
|
|
|
|
virtual HTTP_CODE Render(
|
|
ITagReplacer *pReplacer,
|
|
IWriteStream *pWriteStream,
|
|
void* pState = NULL) const throw(...)
|
|
{
|
|
HTTP_CODE hcErrorCode = HTTP_SUCCESS;
|
|
DWORD dwIndex = 0;
|
|
DWORD dwArraySize = GetTokenCount();
|
|
CStencilState* _pState = reinterpret_cast<CStencilState*>(pState);
|
|
|
|
// 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); // state is required for async
|
|
_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,
|
|
void* pState = NULL) const throw(...)
|
|
{
|
|
const StencilToken* pToken = GetToken(dwIndex);
|
|
DWORD dwNextToken = 0;
|
|
HTTP_CODE hcErrorCode = HTTP_SUCCESS;
|
|
|
|
if (!pToken)
|
|
return STENCIL_INVALIDINDEX;
|
|
|
|
switch (pToken->type)
|
|
{
|
|
case STENCIL_ITERATORSTART:
|
|
{
|
|
HTTP_CODE hcErr = STENCIL_SUCCESS;
|
|
|
|
// 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 = HTTP_ERROR(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 = HTTP_ERROR(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 = HTTP_ERROR(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
|
|
ATLASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
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_ITERATOREND:
|
|
{
|
|
dwNextToken = pToken->dwLoopIndex;
|
|
hcErrorCode = HTTP_SUCCESS;
|
|
ATLASSERT(GetToken(dwNextToken)->type == STENCIL_ITERATORSTART);
|
|
}
|
|
break;
|
|
case STENCIL_CONDITIONALSTART:
|
|
{
|
|
if (pToken->type == STENCIL_CONDITIONALSTART && pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
|
|
{
|
|
// This should have been caught at parse time
|
|
ATLASSERT(FALSE);
|
|
dwNextToken = STENCIL_INVALIDINDEX;
|
|
hcErrorCode = HTTP_ERROR(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 = HTTP_ERROR(500, ISE_SUBERR_STENCIL_MISMATCHIF);
|
|
break;
|
|
}
|
|
|
|
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:
|
|
{
|
|
if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
|
|
{
|
|
// This should have been caught at parse time
|
|
ATLASSERT(FALSE);
|
|
dwNextToken = STENCIL_INVALIDINDEX;
|
|
hcErrorCode = HTTP_ERROR(500, ISE_SUBERR_STENCIL_MISMATCHIF);
|
|
break;
|
|
}
|
|
dwNextToken = pToken->dwLoopIndex+1;
|
|
hcErrorCode = HTTP_SUCCESS;
|
|
}
|
|
break;
|
|
case STENCIL_CONDITIONALEND:
|
|
{
|
|
dwNextToken = dwIndex+1;
|
|
hcErrorCode = HTTP_SUCCESS;
|
|
}
|
|
break;
|
|
case STENCIL_TEXTTAG:
|
|
{
|
|
pWriteStream->WriteStream(pToken->pStart,
|
|
(int)((pToken->pEnd-pToken->pStart)+1), NULL);
|
|
dwNextToken = dwIndex+1;
|
|
}
|
|
break;
|
|
case STENCIL_REPLACEMENT:
|
|
{
|
|
if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
|
|
{
|
|
// This should have been caught at parse time
|
|
ATLASSERT(FALSE);
|
|
dwNextToken = STENCIL_INVALIDINDEX;
|
|
hcErrorCode = HTTP_ERROR(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
|
|
break;
|
|
}
|
|
|
|
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_LOCALE:
|
|
{
|
|
if (pState)
|
|
{
|
|
CStencilState* _pState = reinterpret_cast<CStencilState*>(pState);
|
|
_pState->locale = (LCID) pToken->dwData;
|
|
}
|
|
SetThreadLocale((LCID) pToken->dwData);
|
|
dwNextToken = dwIndex + 1;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
ATLASSERT(FALSE);
|
|
dwNextToken = STENCIL_INVALIDINDEX;
|
|
hcErrorCode = HTTP_ERROR(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]);
|
|
}
|
|
|
|
LPSTR GetBufferStart() const throw()
|
|
{
|
|
return m_pBufferStart;
|
|
}
|
|
|
|
LPSTR GetBufferEnd() const throw()
|
|
{
|
|
return m_pBufferEnd;
|
|
}
|
|
|
|
WORD GetCodePage() const throw()
|
|
{
|
|
return m_nCodePage;
|
|
}
|
|
|
|
// IMemoryCacheClient
|
|
STDMETHOD(QueryInterface)(REFIID riid, void **ppv) throw()
|
|
{
|
|
if (!ppv)
|
|
return E_POINTER;
|
|
|
|
if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
|
|
InlineIsEqualGUID(riid, __uuidof(IMemoryCacheClient)))
|
|
{
|
|
*ppv = static_cast<void*>(this);
|
|
return S_OK;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)() throw()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)() throw()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
STDMETHOD(Free)(const void *pData) throw()
|
|
{
|
|
if (!pData)
|
|
return E_UNEXPECTED;
|
|
|
|
ATLASSERT(*((void **) pData) == static_cast<void*>(this));
|
|
|
|
Uninitialize();
|
|
|
|
delete this;
|
|
return S_OK;
|
|
}
|
|
}; // class CStencil
|
|
|
|
struct StencilIncludeInfo
|
|
{
|
|
public:
|
|
CHAR m_szQueryString[2048];
|
|
CHAR m_szFileName[MAX_PATH];
|
|
};
|
|
|
|
|
|
class CIncludeServerContext :
|
|
public CComObjectRootEx<CComMultiThreadModel>,
|
|
public CWrappedServerContext
|
|
{
|
|
public:
|
|
BEGIN_COM_MAP(CIncludeServerContext)
|
|
COM_INTERFACE_ENTRY(IHttpServerContext)
|
|
END_COM_MAP()
|
|
|
|
IWriteStream * m_pStream;
|
|
const StencilIncludeInfo * m_pIncludeInfo;
|
|
|
|
CIncludeServerContext() throw()
|
|
{
|
|
m_pStream = NULL;
|
|
m_pIncludeInfo = NULL;
|
|
}
|
|
|
|
void Initialize(
|
|
IWriteStream *pStream,
|
|
IHttpServerContext* pServerContext,
|
|
const StencilIncludeInfo * pIncludeInfo) throw()
|
|
{
|
|
m_pStream = pStream;
|
|
m_spParent = pServerContext;
|
|
m_pIncludeInfo = pIncludeInfo;
|
|
}
|
|
|
|
LPCSTR GetRequestMethod() throw()
|
|
{
|
|
return "GET";
|
|
}
|
|
|
|
LPCSTR GetQueryString() throw()
|
|
{
|
|
return m_pIncludeInfo->m_szQueryString;
|
|
}
|
|
|
|
LPCSTR GetPathTranslated() throw()
|
|
{
|
|
return m_pIncludeInfo->m_szFileName;
|
|
}
|
|
|
|
LPCSTR GetScriptPathTranslated() throw()
|
|
{
|
|
return m_pIncludeInfo->m_szFileName;
|
|
}
|
|
|
|
DWORD GetTotalBytes() throw()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
DWORD GetAvailableBytes() throw()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
BYTE *GetAvailableData() throw()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
LPCSTR GetContentType() throw()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw(...)
|
|
{
|
|
HRESULT hr = m_pStream->WriteStream((LPCSTR) pvBuffer, *pdwBytes, pdwBytes);
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/) throw()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/) throw()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SendRedirectResponse(LPCSTR /*pszRedirectURL*/) throw()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SendResponseHeader(
|
|
LPCSTR /*pszHeader*/,
|
|
LPCSTR /*pszStatusCode*/,
|
|
BOOL /*fKeepConn*/) throw()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL DoneWithSession(DWORD /*dwHttpStatusCode*/) throw()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/) throw()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}; // class CIncludeServerContext
|
|
|
|
class CIDServerContext :
|
|
public CComObjectRootEx<CComMultiThreadModel>,
|
|
public CWrappedServerContext
|
|
{
|
|
public:
|
|
CHttpResponse *m_pResponse;
|
|
CHttpRequest *m_pRequest;
|
|
|
|
BEGIN_COM_MAP(CIDServerContext)
|
|
COM_INTERFACE_ENTRY(IHttpServerContext)
|
|
END_COM_MAP()
|
|
|
|
CIDServerContext() throw()
|
|
: m_pResponse(NULL), m_pRequest(NULL)
|
|
{
|
|
}
|
|
|
|
BOOL Initialize(
|
|
CHttpResponse *pResponse,
|
|
CHttpRequest *pRequest) throw()
|
|
{
|
|
m_pResponse = pResponse;
|
|
m_pRequest = pRequest;
|
|
HRESULT hr = m_pRequest->GetServerContext(&m_spParent);
|
|
return (SUCCEEDED(hr));
|
|
}
|
|
|
|
LPCSTR GetRequestMethod() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetMethodString();
|
|
}
|
|
|
|
LPCSTR GetQueryString() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetQueryString();
|
|
}
|
|
|
|
LPCSTR GetPathInfo() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetPathInfo();
|
|
}
|
|
|
|
LPCSTR GetPathTranslated() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetPathTranslated();
|
|
}
|
|
|
|
DWORD GetTotalBytes() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetTotalBytes();
|
|
}
|
|
|
|
DWORD GetAvailableBytes() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetAvailableBytes();
|
|
}
|
|
|
|
BYTE *GetAvailableData() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetAvailableData();
|
|
}
|
|
|
|
LPCSTR GetContentType() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetContentType();
|
|
}
|
|
|
|
LPCSTR GetScriptPathTranslated() throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->GetScriptPathTranslated();
|
|
}
|
|
|
|
BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes) throw()
|
|
{
|
|
ATLASSERT(m_pResponse != NULL);
|
|
return m_pResponse->WriteLen((LPCSTR)pvBuffer, *pdwBytes);
|
|
}
|
|
|
|
BOOL ReadClient(void *pvBuffer, DWORD *pdwSize) throw()
|
|
{
|
|
ATLASSERT(m_pRequest != NULL);
|
|
return m_pRequest->ReadData((LPSTR)pvBuffer, pdwSize);
|
|
}
|
|
|
|
BOOL SendRedirectResponse(LPCSTR pszRedirectURL) throw()
|
|
{
|
|
ATLASSERT(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) throw(...)
|
|
{
|
|
ATLASSERT(m_pResponse != NULL);
|
|
ATLASSERT(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. A handler must be
|
|
// the first tag in the .srf file. 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
|
|
{
|
|
CStringA strDllPath;
|
|
CStringA strHandlerName;
|
|
|
|
CStringPair()throw()
|
|
{
|
|
}
|
|
|
|
CStringPair(CStringA &strDllPath_, CStringA &strHandlerName_) throw(...)
|
|
:strDllPath(strDllPath_), strHandlerName(strHandlerName_)
|
|
{
|
|
}
|
|
};
|
|
|
|
class CStringPairElementTraits :
|
|
public CElementTraitsBase< CStringPair >
|
|
{
|
|
public:
|
|
static ULONG Hash( INARGTYPE pair ) throw()
|
|
{
|
|
return CStringElementTraits<CStringA>::Hash( pair.strDllPath );
|
|
}
|
|
|
|
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
|
|
{
|
|
protected:
|
|
CAtlMap<CStringA, CStringPair,
|
|
CStringElementTraits<CStringA>, CStringPairElementTraits > m_arrExtraHandlers;
|
|
CHAR m_szBaseDir[MAX_PATH];
|
|
CComPtr<IServiceProvider> m_spServiceProvider;
|
|
CComPtr<IIsapiExtension> m_spExtension;
|
|
CComPtr<IStencilCache> m_spStencilCache;
|
|
CComPtr<IDllCache> m_spDllCache;
|
|
|
|
public:
|
|
typedef CAtlMap<CStringA, CStringPair,
|
|
CStringElementTraits<CStringA>, CStringPairElementTraits > mapType;
|
|
typedef CStencil baseType;
|
|
|
|
CHtmlStencil(IAtlMemMgr *pMemMgr=NULL) throw() :
|
|
CStencil(pMemMgr)
|
|
{
|
|
|
|
}
|
|
|
|
void Initialize(IServiceProvider *pProvider) throw(...)
|
|
{
|
|
ATLASSERT(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 throw()
|
|
{
|
|
LPCSTR szQueryBegin = szParamBegin;
|
|
|
|
while (*szQueryBegin && *szQueryBegin != '?' && *szQueryBegin != '}')
|
|
{
|
|
LPSTR szNext = CharNextExA(GetCodePage(), szQueryBegin, 0);
|
|
if (szNext == szQueryBegin)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
szQueryBegin = szNext;
|
|
}
|
|
|
|
char szPath[MAX_PATH];
|
|
szPath[0] = '\0';
|
|
DWORD dwPrefixLen = 0;
|
|
if (*szParamBegin == '"')
|
|
{
|
|
szParamBegin++;
|
|
}
|
|
if (!IsFullPathA(szParamBegin))
|
|
{
|
|
if (*szParamBegin != '\\')
|
|
{
|
|
strcpy(szPath, m_szBaseDir);
|
|
}
|
|
else
|
|
{
|
|
LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
|
|
if (szBackslash)
|
|
{
|
|
strncpy(szPath, m_szBaseDir, szBackslash-m_szBaseDir);
|
|
szPath[szBackslash-m_szBaseDir] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(szPath, m_szBaseDir);
|
|
}
|
|
}
|
|
dwPrefixLen = (DWORD)strlen(szPath);
|
|
}
|
|
|
|
if (*szQueryBegin=='?')
|
|
{
|
|
size_t nMinus = (*(szQueryBegin-1) == '"') ? 1 : 0;
|
|
strncat(szPath, szParamBegin, szQueryBegin-szParamBegin-nMinus);
|
|
szPath[dwPrefixLen + szQueryBegin - szParamBegin] = '\0';
|
|
memcpy(pInfo->m_szQueryString, szQueryBegin + 1, szParamEnd - szQueryBegin);
|
|
pInfo->m_szQueryString[szParamEnd - szQueryBegin] = '\0';
|
|
}
|
|
else
|
|
{
|
|
pInfo->m_szQueryString[0] = '\0';
|
|
size_t nAdd = (*szParamEnd == '"') ? 0 : 1;
|
|
strncat(szPath, szParamBegin, szParamEnd - szParamBegin + nAdd);
|
|
szPath[dwPrefixLen + szParamEnd - szParamBegin + 1] = '\0';
|
|
}
|
|
|
|
PathCanonicalizeA(pInfo->m_szFileName, szPath);
|
|
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
int ParseInclude(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD * /*pBlockStack*/, DWORD * /*pdwTop*/) throw(...)
|
|
{
|
|
LPSTR szStart = szTokenStart;
|
|
LPSTR szEnd = szTokenEnd;
|
|
|
|
FindTagArgs(szStart, szEnd, 7);
|
|
|
|
CHeapPtr<char> szFileNameRelative;
|
|
CHeapPtr<TCHAR> szFileName;
|
|
|
|
ATLTRY(szFileNameRelative.AllocateBytes(max(MAX_PATH, szEnd-szStart+3)))
|
|
ATLTRY(szFileName.AllocateBytes(((szEnd-szStart)+MAX_PATH+3)*sizeof(TCHAR)))
|
|
|
|
if ((char*)szFileNameRelative == NULL ||
|
|
(TCHAR*)szFileName == NULL)
|
|
{
|
|
AddError("Out of memory", szTokenStart);
|
|
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
|
|
}
|
|
|
|
|
|
memcpy(szFileNameRelative, szStart, szEnd-szStart + 1);
|
|
szFileNameRelative[szEnd-szStart + 1] = '\0';
|
|
|
|
if (!IsFullPathA(szFileNameRelative))
|
|
{
|
|
CHeapPtr<CHAR> szTemp;
|
|
ATLTRY(szTemp.AllocateBytes((szEnd-szStart)+MAX_PATH+3));
|
|
if ((CHAR*)szTemp == NULL)
|
|
{
|
|
AddError("Out of memory", szTokenStart);
|
|
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
|
|
}
|
|
if (*szFileNameRelative != '\\')
|
|
{
|
|
strcpy(szTemp, m_szBaseDir);
|
|
}
|
|
else
|
|
{
|
|
LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
|
|
if (szBackslash)
|
|
{
|
|
strncpy(szTemp, m_szBaseDir, szBackslash-m_szBaseDir);
|
|
szTemp[szBackslash-m_szBaseDir] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(szTemp, m_szBaseDir);
|
|
}
|
|
}
|
|
|
|
strcat(szTemp, szFileNameRelative);
|
|
PathCanonicalize(szFileName, CA2CT(szTemp));
|
|
}
|
|
else
|
|
{
|
|
_tcscpy(szFileName, CA2CTEX<MAX_PATH>(szFileNameRelative));
|
|
}
|
|
|
|
LPTSTR szDot = NULL;
|
|
LPTSTR szExtra = _tcschr(szFileName, '?');
|
|
if (!szExtra)
|
|
{
|
|
szExtra = _tcschr(szFileName, '#');
|
|
if (!szExtra)
|
|
{
|
|
szDot = _tcsrchr(szFileName, '.');
|
|
}
|
|
}
|
|
|
|
if (szExtra != NULL)
|
|
{
|
|
// there is some extra information
|
|
LPTSTR szDotTmp = szFileName;
|
|
do
|
|
{
|
|
szDot = szDotTmp;
|
|
szDotTmp = _tcschr(szDotTmp+1, '.');
|
|
} while (szDotTmp && szDotTmp < szExtra);
|
|
}
|
|
|
|
if (!szDot || *szDot != '.')
|
|
{
|
|
AddError("Unexpected error", szTokenStart);
|
|
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
|
|
}
|
|
|
|
LPTSTR szExtEnd = szDot;
|
|
|
|
while (true)
|
|
{
|
|
szExtEnd++;
|
|
if (!*szExtEnd || *szExtEnd == '/' || *szExtEnd == '\\' || *szExtEnd == '?' || *szExtEnd == '#' || *szExtEnd == '"')
|
|
break;
|
|
}
|
|
|
|
if (szDot && szExtEnd-szDot == sizeof(".dll")-sizeof('\0') &&
|
|
!_tcsnicmp(szDot, _T(".dll"), sizeof(".dll")-sizeof('\0')))
|
|
{
|
|
// Do .dll stuff
|
|
DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
|
|
StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
|
|
if (!pInfo)
|
|
return -1;
|
|
|
|
GetIncludeInfo(szStart, szEnd, pInfo);
|
|
// m_arrTokens[dwIndex].pvParam = pInfo;
|
|
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);
|
|
StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
|
|
if (!pInfo)
|
|
return -1;
|
|
|
|
GetIncludeInfo(szStart, szEnd, pInfo);
|
|
// m_arrTokens[dwIndex].pvParam = pInfo;
|
|
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("Could not open included file", szTokenStart);
|
|
else
|
|
AddError("Included file is not a disk file", szTokenStart);
|
|
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
|
|
}
|
|
|
|
CAutoVectorPtr<CHAR> szBufferStart;
|
|
LPSTR szBufferEnd = NULL;
|
|
ULONGLONG dwLen = 0;
|
|
if (FAILED(file.GetSize(dwLen)))
|
|
return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
|
|
|
|
if (!szBufferStart.Allocate((size_t) dwLen))
|
|
AtlThrow(E_OUTOFMEMORY);
|
|
|
|
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);
|
|
// m_arrTokens[dwIndex].bDynamicAlloc = TRUE;
|
|
GetToken(dwIndex)->bDynamicAlloc = TRUE;
|
|
szBufferStart.Detach();
|
|
return dwIndex;
|
|
}
|
|
}
|
|
|
|
PARSE_TOKEN_RESULT ParseSubhandler(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD * /*pBlockStack*/, DWORD * /*pdwTop*/) throw()
|
|
{
|
|
LPSTR szStart = szTokenStart;
|
|
LPSTR 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(*szStart) && *szStart != '}')
|
|
{
|
|
LPSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
|
|
if (szNext == szStart)
|
|
{
|
|
// embedded null
|
|
AddError("Embedded null character in stencil", 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
|
|
LPSTR szHandlerStart = szStart;
|
|
while (*szStart != '}')
|
|
{
|
|
LPSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
|
|
if (szNext == szStart)
|
|
{
|
|
// embedded null
|
|
AddError("Embedded null character in stencil", NULL);
|
|
return INVALID_TOKEN;
|
|
}
|
|
szStart = szNext;
|
|
}
|
|
LPCSTR szHandlerEnd = szStart;
|
|
|
|
_ATLTRY
|
|
{
|
|
CStringA strName(szIdStart, (int)(szIdEnd-szIdStart));
|
|
CStringA strPath(szHandlerStart, (int)(szHandlerEnd-szHandlerStart));
|
|
|
|
CStringA strDllPath;
|
|
CStringA strHandlerName;
|
|
DWORD dwDllPathLen = MAX_PATH+1;
|
|
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();
|
|
return INVALID_TOKEN;
|
|
}
|
|
|
|
strDllPath.ReleaseBuffer(dwDllPathLen);
|
|
strHandlerName.ReleaseBuffer(dwHandlerNameLen);
|
|
|
|
m_arrExtraHandlers.SetAt(strName, CStringPair(strDllPath, strHandlerName));
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return INVALID_TOKEN;
|
|
}
|
|
return RESERVED_TOKEN;
|
|
}
|
|
|
|
virtual PARSE_TOKEN_RESULT ParseToken(LPSTR szTokenStart, LPSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
|
|
{
|
|
LPSTR pStart = szTokenStart;
|
|
pStart += 2; //skip curlies
|
|
pStart = SkipSpace(pStart, GetCodePage());
|
|
DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
|
|
|
|
int nIndex = -1;
|
|
|
|
if (CheckTag("include", 7, pStart, dwLen))
|
|
nIndex = ParseInclude(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
|
|
else if (dwLen > 3 && !memcmp("!--", pStart, 3))
|
|
return RESERVED_TOKEN;
|
|
else if (dwLen > 2 && !memcmp("//", pStart, 2))
|
|
return RESERVED_TOKEN;
|
|
else if (CheckTag("subhandler", 10, pStart, dwLen))
|
|
{
|
|
return ParseSubhandler(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
|
|
}
|
|
else
|
|
{
|
|
return CStencil::ParseToken(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
|
|
}
|
|
if (nIndex < 0)
|
|
return INVALID_TOKEN;
|
|
return RESERVED_TOKEN;
|
|
}
|
|
|
|
mapType* GetExtraHandlers() throw()
|
|
{
|
|
return &m_arrExtraHandlers;
|
|
}
|
|
|
|
void SetBaseDirFromFile(LPCSTR szBaseDir) throw()
|
|
{
|
|
strcpy(m_szBaseDir, szBaseDir);
|
|
LPSTR szSlash = strrchr(m_szBaseDir, '\\');
|
|
if (szSlash)
|
|
{
|
|
szSlash++;
|
|
*szSlash = '\0';
|
|
}
|
|
else
|
|
{
|
|
*m_szBaseDir = '\0';
|
|
}
|
|
}
|
|
|
|
LPCSTR GetBaseDir() throw()
|
|
{
|
|
return m_szBaseDir;
|
|
}
|
|
|
|
|
|
// Any value returned from a replacement method other than HTTP_SUCCESS
|
|
// will stop the rendering of the stencil.
|
|
DWORD RenderToken(
|
|
DWORD dwIndex,
|
|
ITagReplacer* pReplacer,
|
|
IWriteStream *pWriteStream,
|
|
HTTP_CODE *phcErrorCode,
|
|
void* 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)
|
|
{
|
|
ATLASSERT(m_spServiceProvider);
|
|
CComPtr<IHttpServerContext> spServerContext;
|
|
CComPtr<IHttpRequestLookup> spLookup;
|
|
if (FAILED(pReplacer->GetContext(__uuidof(IHttpServerContext), (VOID**) &spServerContext)))
|
|
return HTTP_ERROR(500, 0);
|
|
if (FAILED(pReplacer->GetContext(__uuidof(IHttpRequestLookup), (VOID**) &spLookup)))
|
|
return HTTP_ERROR(500, 0);
|
|
hcErrorCode = RenderInclude(m_spServiceProvider, pWriteStream,
|
|
(StencilIncludeInfo *)pToken->dwData, spServerContext, spLookup,
|
|
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 = HTTP_SUCCESS;
|
|
CComPtr<IHttpServerContext> spContext;
|
|
HRESULT hr = pReplacer->GetContext(__uuidof(IHttpServerContext), (void **)&spContext);
|
|
if (hr == S_OK && spContext)
|
|
{
|
|
CComQIPtr<IPageCacheControl> spControl;
|
|
spControl = spContext;
|
|
if (spControl)
|
|
spControl->Cache(FALSE);
|
|
}
|
|
}
|
|
|
|
if (phcErrorCode)
|
|
*phcErrorCode = hcErrorCode;
|
|
return dwNextToken;
|
|
}
|
|
|
|
ATL_NOINLINE HTTP_CODE RenderInclude(
|
|
IServiceProvider *pServiceProvider,
|
|
IWriteStream *pWriteStream,
|
|
const StencilIncludeInfo *pIncludeInfo,
|
|
IHttpServerContext *pServerContext,
|
|
IHttpRequestLookup *pLookup,
|
|
void* pState = NULL) const throw(...)
|
|
{
|
|
AtlServerRequest* pRequestInfo = NULL;
|
|
HTTP_CODE hcErr = HTTP_SUCCESS;
|
|
|
|
|
|
CComObjectStackEx<CIncludeServerContext> serverContext;
|
|
serverContext.Initialize(pWriteStream, pServerContext, pIncludeInfo);
|
|
|
|
#ifdef _DEBUG
|
|
bool bAsyncAllowed = false;
|
|
#endif
|
|
|
|
CStencilState* _pState = reinterpret_cast<CStencilState*>(pState);
|
|
if (_pState && _pState->pIncludeInfo)
|
|
{
|
|
pRequestInfo = _pState->pIncludeInfo;
|
|
_pState->pIncludeInfo = NULL;
|
|
#ifdef _DEBUG
|
|
bAsyncAllowed = true;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (!m_spStencilCache)
|
|
m_spServiceProvider->QueryService(__uuidof(IStencilCache), __uuidof(IStencilCache), (void **) &m_spStencilCache);
|
|
ATLASSERT(m_spStencilCache);
|
|
|
|
if (!m_spDllCache)
|
|
pServiceProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void **) &m_spDllCache);
|
|
ATLASSERT(m_spDllCache);
|
|
|
|
pRequestInfo = m_spExtension->CreateRequest();
|
|
if (pRequestInfo == NULL)
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
|
|
pRequestInfo->dwRequestState = ATLSRV_STATE_BEGIN;
|
|
pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
|
|
pRequestInfo->pDllCache = m_spDllCache;
|
|
pRequestInfo->pExtension = m_spExtension;
|
|
pRequestInfo->pServerContext = &serverContext;
|
|
if (_pState && _pState->pParentInfo)
|
|
pRequestInfo->pUserData = _pState->pParentInfo->pUserData;
|
|
|
|
// Extract the file extension of the included file by searching
|
|
// for the first '.' from the right.
|
|
// Can't use _tcsrchr because we have to use the stencil's codepage
|
|
LPCSTR szDot = NULL;
|
|
LPCSTR szMark = pIncludeInfo->m_szFileName;
|
|
while (*szMark)
|
|
{
|
|
if (*szMark == '.')
|
|
szDot = szMark;
|
|
|
|
LPCSTR szNext = CharNextExA(GetCodePage(), szMark, 0);
|
|
if (szNext == szMark)
|
|
{
|
|
// embedded null
|
|
return HTTP_FAIL;
|
|
}
|
|
szMark = szNext;
|
|
}
|
|
|
|
if (szDot && _stricmp(szDot, c_AtlSRFExtension) == 0)
|
|
{
|
|
hcErr = m_spExtension->LoadDispatchFile(pIncludeInfo->m_szFileName, pRequestInfo);
|
|
if (hcErr)
|
|
return hcErr;
|
|
}
|
|
else if (szDot && _stricmp(szDot, ".dll") == 0)
|
|
{
|
|
// Get the handler name if they used the asdf.dll?Handler=Default notation
|
|
// REVIEW : case sensitivity on the "Handler"?
|
|
char szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1] = { '\0' };
|
|
|
|
LPSTR szStart = strstr(pIncludeInfo->m_szQueryString, "Handler=");
|
|
if (szStart &&
|
|
((szStart == pIncludeInfo->m_szQueryString) ||
|
|
((szStart > pIncludeInfo->m_szQueryString) && (*(szStart-1) == '&'))))
|
|
{
|
|
szStart += 8; // Skip past "Handler" and the "="
|
|
LPSTR szEnd = strchr(szStart, '&');
|
|
if (szEnd)
|
|
{
|
|
memcpy(szHandlerName, szStart, min((szEnd-szStart), ATL_MAX_HANDLER_NAME_LEN));
|
|
szHandlerName[min((szEnd-szStart), ATL_MAX_HANDLER_NAME_LEN)] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(szHandlerName, szStart);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(szHandlerName, "Default", sizeof("Default"));
|
|
}
|
|
|
|
pRequestInfo->dwRequestType = ATLSRV_REQUEST_DLL;
|
|
|
|
hcErr = m_spExtension->LoadRequestHandler(pIncludeInfo->m_szFileName, szHandlerName, pRequestInfo->pServerContext,
|
|
&pRequestInfo->hInstDll, &pRequestInfo->pHandler);
|
|
if (hcErr)
|
|
return hcErr;
|
|
}
|
|
|
|
DWORD dwStatus;
|
|
hcErr = pRequestInfo->pHandler->GetFlags(&dwStatus);
|
|
|
|
if (hcErr)
|
|
return hcErr;
|
|
|
|
if (dwStatus & (ATLSRV_INIT_USEASYNC | ATLSRV_INIT_USEASYNC_EX))
|
|
{
|
|
#ifdef _DEBUG
|
|
bAsyncAllowed = true;
|
|
#endif
|
|
CComObjectNoLock<CIncludeServerContext>* pNewServerContext = NULL;
|
|
ATLTRY(pNewServerContext = new CComObjectNoLock<CIncludeServerContext>);
|
|
if (pNewServerContext == NULL)
|
|
return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
|
|
pNewServerContext->Initialize(pWriteStream, pServerContext, pIncludeInfo);
|
|
pNewServerContext->AddRef();
|
|
pRequestInfo->pServerContext = pNewServerContext;
|
|
}
|
|
|
|
_ATLTRY
|
|
{
|
|
hcErr = pRequestInfo->pHandler->InitializeChild(pRequestInfo, m_spServiceProvider, pLookup);
|
|
if (hcErr)
|
|
return hcErr;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
pRequestInfo->pfnHandleRequest = &IRequestHandler::HandleRequest;
|
|
}
|
|
|
|
if (pRequestInfo)
|
|
{
|
|
if (!hcErr)
|
|
{
|
|
if (pRequestInfo->pServerContext == NULL)
|
|
pRequestInfo->pServerContext = &serverContext;
|
|
|
|
_ATLTRY
|
|
{
|
|
ATLASSERT(pRequestInfo->pfnHandleRequest != NULL);
|
|
hcErr = (pRequestInfo->pHandler->*pRequestInfo->pfnHandleRequest)(pRequestInfo, pServiceProvider);
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
hcErr = HTTP_FAIL;
|
|
}
|
|
|
|
if (pRequestInfo->pServerContext == &serverContext)
|
|
pRequestInfo->pServerContext = NULL;
|
|
|
|
#ifdef _DEBUG
|
|
// must use ATLSRV_INIT_USEASYNC to use ASYNC returns
|
|
if (IsAsyncStatus(hcErr))
|
|
ATLASSERT(bAsyncAllowed);
|
|
#endif
|
|
|
|
if (IsAsyncStatus(hcErr))
|
|
{
|
|
ATLASSERT(pState); // state is required for async
|
|
if (IsAsyncContinueStatus(hcErr))
|
|
{
|
|
_pState->pIncludeInfo = pRequestInfo;
|
|
pRequestInfo->dwRequestState = ATLSRV_STATE_CONTINUE;
|
|
}
|
|
else if (IsAsyncDoneStatus(hcErr))
|
|
m_spExtension->FreeRequest(pRequestInfo);
|
|
}
|
|
else
|
|
m_spExtension->FreeRequest(pRequestInfo);
|
|
}
|
|
}
|
|
else
|
|
hcErr = HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
|
|
return hcErr;
|
|
}
|
|
}; // class CHtmlStencil
|
|
|
|
|
|
__declspec(selectany) CCRTHeap CStencil::m_crtHeap;
|
|
|
|
//
|
|
// CHtmlTagReplacer
|
|
// This class manages CStencil based objects for HTTP requests. This class will retrieve
|
|
// CStencil based objects from the stencil cache, store CStencil based objects in the
|
|
// stencil cache and allocate and initialize CStencil based objects on a per reqeust
|
|
// basis. Typically, one instance of this class is created for each HTTP request. The
|
|
// instance is destroyed once the request has been completed.
|
|
template <class THandler, class StencilType=CHtmlStencil>
|
|
class CHtmlTagReplacer :
|
|
public ITagReplacerImpl<THandler>
|
|
{
|
|
protected:
|
|
typedef StencilType StencilType;
|
|
|
|
CSimpleArray<HINSTANCE> m_hInstHandlers;
|
|
typedef CAtlMap<CStringA, IRequestHandler*, CStringElementTraits<CStringA> > mapType;
|
|
mapType m_Handlers;
|
|
StencilType *m_pLoadedStencil;
|
|
WORD m_nCodePage;
|
|
CComPtr<IStencilCache> m_spStencilCache;
|
|
|
|
AtlServerRequest m_RequestInfo;
|
|
|
|
public:
|
|
// public members
|
|
|
|
CHtmlTagReplacer() throw() :
|
|
m_pLoadedStencil(NULL)
|
|
{
|
|
memset(&m_RequestInfo, 0x00, sizeof(m_RequestInfo));
|
|
m_nCodePage = CP_THREAD_ACP;
|
|
}
|
|
|
|
~CHtmlTagReplacer() throw()
|
|
{
|
|
// you should call FreeHandlers before
|
|
// the object is destructed
|
|
ATLASSERT(m_hInstHandlers.GetSize() == 0);
|
|
}
|
|
|
|
HTTP_CODE Initialize(AtlServerRequest *pRequestInfo, IHttpServerContext *pSafeSrvCtx=NULL) throw(...)
|
|
{
|
|
CComPtr<IServiceProvider> spServiceProvider;
|
|
THandler *pT = static_cast<THandler*>(this);
|
|
HRESULT hr = pT->GetContext(__uuidof(IServiceProvider), (void **)&spServiceProvider);
|
|
if (FAILED(hr))
|
|
return HTTP_FAIL;
|
|
|
|
spServiceProvider->QueryService(__uuidof(IStencilCache), __uuidof(IStencilCache), (void **) &m_spStencilCache);
|
|
if (!m_spStencilCache)
|
|
{
|
|
ATLASSERT(FALSE);
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
m_RequestInfo.cbSize = sizeof(m_RequestInfo);
|
|
m_RequestInfo.pServerContext = pSafeSrvCtx;
|
|
m_RequestInfo.pUserData = pRequestInfo->pUserData;
|
|
|
|
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();
|
|
THandler *pT = static_cast<THandler*>(this);
|
|
LPCSTR szFileName = pT->m_spServerContext->GetScriptPathTranslated();
|
|
|
|
if (!szFileName)
|
|
return HTTP_FAIL;
|
|
|
|
pStencil->SetBaseDirFromFile(szFileName);
|
|
if (!pStencil)
|
|
return HTTP_ERROR(500,ISE_SUBERR_OUTOFMEM);
|
|
|
|
// load the stencil and parse its replacements
|
|
if (HTTP_SUCCESS == pStencil->LoadFromResource(hInstResource,
|
|
szResourceID, szResourceType))
|
|
{
|
|
_ATLTRY
|
|
{
|
|
if (!pStencil->ParseReplacements(static_cast<ITagReplacer*>(this),
|
|
pStencil->GetBufferStart(), pStencil->GetBufferEnd()))
|
|
return HTTP_ERROR(500, ISE_SUBERR_BADSRF);
|
|
|
|
hcErr = FinishLoadStencil(pStencil, NULL);
|
|
if (!hcErr)
|
|
{
|
|
pStencil->FinishParseReplacements();
|
|
}
|
|
}
|
|
_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];
|
|
sprintf(szName, "%p/%u", hInstResource, nID);
|
|
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 HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM); // out of memory!
|
|
}
|
|
|
|
pStencil->SetBaseDirFromFile(szFileName);
|
|
|
|
// finish loading
|
|
hcErr = pStencil->LoadFile(szFileName);
|
|
if ( !hcErr )
|
|
{
|
|
_ATLTRY
|
|
{
|
|
if (!pStencil->ParseReplacements(static_cast<ITagReplacer*>(this),
|
|
pStencil->GetBufferStart(), pStencil->GetBufferEnd()))
|
|
return HTTP_ERROR(500, ISE_SUBERR_BADSRF);
|
|
|
|
hcErr = FinishLoadStencil(pStencil, pLookup);
|
|
if (!hcErr)
|
|
{
|
|
pStencil->FinishParseReplacements();
|
|
}
|
|
}
|
|
_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, void* pState = NULL) throw(...)
|
|
{
|
|
if (!m_pLoadedStencil)
|
|
return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
|
|
WORD nCodePage = m_pLoadedStencil->GetCodePage();
|
|
if (nCodePage != CP_ACP)
|
|
m_nCodePage = nCodePage;
|
|
|
|
HTTP_CODE hcErr = HTTP_FAIL;
|
|
|
|
hcErr = m_pLoadedStencil->Render(static_cast<ITagReplacer*>(this),
|
|
pStream, pState);
|
|
|
|
if (!IsAsyncStatus(hcErr) && m_pLoadedStencil->GetCacheItem())
|
|
m_spStencilCache->ReleaseStencil(m_pLoadedStencil->GetCacheItem());
|
|
|
|
return hcErr;
|
|
}
|
|
|
|
|
|
HTTP_CODE AddHandler(LPCSTR szName, IRequestHandler *pHandler, HINSTANCE hInstHandler) throw()
|
|
{
|
|
_ATLTRY
|
|
{
|
|
ATLASSERT(szName);
|
|
ATLASSERT(pHandler);
|
|
ATLASSERT(hInstHandler);
|
|
HTTP_CODE hcErr = HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
|
|
if (szName && *szName && pHandler)
|
|
{
|
|
if (m_Handlers.SetAt(szName, pHandler))
|
|
{
|
|
if (hInstHandler && m_hInstHandlers.Add(hInstHandler))
|
|
hcErr = HTTP_SUCCESS;
|
|
}
|
|
}
|
|
return hcErr;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return HTTP_FAIL;
|
|
}
|
|
}
|
|
|
|
void SendHandlerError(LPCSTR szHandlerName) throw()
|
|
{
|
|
#ifndef _ATL_STENCIL_SLIENT_ERRORS
|
|
m_pStream->WriteStream("Handler ");
|
|
m_pStream->WriteStream(szHandlerName);
|
|
m_pStream->WriteStream(" was not found");
|
|
#endif
|
|
}
|
|
|
|
//Implementation
|
|
|
|
void FreeHandlers() throw(...)
|
|
{
|
|
POSITION pos = m_Handlers.GetStartPosition();
|
|
while (pos)
|
|
{
|
|
m_Handlers.GetNextValue(pos)->Release();
|
|
}
|
|
m_Handlers.RemoveAll();
|
|
|
|
int nLen = m_hInstHandlers.GetSize();
|
|
THandler *pT = static_cast<THandler *>(this);
|
|
CComPtr<IDllCache> spDllCache;
|
|
pT->m_spServiceProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache),
|
|
(void **)&spDllCache);
|
|
for (int i=0; i<nLen; i++)
|
|
{
|
|
spDllCache->Free(m_hInstHandlers[i]);
|
|
}
|
|
m_hInstHandlers.RemoveAll();
|
|
}
|
|
|
|
StencilType* GetNewCacheStencil() throw(...)
|
|
{
|
|
StencilType *pStencil = NULL;
|
|
THandler *pT = static_cast<THandler *>(this);
|
|
IAtlMemMgr *pMemMgr;
|
|
if (FAILED(pT->m_spServiceProvider->QueryService(__uuidof(IAtlMemMgr), __uuidof(IAtlMemMgr), (void **)&pMemMgr)))
|
|
pMemMgr = NULL;
|
|
|
|
ATLTRY(pStencil = new StencilType(pMemMgr));
|
|
if (pStencil != NULL)
|
|
pStencil->Initialize(pT->m_spServiceProvider);
|
|
return pStencil;
|
|
}
|
|
|
|
HTTP_CODE CacheStencil(
|
|
LPCSTR szName,
|
|
StencilType* pStencilData) throw()
|
|
{
|
|
THandler *pT = static_cast<THandler *>(this);
|
|
HRESULT hr = E_FAIL;
|
|
|
|
HCACHEITEM hCacheItem = NULL;
|
|
|
|
hr = m_spStencilCache->CacheStencil(szName,
|
|
pStencilData,
|
|
sizeof(StencilType*),
|
|
&hCacheItem,
|
|
pT->m_hInstHandler,
|
|
static_cast<IMemoryCacheClient*>(pStencilData));
|
|
|
|
if (hr == S_OK && hCacheItem)
|
|
pStencilData->SetCacheItem(hCacheItem);
|
|
|
|
return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
|
|
}
|
|
|
|
StencilType *FindCacheStencil(LPCSTR szName) throw()
|
|
{
|
|
if (!szName || !m_spStencilCache)
|
|
return NULL;
|
|
|
|
StencilType *pStencilData = NULL;
|
|
|
|
HCACHEITEM hStencil;
|
|
|
|
if (m_spStencilCache->LookupStencil(szName, &hStencil) != S_OK)
|
|
return NULL;
|
|
|
|
m_spStencilCache->GetStencil(hStencil, reinterpret_cast<void **>(&pStencilData));
|
|
|
|
return pStencilData;
|
|
}
|
|
|
|
void FreeCacheStencil(StencilType* pStencilData) throw()
|
|
{
|
|
CComPtr<IMemoryCacheClient> spCacheClient;
|
|
|
|
if (m_spStencilCache->QueryInterface(__uuidof(IMemoryCacheClient), (void **) &spCacheClient) == S_OK)
|
|
spCacheClient->Free(pStencilData);
|
|
}
|
|
|
|
HTTP_CODE GetHandlerOffset(LPCSTR szHandlerName, DWORD* pdwOffset) throw()
|
|
{
|
|
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);
|
|
return HTTP_SUCCESS;
|
|
}
|
|
*pdwOffset = 0;
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
HTTP_CODE GetReplacementObject(DWORD dwObjOffset, ITagReplacer **ppReplacer) throw(...)
|
|
{
|
|
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);
|
|
|
|
ATLASSERT(pHandler != NULL);
|
|
|
|
hr = pHandler->QueryInterface(__uuidof(ITagReplacer), (void**)ppReplacer);
|
|
|
|
if (hr != S_OK)
|
|
return HTTP_FAIL;
|
|
|
|
return HTTP_SUCCESS;
|
|
}
|
|
|
|
// This is where we would actually load any extra request
|
|
// handlers the HTML stencil might have parsed for us.
|
|
HTTP_CODE FinishLoadStencil(StencilType *pStencil, IHttpRequestLookup * pLookup = NULL) throw(...)
|
|
{
|
|
THandler *pT = static_cast<THandler *>(this);
|
|
ATLASSERT(pStencil);
|
|
if (!pStencil)
|
|
return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED); // unexpected condition
|
|
m_pLoadedStencil = pStencil;
|
|
//load extra handlers if there are any
|
|
StencilType::mapType *pExtraHandlersMap =
|
|
pStencil->GetExtraHandlers();
|
|
|
|
if (pExtraHandlersMap)
|
|
{
|
|
POSITION pos = pExtraHandlersMap->GetStartPosition();
|
|
CStringA name;
|
|
CStringPair path;
|
|
IRequestHandler *pHandler;
|
|
HINSTANCE hInstHandler;
|
|
while(pos)
|
|
{
|
|
pExtraHandlersMap->GetNextAssoc(pos, name, path);
|
|
pHandler = NULL;
|
|
hInstHandler = NULL;
|
|
HTTP_CODE hcErr = pT->m_spExtension->LoadRequestHandler(path.strDllPath, path.strHandlerName,
|
|
pT->m_spServerContext,
|
|
&hInstHandler,
|
|
&pHandler);
|
|
if (!hcErr)
|
|
{
|
|
_ATLTRY
|
|
{
|
|
//map the name to the pointer to request handler
|
|
m_Handlers.SetAt(name, pHandler);
|
|
//store HINSTANCE of handler
|
|
m_hInstHandlers.Add(hInstHandler);
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return HTTP_FAIL;
|
|
}
|
|
|
|
if (pLookup)
|
|
{
|
|
hcErr = pHandler->InitializeChild(&m_RequestInfo, pT->m_spServiceProvider, pLookup);
|
|
if (hcErr != HTTP_SUCCESS)
|
|
return hcErr;
|
|
}
|
|
|
|
}
|
|
else
|
|
return hcErr;
|
|
}
|
|
}
|
|
return HTTP_SUCCESS;
|
|
}
|
|
}; // class CHtmlTagReplacer
|
|
|
|
|
|
// CRequestHandlerT
|
|
// This is the base class for all user request handlers. This class implements
|
|
// the IReplacementHandler interface whose methods will be called to render HTML
|
|
// into a stream. The stream will be returned as the HTTP response upon completion
|
|
// of the HTTP request.
|
|
template < class THandler,
|
|
class ThreadModel=CComSingleThreadModel,
|
|
class TagReplacerType=CHtmlTagReplacer<THandler>
|
|
>
|
|
class CRequestHandlerT :
|
|
public TagReplacerType,
|
|
public CComObjectRootEx<ThreadModel>,
|
|
public IRequestHandlerImpl<THandler>
|
|
{
|
|
protected:
|
|
CStencilState m_state;
|
|
CComObjectStackEx<CIDServerContext> m_SafeSrvCtx;
|
|
typedef CRequestHandlerT<THandler, ThreadModel, TagReplacerType> _requestHandler;
|
|
|
|
public:
|
|
BEGIN_COM_MAP(_requestHandler)
|
|
COM_INTERFACE_ENTRY(IRequestHandler)
|
|
COM_INTERFACE_ENTRY(ITagReplacer)
|
|
END_COM_MAP()
|
|
|
|
// public CRequestHandlerT members
|
|
CHttpResponse m_HttpResponse;
|
|
CHttpRequest m_HttpRequest;
|
|
ATLSRV_REQUESTTYPE m_dwRequestType;
|
|
AtlServerRequest* m_pRequestInfo;
|
|
|
|
CRequestHandlerT() throw()
|
|
{
|
|
m_hInstHandler = NULL;
|
|
m_dwAsyncFlags = 0;
|
|
m_pRequestInfo = NULL;
|
|
}
|
|
|
|
~CRequestHandlerT() throw()
|
|
{
|
|
FreeHandlers(); // free handlers held by CTagReplacer
|
|
}
|
|
|
|
void ClearResponse() throw()
|
|
{
|
|
m_HttpResponse.ClearResponse();
|
|
}
|
|
// Where user initialization should take place
|
|
HTTP_CODE ValidateAndExchange() throw()
|
|
{
|
|
return HTTP_SUCCESS; // continue processing request
|
|
}
|
|
|
|
// Where user Uninitialization should take place
|
|
HTTP_CODE Uninitialize(HTTP_CODE hcError) throw()
|
|
{
|
|
return hcError;
|
|
}
|
|
|
|
HTTP_CODE InitializeInternal(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider) throw()
|
|
{
|
|
// 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) throw(...)
|
|
{
|
|
HTTP_CODE hcErr = HTTP_FAIL;
|
|
ATLASSERT(pRequestInfo);
|
|
ATLASSERT(pProvider);
|
|
|
|
THandler* pT = static_cast<THandler*>(this);
|
|
hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
|
|
if (!hcErr)
|
|
{
|
|
m_HttpResponse.Initialize(m_spServerContext);
|
|
hcErr = pT->CheckValidRequest();
|
|
if (!hcErr)
|
|
{
|
|
hcErr = HTTP_FAIL;
|
|
if (m_HttpRequest.Initialize(m_spServerContext,
|
|
pT->MaxFormSize(),
|
|
pT->FormFlags()))
|
|
{
|
|
if (m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
|
|
{
|
|
hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
|
|
if (!hcErr)
|
|
{
|
|
hcErr = pT->ValidateAndExchange();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hcErr;
|
|
}
|
|
|
|
HTTP_CODE InitializeChild(
|
|
AtlServerRequest *pRequestInfo,
|
|
IServiceProvider *pProvider,
|
|
IHttpRequestLookup *pRequestLookup) throw(...)
|
|
{
|
|
ATLASSERT(pRequestInfo);
|
|
ATLASSERT(pProvider);
|
|
|
|
THandler *pT = static_cast<THandler*>(this);
|
|
HTTP_CODE hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
|
|
if (hcErr)
|
|
return hcErr;
|
|
|
|
if (pRequestLookup)
|
|
{
|
|
// initialize with the pRequestLookup
|
|
m_HttpResponse.Initialize(pRequestLookup);
|
|
|
|
// REVIEW: 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)
|
|
m_HttpResponse.Initialize(m_spServerContext);
|
|
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
|
|
// REVIEW: necessary?
|
|
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*/) throw(...)
|
|
{
|
|
ATLASSERT(pRequestInfo);
|
|
|
|
THandler *pT = static_cast<THandler *>(this);
|
|
HTTP_CODE hcErr = HTTP_SUCCESS;
|
|
|
|
if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
|
|
{
|
|
m_dwRequestType = pRequestInfo->dwRequestType;
|
|
|
|
if (pRequestInfo->dwRequestType==ATLSRV_REQUEST_STENCIL)
|
|
{
|
|
LPCSTR szFileName = pRequestInfo->pServerContext->GetScriptPathTranslated();
|
|
hcErr = HTTP_FAIL;
|
|
if (szFileName)
|
|
hcErr = pT->LoadStencil(szFileName, static_cast<IHttpRequestLookup *>(&m_HttpRequest));
|
|
}
|
|
}
|
|
else if (pRequestInfo->dwRequestState == ATLSRV_STATE_CONTINUE)
|
|
m_HttpResponse.ClearContent();
|
|
|
|
#ifdef ATL_DEBUG_STENCILS
|
|
if (m_pLoadedStencil && !m_pLoadedStencil->ParseSuccessful())
|
|
{
|
|
// An error or series of errors occurred in parsing the stencil
|
|
m_pLoadedStencil->RenderErrors(static_cast<IWriteStream*>(&m_HttpResponse));
|
|
}
|
|
#endif
|
|
|
|
if (hcErr == HTTP_SUCCESS && m_pLoadedStencil)
|
|
{
|
|
// if anything other than HTTP_SUCCESS is returned during
|
|
// the rendering of replacement tags, we return that value
|
|
// here.
|
|
hcErr = pT->RenderStencil(static_cast<IWriteStream*>(&m_HttpResponse), &m_state);
|
|
|
|
if (hcErr == HTTP_SUCCESS && !m_HttpResponse.Flush(TRUE))
|
|
hcErr = HTTP_FAIL;
|
|
}
|
|
|
|
if (IsAsyncFlushStatus(hcErr))
|
|
{
|
|
pRequestInfo->pszBuffer = LPCSTR(m_HttpResponse.m_strContent);
|
|
pRequestInfo->dwBufferLen = m_HttpResponse.m_strContent.GetLength();
|
|
}
|
|
|
|
if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN || IsAsyncDoneStatus(hcErr))
|
|
return pT->Uninitialize(hcErr);
|
|
|
|
else if (!IsAsyncStatus(hcErr))
|
|
m_HttpResponse.ClearContent();
|
|
|
|
return hcErr;
|
|
}
|
|
|
|
HTTP_CODE ServerTransferRequest(LPCSTR szRequest, bool bContinueAfterTransfer=false,
|
|
int nCodePage = 0, void *pState = NULL) throw(...)
|
|
{
|
|
return m_spExtension->TransferRequest(
|
|
m_pRequestInfo,
|
|
m_spServiceProvider,
|
|
static_cast<IWriteStream*>(&m_HttpResponse),
|
|
static_cast<IHttpRequestLookup*>(&m_HttpRequest),
|
|
szRequest,
|
|
nCodePage == 0 ? m_nCodePage : nCodePage,
|
|
bContinueAfterTransfer,
|
|
pState);
|
|
}
|
|
|
|
inline DWORD MaxFormSize() throw()
|
|
{
|
|
return DEFAULT_MAX_FORM_SIZE;
|
|
}
|
|
|
|
inline DWORD FormFlags() throw()
|
|
{
|
|
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() throw()
|
|
{
|
|
LPCSTR szMethod = NULL;
|
|
ATLASSERT(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) throw()
|
|
{
|
|
if (!ppv)
|
|
return E_POINTER;
|
|
if (InlineIsEqualGUID(riid, __uuidof(IHttpServerContext)))
|
|
{
|
|
return m_spServerContext.CopyTo((IHttpServerContext **)ppv);
|
|
}
|
|
if (InlineIsEqualGUID(riid, __uuidof(IHttpRequestLookup)))
|
|
{
|
|
*ppv = static_cast<IHttpRequestLookup*>(&m_HttpRequest);
|
|
m_HttpRequest.AddRef();
|
|
return S_OK;
|
|
}
|
|
if (InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
|
|
{
|
|
*ppv = m_spServiceProvider;
|
|
m_spServiceProvider.p->AddRef();
|
|
return S_OK;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
template <typename Interface>
|
|
HRESULT GetContext(Interface** ppInterface) throw()
|
|
{
|
|
return GetContext(__uuidof(Interface), reinterpret_cast<void**>(ppInterface));
|
|
}
|
|
}; // class CRequestHandlerT
|
|
|
|
} // namespace ATL
|
|
|
|
#pragma warning( pop )
|
|
|
|
#endif // __ATLSTENCIL_H__
|