Add pgs color type setting
[xy_vsfilter.git] / include / atl / atlstencil.h
blob8694a0f4253a641003b6d12836566c0e6f0197d7
1 // This is a part of the Active Template Library.
2 // Copyright (C) Microsoft Corporation
3 // All rights reserved.
4 //
5 // This source code is only intended as a supplement to the
6 // Active Template Library Reference and related
7 // electronic documentation provided with the library.
8 // See these sources for detailed information regarding the
9 // Active Template Library product.
11 #ifndef __ATLSTENCIL_H__
12 #define __ATLSTENCIL_H__
14 #pragma once
16 #include <atlisapi.h>
17 #include <atlfile.h>
18 #include <atlutil.h>
19 #include <math.h>
21 #ifdef ATL_DEBUG_STENCILS
22 #include <atlsrvres.h>
24 #ifndef ATL_STENCIL_MAX_ERROR_LEN
25 #define ATL_STENCIL_MAX_ERROR_LEN 256
26 #endif
27 #endif // ATL_DEBUG_STENCILS
29 #ifndef ATL_NO_MLANG
30 #include <mlang.h>
31 #endif
33 #ifndef _ATL_NO_DEFAULT_LIBS
34 #pragma comment(lib, "shlwapi.lib")
35 #endif // !_ATL_NO_DEFAULT_LIBS
37 #pragma warning( push )
38 #pragma warning(disable: 4127) // conditional expression is constant
39 #pragma warning(disable: 4511) // copy constructor could not be generated
40 #pragma warning(disable: 4512) // assignment operator could not be generated
41 #pragma warning(disable: 4702) // assignment operator could not be generated
42 #pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
43 #pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
44 #pragma warning(disable: 4191) // unsafe conversion from 'functionptr1' to 'functionptr2'
47 #pragma pack(push,_ATL_PACKING)
48 namespace ATL {
50 // Token types
51 // These tags are token tags for the standard tag replacer implementation
52 extern __declspec(selectany) const DWORD STENCIL_TEXTTAG = 0x00000000;
53 extern __declspec(selectany) const DWORD STENCIL_REPLACEMENT = 0x00000001;
54 extern __declspec(selectany) const DWORD STENCIL_ITERATORSTART = 0x00000002;
55 extern __declspec(selectany) const DWORD STENCIL_ITERATOREND = 0x00000003;
56 extern __declspec(selectany) const DWORD STENCIL_CONDITIONALSTART = 0x00000004;
57 extern __declspec(selectany) const DWORD STENCIL_CONDITIONALELSE = 0x00000005;
58 extern __declspec(selectany) const DWORD STENCIL_CONDITIONALEND = 0x00000006;
59 extern __declspec(selectany) const DWORD STENCIL_STENCILINCLUDE = 0x00000007;
60 extern __declspec(selectany) const DWORD STENCIL_STATICINCLUDE = 0x00000008;
61 extern __declspec(selectany) const DWORD STENCIL_LOCALE = 0x00000009;
62 extern __declspec(selectany) const DWORD STENCIL_CODEPAGE = 0x0000000a;
64 // The base for user defined token types
65 extern __declspec(selectany) const DWORD STENCIL_USER_TOKEN_BASE = 0x00001000;
67 // Symbols to use in error handling in the stencil processor
68 #define STENCIL_INVALIDINDEX 0xFFFFFFFF
69 #define STENCIL_INVALIDOFFSET 0xFFFFFFFF
71 // error codes
72 #define STENCIL_SUCCESS HTTP_SUCCESS
73 #define STENCL_FAIL HTTP_FAIL
75 #define STENCIL_BASIC_MAP 0
76 #define STENCIL_ATTR_MAP 1
78 #ifndef ATL_MAX_METHOD_NAME_LEN
79 #define ATL_MAX_METHOD_NAME_LEN 64
80 #endif
82 #ifndef ATL_MAX_BLOCK_STACK
83 #define ATL_MAX_BLOCK_STACK 128
84 #endif
86 template <class TBase, typename T>
87 struct CTagReplacerMethodsEx
89 typedef HTTP_CODE (TBase::*REPLACE_FUNC)();
90 typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX)(T*);
91 typedef HTTP_CODE (TBase::*PARSE_FUNC)(IAtlMemMgr *, LPCSTR, T**);
92 typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX_V)(void *);
93 typedef HTTP_CODE (TBase::*PARSE_FUNC_V)(IAtlMemMgr *, LPCSTR, void**);
95 static REPLACE_FUNC_EX_V CheckRepl(REPLACE_FUNC p) throw()
97 return (REPLACE_FUNC_EX_V) p;
100 static REPLACE_FUNC_EX_V CheckReplEx(REPLACE_FUNC_EX p) throw()
102 return (REPLACE_FUNC_EX_V) p;
105 static PARSE_FUNC_V CheckParse(PARSE_FUNC p) throw()
107 return (PARSE_FUNC_V) p;
111 template <class TBase>
112 struct CTagReplacerMethods
114 union
116 HTTP_CODE (TBase::*pfnMethodEx)(void *);
117 HTTP_CODE (TBase::*pfnMethod)();
119 HTTP_CODE (TBase::*pfnParse)(IAtlMemMgr *pMemMgr, LPCSTR, void **);
122 #define REPLACEMENT_ENTRY_DEFAULT 0
123 #define REPLACEMENT_ENTRY_ARGS 1
125 template <class TBase>
126 struct CTagReplacerMethodEntry
128 int nType; // REPLACEMENT_ENTRY_*
129 LPCSTR szMethodName;
130 CTagReplacerMethods<TBase> Methods;
134 #define BEGIN_REPLACEMENT_METHOD_MAP(className)\
135 public:\
136 void GetReplacementMethodMap(const ATL::CTagReplacerMethodEntry<className> ** ppOut) const\
138 typedef className __className;\
139 static const ATL::CTagReplacerMethodEntry<className> methods[] = {
141 #define REPLACEMENT_METHOD_ENTRY(methodName, methodFunc)\
142 { 0, methodName, { ATL::CTagReplacerMethodsEx<__className, void>::CheckRepl(&__className::methodFunc), NULL } },
144 #define REPLACEMENT_METHOD_ENTRY_EX(methodName, methodFunc, paramType, parseFunc)\
145 { 1, methodName, { ATL::CTagReplacerMethodsEx<__className, paramType>::CheckReplEx(&__className::methodFunc), ATL::CTagReplacerMethodsEx<__className, paramType>::CheckParse(&__className::parseFunc) } },
147 #define REPLACEMENT_METHOD_ENTRY_EX_STR(methodName, methodFunc) \
148 { 1, methodName, { ATL::CTagReplacerMethodsEx<__className, char>::CheckReplEx(&__className::methodFunc), ATL::CTagReplacerMethodsEx<__className, char>::CheckParse(&__className::DefaultParseString) } },
150 #define END_REPLACEMENT_METHOD_MAP()\
151 { 0, NULL, NULL } };\
152 *ppOut = methods;\
155 #define BEGIN_ATTR_REPLACEMENT_METHOD_MAP(className)\
156 public:\
157 void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<className> ** ppOut) const\
159 typedef className __className;\
160 static const ATL::CTagReplacerMethodEntry<className> methods[] = {
162 #define END_ATTR_REPLACEMENT_METHOD_MAP()\
163 { NULL, NULL, NULL } };\
164 *ppOut = methods;\
167 template <class T>
168 class ITagReplacerImpl : public ITagReplacer
170 protected:
171 IWriteStream *m_pStream;
173 public:
174 typedef HTTP_CODE (T::*REPLACEMENT_METHOD)();
175 typedef HTTP_CODE (T::*REPLACEMENT_METHOD_EX)(void *pvParam);
177 ITagReplacerImpl() throw()
178 :m_pStream(NULL)
182 IWriteStream *SetStream(IWriteStream *pStream)
184 IWriteStream *pRetStream = m_pStream;
185 m_pStream = pStream;
186 return pRetStream;
189 // Looks up the replacement method offset. Optionally, it will
190 // look up the replacement method and object offset of an alternate
191 // tag replacer.
192 HTTP_CODE FindReplacementOffset(
193 LPCSTR szMethodName,
194 DWORD *pdwMethodOffset,
195 LPCSTR szHandlerName,
196 DWORD *pdwHandlerOffset,
197 DWORD *pdwMap, void **ppvParam, IAtlMemMgr *pMemMgr)
199 ATLENSURE(szMethodName != NULL);
200 ATLENSURE(pdwMethodOffset != NULL);
201 ATLENSURE((szHandlerName == NULL && pdwHandlerOffset == NULL) ||
202 (szHandlerName != NULL && pdwHandlerOffset != NULL));
203 ATLENSURE(pdwMap != NULL);
204 ATLENSURE(ppvParam != NULL);
205 ATLENSURE(pMemMgr != NULL);
207 // we at least have to be looking up a method offset
208 if (!pdwMethodOffset || !szMethodName)
209 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
211 *pdwMethodOffset = STENCIL_INVALIDOFFSET;
212 HTTP_CODE hcErr = HTTP_FAIL;
213 T *pT = static_cast<T *>(this);
215 char szName[ATL_MAX_METHOD_NAME_LEN+1];
217 // if a handler name was supplied, we will try to
218 // find a different object to handle the method
219 if (szHandlerName && *szHandlerName)
221 if (!pdwHandlerOffset)
222 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
224 hcErr = pT->GetHandlerOffset(szHandlerName, pdwHandlerOffset);
225 // got the alternate handler, now look up the method offset on
226 // the handler.
227 if (!hcErr)
229 CComPtr<ITagReplacer> spAltTagReplacer;
230 hcErr = pT->GetReplacementObject(*pdwHandlerOffset, &spAltTagReplacer);
231 if (!hcErr)
232 hcErr = spAltTagReplacer->FindReplacementOffset(szMethodName, pdwMethodOffset,
233 NULL, NULL, pdwMap, ppvParam, pMemMgr);
234 return hcErr;
236 else
237 return hcErr;
240 if (!SafeStringCopy(szName, szMethodName))
242 return AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
245 // check for params
246 char *szLeftPar = strchr(szName, '(');
247 if (szLeftPar)
249 *szLeftPar = '\0';
250 szLeftPar++;
252 char *szRightPar = strchr(szLeftPar, ')');
253 if (!szRightPar)
254 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
256 *szRightPar = '\0';
258 szMethodName = szName;
262 // No handler name is specified, so we look up the method name in
263 // T's replacement method map
264 const CTagReplacerMethodEntry<T> *pEntry = NULL;
265 pT->GetReplacementMethodMap(&pEntry);
267 hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
268 if (hcErr != HTTP_SUCCESS)
270 pT->GetAttrReplacementMethodMap(&pEntry);
271 hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
272 if (hcErr == HTTP_SUCCESS)
273 *pdwMap = STENCIL_ATTR_MAP;
275 else
277 *pdwMap = STENCIL_BASIC_MAP;
280 // This assert will be triggered if arguments are passed to a replacement method that doesn't handle them
281 ATLASSERT( szLeftPar == NULL || (szLeftPar != NULL && (pEntry != NULL && pEntry[*pdwMethodOffset].Methods.pfnParse != NULL)) );
283 if (hcErr == HTTP_SUCCESS && pEntry && pEntry[*pdwMethodOffset].Methods.pfnParse)
284 hcErr = (pT->*pEntry[*pdwMethodOffset].Methods.pfnParse)(pMemMgr, szLeftPar, ppvParam);
285 return hcErr;
288 HTTP_CODE FindReplacementOffsetInMap(
289 LPCSTR szMethodName,
290 LPDWORD pdwMethodOffset,
291 const CTagReplacerMethodEntry<T> *pEntry) throw()
293 if (pEntry == NULL)
294 return HTTP_FAIL;
296 const CTagReplacerMethodEntry<T> *pEntryHead = pEntry;
298 while (pEntry->szMethodName)
300 if (strcmp(pEntry->szMethodName, szMethodName) == 0)
302 if (pEntry->Methods.pfnMethod)
304 *pdwMethodOffset = (DWORD)(pEntry-pEntryHead);
305 return HTTP_SUCCESS;
308 pEntry++;
311 return HTTP_FAIL;
315 // Used to render a single replacement tag into a stream.
316 // Looks up a pointer to a member function in user code by offseting into the users
317 // replacement map. Much faster than the other overload of this function since
318 // no string compares are performed.
319 HTTP_CODE RenderReplacement(DWORD dwFnOffset, DWORD dwObjOffset, DWORD dwMap, void *pvParam)
321 HTTP_CODE hcErr = HTTP_FAIL;
322 T *pT = static_cast<T *>(this);
324 // if we were not passed an object offset, then we assume
325 // that the function at dwFnOffset is in T's replacement
326 // map
327 if (dwObjOffset == STENCIL_INVALIDOFFSET)
329 // call a function in T's replacement map
330 ATLASSERT(dwFnOffset != STENCIL_INVALIDOFFSET);
331 const CTagReplacerMethodEntry<T> *pEntry = NULL;
332 if (dwMap == STENCIL_BASIC_MAP)
333 pT->GetReplacementMethodMap(&pEntry);
334 else
335 pT->GetAttrReplacementMethodMap(&pEntry);
336 if (pEntry)
338 if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_DEFAULT)
340 REPLACEMENT_METHOD pfn = NULL;
341 pfn = pEntry[dwFnOffset].Methods.pfnMethod;
342 ATLASSERT(pfn);
343 if (pfn)
345 hcErr = (pT->*pfn)();
348 else if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_ARGS)
350 REPLACEMENT_METHOD_EX pfn = NULL;
351 pfn = pEntry[dwFnOffset].Methods.pfnMethodEx;
352 ATLASSERT(pfn);
353 if (pfn)
355 hcErr = (pT->*pfn)(pvParam);
358 else
360 // unknown entry type
361 ATLASSERT(FALSE);
365 else
367 // otherwise, we were passed an object offset. The object
368 // offset is a dword ID that T can use to look up the
369 // ITagReplacer* of a tag replacer that will render this
370 // replacement.
371 CComPtr<ITagReplacer> spAltReplacer = NULL;
372 if (!pT->GetReplacementObject(dwObjOffset, &spAltReplacer))
374 spAltReplacer->SetStream(m_pStream);
375 hcErr = spAltReplacer->RenderReplacement(dwFnOffset, STENCIL_INVALIDOFFSET, dwMap, pvParam);
378 return hcErr;
381 // Default GetHandlerOffset, does nothing
382 HTTP_CODE GetHandlerOffset(LPCSTR /*szHandlerName*/, DWORD* pdwOffset)
384 if (pdwOffset)
385 *pdwOffset = 0;
386 return HTTP_FAIL;
389 // Default GetReplacementObject, does nothing
390 HTTP_CODE GetReplacementObject(DWORD /*dwObjOffset*/, ITagReplacer **ppReplacer)
392 if (ppReplacer)
393 *ppReplacer = NULL;
394 return HTTP_FAIL;
397 void GetReplacementMethodMap(const CTagReplacerMethodEntry<T> ** ppOut) const
399 static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
400 *ppOut = methods;
403 void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<T> **ppOut) const
405 static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
406 *ppOut = methods;
409 HRESULT GetContext(REFIID, void**)
411 return E_NOINTERFACE;
414 virtual HINSTANCE GetResourceInstance()
416 return GetModuleHandle(NULL);
419 HTTP_CODE DefaultParseString(IAtlMemMgr *pMemMgr, LPCSTR szParams, char **ppParam) throw(...)
421 ATLENSURE( pMemMgr != NULL );
422 ATLENSURE( szParams != NULL );
423 ATLENSURE( ppParam != NULL );
425 size_t nLen = strlen(szParams);
426 if (nLen)
428 nLen++;
429 *ppParam = (char *) pMemMgr->Allocate(nLen);
430 if (*ppParam)
431 Checked::memcpy_s(*ppParam, nLen, szParams, nLen);
432 else
433 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
436 return HTTP_SUCCESS;
439 HTTP_CODE DefaultParseUChar(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned char **ppParam) throw(...)
441 ATLENSURE( pMemMgr != NULL );
442 ATLENSURE( szParams != NULL );
443 ATLENSURE( ppParam != NULL );
445 *ppParam = (unsigned char *) pMemMgr->Allocate(sizeof(unsigned char));
446 if (*ppParam)
448 char *szEnd;
449 **ppParam = (unsigned char) strtoul(szParams, &szEnd, 10);
451 else
453 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
455 return HTTP_SUCCESS;
458 HTTP_CODE DefaultParseShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, short **ppParam) throw(...)
460 ATLENSURE( pMemMgr != NULL );
461 ATLENSURE( szParams != NULL );
462 ATLENSURE( ppParam != NULL );
464 *ppParam = (short *) pMemMgr->Allocate(sizeof(short));
465 if (*ppParam)
467 **ppParam = (short)atoi(szParams);
469 else
471 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
473 return HTTP_SUCCESS;
476 HTTP_CODE DefaultParseUShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned short **ppParam) throw(...)
478 ATLENSURE( pMemMgr != NULL );
479 ATLENSURE( szParams != NULL );
480 ATLENSURE( ppParam != NULL );
482 *ppParam = (unsigned short *) pMemMgr->Allocate(sizeof(short));
483 if (*ppParam)
485 char *szEnd;
486 **ppParam = (unsigned short) strtoul(szParams, &szEnd, 10);
488 else
490 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
492 return HTTP_SUCCESS;
495 HTTP_CODE DefaultParseInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, int **ppParam) throw(...)
497 ATLENSURE( pMemMgr != NULL );
498 ATLENSURE( szParams != NULL );
499 ATLENSURE( ppParam != NULL );
501 *ppParam = (int *) pMemMgr->Allocate(sizeof(int));
502 if (*ppParam)
504 **ppParam = atoi(szParams);
506 else
508 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
510 return HTTP_SUCCESS;
513 HTTP_CODE DefaultParseUInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned int **ppParam) throw(...)
515 ATLENSURE( pMemMgr != NULL );
516 ATLENSURE( szParams != NULL );
517 ATLENSURE( ppParam != NULL );
519 *ppParam = (unsigned int *) pMemMgr->Allocate(sizeof(unsigned int));
520 if (*ppParam)
522 char *szEnd;
523 **ppParam = strtoul(szParams, &szEnd, 10);
525 else
527 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
529 return HTTP_SUCCESS;
532 HTTP_CODE DefaultParseInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, __int64 **ppParam) throw(...)
534 ATLENSURE( pMemMgr != NULL );
535 ATLENSURE( szParams != NULL );
536 ATLENSURE( ppParam != NULL );
538 *ppParam = (__int64 *) pMemMgr->Allocate(sizeof(__int64));
539 if (*ppParam)
541 **ppParam = _atoi64(szParams);
543 else
545 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
547 return HTTP_SUCCESS;
550 HTTP_CODE DefaultParseUInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned __int64 **ppParam) throw(...)
552 ATLENSURE( pMemMgr != NULL );
553 ATLENSURE( szParams != NULL );
554 ATLENSURE( ppParam != NULL );
556 *ppParam = (unsigned __int64 *) pMemMgr->Allocate(sizeof(unsigned __int64));
557 if (*ppParam)
559 char *szEnd;
560 **ppParam = _strtoui64(szParams, &szEnd, 10);
562 else
564 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
566 return HTTP_SUCCESS;
569 HTTP_CODE DefaultParseBool(IAtlMemMgr *pMemMgr, LPCSTR szParams, bool **ppParam) throw(...)
571 ATLENSURE( pMemMgr != NULL );
572 ATLENSURE( szParams != NULL );
573 ATLENSURE( ppParam != NULL );
575 *ppParam = (bool *) pMemMgr->Allocate(sizeof(bool));
576 if (*ppParam)
578 if (!_strnicmp(szParams, "true", sizeof("true")-sizeof('\0')))
579 **ppParam = true;
580 else
581 **ppParam = false;
583 else
585 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
588 return HTTP_SUCCESS;
591 HTTP_CODE DefaultParseDouble(IAtlMemMgr *pMemMgr, LPCSTR szParams, double **ppParam) throw(...)
593 ATLENSURE( pMemMgr != NULL );
594 ATLENSURE( szParams != NULL );
595 ATLENSURE( ppParam != NULL );
597 *ppParam = (double *) pMemMgr->Allocate(sizeof(double));
598 if (*ppParam)
600 **ppParam = atof(szParams);
602 else
604 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
606 return HTTP_SUCCESS;
609 HTTP_CODE DefaultParseFloat(IAtlMemMgr *pMemMgr, LPCSTR szParams, float **ppParam) throw(...)
611 ATLENSURE( pMemMgr != NULL );
612 ATLENSURE( szParams != NULL );
613 ATLENSURE( ppParam != NULL );
615 errno_t errnoValue = 0;
616 *ppParam = (float *) pMemMgr->Allocate(sizeof(float));
617 if (*ppParam)
619 errno_t saveErrno = Checked::get_errno();
620 Checked::set_errno(0);
621 **ppParam = (float) atof(szParams);
622 errnoValue = Checked::get_errno();
623 Checked::set_errno(saveErrno);
625 else
627 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
629 if ((**ppParam == -HUGE_VAL) || (**ppParam == HUGE_VAL) || (errnoValue == ERANGE))
631 return HTTP_FAIL;
633 return HTTP_SUCCESS;
637 inline LPCSTR SkipSpace(LPCSTR sz, WORD nCodePage) throw()
639 if (sz == NULL)
640 return NULL;
642 while (isspace(static_cast<unsigned char>(*sz)))
643 sz = CharNextExA(nCodePage, sz, 0);
644 return sz;
647 inline LPCSTR RSkipSpace(LPCSTR pStart, LPCSTR sz, WORD nCodePage) throw()
649 if (sz == NULL || pStart == NULL)
650 return NULL;
652 while (isspace(static_cast<unsigned char>(*sz)) && sz != pStart)
653 sz = CharPrevExA(nCodePage, pStart, sz, 0);
654 return sz;
658 // StencilToken
659 // The stencil class will create an array of these tokens during the parse
660 // phase and use them during rendering to render the stencil
661 struct StencilToken
663 LPCSTR pStart; // Start of fragment to be rendered
664 LPCSTR pEnd; // End of fragment to be rendered
665 DWORD type; // Type of token
666 DWORD dwFnOffset; // Offset into the replacement map for the handler function.
667 DWORD dwMap;
668 DWORD dwObjOffset; // An identifier for the caller to use in identifiying the
669 // object that will render this token.
670 CHAR szHandlerName[ATL_MAX_HANDLER_NAME_LEN + 1]; // Name of handler object.
671 CHAR szMethodName[ATL_MAX_METHOD_NAME_LEN + 1]; // Name of handler method.
672 DWORD dwLoopIndex; // Offset into array of StencilTokens of the other loop tag
673 DWORD_PTR dwData;
674 BOOL bDynamicAlloc;
679 // Class CStencil
680 // The CStencil class is used to map in a stencil from a file or resource
681 // and parse the stencil into an array of StencilTokens. We then render
682 // the stencil from the array of tokens. This class's parse and render
683 // functions depend on an IReplacementHandlerLookup interface pointer to be
684 // passed so it can retrieve the IReplacementHandler interface pointer of the
685 // handler object that will be called to render replacement tags
686 class CStencil :
687 public IMemoryCacheClient
689 private:
690 LPCSTR m_pBufferStart; // Beginning of CHAR buffer that holds the stencil.
691 // For mapped files this is the beginning of the mapping.
692 LPCSTR m_pBufferEnd; // End of CHAR buffer that holds the stencil.
693 CAtlArray<StencilToken> m_arrTokens; //An array of tokens.
694 FILETIME m_ftLastModified; // Last modified time (0 for resource)
695 FILETIME m_ftLastChecked; // Last time we retrieved last modified time (0 for resource)
696 HCACHEITEM m_hCacheItem;
697 WORD m_nCodePage;
698 BOOL m_bUseLocaleACP;
699 char m_szDllPath[MAX_PATH];
700 char m_szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1]; // Room for the path, the handler
701 // the '/' and the '\0'
702 #ifdef ATL_DEBUG_STENCILS
703 struct ParseError
705 char m_szError[ATL_STENCIL_MAX_ERROR_LEN];
706 LPCSTR m_szPosition;
707 LPCSTR m_szStartLine;
708 LPCSTR m_szEndLine;
709 int m_nLineNumber;
711 bool operator==(const ParseError& that) const throw()
713 return (m_nLineNumber == that.m_nLineNumber);
717 CSimpleArray<ParseError> m_Errors;
718 HINSTANCE m_hResInst;
720 class CParseErrorProvider : public ITagReplacerImpl<CParseErrorProvider>,
721 public CComObjectRootEx<CComSingleThreadModel>
723 public:
724 BEGIN_COM_MAP(CParseErrorProvider)
725 COM_INTERFACE_ENTRY(ITagReplacer)
726 END_COM_MAP()
727 CSimpleArray<ParseError> *m_pErrors;
728 int m_nCurrentError;
730 CParseErrorProvider() throw() :
731 m_pErrors(NULL),
732 m_nCurrentError(-1)
736 void Initialize(CSimpleArray<ParseError> *pErrors) throw()
738 m_pErrors = pErrors;
741 HTTP_CODE OnGetNextError() throw()
743 m_nCurrentError++;
744 if (m_nCurrentError >= m_pErrors->GetSize() ||
745 m_nCurrentError < 0 )
747 m_nCurrentError = -1;
748 return HTTP_S_FALSE;
750 else
751 return HTTP_SUCCESS;
754 HTTP_CODE OnGetErrorLineNumber() throw(...)
756 if (m_pErrors->GetSize() == 0)
757 return HTTP_SUCCESS;
758 if (m_nCurrentError > m_pErrors->GetSize() ||
759 m_nCurrentError < 0)
760 m_nCurrentError = 0;
762 CWriteStreamHelper c(m_pStream);
763 if (!c.Write((*m_pErrors)[m_nCurrentError].m_nLineNumber))
764 return HTTP_FAIL;
766 return HTTP_SUCCESS;
769 HTTP_CODE OnGetErrorText() throw(...)
771 if (m_pErrors->GetSize() == 0)
772 return HTTP_SUCCESS;
773 if (m_nCurrentError > m_pErrors->GetSize() ||
774 m_nCurrentError < 0)
775 m_nCurrentError = 0;
777 CWriteStreamHelper c(m_pStream);
779 if (!c.Write(static_cast<LPCSTR>((*m_pErrors)[m_nCurrentError].m_szError)))
780 return HTTP_FAIL;
782 return HTTP_SUCCESS;
785 HTTP_CODE OnGetErrorLine() throw(...)
787 ATLASSUME(m_pStream != NULL);
789 if (m_pErrors->GetSize() == 0)
790 return HTTP_SUCCESS;
791 if (m_nCurrentError > m_pErrors->GetSize() ||
792 m_nCurrentError < 0)
793 m_nCurrentError = 0;
795 m_pStream->WriteStream((*m_pErrors)[m_nCurrentError].m_szStartLine,
796 (int)((*m_pErrors)[m_nCurrentError].m_szEndLine - (*m_pErrors)[m_nCurrentError].m_szStartLine), NULL);
798 return HTTP_SUCCESS;
801 BEGIN_REPLACEMENT_METHOD_MAP(CParseErrorProvider)
802 REPLACEMENT_METHOD_ENTRY("GetNextError", OnGetNextError)
803 REPLACEMENT_METHOD_ENTRY("GetErrorText", OnGetErrorText)
804 REPLACEMENT_METHOD_ENTRY("GetErrorLine", OnGetErrorLine)
805 REPLACEMENT_METHOD_ENTRY("GetErrorLineNumber", OnGetErrorLineNumber)
806 END_REPLACEMENT_METHOD_MAP()
809 #else
810 bool m_bErrorsOccurred;
811 #endif
813 class CSaveThreadLocale
815 LCID m_locale;
816 public:
817 CSaveThreadLocale() throw()
819 m_locale = GetThreadLocale();
821 ~CSaveThreadLocale() throw()
823 SetThreadLocale(m_locale);
827 HTTP_CODE LoadFromResourceInternal(HINSTANCE hInstRes, HRSRC hRsrc) throw()
829 ATLASSERT( hRsrc != NULL );
831 HGLOBAL hgResource = NULL;
832 hgResource = LoadResource(hInstRes, hRsrc);
833 if (!hgResource)
835 return HTTP_FAIL;
838 DWORD dwSize = SizeofResource(hInstRes, hRsrc);
839 if (dwSize != 0)
841 m_pBufferStart = (LPSTR)LockResource(hgResource);
842 if (m_pBufferStart != NULL)
844 m_pBufferEnd = m_pBufferStart+dwSize;
845 return HTTP_SUCCESS;
849 // failed to load resource
850 return HTTP_FAIL;
853 protected:
855 ITagReplacer *m_pReplacer;
856 IAtlMemMgr *m_pMemMgr;
857 static CCRTHeap m_crtHeap;
859 inline BOOL CheckTag(LPCSTR szTag, DWORD dwTagLen, LPCSTR szStart, DWORD dwLen) throw()
861 if (dwLen < dwTagLen)
862 return FALSE;
864 if (memcmp(szStart, szTag, dwTagLen))
865 return FALSE;
867 if (isspace(static_cast<unsigned char>(szStart[dwTagLen])) || szStart[dwTagLen] == '}')
868 return TRUE;
870 return FALSE;
873 inline void FindTagArgs(LPCSTR& szstart, LPCSTR& szend, int nKeywordChars) throw()
875 // this function should only be called after finding a valid tag
876 // the first two characters of szstart should be {{
877 ATLASSERT(szstart[0] == '{' && szstart[1] == '{');
879 if (*szstart == '{')
880 szstart += 2; // move past {{
881 szstart = SkipSpace(szstart, m_nCodePage); // move past whitespace
882 szstart += nKeywordChars; // move past keyword
883 szstart = SkipSpace(szstart, m_nCodePage); // move past whitespace after keyword
884 if (*szend == '}')
885 szend -=2; // chop off }}
886 szend = RSkipSpace(szstart, szend, m_nCodePage); // chop of trailing whitespace
889 DWORD CheckTopAndPop(DWORD *pBlockStack, DWORD *pdwTop, DWORD dwToken) throw()
891 if (*pdwTop == 0)
892 return STENCIL_INVALIDINDEX;
893 if (m_arrTokens[pBlockStack[*pdwTop]].type == dwToken)
895 *pdwTop = (*pdwTop) - 1;
896 return pBlockStack[(*pdwTop)+1];
898 return STENCIL_INVALIDINDEX;
901 DWORD PushToken(DWORD *pBlockStack, DWORD *pdwTop, DWORD dwIndex) throw()
903 if (*pdwTop < (ATL_MAX_BLOCK_STACK-1))
905 *pdwTop = (*pdwTop) + 1;
906 pBlockStack[*pdwTop] = dwIndex;
908 else
910 dwIndex = STENCIL_INVALIDINDEX;
913 return dwIndex;
916 public:
918 enum PARSE_TOKEN_RESULT { INVALID_TOKEN, NORMAL_TOKEN, RESERVED_TOKEN };
920 CStencil(IAtlMemMgr *pMemMgr=NULL) throw()
922 m_pBufferStart = NULL;
923 m_pBufferEnd = NULL;
924 m_hCacheItem = NULL;
925 m_ftLastModified.dwLowDateTime = 0;
926 m_ftLastModified.dwHighDateTime = 0;
927 m_ftLastChecked.dwLowDateTime = 0;
928 m_ftLastChecked.dwHighDateTime = 0;
929 m_arrTokens.SetCount(0, 128);
930 m_nCodePage = CP_ACP;
931 m_bUseLocaleACP = TRUE;
932 m_szHandlerName[0] = '\0';
933 m_szDllPath[0] = '\0';
934 m_pMemMgr = pMemMgr;
935 if (!pMemMgr)
936 m_pMemMgr = &m_crtHeap;
937 #ifdef ATL_DEBUG_STENCILS
938 m_hResInst = NULL;
939 #else
940 m_bErrorsOccurred = false;
941 #endif
945 virtual ~CStencil() throw()
947 Uninitialize();
950 #ifdef ATL_DEBUG_STENCILS
952 bool RenderErrors(IWriteStream *pStream) throw(...)
954 if (pStream == NULL)
956 return false;
959 CComObjectStackEx<CParseErrorProvider> Errors;
961 Errors.Initialize(&m_Errors);
962 CStencil ErrorStencil;
964 if (m_hResInst != NULL)
966 CFixedStringT<CStringA, 256> strErrorStencil;
967 _ATLTRY
969 if (strErrorStencil.LoadString(m_hResInst, IDS_STENCIL_ERROR_STENCIL) == FALSE)
971 return false;
974 _ATLCATCHALL()
976 return false;
979 HTTP_CODE hcRet = ErrorStencil.LoadFromString(strErrorStencil, strErrorStencil.GetLength());
981 if (hcRet == HTTP_SUCCESS)
983 if (ErrorStencil.ParseReplacements(static_cast<ITagReplacer *>(&Errors)) != false)
985 ErrorStencil.FinishParseReplacements();
986 if (ErrorStencil.ParseSuccessful() != false)
988 hcRet = ErrorStencil.Render(static_cast<ITagReplacer *>(&Errors), pStream);
990 if (HTTP_ERROR_CODE(hcRet) < 400)
992 return true;
999 return false;
1002 void SetErrorResource(HINSTANCE hResInst)
1004 m_hResInst = hResInst;
1007 bool ParseSuccessful()
1009 return (m_Errors.GetSize() == 0);
1012 bool AddErrorInternal(LPCSTR szErrorText, LPCSTR szPosition) throw()
1014 int nLineNum = 0;
1015 LPCSTR szStartLine = NULL;
1016 LPCSTR szPtr = m_pBufferStart;
1017 while (szPtr < szPosition)
1019 if (*szPtr == '\n')
1021 szStartLine = szPtr + 1;
1022 nLineNum++;
1025 LPSTR szNext = CharNextExA(m_nCodePage, szPtr, 0);
1026 if (szNext == szPtr)
1028 break;
1030 szPtr = szNext;
1032 LPCSTR szEndLine = szPtr;
1033 while (*szPtr)
1035 if (*szPtr == '\n')
1036 break;
1037 szEndLine = szPtr;
1038 LPSTR szNext = CharNextExA(m_nCodePage, szPtr, 0);
1039 if (szNext == szPtr)
1041 break;
1043 szPtr = szNext;
1046 ParseError p;
1047 SafeStringCopy(p.m_szError, szErrorText);
1048 p.m_szPosition = szPosition;
1049 p.m_nLineNumber = nLineNum;
1050 p.m_szStartLine = szStartLine;
1051 p.m_szEndLine = szEndLine;
1053 return (m_Errors.Add(p) == TRUE);
1056 bool AddError(UINT uID, LPCSTR szPosition) throw()
1058 if (m_hResInst != NULL)
1060 _ATLTRY
1062 CFixedStringT<CStringA, 256> strRes;
1063 if (strRes.LoadString(m_hResInst, uID) != FALSE)
1065 return AddErrorInternal(strRes, szPosition);
1068 _ATLCATCHALL()
1072 return AddErrorInternal("Could not load resource for error string", szPosition);
1075 bool AddReplacementError(LPCSTR szReplacement, LPCSTR szPosition) throw()
1077 if (m_hResInst != NULL)
1079 _ATLTRY
1081 CFixedStringT<CStringA, 256> strRes;
1082 if (strRes.LoadString(m_hResInst, IDS_STENCIL_UNRESOLVED_REPLACEMENT) != FALSE)
1084 CFixedStringT<CStringA, 256> strErrorText;
1085 strErrorText.Format(strRes, szReplacement);
1086 return AddErrorInternal(strErrorText, szPosition);
1088 else
1090 return AddErrorInternal("Could not load resource for error string", szPosition);
1093 _ATLCATCHALL()
1095 return false;
1099 return false;
1102 #else
1104 bool ParseSuccessful()
1106 return !m_bErrorsOccurred;
1109 bool AddError(UINT /*uID*/, LPCSTR /*szPosition*/) throw()
1111 m_bErrorsOccurred = true;
1112 return true;
1115 bool AddReplacementError(LPCSTR /*szReplacement*/, LPCSTR /*szPosition*/) throw()
1117 m_bErrorsOccurred = true;
1118 return true;
1121 void SetErrorResource(HINSTANCE) throw()
1125 #endif
1127 // Call Uninitialize if you want to re-use an already initialized CStencil
1128 void Uninitialize() throw()
1130 int nSize = (int) m_arrTokens.GetCount();
1131 for (int nIndex = 0; nIndex < nSize; nIndex++)
1133 if (m_arrTokens[nIndex].bDynamicAlloc)
1134 delete [] m_arrTokens[nIndex].pStart;
1135 if (m_arrTokens[nIndex].dwData != 0 && m_arrTokens[nIndex].type != STENCIL_LOCALE)
1136 m_pMemMgr->Free((void *) m_arrTokens[nIndex].dwData);
1139 m_arrTokens.RemoveAll();
1140 if ((m_ftLastModified.dwLowDateTime || m_ftLastModified.dwHighDateTime) && m_pBufferStart)
1142 delete [] m_pBufferStart;
1144 m_pBufferStart = NULL;
1145 m_pBufferEnd = NULL;
1148 void GetLastModified(FILETIME *pftLastModified)
1150 ATLENSURE(pftLastModified);
1151 *pftLastModified = m_ftLastModified;
1154 void GetLastChecked(FILETIME *pftLastChecked)
1156 ATLENSURE(pftLastChecked);
1157 *pftLastChecked = m_ftLastChecked;
1160 void SetLastChecked(FILETIME *pftLastChecked)
1162 ATLENSURE(pftLastChecked);
1163 m_ftLastChecked = *pftLastChecked;
1166 HCACHEITEM GetCacheItem()
1168 return m_hCacheItem;
1171 void SetCacheItem(HCACHEITEM hCacheItem)
1173 ATLASSUME(m_hCacheItem == NULL);
1174 m_hCacheItem = hCacheItem;
1177 bool GetHandlerName(__out_ecount_z(nPathLen) LPSTR szDllPath, __in size_t nPathLen, __out_ecount_z(nHandlerNameLen) LPSTR szHandlerName, __in size_t nHandlerNameLen) throw()
1179 if(strlen(m_szDllPath) >= nPathLen)
1181 return false;
1184 if(strlen(m_szHandlerName) >= nHandlerNameLen)
1186 return false;
1189 if(0 != strcpy_s(szDllPath, nPathLen, m_szDllPath))
1191 return false;
1194 if(0 != strcpy_s(szHandlerName, nHandlerNameLen, m_szHandlerName))
1196 return false;
1199 return true;
1202 // Adds a token to the token array, handler name, method name
1203 // and handler function offset are optional
1204 ATL_NOINLINE DWORD AddToken(
1205 LPCSTR pStart,
1206 LPCSTR pEnd,
1207 DWORD dwType,
1208 LPCSTR szHandlerName = NULL,
1209 LPCSTR szMethodName = NULL,
1210 DWORD dwFnOffset = STENCIL_INVALIDOFFSET,
1211 DWORD dwObjOffset = STENCIL_INVALIDOFFSET,
1212 DWORD_PTR dwData = 0,
1213 DWORD dwMap = 0,
1214 BOOL bDynamicAlloc = 0) throw()
1216 StencilToken t;
1218 memset(&t, 0x00, sizeof(t));
1220 t.pStart = pStart;
1221 t.pEnd = pEnd;
1222 t.type = dwType;
1223 t.dwLoopIndex = STENCIL_INVALIDINDEX;
1224 t.dwFnOffset = dwFnOffset;
1225 t.dwObjOffset = dwObjOffset;
1226 t.dwData = dwData;
1227 t.dwMap = dwMap;
1228 t.bDynamicAlloc = bDynamicAlloc;
1230 // this should never assert unless the user has overriden something incorrectly
1231 if ((szHandlerName != NULL) && (*szHandlerName))
1233 ATLVERIFY( SafeStringCopy(t.szHandlerName, szHandlerName) );
1235 if ((szMethodName != NULL) && (*szMethodName))
1237 ATLVERIFY( SafeStringCopy(t.szMethodName, szMethodName) );
1240 _ATLTRY
1242 return (DWORD) m_arrTokens.Add(t);
1244 _ATLCATCHALL()
1246 return STENCIL_INVALIDINDEX;
1250 HTTP_CODE LoadFromFile(LPCSTR szFileName) throw()
1252 HRESULT hr = E_FAIL;
1253 ULONGLONG dwLen = 0;
1254 CAtlFile file;
1256 _ATLTRY
1258 hr = file.Create(CA2CTEX<MAX_PATH>(szFileName), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
1259 if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
1260 return AtlsHttpError(500, ISE_SUBERR_STENCIL_LOAD_FAIL); // couldn't load SRF!
1262 if (GetFileTime(file, NULL, NULL, &m_ftLastModified))
1264 if (SUCCEEDED(file.GetSize(dwLen)))
1266 ATLASSERT(!m_pBufferStart);
1268 GetSystemTimeAsFileTime(&m_ftLastChecked);
1270 m_pBufferStart = NULL;
1271 CAutoVectorPtr<char> buffer;
1272 if (!buffer.Allocate((size_t) dwLen))
1273 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // out of memory
1275 DWORD dwRead = 0;
1276 hr = file.Read(buffer, (DWORD) dwLen, dwRead);
1277 if (FAILED(hr))
1278 return AtlsHttpError(500, ISE_SUBERR_READFILEFAIL); // ReadFile failed
1280 m_pBufferStart = buffer.Detach();
1281 m_pBufferEnd = m_pBufferStart + dwRead;
1285 _ATLCATCHALL()
1287 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
1290 return HTTP_SUCCESS;
1293 // loads a stencil from the specified resource.
1294 HTTP_CODE LoadFromResource(HINSTANCE hInstRes, LPCSTR szID, LPCSTR szType = NULL) throw()
1296 if (szType == NULL)
1298 szType = (LPCSTR) RT_HTML;
1301 HRSRC hRsrc = FindResourceA(hInstRes, szID, szType);
1302 if (hRsrc != NULL)
1304 return LoadFromResourceInternal(hInstRes, hRsrc);
1307 return HTTP_FAIL;
1310 HTTP_CODE LoadFromResourceEx(HINSTANCE hInstRes, LPCSTR szID, WORD wLanguage, LPCSTR szType = NULL) throw()
1312 if (szType == NULL)
1314 szType = (LPCSTR) RT_HTML;
1317 HRSRC hRsrc = FindResourceExA(hInstRes, szType, szID, wLanguage);
1318 if (hRsrc != NULL)
1320 return LoadFromResourceInternal(hInstRes, hRsrc);
1323 return HTTP_FAIL;
1326 // loads a stencil from the specified resource
1327 HTTP_CODE LoadFromResource(HINSTANCE hInstRes, UINT nId, LPCSTR szType = NULL) throw()
1329 return LoadFromResource(hInstRes, MAKEINTRESOURCEA(nId), szType);
1332 HTTP_CODE LoadFromResourceEx(HINSTANCE hInstRes, UINT nId, WORD wLanguage, LPCSTR szType = NULL) throw()
1334 return LoadFromResourceEx(hInstRes, MAKEINTRESOURCEA(nId), wLanguage, szType);
1337 // loads a stencil from a string
1338 HTTP_CODE LoadFromString(LPCSTR szString, DWORD dwSize) throw()
1340 m_pBufferStart = szString;
1341 m_pBufferEnd = m_pBufferStart+dwSize;
1342 return HTTP_SUCCESS;
1345 // Cracks the loaded stencil into an array of StencilTokens in preparation for
1346 // rendering. LoadStencil must be called prior to calling this function.
1347 virtual bool ParseReplacements(ITagReplacer* pReplacer) throw(...)
1349 return ParseReplacementsFromBuffer(pReplacer, GetBufferStart(), GetBufferEnd());
1352 virtual bool FinishParseReplacements() throw(...)
1354 DWORD dwSize = (DWORD) m_arrTokens.GetCount();
1355 for (DWORD dwIndex = 0; dwIndex < dwSize; dwIndex++)
1357 StencilToken& token = m_arrTokens[dwIndex];
1358 bool bUnclosedBlock = ((token.type == STENCIL_CONDITIONALSTART ||
1359 token.type == STENCIL_CONDITIONALELSE ||
1360 token.type == STENCIL_ITERATORSTART) &&
1361 token.dwLoopIndex == STENCIL_INVALIDINDEX);
1362 if ((token.szMethodName[0] && token.dwFnOffset == STENCIL_INVALIDOFFSET) || bUnclosedBlock)
1364 if (bUnclosedBlock ||
1365 m_pReplacer->FindReplacementOffset(
1366 token.szMethodName, &token.dwFnOffset,
1367 token.szHandlerName, &token.dwObjOffset,
1368 &token.dwMap, (void **)(&token.dwData), m_pMemMgr) != HTTP_SUCCESS)
1370 if (bUnclosedBlock && token.type == STENCIL_CONDITIONALSTART)
1372 AddError(IDS_STENCIL_UNCLOSEDBLOCK_IF, token.pStart);
1374 else if (bUnclosedBlock && token.type == STENCIL_CONDITIONALELSE)
1376 AddError(IDS_STENCIL_UNCLOSEDBLOCK_ELSE, token.pStart);
1378 else if (bUnclosedBlock && token.type == STENCIL_ITERATORSTART)
1380 AddError(IDS_STENCIL_UNCLOSEDBLOCK_WHILE, token.pStart);
1382 else
1384 AddReplacementError(token.szMethodName, token.pStart);
1387 // unresolved replacement, convert it to a text token
1388 token.type = STENCIL_TEXTTAG;
1390 // convert all linked tokens to text tokens as well
1391 // this includes: endif, else, endwhile
1392 DWORD dwLoopIndex = token.dwLoopIndex;
1393 while (dwLoopIndex != dwIndex && dwLoopIndex != STENCIL_INVALIDINDEX)
1395 m_arrTokens[dwLoopIndex].type = STENCIL_TEXTTAG;
1396 dwLoopIndex = m_arrTokens[dwLoopIndex].dwLoopIndex;
1402 return ParseSuccessful();
1405 virtual bool Parse(ITagReplacer *pReplacer) throw( ... )
1407 if (ParseReplacements(pReplacer))
1409 return FinishParseReplacements();
1411 return false;
1415 DWORD ParseReplacement( LPCSTR szTokenStart,
1416 LPCSTR szTokenEnd,
1417 DWORD dwTokenType = STENCIL_REPLACEMENT,
1418 DWORD dwKeywordLen = 0) throw()
1420 // hold on to the start and end pointers (before removing curlies and whitespace)
1421 // this is needed so that we can convert the token to a text token if the method
1422 // is not resolved (in FinishParseReplacements)
1423 LPCSTR szStart = szTokenStart;
1424 LPCSTR szEnd = szTokenEnd;
1426 FindTagArgs(szTokenStart, szTokenEnd, dwKeywordLen);
1428 char szMethodName[ATL_MAX_METHOD_NAME_LEN+1];
1429 char szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1];
1431 DWORD dwIndex;
1432 //look up the handler name, method name and handler interface
1433 if (HTTP_SUCCESS == GetHandlerAndMethodNames(szTokenStart, szTokenEnd,
1434 szMethodName, ATL_MAX_METHOD_NAME_LEN+1,
1435 szHandlerName, ATL_MAX_HANDLER_NAME_LEN+1))
1436 dwIndex = AddToken(szStart, szEnd, dwTokenType,
1437 szHandlerName, szMethodName,
1438 STENCIL_INVALIDINDEX, STENCIL_INVALIDINDEX,
1439 0, STENCIL_BASIC_MAP);
1440 else
1441 dwIndex = STENCIL_INVALIDINDEX;
1442 return dwIndex;
1445 DWORD ParseWhile(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
1447 DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_ITERATORSTART, sizeof("while")-1);
1448 if (dwIndex == STENCIL_INVALIDINDEX)
1449 return dwIndex;
1450 return PushToken(pBlockStack, pdwTop, dwIndex);
1453 DWORD ParseEndWhile(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
1455 DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_ITERATORSTART);
1456 if (dwTopIndex == STENCIL_INVALIDINDEX)
1458 AddError(IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE, szTokenStart);
1459 return dwTopIndex;
1462 DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_ITERATOREND);
1463 if (dwIndex != STENCIL_INVALIDINDEX)
1465 m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
1466 m_arrTokens[dwIndex].dwLoopIndex = dwTopIndex;
1468 return dwIndex;
1471 DWORD ParseIf(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
1473 DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_CONDITIONALSTART, sizeof("if")-1);
1474 if (dwIndex == STENCIL_INVALIDINDEX)
1475 return dwIndex;
1476 return PushToken(pBlockStack, pdwTop, dwIndex);
1479 DWORD ParseElse(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
1481 DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
1482 if (dwTopIndex == STENCIL_INVALIDINDEX)
1484 AddError(IDS_STENCIL_UNOPENEDBLOCK_ELSE, szTokenStart);
1485 return dwTopIndex;
1488 DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALELSE);
1489 if (dwIndex != STENCIL_INVALIDINDEX)
1491 m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
1493 return PushToken(pBlockStack, pdwTop, dwIndex);
1495 return dwIndex;
1498 DWORD ParseEndIf(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
1500 DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
1501 if (dwTopIndex == STENCIL_INVALIDINDEX)
1503 dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALELSE);
1504 if (dwTopIndex == STENCIL_INVALIDINDEX)
1506 AddError(IDS_STENCIL_UNOPENEDBLOCK_ENDIF, szTokenStart);
1507 return dwTopIndex;
1510 DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALEND);
1511 if (dwIndex != STENCIL_INVALIDINDEX)
1513 m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
1515 return dwIndex;
1518 DWORD ParseLocale(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
1520 LPCSTR szstart = szTokenStart;
1521 LPCSTR szend = szTokenEnd;
1522 LCID locale = 0xFFFFFFFF;
1523 FindTagArgs(szstart, szend, 6);
1524 #ifndef ATL_NO_MLANG
1525 if (isdigit(static_cast<unsigned char>(szstart[0])))
1527 locale = (LCID) atoi(szstart);
1529 else
1531 HRESULT hr;
1533 CComPtr<IMultiLanguage> pML;
1534 hr = pML.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
1535 if (FAILED(hr))
1537 ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
1538 AddError(IDS_STENCIL_MLANG_COCREATE, szTokenStart);
1540 else
1542 CStringW str(szstart, (int)((szend-szstart)+1));
1544 #ifdef __IMultiLanguage2_INTERFACE_DEFINED__
1546 // use IMultiLanguage2 if possible
1547 CComPtr<IMultiLanguage2> spML2;
1548 hr = pML.QueryInterface(&spML2);
1549 if (FAILED(hr) || !spML2.p)
1550 hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
1551 else
1552 hr = spML2->GetLcidFromRfc1766(&locale, CComBSTR(str));
1554 #else // __IMultiLanguage2_INTERFACE_DEFINED__
1556 hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
1558 #endif // __IMultiLanguage2_INTERFACE_DEFINED__
1560 if (FAILED(hr))
1562 AddError(IDS_STENCIL_MLANG_LCID, szTokenStart);
1565 if (FAILED(hr))
1566 locale = 0xFFFFFFFF;
1568 #else
1569 locale = (LCID) atoi(szstart);
1570 #endif
1572 if (m_bUseLocaleACP)
1574 TCHAR szACP[7];
1575 if (GetLocaleInfo(locale, LOCALE_IDEFAULTANSICODEPAGE, szACP, 7) != 0)
1577 m_nCodePage = (WORD) _ttoi(szACP);
1579 else
1581 AddError(IDS_STENCIL_MLANG_GETLOCALE, szTokenStart);
1584 DWORD dwCurrentTokenIndex = STENCIL_INVALIDINDEX;
1585 if (locale != 0xFFFFFFFF)
1586 dwCurrentTokenIndex = AddToken(NULL, NULL, STENCIL_LOCALE,
1587 NULL, NULL, STENCIL_INVALIDOFFSET, STENCIL_INVALIDOFFSET, locale);
1588 else
1589 return STENCIL_INVALIDINDEX;
1590 return dwCurrentTokenIndex;
1593 DWORD ParseCodepage(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
1595 LPCSTR szstart = szTokenStart;
1596 LPCSTR szend = szTokenEnd;
1597 WORD nCodePage = 0xFFFF;
1599 FindTagArgs(szstart, szend, 8);
1600 #ifndef ATL_NO_MLANG
1601 if (isdigit(static_cast<unsigned char>(szstart[0])))
1603 nCodePage = (WORD) atoi(szstart);
1605 else
1607 HRESULT hr;
1609 CComPtr<IMultiLanguage> pML;
1610 hr = pML.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
1611 if (FAILED(hr))
1613 ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
1614 AddError(IDS_STENCIL_MLANG_COCREATE, szTokenStart);
1616 else
1618 CStringW str(szstart, (int)((szend-szstart)+1));
1619 MIMECSETINFO info;
1621 #ifdef __IMultiLanguage2_INTERFACE_DEFINED__
1623 // use IMultiLanguage2 if possible
1624 CComPtr<IMultiLanguage2> spML2;
1625 hr = pML.QueryInterface(&spML2);
1626 if (FAILED(hr) || !spML2.p)
1627 hr = pML->GetCharsetInfo(CComBSTR(str), &info);
1628 else
1629 hr = spML2->GetCharsetInfo(CComBSTR(str), &info);
1631 #else // __IMultiLanguage2_INTERFACE_DEFINED__
1633 hr = pML->GetCharsetInfo(CComBSTR(str), &info);
1635 #endif // __IMultiLanguage2_INTERFACE_DEFINED__
1637 // for most character sets, uiCodePage and uiInternetEncoding
1638 // are the same. UTF-8 is the exception that we're concerned about.
1639 // for that character set, we want uiInternetEncoding (65001 - UTF-8)
1640 // instead of uiCodePage (1200 - UCS-2)
1641 if (SUCCEEDED(hr))
1643 nCodePage = (WORD) info.uiInternetEncoding;
1645 else
1647 AddError(IDS_STENCIL_MLANG_GETCHARSET, szTokenStart);
1650 if (FAILED(hr))
1651 nCodePage = 0xFFFF;
1653 #else
1654 nCodePage = (WORD) atoi(szstart);
1655 #endif
1656 if (nCodePage != 0xFFFF)
1657 m_nCodePage = nCodePage;
1658 m_bUseLocaleACP = FALSE;
1660 return STENCIL_INVALIDINDEX;
1663 PARSE_TOKEN_RESULT ParseHandler(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
1665 LPCSTR szstart = szTokenStart;
1666 LPCSTR szend = szTokenEnd;
1668 if (m_szHandlerName[0] && m_szDllPath[0])
1669 return RESERVED_TOKEN; // already found the handler and path dll names
1671 FindTagArgs(szstart, szend, 7);
1672 size_t nlen = (szend-szstart)+1;
1673 char szHandlerDllName[MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1];
1674 if (nlen < MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1)
1676 Checked::memcpy_s(szHandlerDllName, MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1, szstart, szend-szstart+1);
1677 szHandlerDllName[szend-szstart+1] = '\0';
1679 DWORD dwDllPathLen = MAX_PATH;
1680 DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
1682 if (!_AtlCrackHandler(szHandlerDllName, m_szDllPath, &dwDllPathLen, m_szHandlerName, &dwHandlerNameLen))
1684 AddError(IDS_STENCIL_INVALID_HANDLER, szTokenStart);
1685 return INVALID_TOKEN;
1689 return RESERVED_TOKEN;
1692 virtual PARSE_TOKEN_RESULT ParseToken(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop)
1694 LPCSTR pStart = szTokenStart;
1695 pStart += 2; //skip curlies
1696 pStart = SkipSpace(pStart, m_nCodePage);
1697 DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
1699 DWORD dwIndex = STENCIL_INVALIDINDEX;
1700 PARSE_TOKEN_RESULT ret = RESERVED_TOKEN;
1702 if (CheckTag("endwhile", 8, pStart, dwLen))
1703 dwIndex = ParseEndWhile(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
1704 else if (CheckTag("while", 5, pStart, dwLen))
1705 dwIndex = ParseWhile(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
1706 else if (CheckTag("endif", 5, pStart, dwLen))
1707 dwIndex = ParseEndIf(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
1708 else if (CheckTag("else", 4, pStart, dwLen))
1709 dwIndex = ParseElse(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
1710 else if (CheckTag("if", 2, pStart, dwLen))
1711 dwIndex = ParseIf(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
1712 else if (CheckTag("locale", 6, pStart, dwLen))
1713 dwIndex = ParseLocale(szTokenStart, szTokenEnd);
1714 else if (CheckTag("handler", 7, pStart, dwLen))
1716 return ParseHandler(szTokenStart, szTokenEnd);
1718 else if (CheckTag("codepage", 8, pStart, dwLen))
1720 ParseCodepage(szTokenStart, szTokenEnd);
1721 return RESERVED_TOKEN;
1723 else
1725 dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_REPLACEMENT);
1726 if (dwIndex == STENCIL_INVALIDINDEX)
1727 return INVALID_TOKEN;
1728 ret = NORMAL_TOKEN;
1731 if (dwIndex == STENCIL_INVALIDINDEX)
1732 return INVALID_TOKEN;
1733 return ret;
1736 virtual bool ParseReplacementsFromBuffer(ITagReplacer* pReplacer, LPCSTR pStart, LPCSTR pEnd)
1738 LPCSTR szCurr = pStart;
1739 DWORD BlockStack[ATL_MAX_BLOCK_STACK];
1740 DWORD dwTop = 0;
1742 m_pReplacer = pReplacer;
1744 DWORD dwCurrentTokenIndex = 0;
1746 if (!szCurr)
1748 ATLASSERT(FALSE);
1749 AddError(IDS_STENCIL_NULLPARAM, NULL);
1750 return false;
1753 LPCSTR szEnd = pEnd;
1754 if (szEnd <= szCurr)
1756 ATLASSERT(FALSE);
1757 AddError(IDS_STENCIL_INVALIDSTRING, NULL);
1758 return true;
1761 while(szCurr < szEnd)
1763 //mark the start of this block, then find the end of the block
1764 //the end is denoted by an opening curly
1765 LPCSTR szStart = szCurr;
1766 while (szCurr < szEnd && (*szCurr != '{' || szCurr[1] != '{'))
1768 LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
1769 if (szNext == szCurr)
1771 // embedded null
1772 AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
1773 return true;
1775 szCurr = szNext;
1778 //special case for the last text block, if there is one
1779 if (szCurr >= szEnd)
1781 // add the last token. This is everything after the last
1782 // double curly block, which is text.
1783 dwCurrentTokenIndex = AddToken(szStart, szEnd-1, STENCIL_TEXTTAG);
1784 break;
1787 //if there are any characters between szStart and szCurr inclusive,
1788 //copy them to a text token.
1789 if (szCurr-1 >= szStart)
1790 dwCurrentTokenIndex = AddToken(szStart, szCurr-1, STENCIL_TEXTTAG);
1792 if (dwCurrentTokenIndex == STENCIL_INVALIDINDEX)
1794 AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
1795 return false;
1798 //find the end of the tag
1799 LPSTR szEndTag;
1800 szStart = szCurr;
1801 szCurr += 2; // Skip over the two '{' s
1802 while (szCurr < szEnd)
1804 if (szCurr[0] == '}' && szCurr[1] == '}')
1805 break;
1806 else if (szCurr[0] == '{')
1807 break;
1809 LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
1810 if (szNext == szCurr)
1812 // embedded null
1813 AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
1814 return true;
1816 szCurr = szNext;
1819 if (szCurr >= szEnd)
1821 AddError(IDS_STENCIL_UNMATCHED_TAG_START, szStart);
1822 if (AddToken(szStart, szCurr-1, STENCIL_TEXTTAG) == STENCIL_INVALIDINDEX)
1824 AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
1825 return false;
1828 break;
1831 if (szCurr[0] == '{')
1833 if (szCurr[1] != '{')
1835 szCurr--;
1837 AddError(IDS_STENCIL_MISMATCHED_TAG_START, szStart);
1838 if (AddToken(szStart, szCurr-1, STENCIL_TEXTTAG) == STENCIL_INVALIDINDEX)
1840 AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
1841 return false;
1844 continue;
1847 szEndTag = CharNextExA(m_nCodePage, szCurr, 0);
1848 if (szEndTag == szCurr)
1850 // embedded null
1851 AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
1852 return true;
1855 PARSE_TOKEN_RESULT ret = ParseToken(szStart, szEndTag, BlockStack, &dwTop);
1857 if (ret == INVALID_TOKEN)
1859 dwCurrentTokenIndex = AddToken(szStart, szEndTag, STENCIL_TEXTTAG);
1860 if (dwCurrentTokenIndex == STENCIL_INVALIDINDEX)
1862 AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
1863 return false;
1866 szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
1867 continue;
1870 szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
1871 if (szEndTag == szCurr)
1873 // embedded null
1874 AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
1875 return true;
1878 if (ret == RESERVED_TOKEN)
1880 if (szCurr < szEnd && *szCurr == '\n')
1881 szCurr++;
1882 else if ((szCurr+1 < szEnd && *szCurr == '\r' && *(szCurr+1) == '\n'))
1883 szCurr += 2;
1887 return true;
1890 HTTP_CODE GetHandlerAndMethodNames(
1891 __in LPCSTR pStart,
1892 __in LPCSTR pEnd,
1893 __out_ecount_z(nMethodNameLen) LPSTR pszMethodName,
1894 __in size_t nMethodNameLen,
1895 __out_ecount_z(nHandlerNameLen) LPSTR pszHandlerName,
1896 __in size_t nHandlerNameLen) throw()
1899 ATLASSERT(pStart);
1900 ATLASSERT(pEnd);
1901 ATLASSERT(pEnd > pStart);
1903 if (!pszMethodName || !pszHandlerName || nMethodNameLen < 1 || nHandlerNameLen < 1)
1905 ATLASSERT(FALSE);
1906 AddError(IDS_STENCIL_BAD_PARAMETER, pStart);
1907 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
1911 *pszMethodName = '\0';
1912 *pszHandlerName = '\0';
1913 CHAR szMethodString[ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1];
1914 HTTP_CODE hcErr = HTTP_SUCCESS;
1916 // copy the method string
1918 size_t nMethodLen = (pEnd-pStart)+1;
1919 if (nMethodLen >= (ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1))
1921 AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart);
1922 return AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
1925 Checked::memcpy_s(szMethodString, ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1, pStart, nMethodLen);
1926 szMethodString[nMethodLen] = '\0';
1929 // now crack the method string and get the handler
1930 // id and function name
1932 LPSTR szParen = strchr(szMethodString, '(');
1933 LPSTR szDot = strchr(szMethodString, '.');
1934 if (szDot && (!szParen || (szDot < szParen)))
1936 *szDot = '\0';
1937 szDot++;
1939 // copy method name
1940 if (strlen(szDot) < nMethodNameLen)
1941 Checked::strcpy_s(pszMethodName, nMethodNameLen, szDot);
1942 else
1944 AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart + (szDot - szMethodString));
1945 hcErr = AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
1947 // copy handler name
1948 if (!hcErr)
1950 if (strlen(szMethodString) < nHandlerNameLen)
1951 Checked::strcpy_s(pszHandlerName, nHandlerNameLen, szMethodString);
1952 else
1954 AddError(IDS_STENCIL_HANDLERNAME_TOO_LONG, pStart);
1955 hcErr = AtlsHttpError(500, ISE_SUBERR_LONGHANDLERNAME);
1959 else
1961 // only a method name so just copy it.
1962 if (strlen(szMethodString) < nMethodNameLen)
1963 Checked::strcpy_s(pszMethodName, nMethodNameLen, szMethodString);
1964 else
1966 AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart);
1967 hcErr = AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
1970 return hcErr;
1973 virtual HTTP_CODE Render(
1974 ITagReplacer *pReplacer,
1975 IWriteStream *pWriteStream,
1976 CStencilState* pState = NULL) const throw(...)
1978 ATLENSURE(pReplacer != NULL);
1979 ATLENSURE(pWriteStream != NULL);
1981 HTTP_CODE hcErrorCode = HTTP_SUCCESS;
1982 DWORD dwIndex = 0;
1983 DWORD dwArraySize = GetTokenCount();
1985 // set up locale info
1986 CSaveThreadLocale lcidSave;
1988 if (pState)
1990 dwIndex = pState->dwIndex;
1992 // restore the locale if we're restarting rendering
1993 if (pState->locale != CP_ACP)
1994 SetThreadLocale(pState->locale);
1997 pReplacer->SetStream(pWriteStream);
1998 while (dwIndex < dwArraySize)
2000 // RenderToken advances dwIndex appropriately for us.
2001 dwIndex = RenderToken(dwIndex, pReplacer, pWriteStream, &hcErrorCode, pState);
2003 if (dwIndex == STENCIL_INVALIDINDEX ||
2004 hcErrorCode != HTTP_SUCCESS)
2005 break;
2008 if (IsAsyncStatus(hcErrorCode))
2010 ATLASSERT( pState != NULL ); // state is required for async
2011 if (pState)
2012 pState->dwIndex = dwIndex;
2014 // lcidSave destructor will restore the locale info in case it was changed
2016 return hcErrorCode;
2019 inline BOOL IsValidIndex(DWORD dwIndex) const throw()
2021 if (dwIndex == STENCIL_INVALIDINDEX)
2022 return FALSE;
2023 if (dwIndex < GetTokenCount())
2024 return TRUE;
2025 else
2026 return FALSE;
2029 virtual DWORD RenderToken(
2030 DWORD dwIndex,
2031 ITagReplacer *pReplacer,
2032 IWriteStream *pWriteStream,
2033 HTTP_CODE *phcErrorCode,
2034 CStencilState* pState = NULL) const throw(...)
2036 ATLENSURE(pReplacer != NULL);
2037 ATLENSURE(pWriteStream != NULL);
2039 const StencilToken* pToken = GetToken(dwIndex);
2040 DWORD dwNextToken = 0;
2041 HTTP_CODE hcErrorCode = HTTP_SUCCESS;
2043 if (!pToken)
2044 return STENCIL_INVALIDINDEX;
2046 switch (pToken->type)
2048 case STENCIL_TEXTTAG:
2050 pWriteStream->WriteStream(pToken->pStart,
2051 (int)((pToken->pEnd-pToken->pStart)+1), NULL);
2052 dwNextToken = dwIndex+1;
2054 break;
2055 case STENCIL_ITERATORSTART:
2057 HTTP_CODE hcErr = STENCIL_SUCCESS;
2059 #ifdef ATL_DEBUG_STENCILS
2060 // A 'while' token has to at least be followed by an endwhile!
2061 if (!IsValidIndex(dwIndex+1))
2063 // This should have been caught at parse time
2064 dwNextToken = STENCIL_INVALIDINDEX;
2065 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDINDEX);
2066 ATLASSERT(FALSE);
2067 break;
2070 // End of loop should be valid
2071 if (!IsValidIndex(pToken->dwLoopIndex))
2073 // This should have been caught at parse time
2074 dwNextToken = STENCIL_INVALIDINDEX;
2075 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHWHILE);
2076 ATLASSERT(FALSE);
2077 break;
2080 if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
2082 // This should have been caught at parse time
2083 dwNextToken = STENCIL_INVALIDINDEX;
2084 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
2085 ATLASSERT(FALSE);
2086 break;
2088 #endif // ATL_DEBUG_STENCILS
2090 DWORD dwLoopIndex = pToken->dwLoopIndex; // points to the end of the loop
2092 // Call the replacement method
2093 // if it returns HTTP_SUCCESS, enter the loop
2094 // if it returns HTTP_S_FALSE, terminate the loop
2095 hcErr = pReplacer->RenderReplacement(pToken->dwFnOffset,
2096 pToken->dwObjOffset, pToken->dwMap, (void *) pToken->dwData);
2098 if (hcErr == HTTP_SUCCESS)
2100 dwNextToken = dwIndex+1;
2101 hcErrorCode = HTTP_SUCCESS;
2103 else if (hcErr == HTTP_S_FALSE)
2105 dwNextToken = dwLoopIndex+1;
2106 hcErrorCode = HTTP_SUCCESS;
2108 else
2110 dwNextToken = STENCIL_INVALIDINDEX;
2111 hcErrorCode = hcErr;
2112 break;
2115 break;
2116 case STENCIL_REPLACEMENT:
2118 #ifdef ATL_DEBUG_STENCILS
2119 if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
2121 // This should have been caught at parse time
2122 ATLASSERT(FALSE);
2123 dwNextToken = STENCIL_INVALIDINDEX;
2124 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
2125 break;
2127 #endif // ATL_DEBUG_STENCILS
2129 hcErrorCode = pReplacer->RenderReplacement(pToken->dwFnOffset,
2130 pToken->dwObjOffset, pToken->dwMap, (void *)pToken->dwData);
2132 if (IsAsyncContinueStatus(hcErrorCode))
2133 dwNextToken = dwIndex; // call the tag again after we get back
2134 else
2136 dwNextToken = dwIndex + 1;
2138 // when returned from a handler, these indicate that the handler is done
2139 // and that we should move on to the next handler when called back
2140 if (hcErrorCode == HTTP_SUCCESS_ASYNC_DONE)
2141 hcErrorCode = HTTP_SUCCESS_ASYNC;
2142 else if (hcErrorCode == HTTP_SUCCESS_ASYNC_NOFLUSH_DONE)
2143 hcErrorCode = HTTP_SUCCESS_ASYNC_NOFLUSH;
2146 break;
2147 case STENCIL_ITERATOREND:
2149 dwNextToken = pToken->dwLoopIndex;
2150 hcErrorCode = HTTP_SUCCESS;
2151 ATLASSERT(GetToken(dwNextToken)->type == STENCIL_ITERATORSTART);
2153 break;
2154 case STENCIL_CONDITIONALSTART:
2156 #ifdef ATL_DEBUG_STENCILS
2157 if (pToken->type == STENCIL_CONDITIONALSTART && pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
2159 // This should have been caught at parse time
2160 ATLASSERT(FALSE);
2161 dwNextToken = STENCIL_INVALIDINDEX;
2162 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
2163 break;
2166 if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
2168 // This should have been caught at parse time
2169 ATLASSERT(FALSE);
2170 dwNextToken = STENCIL_INVALIDINDEX;
2171 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHIF);
2172 break;
2174 #endif // ATL_DEBUG_STENCILS
2176 DWORD dwLoopIndex = pToken->dwLoopIndex; // points to the end of the loop
2178 HTTP_CODE hcErr;
2179 // Call the replacement method.
2180 // If it returns HTTP_SUCCESS, we render everything up to
2181 // the end of the conditional.
2182 // if it returns HTTP_S_FALSE, the condition is not met and we
2183 // render the else part if it exists or jump past the endif otherwise
2184 hcErr = pReplacer->RenderReplacement(pToken->dwFnOffset,
2185 pToken->dwObjOffset, pToken->dwMap, (void *)pToken->dwData);
2187 if (hcErr == HTTP_SUCCESS)
2189 dwNextToken = dwIndex+1;
2190 hcErrorCode = HTTP_SUCCESS;
2192 else if (hcErr == HTTP_S_FALSE)
2194 dwNextToken = dwLoopIndex+1;
2195 hcErrorCode = HTTP_SUCCESS;
2197 else
2199 dwNextToken = STENCIL_INVALIDINDEX;
2200 hcErrorCode = hcErr;
2201 break;
2204 break;
2205 case STENCIL_CONDITIONALELSE:
2207 #ifdef ATL_DEBUG_STENCILS
2208 if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
2210 // This should have been caught at parse time
2211 ATLASSERT(FALSE);
2212 dwNextToken = STENCIL_INVALIDINDEX;
2213 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHIF);
2214 break;
2216 #endif // ATL_DEBUG_STENCILS
2218 dwNextToken = pToken->dwLoopIndex+1;
2219 hcErrorCode = HTTP_SUCCESS;
2221 break;
2222 case STENCIL_CONDITIONALEND:
2224 dwNextToken = dwIndex+1;
2225 hcErrorCode = HTTP_SUCCESS;
2227 break;
2228 case STENCIL_LOCALE:
2230 if (pState)
2232 pState->locale = (LCID) pToken->dwData;
2234 SetThreadLocale((LCID) pToken->dwData);
2235 dwNextToken = dwIndex + 1;
2237 break;
2238 default:
2240 ATLASSERT(FALSE);
2241 dwNextToken = STENCIL_INVALIDINDEX;
2242 hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_UNEXPECTEDTYPE);
2243 break;
2247 ATLASSERT(dwNextToken != dwIndex || IsAsyncContinueStatus(hcErrorCode));
2249 if (phcErrorCode)
2250 *phcErrorCode = hcErrorCode;
2252 return dwNextToken;
2255 DWORD GetTokenCount() const throw()
2257 return (DWORD) m_arrTokens.GetCount();
2260 const StencilToken* GetToken(DWORD dwIndex) const throw()
2262 return &(m_arrTokens[dwIndex]);
2265 StencilToken* GetToken(DWORD dwIndex) throw()
2267 return &(m_arrTokens[dwIndex]);
2270 LPCSTR GetBufferStart() const throw()
2272 return m_pBufferStart;
2275 LPCSTR GetBufferEnd() const throw()
2277 return m_pBufferEnd;
2280 WORD GetCodePage() const throw()
2282 return m_nCodePage;
2285 // IMemoryCacheClient
2286 STDMETHOD(QueryInterface)(REFIID riid, void **ppv)
2288 if (!ppv)
2289 return E_POINTER;
2291 if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
2292 InlineIsEqualGUID(riid, __uuidof(IMemoryCacheClient)))
2294 *ppv = static_cast<IMemoryCacheClient*>(this);
2295 return S_OK;
2298 *ppv = NULL;
2299 return E_NOINTERFACE;
2302 STDMETHOD_(ULONG, AddRef)()
2304 return 1;
2307 STDMETHOD_(ULONG, Release)()
2309 return 1;
2312 STDMETHOD(Free)(const void *pData)
2314 if (!pData)
2315 return E_POINTER;
2317 ATLASSERT(*((void **) pData) == static_cast<void*>(this));
2319 delete this;
2321 return S_OK;
2323 }; // class CStencil
2325 struct StencilIncludeInfo
2327 public:
2328 CHAR m_szQueryString[ATL_URL_MAX_URL_LENGTH+1];
2329 CHAR m_szFileName[MAX_PATH];
2333 class CIncludeServerContext :
2334 public CComObjectRootEx<CComMultiThreadModel>,
2335 public CWrappedServerContext
2337 public:
2338 BEGIN_COM_MAP(CIncludeServerContext)
2339 COM_INTERFACE_ENTRY(IHttpServerContext)
2340 END_COM_MAP()
2342 IWriteStream * m_pStream;
2343 const StencilIncludeInfo * m_pIncludeInfo;
2345 CIncludeServerContext() throw()
2347 m_pStream = NULL;
2348 m_pIncludeInfo = NULL;
2351 void Initialize(
2352 IWriteStream *pStream,
2353 IHttpServerContext* pServerContext,
2354 const StencilIncludeInfo * pIncludeInfo) throw()
2356 ATLASSERT(pStream != NULL);
2357 ATLASSERT(pServerContext != NULL);
2358 ATLASSERT(pIncludeInfo != NULL);
2359 m_pStream = pStream;
2360 m_spParent = pServerContext;
2361 m_pIncludeInfo = pIncludeInfo;
2364 void Initialize(CIncludeServerContext *pOtherContext)
2366 ATLENSURE(pOtherContext != NULL);
2367 m_pStream = pOtherContext->m_pStream;
2368 m_spParent = pOtherContext->m_spParent;
2369 m_pIncludeInfo = pOtherContext->m_pIncludeInfo;
2372 LPCSTR GetRequestMethod()
2374 return "GET";
2377 LPCSTR GetQueryString()
2379 ATLASSUME(m_pIncludeInfo != NULL);
2380 return m_pIncludeInfo->m_szQueryString;
2383 LPCSTR GetPathTranslated()
2385 ATLASSUME(m_pIncludeInfo != NULL);
2386 return m_pIncludeInfo->m_szFileName;
2389 LPCSTR GetScriptPathTranslated()
2391 ATLASSUME(m_pIncludeInfo != NULL);
2392 return m_pIncludeInfo->m_szFileName;
2395 DWORD GetTotalBytes()
2397 return 0;
2400 DWORD GetAvailableBytes()
2402 return 0;
2405 BYTE *GetAvailableData()
2407 return NULL;
2410 LPCSTR GetContentType()
2412 return 0;
2415 BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes)
2417 ATLASSUME(m_pStream != NULL);
2418 ATLENSURE(pvBuffer != NULL);
2419 ATLENSURE(pdwBytes != NULL);
2421 HRESULT hr = S_OK;
2422 _ATLTRY
2424 hr = m_pStream->WriteStream((LPCSTR) pvBuffer, *pdwBytes, pdwBytes);
2426 _ATLCATCHALL()
2428 hr = E_FAIL;
2431 return SUCCEEDED(hr);
2434 BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
2436 return FALSE;
2439 BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
2441 return FALSE;
2444 BOOL SendRedirectResponse(LPCSTR /*pszRedirectURL*/)
2446 return FALSE;
2449 BOOL SendResponseHeader(
2450 LPCSTR /*pszHeader*/,
2451 LPCSTR /*pszStatusCode*/,
2452 BOOL /*fKeepConn*/)
2454 return TRUE;
2457 BOOL DoneWithSession(DWORD /*dwHttpStatusCode*/)
2459 return TRUE;
2462 BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/)
2464 return FALSE;
2466 }; // class CIncludeServerContext
2468 class CIDServerContext :
2469 public CComObjectRootEx<CComMultiThreadModel>,
2470 public CWrappedServerContext
2472 public:
2473 CHttpResponse *m_pResponse;
2474 CHttpRequest *m_pRequest;
2476 BEGIN_COM_MAP(CIDServerContext)
2477 COM_INTERFACE_ENTRY(IHttpServerContext)
2478 END_COM_MAP()
2480 CIDServerContext() throw()
2481 : m_pResponse(NULL), m_pRequest(NULL)
2485 BOOL Initialize(
2486 CHttpResponse *pResponse,
2487 CHttpRequest *pRequest) throw()
2489 ATLASSERT(pResponse != NULL);
2490 ATLASSERT(pRequest != NULL);
2491 m_pResponse = pResponse;
2492 m_pRequest = pRequest;
2493 if(!m_pRequest)
2495 return FALSE;
2498 HRESULT hr = m_pRequest->GetServerContext(&m_spParent);
2499 return (SUCCEEDED(hr));
2502 LPCSTR GetRequestMethod()
2504 ATLASSUME(m_pRequest != NULL);
2505 return m_pRequest->GetMethodString();
2508 LPCSTR GetQueryString()
2510 ATLASSUME(m_pRequest != NULL);
2511 return m_pRequest->GetQueryString();
2514 LPCSTR GetPathInfo()
2516 ATLASSUME(m_pRequest != NULL);
2517 return m_pRequest->GetPathInfo();
2520 LPCSTR GetPathTranslated()
2522 ATLASSUME(m_pRequest != NULL);
2523 return m_pRequest->GetPathTranslated();
2526 DWORD GetTotalBytes()
2528 ATLASSUME(m_pRequest != NULL);
2529 return m_pRequest->GetTotalBytes();
2532 DWORD GetAvailableBytes()
2534 ATLASSUME(m_pRequest != NULL);
2535 return m_pRequest->GetAvailableBytes();
2538 BYTE *GetAvailableData()
2540 ATLASSUME(m_pRequest != NULL);
2541 return m_pRequest->GetAvailableData();
2544 LPCSTR GetContentType()
2546 ATLASSUME(m_pRequest != NULL);
2547 return m_pRequest->GetContentType();
2550 LPCSTR GetScriptPathTranslated()
2552 ATLASSUME(m_pRequest != NULL);
2553 return m_pRequest->GetScriptPathTranslated();
2556 BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes)
2558 ATLASSUME(m_pResponse != NULL);
2559 return m_pResponse->WriteLen((LPCSTR)pvBuffer, *pdwBytes);
2562 BOOL ReadClient(void *pvBuffer, DWORD *pdwSize)
2564 ATLASSUME(m_pRequest != NULL);
2565 return m_pRequest->ReadData((LPSTR)pvBuffer, pdwSize);
2568 BOOL SendRedirectResponse(LPCSTR pszRedirectURL)
2570 ATLASSUME(m_pResponse != NULL);
2571 return m_pResponse->Redirect(pszRedirectURL);
2574 BOOL TransmitFile(
2575 HANDLE hFile,
2576 PFN_HSE_IO_COMPLETION pfn,
2577 void *pContext,
2578 LPCSTR szStatusCode,
2579 DWORD dwBytesToWrite,
2580 DWORD dwOffset,
2581 void *pvHead,
2582 DWORD dwHeadLen,
2583 void *pvTail,
2584 DWORD dwTailLen,
2585 DWORD dwFlags)
2587 ATLASSUME(m_pResponse != NULL);
2588 ATLASSUME(m_spParent != NULL);
2590 m_pResponse->Flush();
2591 return m_spParent->TransmitFile(hFile, pfn, pContext, szStatusCode,
2592 dwBytesToWrite, dwOffset, pvHead, dwHeadLen, pvTail, dwTailLen, dwFlags);
2595 }; // class CIDServerContext
2598 // CHtmlStencil
2599 // CHtmlStencil is a specialization of CStencil. CHtmlStencil adds the following
2600 // capabilities to CStencil:
2602 // Support for rendering {{include }} tags
2603 // The {{include }} tags specify another stencil to be included in-place during
2604 // stencil rendering. The {{include }} tag takes a single parameter which is the
2605 // URL of the stencil to include. That URL can optionally include parameters.
2606 // An example:
2607 // {{include mystencil.srf?param1=value1}}
2609 // We also grab the handler name and the name of any subhandlers. The syntax for the
2610 // handler specification is:
2611 // {{handler MyDynamicHandler.dll/Default}}
2612 // which would cause the MyDynamicHandler.dll to be loaded. Once loaded, the stencil
2613 // processor will ask for the IReplacementHandler interface of the object named "Default".
2615 // Additional handlers can be specified after the default handler. An example of an
2616 // additional handler would be:
2617 // {{subhandler OtherHandler MyOtherHandler.dll/Default}}
2618 // would cause the MyOtherHandler.dll to be loaded. Once loaded, the stencil processor will
2619 // ask for the IReplacementHandler interface of the object named "Default" and use it in
2620 // processing the stencil anywhere it sees a stencil tag of the form
2621 // {{OtherHandler.RenderReplacement}}
2623 struct CStringPair
2625 typedef CFixedStringT<CStringA, MAX_PATH> PathStrType;
2626 typedef CFixedStringT<CStringA, ATL_MAX_HANDLER_NAME_LEN+1> HdlrNameStrType;
2627 PathStrType strDllPath;
2628 HdlrNameStrType strHandlerName;
2630 CStringPair()throw()
2634 CStringPair(PathStrType &strDllPath_, HdlrNameStrType &strHandlerName_) throw(...)
2635 :strDllPath(strDllPath_), strHandlerName(strHandlerName_)
2639 CStringPair(CStringA &strDllPath_, CStringA &strHandlerName_) throw(...)
2640 :strDllPath(strDllPath_), strHandlerName(strHandlerName_)
2645 class CStringPairElementTraits :
2646 public CElementTraitsBase< CStringPair >
2648 private:
2650 static ULONG HashStr( ULONG nHash, CStringElementTraits<CStringA>::INARGTYPE str )
2652 ATLENSURE( str != NULL );
2653 const CStringA::XCHAR* pch = str;
2654 while( *pch != 0 )
2656 nHash = (nHash<<5)+nHash+(*pch);
2657 pch++;
2660 return( nHash );
2663 public:
2664 static ULONG Hash( INARGTYPE pair ) throw()
2666 ULONG nHash = HashStr(0, pair.strDllPath);
2667 return HashStr(nHash, pair.strHandlerName);
2670 static bool CompareElements( INARGTYPE pair1, INARGTYPE pair2 ) throw()
2672 return( (pair1.strDllPath == pair2.strDllPath) && (pair1.strHandlerName == pair2.strHandlerName) );
2675 static int CompareElementsOrdered( INARGTYPE pair1, INARGTYPE pair2 ) throw()
2677 return( pair1.strDllPath.Compare( pair2.strDllPath ) );
2681 class CHtmlStencil : public CStencil
2683 private:
2685 ATL_NOINLINE HTTP_CODE RenderInclude(
2686 ITagReplacer *pReplacer,
2687 const StencilToken *pToken,
2688 IWriteStream *pWriteStream,
2689 CStencilState *pState) const
2691 ATLASSUME(m_spServiceProvider);
2692 CComPtr<IHttpServerContext> spServerContext;
2693 CComPtr<IHttpRequestLookup> spLookup;
2694 if (FAILED(pReplacer->GetContext(__uuidof(IHttpServerContext), (VOID**) &spServerContext)))
2696 return AtlsHttpError(500, 0);
2698 if (FAILED(pReplacer->GetContext(__uuidof(IHttpRequestLookup), (VOID**) &spLookup)))
2700 return AtlsHttpError(500, 0);
2702 return RenderInclude(m_spServiceProvider, pWriteStream,
2703 (StencilIncludeInfo *)pToken->dwData, spServerContext, spLookup,
2704 pState);
2707 ATL_NOINLINE HTTP_CODE NoCachePage(ITagReplacer *pReplacer) const
2709 CComPtr<IHttpServerContext> spContext;
2710 HRESULT hr = pReplacer->GetContext(__uuidof(IHttpServerContext), (void **)&spContext);
2711 if (hr == S_OK && spContext)
2713 CComQIPtr<IPageCacheControl> spControl;
2714 spControl = spContext;
2715 if (spControl)
2716 spControl->Cache(FALSE);
2718 return HTTP_SUCCESS;
2722 // CAllocIncludeAsyncContext is an unsupported implementation detail of RenderInclude
2723 class CAllocIncludeAsyncContext :
2724 public CAllocContextBase
2726 public:
2727 CAllocIncludeAsyncContext(CIncludeServerContext *pBase) :
2728 m_pBase(pBase)
2732 HTTP_CODE Alloc(IHttpServerContext **ppNewContext)
2734 ATLASSUME(m_pBase);
2735 if (!ppNewContext)
2736 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
2737 *ppNewContext = NULL;
2738 CComObjectNoLock<CIncludeServerContext>* pNewServerContext = NULL;
2739 ATLTRY(pNewServerContext = new CComObjectNoLock<CIncludeServerContext>);
2740 if (pNewServerContext == NULL)
2741 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
2742 pNewServerContext->Initialize(m_pBase);
2743 pNewServerContext->AddRef();
2744 *ppNewContext = pNewServerContext;
2745 return HTTP_SUCCESS;
2748 private:
2749 CIncludeServerContext *m_pBase;
2750 }; // CAllocIncludeAsyncContext
2753 ATL_NOINLINE HTTP_CODE RenderInclude(
2754 IServiceProvider *pServiceProvider,
2755 IWriteStream *pWriteStream,
2756 const StencilIncludeInfo *pIncludeInfo,
2757 IHttpServerContext *pServerContext,
2758 IHttpRequestLookup *pLookup,
2759 CStencilState* pState = NULL) const throw(...)
2761 CComObjectStackEx<CIncludeServerContext> serverContext;
2762 serverContext.Initialize(pWriteStream, pServerContext, pIncludeInfo);
2763 CAllocIncludeAsyncContext AsyncAllocObj(&serverContext);
2764 return _AtlRenderInclude(static_cast<IHttpServerContext*>(&serverContext),
2765 pIncludeInfo->m_szFileName,
2766 pIncludeInfo->m_szQueryString,
2767 GetCodePage(),
2768 &AsyncAllocObj,
2769 pServiceProvider,
2770 pLookup,
2771 pState);
2775 protected:
2776 CAtlMap<CStringA, CStringPair,
2777 CStringElementTraits<CStringA>, CStringPairElementTraits > m_arrExtraHandlers;
2778 CHAR m_szBaseDir[MAX_PATH];
2779 CComPtr<IServiceProvider> m_spServiceProvider;
2780 CComPtr<IIsapiExtension> m_spExtension;
2781 CComPtr<IStencilCache> m_spStencilCache;
2782 CComPtr<IDllCache> m_spDllCache;
2784 public:
2785 typedef CAtlMap<CStringA, CStringPair,
2786 CStringElementTraits<CStringA>, CStringPairElementTraits > mapType;
2787 typedef CStencil baseType;
2789 CHtmlStencil(IAtlMemMgr *pMemMgr=NULL) throw() :
2790 CStencil(pMemMgr)
2795 void Initialize(IServiceProvider *pProvider) throw(...)
2797 ATLENSURE(pProvider);
2798 if (m_spServiceProvider)
2799 m_spServiceProvider.Release();
2801 m_spServiceProvider = pProvider;
2802 if (!m_spDllCache)
2803 pProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void **) &m_spDllCache);
2805 if (!m_spExtension)
2806 pProvider->QueryInterface(__uuidof(IIsapiExtension), (void **) &m_spExtension);
2809 BOOL GetIncludeInfo(LPCSTR szParamBegin, LPCSTR szParamEnd, StencilIncludeInfo *pInfo) const
2811 ATLENSURE(szParamBegin != NULL);
2812 ATLENSURE(szParamEnd != NULL);
2813 ATLENSURE(pInfo != NULL);
2815 LPCSTR szQueryBegin = szParamBegin;
2817 while (*szQueryBegin && *szQueryBegin != '?' && *szQueryBegin != '}')
2819 LPSTR szNext = CharNextExA(GetCodePage(), szQueryBegin, 0);
2820 if (szNext == szQueryBegin)
2822 return FALSE;
2825 szQueryBegin = szNext;
2828 CFixedStringT<CStringA, MAX_PATH> strPath;
2830 _ATLTRY
2832 DWORD dwPrefixLen = 0;
2833 if (*szParamBegin == '"')
2835 szParamBegin++;
2837 if (!IsFullPathA(szParamBegin))
2839 if (*szParamBegin != '\\')
2841 strPath = m_szBaseDir;
2843 else
2845 LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
2846 if (szBackslash)
2848 #pragma warning(push)
2849 #pragma warning(disable: 6204)
2850 /* prefast noise VSW 492749 */
2851 strPath.SetString(m_szBaseDir, (int)(szBackslash-m_szBaseDir));
2852 #pragma warning(pop)
2854 else
2856 strPath = m_szBaseDir;
2859 dwPrefixLen = strPath.GetLength();
2862 if (*szQueryBegin=='?')
2864 size_t nMinus = (*(szQueryBegin-1) == '"') ? 1 : 0;
2865 strPath.Append(szParamBegin, (int)(szQueryBegin-szParamBegin-nMinus));
2866 if ((szParamEnd-szQueryBegin) > ATL_URL_MAX_PATH_LENGTH || ((szParamEnd - szQueryBegin) < 0))
2868 // query string is too long
2869 return FALSE;
2871 Checked::memcpy_s(pInfo->m_szQueryString, ATL_URL_MAX_URL_LENGTH+1, szQueryBegin + 1, szParamEnd - szQueryBegin);
2872 pInfo->m_szQueryString[szParamEnd - szQueryBegin] = '\0';
2874 else
2876 pInfo->m_szQueryString[0] = '\0';
2877 size_t nAdd = (*szParamEnd == '"') ? 0 : 1;
2878 strPath.Append(szParamBegin, (int)(szParamEnd - szParamBegin + nAdd));
2881 _ATLCATCHALL()
2883 // out of memory
2884 return FALSE;
2887 if (strPath.GetLength() > MAX_PATH-1)
2889 // path is too long
2890 return FALSE;
2893 // strPath is <= MAX_PATH-1
2894 return PathCanonicalizeA(pInfo->m_szFileName, strPath);
2897 DWORD ParseInclude(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw(...)
2899 ATLENSURE(szTokenStart != NULL);
2900 ATLENSURE(szTokenEnd != NULL);
2902 LPCSTR szStart = szTokenStart;
2903 LPCSTR szEnd = szTokenEnd;
2905 FindTagArgs(szStart, szEnd, 7);
2907 CFixedStringT<CStringA, MAX_PATH> strFileNameRelative;
2908 CFixedStringT<CString, MAX_PATH> strFileName;
2910 _ATLTRY
2912 strFileNameRelative.SetString(szStart, (int)(szEnd-szStart + 1));
2914 if (!IsFullPathA(strFileNameRelative))
2916 CFixedStringT<CStringA, MAX_PATH> strTemp;
2917 if (*((LPCSTR)strFileNameRelative) != '\\')
2919 strTemp = m_szBaseDir;
2921 else
2923 LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
2924 if (szBackslash)
2926 #pragma warning(push)
2927 #pragma warning(disable: 6204)
2928 #pragma warning(disable: 6535)
2929 /* prefast noise VSW 492749 */
2930 /* prefast noise VSW 493256 */
2931 strTemp.SetString(m_szBaseDir, (int)(szBackslash-m_szBaseDir));
2932 #pragma warning(pop)
2934 else
2936 strTemp = m_szBaseDir;
2940 strTemp.Append(strFileNameRelative, strFileNameRelative.GetLength());
2941 CFixedStringT<CString, MAX_PATH> strConv = (LPCTSTR) CA2CT(strTemp);
2942 LPTSTR szFileBuf = strFileName.GetBuffer(strConv.GetLength()+1);
2943 if (szFileBuf == NULL)
2945 AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
2946 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
2949 if (!PathCanonicalize(szFileBuf, strConv))
2951 return STENCIL_INVALIDINDEX;
2954 strFileName.ReleaseBuffer();
2956 else
2958 strFileName = CA2CTEX<MAX_PATH>(strFileNameRelative);
2961 _ATLCATCHALL()
2963 AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
2964 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
2967 LPCTSTR szFileName = strFileName;
2969 LPCTSTR szDot = NULL;
2970 LPCTSTR szExtra = _tcschr(szFileName, '?');
2971 if (!szExtra)
2973 szExtra = _tcschr(szFileName, '#');
2974 if (!szExtra)
2976 szDot = _tcsrchr(szFileName, '.');
2980 if (szExtra != NULL)
2982 // there is some extra information
2983 LPCTSTR szDotTmp = szFileName;
2986 szDot = szDotTmp;
2987 szDotTmp = _tcschr(szDotTmp+1, '.');
2988 } while (szDotTmp && szDotTmp < szExtra);
2991 if (!szDot || *szDot != '.')
2993 AddError(IDS_STENCIL_UNEXPECTED, szTokenStart);
2994 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
2997 LPCTSTR szExtEnd = szDot;
2999 while (true)
3001 szExtEnd++;
3002 if (!*szExtEnd || *szExtEnd == '/' || *szExtEnd == '\\' || *szExtEnd == '?' || *szExtEnd == '#' || *szExtEnd == '"')
3003 break;
3006 if (szDot && (size_t)(szExtEnd-szDot) == _tcslen(c_tAtlDLLExtension) &&
3007 !_tcsnicmp(szDot, c_tAtlDLLExtension, _tcslen(c_tAtlDLLExtension)))
3009 // Do .dll stuff
3010 DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
3011 if (dwIndex == STENCIL_INVALIDINDEX)
3013 AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
3014 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
3016 StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
3017 if (!pInfo)
3019 return STENCIL_INVALIDINDEX;
3022 if (!GetIncludeInfo(szStart, szEnd, pInfo))
3024 return STENCIL_INVALIDINDEX;
3027 GetToken(dwIndex)->dwData = (DWORD_PTR) pInfo;
3028 return dwIndex;
3030 else if (szDot && (size_t)(szExtEnd-szDot) == _tcslen(c_tAtlSRFExtension) &&
3031 !_tcsnicmp(szDot, c_tAtlSRFExtension, _tcslen(c_tAtlSRFExtension)))
3033 // Do .srf stuff
3034 DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
3035 if (dwIndex == STENCIL_INVALIDINDEX)
3037 AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
3038 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
3040 StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
3041 if (!pInfo)
3043 return STENCIL_INVALIDINDEX;
3046 if (!GetIncludeInfo(szStart, szEnd, pInfo))
3048 return STENCIL_INVALIDINDEX;
3051 GetToken(dwIndex)->dwData = (DWORD_PTR) pInfo;
3052 return dwIndex;
3054 else
3056 // Assume static content
3057 CAtlFile file;
3059 HRESULT hr = file.Create(szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
3060 if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
3062 if (FAILED(hr))
3064 AddError(IDS_STENCIL_INCLUDE_ERROR, szTokenStart);
3066 else
3068 AddError(IDS_STENCIL_INCLUDE_INVALID, szTokenStart);
3070 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
3073 CAutoVectorPtr<CHAR> szBufferStart;
3074 LPSTR szBufferEnd = NULL;
3075 ULONGLONG dwLen = 0;
3076 if (FAILED(file.GetSize(dwLen)))
3078 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
3081 if (!szBufferStart.Allocate((size_t) dwLen))
3083 AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
3084 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
3087 DWORD dwRead;
3088 if (FAILED(file.Read(szBufferStart, (DWORD) dwLen, dwRead)))
3090 return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
3093 szBufferEnd = szBufferStart + dwRead-1;
3095 DWORD dwIndex = AddToken(szBufferStart, szBufferEnd, STENCIL_STATICINCLUDE);
3096 if (dwIndex != STENCIL_INVALIDINDEX)
3098 GetToken(dwIndex)->bDynamicAlloc = TRUE;
3099 szBufferStart.Detach();
3102 return dwIndex;
3106 PARSE_TOKEN_RESULT ParseSubhandler(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
3108 ATLASSERT(szTokenStart != NULL);
3109 ATLASSERT(szTokenEnd != NULL);
3111 LPCSTR szStart = szTokenStart;
3112 LPCSTR szEnd = szTokenEnd;
3114 // move to the start of the arguments
3115 // (the first char past 'subhandler'
3116 FindTagArgs(szStart, szEnd, 10);
3118 // skip any space to bring us to the start
3119 // of the id for the subhandler.
3120 szStart = SkipSpace(szStart, GetCodePage());
3122 // id names cannot contain spaces. Mark the
3123 // beginning and end if the subhandler id
3124 LPCSTR szIdStart = szStart;
3125 while (!isspace(static_cast<unsigned char>(*szStart)) && *szStart != '}')
3127 if (!isalnum(static_cast<unsigned char>(*szStart)))
3129 // id names can only contain alphanumeric characters
3130 return INVALID_TOKEN;
3133 LPSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
3134 if (szNext == szStart)
3136 // embedded null
3137 AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
3138 return INVALID_TOKEN;
3140 szStart = szNext;
3142 LPCSTR szIdEnd = szStart;
3144 // skip space to bring us to the beginning of the
3145 // the dllpath/handlername
3146 szStart = SkipSpace(szStart, GetCodePage());
3148 // everything up to the end if the tag is
3149 // part of the dllpath/handlername
3150 LPCSTR szHandlerStart = szStart;
3151 while (*szStart != '}')
3153 LPCSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
3154 if (szNext == szStart)
3156 // embedded null
3157 AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
3158 return INVALID_TOKEN;
3160 szStart = szNext;
3162 LPCSTR szHandlerEnd = szStart;
3164 _ATLTRY
3166 CStringPair::HdlrNameStrType strName(szIdStart, (int)(szIdEnd-szIdStart));
3167 CStringPair::PathStrType strPath(szHandlerStart, (int)(szHandlerEnd-szHandlerStart));
3169 CStringPair::PathStrType strDllPath;
3170 CStringPair::HdlrNameStrType strHandlerName;
3171 DWORD dwDllPathLen = MAX_PATH;
3172 DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
3174 LPSTR szDllPath = strDllPath.GetBuffer(dwDllPathLen);
3175 LPSTR szHandlerName = strHandlerName.GetBuffer(dwHandlerNameLen);
3177 if (!_AtlCrackHandler(strPath, szDllPath, &dwDllPathLen, szHandlerName, &dwHandlerNameLen))
3179 strDllPath.ReleaseBuffer();
3180 strHandlerName.ReleaseBuffer();
3181 AddError(IDS_STENCIL_INVALID_SUBHANDLER, szTokenStart);
3182 return INVALID_TOKEN;
3185 strDllPath.ReleaseBuffer(dwDllPathLen);
3186 strHandlerName.ReleaseBuffer(dwHandlerNameLen);
3188 m_arrExtraHandlers.SetAt(strName, CStringPair(strDllPath, strHandlerName));
3190 _ATLCATCHALL()
3192 AddError(IDS_STENCIL_OUTOFMEMORY, NULL);
3193 return INVALID_TOKEN;
3195 return RESERVED_TOKEN;
3198 virtual PARSE_TOKEN_RESULT ParseToken(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop)
3200 ATLASSERT(szTokenStart != NULL);
3201 ATLASSERT(szTokenEnd != NULL);
3203 LPCSTR pStart = szTokenStart;
3204 pStart += 2; //skip curlies
3205 pStart = SkipSpace(pStart, GetCodePage());
3206 DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
3208 DWORD dwIndex = STENCIL_INVALIDINDEX;
3210 if (CheckTag("include", sizeof("include")-1, pStart, dwLen))
3212 dwIndex = ParseInclude(szTokenStart, szTokenEnd);
3214 else if (dwLen > 3 && !memcmp("!--", pStart, 3))
3216 return RESERVED_TOKEN;
3218 else if (dwLen > 2 && !memcmp("//", pStart, 2))
3220 return RESERVED_TOKEN;
3222 else if (CheckTag("subhandler", sizeof("subhandler")-1, pStart, dwLen))
3224 return ParseSubhandler(szTokenStart, szTokenEnd);
3226 else
3228 return CStencil::ParseToken(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
3230 if (dwIndex == STENCIL_INVALIDINDEX)
3232 return INVALID_TOKEN;
3234 return RESERVED_TOKEN;
3237 mapType* GetExtraHandlers() throw()
3239 return &m_arrExtraHandlers;
3242 BOOL SetBaseDirFromFile(LPCSTR szBaseDir)
3244 if (!SafeStringCopy(m_szBaseDir, szBaseDir))
3246 return FALSE;
3249 LPSTR szSlash = strrchr(m_szBaseDir, '\\');
3250 if (szSlash)
3252 szSlash++;
3253 *szSlash = '\0';
3255 else
3257 *m_szBaseDir = '\0';
3260 return TRUE;
3263 LPCSTR GetBaseDir()
3265 return m_szBaseDir;
3268 DWORD RenderToken(
3269 DWORD dwIndex,
3270 ITagReplacer* pReplacer,
3271 IWriteStream *pWriteStream,
3272 HTTP_CODE *phcErrorCode,
3273 CStencilState* pState = NULL) const throw(...)
3275 DWORD dwNextToken = STENCIL_INVALIDINDEX;
3276 HTTP_CODE hcErrorCode = HTTP_SUCCESS;
3277 const StencilToken* pToken = GetToken(dwIndex);
3278 if (pToken)
3280 if (pToken->type == STENCIL_STENCILINCLUDE)
3282 hcErrorCode = RenderInclude(pReplacer, pToken, pWriteStream, pState);
3283 if (hcErrorCode == HTTP_SUCCESS || IsAsyncDoneStatus(hcErrorCode))
3285 dwNextToken = dwIndex+1;
3287 else if (IsAsyncContinueStatus(hcErrorCode))
3289 dwNextToken = dwIndex;
3292 else if (pToken->type == STENCIL_STATICINCLUDE)
3294 pWriteStream->WriteStream(pToken->pStart,
3295 (int)((pToken->pEnd-pToken->pStart)+1), NULL);
3296 dwNextToken = dwIndex+1;
3298 else
3300 dwNextToken = baseType::RenderToken(dwIndex, pReplacer,
3301 pWriteStream, &hcErrorCode, pState);
3305 if (hcErrorCode == HTTP_SUCCESS_NO_CACHE)
3307 hcErrorCode = NoCachePage(pReplacer);
3310 if (phcErrorCode)
3312 *phcErrorCode = hcErrorCode;
3314 return dwNextToken;
3316 }; // class CHtmlStencil
3319 __declspec(selectany) CCRTHeap CStencil::m_crtHeap;
3322 // CHtmlTagReplacer
3323 // This class manages CStencil based objects for HTTP requests. This class will retrieve
3324 // CStencil based objects from the stencil cache, store CStencil based objects in the
3325 // stencil cache and allocate and initialize CStencil based objects on a per reqeust
3326 // basis. Typically, one instance of this class is created for each HTTP request. The
3327 // instance is destroyed once the request has been completed.
3328 template <class THandler, class StencilType=CHtmlStencil>
3329 class CHtmlTagReplacer :
3330 public ITagReplacerImpl<THandler>
3332 protected:
3333 typedef StencilType StencilType;
3335 CSimpleArray<HINSTANCE> m_hInstHandlers;
3336 typedef CAtlMap<CStringA, IRequestHandler*, CStringElementTraits<CStringA> > mapType;
3337 mapType m_Handlers;
3338 StencilType *m_pLoadedStencil;
3339 WORD m_nCodePage;
3340 CComPtr<IStencilCache> m_spStencilCache;
3342 AtlServerRequest m_RequestInfo;
3344 public:
3345 // public members
3347 CHtmlTagReplacer() throw() :
3348 m_pLoadedStencil(NULL)
3350 memset(&m_RequestInfo, 0x00, sizeof(m_RequestInfo));
3351 m_nCodePage = CP_THREAD_ACP;
3354 ~CHtmlTagReplacer() throw()
3356 // you should call FreeHandlers before
3357 // the object is destructed
3358 ATLASSUME(m_hInstHandlers.GetSize() == 0);
3361 HTTP_CODE Initialize(AtlServerRequest *pRequestInfo, IHttpServerContext *pSafeSrvCtx=NULL) throw(...)
3363 ATLASSERT(pRequestInfo != NULL);
3365 CComPtr<IServiceProvider> spServiceProvider;
3366 THandler *pT = static_cast<THandler*>(this);
3367 HRESULT hr = pT->GetContext(__uuidof(IServiceProvider), (void **)&spServiceProvider);
3368 if (FAILED(hr))
3369 return HTTP_FAIL;
3371 spServiceProvider->QueryService(__uuidof(IStencilCache), __uuidof(IStencilCache), (void **) &m_spStencilCache);
3372 if (!m_spStencilCache)
3374 ATLASSERT(FALSE);
3375 return HTTP_FAIL;
3378 // copy the AtlServerRequest into the safe version
3379 Checked::memcpy_s(&m_RequestInfo, sizeof(m_RequestInfo), pRequestInfo, sizeof(m_RequestInfo));
3381 // override appropriate fields
3382 m_RequestInfo.cbSize = sizeof(m_RequestInfo);
3383 m_RequestInfo.pServerContext = pSafeSrvCtx;
3385 return HTTP_SUCCESS;
3388 HTTP_CODE LoadStencilResource(
3389 HINSTANCE hInstResource,
3390 LPCSTR szResourceID,
3391 LPCSTR szResourceType = NULL, LPCSTR szStencilName=NULL) throw(...)
3393 if (!szResourceType)
3394 szResourceType = (LPCSTR) (RT_HTML);
3395 // look up stencil in cache
3396 HTTP_CODE hcErr = HTTP_SUCCESS;
3398 // check the cache first
3399 StencilType *pStencil = FindCacheStencil(szStencilName ? szStencilName : szResourceID);
3400 if (!pStencil)
3402 // create a new stencil
3403 pStencil = GetNewCacheStencil();
3404 if (!pStencil)
3406 return AtlsHttpError(500,ISE_SUBERR_OUTOFMEM);
3409 THandler *pT = static_cast<THandler*>(this);
3410 LPCSTR szFileName = pT->m_spServerContext->GetScriptPathTranslated();
3412 if (!szFileName)
3413 return HTTP_FAIL;
3415 if (!pStencil->SetBaseDirFromFile(szFileName))
3417 return HTTP_FAIL;
3420 pStencil->SetErrorResource(GetResourceInstance());
3422 // load the stencil and parse its replacements
3423 if (HTTP_SUCCESS == pStencil->LoadFromResource(hInstResource,
3424 szResourceID, szResourceType))
3426 _ATLTRY
3428 if (!pStencil->ParseReplacements(this))
3430 return AtlsHttpError(500, ISE_SUBERR_BADSRF);
3433 hcErr = FinishLoadStencil(pStencil, NULL);
3434 if (!hcErr)
3436 #ifdef ATL_DEBUG_STENCILS
3437 pStencil->FinishParseReplacements();
3438 #else
3439 if (!pStencil->FinishParseReplacements())
3441 return AtlsHttpError(500, ISE_SUBERR_BADSRF);
3443 #endif // ATL_DEBUG_STENCILS
3446 _ATLCATCHALL()
3448 return HTTP_FAIL;
3451 else
3453 hcErr = HTTP_FAIL;
3456 // if everything went OK, put the stencil in the stencil cache.
3457 if (!hcErr)
3459 hcErr = CacheStencil(szStencilName ? szStencilName : szResourceID, pStencil);
3462 if (pStencil && hcErr) // something went wrong, free the stencil data
3464 FreeCacheStencil(pStencil);
3467 else
3469 hcErr = FinishLoadStencil(pStencil);
3472 return hcErr;
3475 HTTP_CODE LoadStencilResource(HINSTANCE hInstResource, UINT nID, LPCSTR szResourceType = NULL) throw(...)
3477 if (!szResourceType)
3478 szResourceType = (LPCSTR) RT_HTML;
3479 char szName[80];
3480 int nResult = sprintf_s(szName, sizeof(szName), "%p/%u", hInstResource, nID);
3481 if ((nResult < 0) || (nResult == sizeof(szName)))
3483 return HTTP_FAIL;
3485 return LoadStencilResource(hInstResource, MAKEINTRESOURCEA(nID), szResourceType, szName);
3488 HTTP_CODE LoadStencil(LPCSTR szFileName, IHttpRequestLookup * pLookup = NULL) throw(...)
3490 if (!szFileName)
3492 return HTTP_FAIL;
3495 HTTP_CODE hcErr = HTTP_FAIL;
3496 // try to find the stencil in the cache
3497 StencilType *pStencil = FindCacheStencil(szFileName);
3499 if (!pStencil)
3501 // not in cache. Create a new one
3502 pStencil = GetNewCacheStencil();
3503 if (!pStencil)
3505 return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // out of memory!
3508 if (!pStencil->SetBaseDirFromFile(szFileName))
3510 return HTTP_FAIL;
3513 pStencil->SetErrorResource(GetResourceInstance());
3515 // finish loading
3516 hcErr = pStencil->LoadFromFile(szFileName);
3517 if (!hcErr)
3519 _ATLTRY
3521 if (!pStencil->ParseReplacements(static_cast<ITagReplacer*>(this)))
3523 return AtlsHttpError(500, ISE_SUBERR_BADSRF);
3526 hcErr = FinishLoadStencil(pStencil, pLookup);
3527 if (!hcErr)
3529 #ifdef ATL_DEBUG_STENCILS
3530 pStencil->FinishParseReplacements();
3531 #else
3532 if (!pStencil->FinishParseReplacements())
3534 return AtlsHttpError(500, ISE_SUBERR_BADSRF);
3536 #endif // ATL_DEBUG_STENCILS
3539 _ATLCATCHALL()
3541 return HTTP_FAIL;
3545 // if everything is OK, cache the stencil
3546 if (!hcErr)
3548 hcErr = CacheStencil(szFileName, pStencil);
3551 if (pStencil && hcErr) // something went wrong, free stencil data
3552 FreeCacheStencil(pStencil);
3554 else
3556 hcErr = FinishLoadStencil(pStencil, pLookup);
3558 return hcErr;
3561 HTTP_CODE RenderStencil(IWriteStream* pStream, CStencilState* pState = NULL) throw(...)
3563 if (!m_pLoadedStencil)
3564 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
3566 WORD nCodePage = m_pLoadedStencil->GetCodePage();
3567 if (nCodePage != CP_ACP)
3568 m_nCodePage = nCodePage;
3570 HTTP_CODE hcErr = HTTP_FAIL;
3572 hcErr = m_pLoadedStencil->Render(static_cast<ITagReplacer*>(this),
3573 pStream, pState);
3575 if (!IsAsyncStatus(hcErr) && m_pLoadedStencil->GetCacheItem())
3576 m_spStencilCache->ReleaseStencil(m_pLoadedStencil->GetCacheItem());
3578 return hcErr;
3582 //Implementation
3584 void FreeHandlers() throw(...)
3586 POSITION pos = m_Handlers.GetStartPosition();
3587 while (pos)
3589 m_Handlers.GetValueAt(pos)->UninitializeHandler();
3590 m_Handlers.GetNextValue(pos)->Release();
3592 m_Handlers.RemoveAll();
3594 int nLen = m_hInstHandlers.GetSize();
3595 if (nLen != 0)
3597 THandler *pT = static_cast<THandler *>(this);
3598 CComPtr<IDllCache> spDllCache;
3599 pT->m_spServiceProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache),
3600 (void **)&spDllCache);
3601 for (int i=0; i<nLen; i++)
3603 spDllCache->Free(m_hInstHandlers[i]);
3605 m_hInstHandlers.RemoveAll();
3609 StencilType* GetNewCacheStencil() throw(...)
3611 StencilType *pStencil = NULL;
3612 THandler *pT = static_cast<THandler *>(this);
3613 IAtlMemMgr *pMemMgr;
3614 if (FAILED(pT->m_spServiceProvider->QueryService(__uuidof(IAtlMemMgr), __uuidof(IAtlMemMgr), (void **)&pMemMgr)))
3615 pMemMgr = NULL;
3617 ATLTRY(pStencil = new StencilType(pMemMgr));
3618 if (pStencil != NULL)
3620 pStencil->Initialize(pT->m_spServiceProvider);
3622 return pStencil;
3625 HTTP_CODE CacheStencil(
3626 LPCSTR szName,
3627 StencilType* pStencilData) throw()
3629 THandler *pT = static_cast<THandler *>(this);
3630 HRESULT hr = E_FAIL;
3632 HCACHEITEM hCacheItem = NULL;
3634 hr = m_spStencilCache->CacheStencil(szName,
3635 pStencilData,
3636 sizeof(StencilType*),
3637 &hCacheItem,
3638 pT->m_hInstHandler,
3639 static_cast<IMemoryCacheClient*>(pStencilData));
3641 if (hr == S_OK && hCacheItem)
3643 _ATLTRY
3645 pStencilData->SetCacheItem(hCacheItem);
3647 _ATLCATCHALL()
3649 hr = E_FAIL;
3653 return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
3656 StencilType *FindCacheStencil(LPCSTR szName) throw()
3658 if (!szName || !m_spStencilCache)
3659 return NULL;
3661 StencilType *pStencilData = NULL;
3663 HCACHEITEM hStencil;
3665 if (m_spStencilCache->LookupStencil(szName, &hStencil) != S_OK)
3666 return NULL;
3668 m_spStencilCache->GetStencil(hStencil, reinterpret_cast<void **>(&pStencilData));
3670 return pStencilData;
3673 void FreeCacheStencil(StencilType* pStencilData)
3675 ATLASSERT( pStencilData != NULL );
3677 if(!pStencilData)
3679 return;
3682 IMemoryCacheClient *pMemCacheClient = static_cast<IMemoryCacheClient *>(pStencilData);
3684 if(!pMemCacheClient)
3686 return;
3689 _ATLTRY
3691 pMemCacheClient->Free(pStencilData);
3693 _ATLCATCHALL()
3698 HTTP_CODE GetHandlerOffset(LPCSTR szHandlerName, DWORD* pdwOffset)
3700 if (!pdwOffset)
3701 return HTTP_FAIL;
3703 mapType::CPair *p = m_Handlers.Lookup(szHandlerName);
3704 if (p)
3706 DWORD dwIndex = 0;
3707 POSITION pos = m_Handlers.GetStartPosition();
3708 while (pos)
3710 const mapType::CPair *p1 = m_Handlers.GetNext(pos);
3711 if (p1 == p)
3713 *pdwOffset = dwIndex;
3714 return HTTP_SUCCESS;
3716 dwIndex++;
3718 ATLASSERT(FALSE);
3720 *pdwOffset = 0;
3721 return HTTP_FAIL;
3724 HTTP_CODE GetReplacementObject(DWORD dwObjOffset, ITagReplacer **ppReplacer)
3726 HRESULT hr = E_FAIL;
3728 POSITION pos = m_Handlers.GetStartPosition();
3729 for (DWORD dwIndex=0; dwIndex < dwObjOffset; dwIndex++)
3730 m_Handlers.GetNext(pos);
3732 ATLASSERT(pos != NULL);
3734 IRequestHandler *pHandler = NULL;
3735 pHandler = m_Handlers.GetValueAt(pos);
3737 ATLENSURE(pHandler != NULL);
3739 hr = pHandler->QueryInterface(__uuidof(ITagReplacer), (void**)ppReplacer);
3741 if (hr != S_OK)
3742 return HTTP_FAIL;
3744 return HTTP_SUCCESS;
3747 // This is where we would actually load any extra request
3748 // handlers the HTML stencil might have parsed for us.
3749 HTTP_CODE FinishLoadStencil(StencilType *pStencil, IHttpRequestLookup * pLookup = NULL) throw(...)
3751 THandler *pT = static_cast<THandler *>(this);
3752 ATLASSERT(pStencil);
3753 if (!pStencil)
3754 return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED); // unexpected condition
3755 m_pLoadedStencil = pStencil;
3756 //load extra handlers if there are any
3757 StencilType::mapType *pExtraHandlersMap =
3758 pStencil->GetExtraHandlers();
3760 if (pExtraHandlersMap)
3762 POSITION pos = pExtraHandlersMap->GetStartPosition();
3763 CStringA name;
3764 CStringPair path;
3765 IRequestHandler *pHandler;
3766 HINSTANCE hInstHandler;
3767 while(pos)
3769 pExtraHandlersMap->GetNextAssoc(pos, name, path);
3770 pHandler = NULL;
3771 hInstHandler = NULL;
3772 HTTP_CODE hcErr = pT->m_spExtension->LoadRequestHandler(path.strDllPath, path.strHandlerName,
3773 pT->m_spServerContext,
3774 &hInstHandler,
3775 &pHandler);
3776 if (!hcErr)
3778 _ATLTRY
3780 //map the name to the pointer to request handler
3781 m_Handlers.SetAt(name, pHandler);
3782 //store HINSTANCE of handler
3783 m_hInstHandlers.Add(hInstHandler);
3785 _ATLCATCHALL()
3787 return HTTP_FAIL;
3790 if (pLookup)
3792 hcErr = pHandler->InitializeChild(&m_RequestInfo, pT->m_spServiceProvider, pLookup);
3793 if (hcErr != HTTP_SUCCESS)
3794 return hcErr;
3798 else
3799 return hcErr;
3802 return HTTP_SUCCESS;
3804 }; // class CHtmlTagReplacer
3807 // CRequestHandlerT
3808 // This is the base class for all user request handlers. This class implements
3809 // the IReplacementHandler interface whose methods will be called to render HTML
3810 // into a stream. The stream will be returned as the HTTP response upon completion
3811 // of the HTTP request.
3812 template < class THandler,
3813 class ThreadModel=CComSingleThreadModel,
3814 class TagReplacerType=CHtmlTagReplacer<THandler>
3816 class CRequestHandlerT :
3817 public TagReplacerType,
3818 public CComObjectRootEx<ThreadModel>,
3819 public IRequestHandlerImpl<THandler>
3821 protected:
3822 CStencilState m_state;
3823 CComObjectStackEx<CIDServerContext> m_SafeSrvCtx;
3824 typedef CRequestHandlerT<THandler, ThreadModel, TagReplacerType> _requestHandler;
3826 public:
3827 BEGIN_COM_MAP(_requestHandler)
3828 COM_INTERFACE_ENTRY(IRequestHandler)
3829 COM_INTERFACE_ENTRY(ITagReplacer)
3830 END_COM_MAP()
3832 // public CRequestHandlerT members
3833 CHttpResponse m_HttpResponse;
3834 CHttpRequest m_HttpRequest;
3835 ATLSRV_REQUESTTYPE m_dwRequestType;
3836 AtlServerRequest* m_pRequestInfo;
3838 CRequestHandlerT() throw()
3840 m_hInstHandler = NULL;
3841 m_dwAsyncFlags = 0;
3842 m_pRequestInfo = NULL;
3845 ~CRequestHandlerT() throw()
3847 _ATLTRY
3849 FreeHandlers(); // free handlers held by CTagReplacer
3851 _ATLCATCHALL()
3856 void ClearResponse() throw()
3858 m_HttpResponse.ClearResponse();
3860 // Where user initialization should take place
3861 HTTP_CODE ValidateAndExchange()
3863 return HTTP_SUCCESS; // continue processing request
3866 // Where user Uninitialization should take place
3867 HTTP_CODE Uninitialize(HTTP_CODE hcError)
3869 return hcError;
3872 HTTP_CODE InitializeInternal(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
3874 // Initialize our internal references to required services
3875 m_pRequestInfo = pRequestInfo;
3876 m_state.pParentInfo = pRequestInfo;
3877 m_hInstHandler = pRequestInfo->hInstDll;
3878 m_spServerContext = pRequestInfo->pServerContext;
3879 m_spServiceProvider = pProvider;
3880 return HTTP_SUCCESS;
3883 HTTP_CODE InitializeHandler(
3884 AtlServerRequest *pRequestInfo,
3885 IServiceProvider *pProvider)
3887 HTTP_CODE hcErr = HTTP_FAIL;
3888 ATLASSERT(pRequestInfo);
3889 ATLASSERT(pProvider);
3891 THandler* pT = static_cast<THandler*>(this);
3892 hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
3893 if (!hcErr)
3895 m_HttpResponse.Initialize(m_spServerContext);
3896 hcErr = pT->CheckValidRequest();
3897 if (!hcErr)
3899 hcErr = HTTP_FAIL;
3900 if (m_HttpRequest.Initialize(m_spServerContext,
3901 pT->MaxFormSize(),
3902 pT->FormFlags()))
3904 if (m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
3906 hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
3907 if (!hcErr)
3909 hcErr = pT->ValidateAndExchange();
3915 return hcErr;
3918 HTTP_CODE InitializeChild(
3919 AtlServerRequest *pRequestInfo,
3920 IServiceProvider *pProvider,
3921 IHttpRequestLookup *pRequestLookup)
3923 ATLASSERT(pRequestInfo);
3924 ATLASSERT(pProvider);
3926 THandler *pT = static_cast<THandler*>(this);
3927 HTTP_CODE hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
3928 if (hcErr)
3929 return hcErr;
3931 if (pRequestLookup)
3933 // initialize with the pRequestLookup
3934 if(!m_HttpResponse.Initialize(pRequestLookup))
3936 return HTTP_FAIL;
3939 // Initialize with the IHttpServerContext if it exists
3940 // the only time this is different than the previous call to
3941 // initialize is if the user passes a different IHttpServerContext
3942 // in pRequestInfo than the one extracted from pRequestLookup.
3943 if (m_spServerContext)
3945 if(!m_HttpResponse.Initialize(m_spServerContext))
3947 return HTTP_FAIL;
3950 hcErr = pT->CheckValidRequest();
3951 if (hcErr)
3953 return hcErr;
3956 // initialize with the pRequestLookup to chain query parameters
3957 m_HttpRequest.Initialize(pRequestLookup);
3959 // initialize with the m_spServerContext to get additional query params
3960 // if they exist.
3961 if (m_spServerContext)
3963 m_HttpRequest.Initialize(m_spServerContext);
3967 m_HttpResponse.SetBufferOutput(false); // child cannot buffer
3969 // initialize the safe server context
3970 if (!m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
3972 return HTTP_FAIL;
3975 hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
3976 if (hcErr)
3978 return hcErr;
3981 return pT->ValidateAndExchange();
3984 // HandleRequest is called to perform default processing of HTTP requests. Users
3985 // can override this function in their derived classes if they need to perform
3986 // specific initialization prior to processing this request or want to change the
3987 // way the request is processed.
3988 HTTP_CODE HandleRequest(
3989 AtlServerRequest *pRequestInfo,
3990 IServiceProvider* /*pServiceProvider*/)
3992 ATLENSURE(pRequestInfo);
3994 THandler *pT = static_cast<THandler *>(this);
3995 HTTP_CODE hcErr = HTTP_SUCCESS;
3997 if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
3999 m_dwRequestType = pRequestInfo->dwRequestType;
4001 if (pRequestInfo->dwRequestType==ATLSRV_REQUEST_STENCIL)
4003 LPCSTR szFileName = pRequestInfo->pServerContext->GetScriptPathTranslated();
4004 hcErr = HTTP_FAIL;
4005 if (szFileName)
4006 hcErr = pT->LoadStencil(szFileName, static_cast<IHttpRequestLookup *>(&m_HttpRequest));
4009 else if (pRequestInfo->dwRequestState == ATLSRV_STATE_CONTINUE)
4010 m_HttpResponse.ClearContent();
4012 #ifdef ATL_DEBUG_STENCILS
4013 if (m_pLoadedStencil && !m_pLoadedStencil->ParseSuccessful())
4015 // An error or series of errors occurred in parsing the stencil
4016 _ATLTRY
4018 m_pLoadedStencil->RenderErrors(static_cast<IWriteStream*>(&m_HttpResponse));
4020 _ATLCATCHALL()
4022 return HTTP_FAIL;
4025 #endif
4027 if (hcErr == HTTP_SUCCESS && m_pLoadedStencil)
4029 // if anything other than HTTP_SUCCESS is returned during
4030 // the rendering of replacement tags, we return that value
4031 // here.
4032 hcErr = pT->RenderStencil(static_cast<IWriteStream*>(&m_HttpResponse), &m_state);
4034 if (hcErr == HTTP_SUCCESS && !m_HttpResponse.Flush(TRUE))
4035 hcErr = HTTP_FAIL;
4038 if (IsAsyncFlushStatus(hcErr))
4040 pRequestInfo->pszBuffer = LPCSTR(m_HttpResponse.m_strContent);
4041 pRequestInfo->dwBufferLen = m_HttpResponse.m_strContent.GetLength();
4044 if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN || IsAsyncDoneStatus(hcErr))
4045 return pT->Uninitialize(hcErr);
4047 else if (!IsAsyncStatus(hcErr))
4048 m_HttpResponse.ClearContent();
4050 return hcErr;
4053 HTTP_CODE ServerTransferRequest(LPCSTR szRequest, bool bContinueAfterTransfer=false,
4054 WORD nCodePage = 0, CStencilState *pState = NULL) throw(...)
4056 return m_spExtension->TransferRequest(
4057 m_pRequestInfo,
4058 m_spServiceProvider,
4059 static_cast<IWriteStream*>(&m_HttpResponse),
4060 static_cast<IHttpRequestLookup*>(&m_HttpRequest),
4061 szRequest,
4062 nCodePage == 0 ? m_nCodePage : nCodePage,
4063 bContinueAfterTransfer,
4064 pState);
4067 inline DWORD MaxFormSize()
4069 return DEFAULT_MAX_FORM_SIZE;
4072 inline DWORD FormFlags()
4074 return ATL_FORM_FLAG_IGNORE_FILES;
4077 // Override this function to check if the request
4078 // is valid. This function is called after m_HttpResponse
4079 // has been initialized, so you can use it if you need
4080 // to return an error to the client. This is also a
4081 // good place to initialize any internal class data needed
4082 // to handle the request. CRequestHandlerT::CheckValidRequest
4083 // is called after CRequestHandlerT::InitializeInternal is
4084 // called, so your override of this method will have access to
4085 // m_pRequestInfo (this request's AtlServerRequest structure),
4086 // m_hInstHandler (the HINSTANCE of this handler dll),
4087 // m_spServerContext (the IHttpServerContext interface for this request),
4088 // m_spServiceProvider (the IServiceProvider interface for this request).
4089 // You should call CRequestHandlerT::CheckValidRequest in your override
4090 // if you override this function.
4092 // Note that m_HttpRequest has not been initialized, so
4093 // you cannot use it. This function is intended to
4094 // do simple checking throught IHttpServerContext to avoid
4095 // expensive initialization of m_HttpRequest.
4096 HTTP_CODE CheckValidRequest()
4098 LPCSTR szMethod = NULL;
4099 ATLASSUME(m_pRequestInfo);
4100 szMethod = m_pRequestInfo->pServerContext->GetRequestMethod();
4101 if (strcmp(szMethod, "GET") && strcmp(szMethod, "POST") && strcmp(szMethod, "HEAD"))
4102 return HTTP_NOT_IMPLEMENTED;
4104 return HTTP_SUCCESS;
4107 HRESULT GetContext(REFIID riid, void** ppv)
4109 if (!ppv)
4110 return E_POINTER;
4111 if (InlineIsEqualGUID(riid, __uuidof(IHttpServerContext)))
4113 return m_spServerContext.CopyTo((IHttpServerContext **)ppv);
4115 if (InlineIsEqualGUID(riid, __uuidof(IHttpRequestLookup)))
4117 *ppv = static_cast<IHttpRequestLookup*>(&m_HttpRequest);
4118 m_HttpRequest.AddRef();
4119 return S_OK;
4121 if (InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
4123 *ppv = m_spServiceProvider;
4124 m_spServiceProvider.p->AddRef();
4125 return S_OK;
4127 return E_NOINTERFACE;
4130 HINSTANCE GetResourceInstance()
4132 if (m_pRequestInfo != NULL)
4134 return m_pRequestInfo->hInstDll;
4137 return NULL;
4140 template <typename Interface>
4141 HRESULT GetContext(Interface** ppInterface) throw(...)
4143 return GetContext(__uuidof(Interface), reinterpret_cast<void**>(ppInterface));
4145 }; // class CRequestHandlerT
4147 } // namespace ATL
4148 #pragma pack(pop)
4150 #pragma warning( pop )
4152 #endif // __ATLSTENCIL_H__