Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mpc-hc/mpc-hc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrevil_xxl <drevil_xxl@users.sourceforge.net>2009-07-29 23:29:22 +0400
committerdrevil_xxl <drevil_xxl@users.sourceforge.net>2009-07-29 23:29:22 +0400
commit734f0a2b5626ba70ab9e64e4d7e324f267ab3686 (patch)
tree40bdfd39cb7949526b40aa47c64e609bcabc14d2 /include/atl
parent9e027f690a89b1b6ea749acf9624a2f54e544206 (diff)
included ATL Server Headers directory into project files.
git-svn-id: https://mpc-hc.svn.sourceforge.net/svnroot/mpc-hc/trunk@1198 10f7b99b-c216-0410-bff0-8a66a9350fd8
Diffstat (limited to 'include/atl')
-rw-r--r--include/atl/atlcache.h3246
-rw-r--r--include/atl/atlcrypt.h376
-rw-r--r--include/atl/atlcrypt.inl1062
-rw-r--r--include/atl/atlextmgmt.h1279
-rw-r--r--include/atl/atlhtml.h1682
-rw-r--r--include/atl/atlhttp.h725
-rw-r--r--include/atl/atlhttp.inl3144
-rw-r--r--include/atl/atlisapi.h10609
-rw-r--r--include/atl/atlmime.h2406
-rw-r--r--include/atl/atlperf.h663
-rw-r--r--include/atl/atlperf.inl2894
-rw-r--r--include/atl/atlrx.h2013
-rw-r--r--include/atl/atlserr.h174
-rw-r--r--include/atl/atlsession.h2490
-rw-r--r--include/atl/atlsharedsvc.h202
-rw-r--r--include/atl/atlsiface.h802
-rw-r--r--include/atl/atlsmtpconnection.h926
-rw-r--r--include/atl/atlsmtputil.h220
-rw-r--r--include/atl/atlsoap.h8174
-rw-r--r--include/atl/atlspriv.h1017
-rw-r--r--include/atl/atlspriv.inl361
-rw-r--r--include/atl/atlsrv.rc146
-rw-r--r--include/atl/atlsrvres.h150
-rw-r--r--include/atl/atlstencil.h4152
-rw-r--r--include/atl/l.chs/atlsrv.rc146
-rw-r--r--include/atl/l.cht/atlsrv.rc146
-rw-r--r--include/atl/l.deu/atlsrv.rc146
-rw-r--r--include/atl/l.esp/atlsrv.rc146
-rw-r--r--include/atl/l.fra/atlsrv.rc146
-rw-r--r--include/atl/l.ita/atlsrv.rc146
-rw-r--r--include/atl/l.jpn/atlsrv.rc146
-rw-r--r--include/atl/l.kor/atlsrv.rc146
-rw-r--r--include/atl/res/dllmgr.srf29
-rw-r--r--include/atl/res/stencilmgr.srf69
-rw-r--r--include/atl/res/threadmgr.srf24
35 files changed, 50203 insertions, 0 deletions
diff --git a/include/atl/atlcache.h b/include/atl/atlcache.h
new file mode 100644
index 000000000..7eacf9aa8
--- /dev/null
+++ b/include/atl/atlcache.h
@@ -0,0 +1,3246 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLCACHE_H__
+#define __ATLCACHE_H__
+
+#pragma once
+
+#include <atltime.h>
+#include <atlutil.h>
+#include <atlcoll.h>
+#include <atlperf.h>
+#include <atlcom.h>
+#include <atlstr.h>
+#include <atlsrvres.h>
+#include <atldbcli.h>
+#include <atlspriv.h>
+#include <atlutil.h>
+
+#pragma warning (push)
+#ifndef _ATL_NO_PRAGMA_WARNINGS
+#pragma warning(disable: 4511) // copy constructor could not be generated
+#pragma warning(disable: 4512) // assignment operator could not be generated
+#endif //!_ATL_NO_PRAGMA_WARNINGS
+
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+
+#ifndef _CPPUNWIND
+#pragma warning(disable: 4702) // unreachable code
+#endif
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+//forward declarations;
+class CStdStatClass;
+class CPerfStatClass;
+
+typedef struct __CACHEITEM
+{
+} *HCACHEITEM;
+
+//Implementation of a cache that stores pointers to void
+extern "C" __declspec(selectany) const IID IID_IMemoryCacheClient = {0xb721b49d, 0xbb57, 0x47bc, { 0xac, 0x43, 0xa8, 0xd4, 0xc0, 0x7d, 0x18, 0x3d } };
+extern "C" __declspec(selectany) const IID IID_IMemoryCache = { 0x9c6cfb46, 0xfbde, 0x4f8b, { 0xb9, 0x44, 0x2a, 0xa0, 0x5d, 0x96, 0xeb, 0x5c } };
+extern "C" __declspec(selectany) const IID IID_IMemoryCacheControl = { 0x7634b28b, 0xd819, 0x409d, { 0xb9, 0x6e, 0xfc, 0x9f, 0x3a, 0xba, 0x32, 0x9f } };
+extern "C" __declspec(selectany) const IID IID_IMemoryCacheStats = { 0xd4b6df2d, 0x4bc0, 0x4734, { 0x8a, 0xce, 0xb7, 0x3a, 0xb, 0x97, 0x59, 0x56 } };
+
+__interface ATL_NO_VTABLE __declspec(uuid("b721b49d-bb57-47bc-ac43-a8d4c07d183d"))
+ IMemoryCacheClient : public IUnknown
+{
+ // IMemoryCacheClient methods
+ STDMETHOD( Free )(const void *pvData);
+};
+
+__interface ATL_NO_VTABLE __declspec(uuid("9c6cfb46-fbde-4f8b-b944-2aa05d96eb5c"))
+ IMemoryCache : public IUnknown
+{
+ // IMemoryCache Methods
+ STDMETHOD(Add)(LPCSTR szKey, void *pvData, DWORD dwSize,
+ FILETIME *pftExpireTime,
+ HINSTANCE hInstClient, HCACHEITEM *phEntry,
+ IMemoryCacheClient *pClient);
+
+ STDMETHOD(LookupEntry)(LPCSTR szKey, HCACHEITEM * phEntry);
+ STDMETHOD(GetData)(const HCACHEITEM hEntry, void **ppvData, DWORD *pdwSize) const;
+ STDMETHOD(ReleaseEntry)(const HCACHEITEM hEntry);
+ STDMETHOD(RemoveEntry)(const HCACHEITEM hEntry);
+ STDMETHOD(RemoveEntryByKey)(LPCSTR szKey);
+
+ STDMETHOD(Flush)();
+};
+
+__interface ATL_NO_VTABLE __declspec(uuid("7634b28b-d819-409d-b96e-fc9f3aba329f"))
+ IMemoryCacheControl : public IUnknown
+{
+ // IMemoryCacheControl Methods
+ STDMETHOD(SetMaxAllowedSize)(DWORD dwSize);
+ STDMETHOD(GetMaxAllowedSize)(DWORD *pdwSize);
+ STDMETHOD(SetMaxAllowedEntries)(DWORD dwSize);
+ STDMETHOD(GetMaxAllowedEntries)(DWORD *pdwSize);
+ STDMETHOD(ResetCache)();
+};
+
+__interface ATL_NO_VTABLE __declspec(uuid("d4b6df2d-4bc0-4734-8ace-b73a0b975956"))
+ IMemoryCacheStats : public IUnknown
+{
+ // IMemoryCacheStats Methods
+ STDMETHOD(ClearStats)();
+ STDMETHOD(GetHitCount)(DWORD *pdwSize);
+ STDMETHOD(GetMissCount)(DWORD *pdwSize);
+ STDMETHOD(GetCurrentAllocSize)(DWORD *pdwSize);
+ STDMETHOD(GetMaxAllocSize)(DWORD *pdwSize);
+ STDMETHOD(GetCurrentEntryCount)(DWORD *pdwSize);
+ STDMETHOD(GetMaxEntryCount)(DWORD *pdwSize);
+
+};
+
+struct DLL_CACHE_ENTRY
+{
+ HINSTANCE hInstDll;
+ DWORD dwRefs;
+ BOOL bAlive;
+ CHAR szDllName[MAX_PATH];
+};
+
+inline bool operator==(const DLL_CACHE_ENTRY& entry1, const DLL_CACHE_ENTRY& entry2)
+{
+ return (entry1.hInstDll == entry2.hInstDll);
+}
+
+//
+// IDllCache
+// An interface that is used to load and unload Dlls.
+//
+__interface ATL_NO_VTABLE __declspec(uuid("A12478AB-D261-42f9-B525-7589143C1C97"))
+ IDllCache : public IUnknown
+{
+ // IDllCache methods
+ virtual HINSTANCE Load(LPCSTR szFileName, void *pPeerInfo);
+ virtual BOOL Free(HINSTANCE hInstance);
+ virtual BOOL AddRefModule(HINSTANCE hInstance);
+ virtual BOOL ReleaseModule(HINSTANCE hInstance);
+ virtual HRESULT GetEntries(DWORD dwCount, DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied);
+ virtual HRESULT Flush();
+};
+
+#ifndef ATL_CACHE_KEY_LENGTH
+#define ATL_CACHE_KEY_LENGTH 128
+#endif
+
+typedef CFixedStringT<CStringA, ATL_CACHE_KEY_LENGTH> CFixedStringKey;
+
+struct CFlusherCacheData
+{
+ CFlusherCacheData *pNext;
+ CFlusherCacheData *pPrev;
+ DWORD dwAccessed;
+
+ CFlusherCacheData()
+ {
+ pNext = NULL;
+ pPrev = NULL;
+ dwAccessed = 0;
+ }
+};
+
+// No flusher -- only expired entries will be removed from the cache
+// Also gives the skeleton for all of the flushers
+class CNoFlusher
+{
+public:
+ void Add(CFlusherCacheData * /*pItem*/) { }
+ void Remove(CFlusherCacheData * /*pItem*/) { }
+ void Access(CFlusherCacheData * /*pItem*/) { }
+ CFlusherCacheData * GetStart() const { return NULL; }
+ CFlusherCacheData * GetNext(CFlusherCacheData * /*pCur*/) const { return NULL; }
+ void Release(CFlusherCacheData * /*pItem*/){ }
+};
+
+// Old flusher -- oldest items are flushed first
+class COldFlusher
+{
+public:
+ CFlusherCacheData * pHead;
+ CFlusherCacheData * pTail;
+
+ COldFlusher() : pHead(NULL), pTail(NULL)
+ {
+ }
+
+ // Add it to the tail of the list
+ void Add(CFlusherCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+
+ pItem->pNext = NULL;
+ pItem->pPrev = pTail;
+ if (pHead)
+ {
+ pTail->pNext = pItem;
+ pTail = pItem;
+ }
+ else
+ {
+ pHead = pItem;
+ pTail = pItem;
+ }
+ }
+
+ void Remove(CFlusherCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+
+ CFlusherCacheData * pPrev = pItem->pPrev;
+ CFlusherCacheData * pNext = pItem->pNext;
+
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ pHead = pNext;
+
+ if (pNext)
+ pNext->pPrev = pPrev;
+ else
+ pTail = pPrev;
+
+ }
+
+ void Access(CFlusherCacheData * /*pItem*/)
+ {
+ }
+
+ void Release(CFlusherCacheData * /*pItem*/)
+ {
+ }
+
+ CFlusherCacheData * GetStart() const
+ {
+ return pHead;
+ }
+
+ CFlusherCacheData * GetNext(CFlusherCacheData * pCur) const
+ {
+ if (pCur != NULL)
+ return pCur->pNext;
+ else
+ return NULL;
+ }
+};
+
+// Least recently used flusher -- the item that was accessed the longest time ago is flushed
+class CLRUFlusher : public COldFlusher
+{
+public:
+ // Move it to the tail of the list
+ void Access(CFlusherCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+
+ Remove(pItem);
+ Add(pItem);
+ }
+};
+
+// Least often used flusher
+class CLOUFlusher : public COldFlusher
+{
+public:
+ // Adds to the tail of the list
+ void Add(CFlusherCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+ pItem->dwAccessed = 1;
+ COldFlusher::Add(pItem);
+ }
+
+ void Access(CFlusherCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+ pItem->dwAccessed++;
+
+ CFlusherCacheData * pMark = static_cast<CFlusherCacheData *>(pItem->pPrev);
+ if (!pMark) // The item is already at the head
+ return;
+
+ if (pMark->dwAccessed >= pItem->dwAccessed) // The element before it has
+ return; // been accessed more times
+
+ Remove(pItem);
+
+ while (pMark && (pMark->dwAccessed < pItem->dwAccessed))
+ pMark = static_cast<CFlusherCacheData *>(pMark->pPrev);
+
+ // pMark points to the first element that has been accessed more times,
+ // so add pItem after pMark
+ if (pMark)
+ {
+ CFlusherCacheData *pNext = static_cast<CFlusherCacheData *>(pMark->pNext);
+ pMark->pNext = pItem;
+ pItem->pPrev = pMark;
+
+ pItem->pNext = pNext;
+ pNext->pPrev = pItem;
+ }
+ else // Ran out of items -- put it on the head
+ {
+ pItem->pNext = pHead;
+ pItem->pPrev = NULL;
+ if (pHead)
+ pHead->pPrev = pItem;
+ else // the list was empty
+ pTail = pItem;
+ pHead = pItem;
+ }
+ }
+
+ // We start at the tail and move forward for this flusher
+ CFlusherCacheData * GetStart() const
+ {
+ return pTail;
+ }
+
+ CFlusherCacheData * GetNext(CFlusherCacheData * pCur) const
+ {
+ if (pCur != NULL)
+ return static_cast<CFlusherCacheData *>(pCur->pPrev);
+ else
+ return NULL;
+ }
+};
+
+template <class CFirst, class CSecond>
+class COrFlushers
+{
+ CFirst m_First;
+ CSecond m_Second;
+ BOOL m_bWhich;
+public:
+ COrFlushers()
+ {
+ m_bWhich = FALSE;
+ }
+
+ BOOL Switch()
+ {
+ m_bWhich = !m_bWhich;
+ return m_bWhich;
+ }
+
+ void Add(CFlusherCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ m_First.Add(pItem);
+ m_Second.Add(pItem);
+ }
+
+ void Remove(CFlusherCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ m_First.Remove(pItem);
+ m_Second.Remove(pItem);
+ }
+
+ void Access(CFlusherCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ m_First.Access(pItem);
+ m_Second.Access(pItem);
+ }
+ void Release(CFlusherCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ m_First.Release(pItem);
+ m_Second.Release(pItem);
+ }
+
+ CFlusherCacheData * GetStart() const
+ {
+ if (m_bWhich)
+ return m_First.GetStart();
+ else
+ return m_Second.GetStart();
+ }
+
+ CFlusherCacheData * GetNext(CFlusherCacheData * pCur) const
+ {
+ if (m_bWhich)
+ return m_First.GetNext(pCur);
+ else
+ return m_Second.GetNext(pCur);
+ }
+};
+
+struct CCullerCacheData
+{
+ CCullerCacheData()
+ {
+ pNext = NULL;
+ pPrev = NULL;
+ nLifespan = 0;
+ }
+ CCullerCacheData *pNext;
+ CCullerCacheData *pPrev;
+ ULONGLONG nLifespan;
+ CFileTime cftExpireTime;
+};
+
+class CNoExpireCuller
+{
+public:
+ void Add(CCullerCacheData * /*pItem*/) { }
+ void Commit(CCullerCacheData * /*pItem*/) { }
+ void Access(CCullerCacheData * /*pItem*/) { }
+ void Remove(CCullerCacheData * /*pItem*/) { }
+ void Start() { }
+ BOOL IsExpired(CCullerCacheData * /*pItem*/) { return FALSE; }
+ CCullerCacheData * GetExpired() { return NULL; }
+ void Release(CCullerCacheData * /*pItem*/){}
+
+};
+
+class CExpireCuller
+{
+public:
+ CFileTime m_cftCurrent;
+ CCullerCacheData *pHead;
+ CCullerCacheData *pTail;
+
+ CExpireCuller()
+ {
+ pHead = NULL;
+ pTail = NULL;
+ }
+
+ // Element is being added -- perform necessary initialization
+ void Add(CCullerCacheData * pItem)
+ {
+ (pItem);
+ ATLASSERT(pItem);
+ }
+
+ // Expiration data has been set -- add to main list
+ // Head is the first item to expire
+ // a FILETIME of 0 indicates that the item should never expire
+ void Commit(CCullerCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+ if (!pHead)
+ {
+ pHead = pItem;
+ pTail = pItem;
+ pItem->pNext = NULL;
+ pItem->pPrev = NULL;
+ return;
+ }
+
+ if (CFileTime(pItem->cftExpireTime) == 0)
+ {
+ pTail->pNext = pItem;
+ pItem->pPrev = pTail;
+ pItem->pNext = NULL;
+ pTail = pItem;
+ return;
+ }
+
+ CCullerCacheData * pMark = pHead;
+ while (pMark && (pMark->cftExpireTime < pItem->cftExpireTime))
+ pMark = pMark->pNext;
+
+ if (pMark) // An entry was found that expires after the added entry
+ {
+ CCullerCacheData *pPrev = pMark->pPrev;
+ if (pPrev)
+ pPrev->pNext = pItem;
+ else
+ pHead = pItem;
+
+ pItem->pNext = pMark;
+ pItem->pPrev = pPrev;
+ pMark->pPrev = pItem;
+ }
+ else // Ran out of items -- put it on the tail
+ {
+ if (pTail)
+ pTail->pNext = pItem;
+ pItem->pPrev = pTail;
+ pItem->pNext = NULL;
+ pTail = pItem;
+ }
+ }
+
+ void Access(CCullerCacheData * /*pItem*/)
+ {
+ }
+
+ void Release(CCullerCacheData * /*pItem*/)
+ {
+ }
+
+ void Remove(CCullerCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+ CCullerCacheData *pPrev = pItem->pPrev;
+ CCullerCacheData *pNext = pItem->pNext;
+
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ pHead = pNext;
+
+ if (pNext)
+ pNext->pPrev = pPrev;
+ else
+ pTail = pPrev;
+
+ }
+
+ // About to start culling
+ void Start()
+ {
+ m_cftCurrent = CFileTime::GetCurrentTime();
+ }
+
+ BOOL IsExpired(CCullerCacheData *pItem)
+ {
+ if ((pItem->cftExpireTime != 0) &&
+ m_cftCurrent > pItem->cftExpireTime)
+ return TRUE;
+
+ return FALSE;
+ }
+
+ // Get the next expired entry
+ CCullerCacheData * GetExpired()
+ {
+ if (!pHead)
+ return NULL;
+ if (IsExpired(pHead))
+ return pHead;
+
+ return NULL;
+ }
+};
+
+class CLifetimeCuller : public CExpireCuller
+{
+public:
+ void Add(CCullerCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+ pItem->nLifespan = 0;
+ CExpireCuller::Add(pItem);
+ }
+
+ void Commit(CCullerCacheData * pItem)
+ {
+ ATLENSURE(pItem);
+ if (pItem->nLifespan == 0)
+ pItem->cftExpireTime = 0;
+ else
+ pItem->cftExpireTime = CFileTime(CFileTime::GetCurrentTime().GetTime() + pItem->nLifespan);
+ CExpireCuller::Commit(pItem);
+ }
+
+ void Access(CCullerCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ CExpireCuller::Remove(pItem);
+ Commit(pItem);
+ }
+
+ CCullerCacheData * GetExpired()
+ {
+ return static_cast<CCullerCacheData *>(CExpireCuller::GetExpired());
+ }
+};
+
+template <__int64 ftLifespan>
+class CFixedLifetimeCuller : public CExpireCuller
+{
+public:
+ void Commit(CCullerCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ __int64 nLifeSpan = ftLifespan;
+ if (nLifeSpan == 0)
+ pItem->cftExpireTime = 0;
+ else
+ pItem->cftExpireTime = CFileTime::GetCurrentTime() + CFileTimeSpan(ftLifespan);
+
+ CExpireCuller::Commit(pItem);
+ }
+
+ void Access(CCullerCacheData * pItem)
+ {
+ ATLASSERT(pItem);
+ CExpireCuller::Remove(pItem);
+ Commit(pItem);
+ }
+
+ CCullerCacheData * GetExpired()
+ {
+ return static_cast<CCullerCacheData *>(CExpireCuller::GetExpired());
+ }
+};
+
+
+template <class CFirst, class CSecond>
+class COrCullers
+{
+ CFirst m_First;
+ CSecond m_Second;
+public:
+ void Add(CCullerCacheData * pItem)
+ {
+ m_First.Add(pItem);
+ m_Second.Add(pItem);
+ }
+
+ void Access(CCullerCacheData * pItem)
+ {
+ m_First.Access(pItem);
+ m_Second.Access(pItem);
+ }
+
+ void Remove(CCullerCacheData * pItem)
+ {
+ m_First.Remove(pItem);
+ m_Second.Remove(pItem);
+ }
+
+ void Start()
+ {
+ m_First.Start();
+ m_Second.Start();
+ }
+
+ void Release(CCullerCacheData *pItem)
+ {
+ m_First.Release(pItem);
+ m_Second.Release(pItem);
+ }
+
+ void Commit(CCullerCacheData * pItem)
+ {
+ m_First.Commit(pItem);
+ m_Second.Commit(pItem);
+ }
+ CCullerCacheData * GetExpired()
+ {
+ CCullerCacheData *pItem = m_First.GetExpired();
+ if (!pItem)
+ pItem = m_Second.GetExpired();
+
+ return pItem;
+ }
+
+ BOOL IsExpired(CCullerCacheData * pItem)
+ {
+ return (m_First.IsExpired(pItem) || m_Second.IsExpired(pItem));
+ }
+};
+
+//
+//CMemoryCacheBase
+// Description:
+// This class provides the implementation of a generic cache that stores
+// elements in memory. CMemoryCacheBase uses the CCacheDataBase generic
+// cache element structure to hold items in the cache. The cache is
+// implemented using the CAtlMap map class. CMemoryCache uses a wide
+// character string as it's Key type to identify entries. Entries must
+// have unique key values. If you try to add an entry with a key that
+// is exactly the same as an existing key, the existing entry will be
+// overwritten.
+//
+// Template Parameters:
+// T: The class that inherits from this class. This class must implement
+// void OnDestroyEntry(NodeType *pEntry);
+// DataType: Specifies the type of the element to be stored in the memory
+// cache such as CString or void*
+// NodeInfo: Specifies any additional data that should be stored in each item
+// in the cache
+// keyType, keyTrait : specifies the key type and traits (see CAtlMap)
+// Flusher : the class responsible for determining which data should be flushed
+// when the cache is at a configuration limit
+// Culler : the class responsible for determining which data should be removed
+// from the cache due to expiration
+// SyncClass:Specifies the class that will be used for thread synchronization
+// when accessing the cache. The class interface for SyncClass must
+// be identical to that of CComCriticalSection (see atlbase.h)
+// StatClass: Class used to contain statistics about this cache.
+template <class T,
+ class DataType,
+ class NodeInfo=CCacheDataBase,
+ class keyType=CFixedStringKey,
+ class KeyTrait=CStringElementTraits<CFixedStringKey >,
+ class Flusher=COldFlusher,
+ class Culler=CExpireCuller,
+ class SyncClass=CComCriticalSection,
+ class StatClass=CStdStatClass >
+ class CMemoryCacheBase
+{
+protected:
+ typedef keyType keytype;
+ struct NodeType : public __CACHEITEM,
+ public NodeInfo,
+ public CFlusherCacheData,
+ public CCullerCacheData
+ {
+ NodeType()
+ {
+ pos = NULL;
+ dwSize = 0;
+ dwRef = 0;
+ }
+
+ DataType Data;
+ POSITION pos;
+ DWORD dwSize;
+ DWORD dwRef;
+ };
+
+ typedef CAtlMap<keyType, NodeType *, KeyTrait> mapType;
+ SyncClass m_syncObj;
+ StatClass m_statObj;
+ Flusher m_flusher;
+ Culler m_culler;
+
+ //memory cache configuration parameters
+ DWORD m_dwMaxAllocationSize;
+ DWORD m_dwMaxEntries;
+
+ BOOL m_bInitialized;
+public:
+
+ mapType m_hashTable;
+ CMemoryCacheBase() :
+ m_dwMaxAllocationSize(0xFFFFFFFF),
+ m_dwMaxEntries(0xFFFFFFFF),
+ m_bInitialized(FALSE)
+ {
+
+ }
+
+ //Initializes the cache and the cache synchronization object
+ //Also the performance monitoring
+ HRESULT Initialize()
+ {
+ if (m_bInitialized)
+ return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
+ HRESULT hr;
+ hr = m_syncObj.Init();
+
+ if (hr == S_OK)
+ hr = m_statObj.Initialize();
+
+ m_bInitialized = TRUE;
+
+ return hr;
+ }
+
+ //removes all entries whether or not they are initialized.
+ HRESULT Uninitialize()
+ {
+ if (!m_bInitialized)
+ return S_OK;
+
+ //clear out the hash table
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ RemoveAllEntries();
+ m_statObj.Uninitialize();
+
+ m_syncObj.Unlock();
+ m_syncObj.Term();
+
+ m_bInitialized = FALSE;
+
+ return S_OK;
+ }
+
+ //Adds an entry to the cache.
+ //Also, adds an initial reference on the entry if phEntry is not NULL
+ HRESULT AddEntry(
+ const keyType &Key, //key for entry
+ const DataType &data, //See the DataType template parameter
+ DWORD dwSize, //Size of memory to be stored in the cache
+ HCACHEITEM *phEntry = NULL //out pointer that will contain a handle to the new
+ //cache entry on success.
+ )
+ {
+ _ATLTRY
+ {
+ ATLASSUME(m_bInitialized);
+
+ CAutoPtr<NodeType> spEntry(new NodeType);
+
+ if (!spEntry)
+ return E_OUTOFMEMORY;
+
+ NodeType *pEntry = spEntry;
+
+ //fill entry
+ if (phEntry)
+ {
+ *phEntry = static_cast<HCACHEITEM>(pEntry);
+ pEntry->dwRef++;
+ }
+ pEntry->Data = data;
+ pEntry->dwSize = dwSize;
+
+ CComCritSecLock<SyncClass> lock(m_syncObj, false);
+
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ POSITION pos = (POSITION)m_hashTable.Lookup(Key);
+
+ if (pos != NULL)
+ {
+ RemoveAt(pos, FALSE);
+ m_hashTable.GetValueAt(pos) = pEntry;
+ }
+ else
+ {
+ pos = m_hashTable.SetAt(Key, pEntry);
+ }
+ spEntry.Detach();
+
+ pEntry->pos = pos;
+ m_statObj.AddElement(dwSize);
+ m_flusher.Add(pEntry);
+ m_culler.Add(pEntry);
+
+ lock.Unlock();
+
+ if (!phEntry)
+ return CommitEntry(static_cast<HCACHEITEM>(pEntry));
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+ }
+
+ // Commits the entry to the cache
+ HRESULT CommitEntry(const HCACHEITEM hEntry)
+ {
+ ATLASSUME(m_bInitialized);
+ if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
+ return E_INVALIDARG;
+
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ NodeType *pEntry = static_cast<NodeType *>(hEntry);
+ m_culler.Commit(pEntry);
+ m_syncObj.Unlock();
+ return S_OK;
+ }
+
+ // Looks up an entry and returns a handle to it,
+ // also updates access count and reference count
+ HRESULT LookupEntry(const keyType &Key, HCACHEITEM * phEntry)
+ {
+ ATLASSUME(m_bInitialized);
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = E_FAIL;
+
+ POSITION pos = (POSITION)m_hashTable.Lookup(Key);
+ if (pos != NULL)
+ {
+ NodeType * pEntry = m_hashTable.GetValueAt(pos);
+ m_flusher.Access(pEntry);
+ m_culler.Access(pEntry);
+ if (phEntry)
+ {
+ pEntry->dwRef++;
+ *phEntry = static_cast<HCACHEITEM>(pEntry);
+ }
+
+ m_statObj.Hit();
+
+ hr = S_OK;
+ }
+ else
+ {
+ *phEntry = NULL;
+ m_statObj.Miss();
+ }
+ m_syncObj.Unlock();
+
+ return hr;
+ }
+
+ // Gets the data based on the handle. Is thread-safe as long as there is a
+ // reference on the data
+ HRESULT GetEntryData(const HCACHEITEM hEntry, DataType *pData, DWORD *pdwSize) const
+ {
+ ATLASSUME(m_bInitialized);
+ ATLASSERT(pData != NULL || pdwSize != NULL); // At least one should not be NULL
+
+ if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
+ return E_INVALIDARG;
+
+ NodeType * pEntry = static_cast<NodeType *>(hEntry);
+ if (pData)
+ *pData = pEntry->Data;
+ if (pdwSize)
+ *pdwSize = pEntry->dwSize;
+
+ return S_OK;
+ }
+
+ // Unreferences the entry based on the handle
+ DWORD ReleaseEntry(const HCACHEITEM hEntry)
+ {
+ ATLASSUME(m_bInitialized);
+ if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
+ return (DWORD)-1;
+
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ return (DWORD)-1;
+
+ NodeType * pEntry = static_cast<NodeType *>(hEntry);
+ m_flusher.Release(pEntry);
+ m_culler.Release(pEntry);
+ ATLASSERT(pEntry->dwRef > 0);
+
+ DWORD dwRef = --pEntry->dwRef;
+ if ((pEntry->pos == NULL) && (pEntry->dwRef == 0))
+ InternalRemoveEntry(pEntry);
+
+ m_syncObj.Unlock();
+
+ return dwRef;
+ }
+
+ // Increments the entry's reference count
+ DWORD AddRefEntry(const HCACHEITEM hEntry)
+ {
+ ATLASSUME(m_bInitialized);
+ if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
+ return (DWORD)-1;
+
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ return (DWORD)-1;
+
+ NodeType * pEntry = static_cast<NodeType *>(hEntry);
+ m_flusher.Access(pEntry);
+ m_culler.Access(pEntry);
+ DWORD dwRef = ++pEntry->dwRef;
+ m_syncObj.Unlock();
+
+ return dwRef;
+ }
+
+ // Removes an entry from the cache regardless of whether or
+ // not it has expired. If there are references, it detaches
+ // the entry so that future lookups will fail, and when
+ // the ref count drops to zero, it will be deleted
+ HRESULT RemoveEntryByKey(const keyType &Key)
+ {
+ ATLASSUME(m_bInitialized);
+ HCACHEITEM hEntry;
+ HRESULT hr = LookupEntry(Key, &hEntry);
+ if (hr == S_OK)
+ hr = RemoveEntry(hEntry);
+
+ return hr;
+ }
+
+ // Removes the element from the cache. If there are still
+ // references, then the entry is detached.
+ HRESULT RemoveEntry(const HCACHEITEM hEntry)
+ {
+ ATLASSUME(m_bInitialized);
+ if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
+ return E_INVALIDARG;
+
+ _ATLTRY
+ {
+ CComCritSecLock<SyncClass> lock(m_syncObj, false);
+
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ NodeType * pEntry = static_cast<NodeType *>(hEntry);
+ m_flusher.Release(pEntry);
+ m_culler.Release(pEntry);
+ ATLASSERT(pEntry->dwRef > 0);
+ pEntry->dwRef--;
+ if (pEntry->pos)
+ RemoveAt(pEntry->pos, TRUE);
+ else if ((long)pEntry->dwRef == 0)
+ InternalRemoveEntry(pEntry);
+ lock.Unlock();
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ // CullEntries removes all expired items
+ HRESULT CullEntries()
+ {
+ ATLASSUME(m_bInitialized);
+
+ _ATLTRY
+ {
+ CComCritSecLock<SyncClass> lock(m_syncObj, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ m_culler.Start();
+
+ while (NodeType *pNode = static_cast<NodeType *>(m_culler.GetExpired()))
+ RemoveAt(pNode->pos, TRUE);
+
+ lock.Unlock();
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ // FlushEntries reduces the cache to meet the configuration requirements
+ HRESULT FlushEntries()
+ {
+ ATLASSUME(m_bInitialized);
+ HRESULT hr = CullEntries();
+ if (FAILED(hr))
+ return hr;
+
+ _ATLTRY
+ {
+ CComCritSecLock<SyncClass> lock(m_syncObj, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ NodeType * pNode = static_cast<NodeType *>(m_flusher.GetStart());
+
+ while (pNode &&
+ (((m_statObj.GetCurrentEntryCount() > m_dwMaxEntries)) ||
+ ((m_statObj.GetCurrentAllocSize() > m_dwMaxAllocationSize))))
+ {
+ NodeType *pNext = static_cast<NodeType *>(m_flusher.GetNext(pNode));
+
+ if (pNode->dwRef == 0)
+ RemoveAt(pNode->pos, TRUE);
+
+ pNode = pNext;
+ }
+ lock.Unlock();
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize)
+ {
+ m_dwMaxAllocationSize = dwSize;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_dwMaxAllocationSize;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize)
+ {
+ m_dwMaxEntries = dwSize;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_dwMaxEntries;
+ return S_OK;
+ }
+
+
+ HRESULT ResetCache()
+ {
+ ATLASSUME(m_bInitialized);
+ HRESULT hr = E_UNEXPECTED;
+ if (SUCCEEDED(ClearStats()))
+ hr = RemoveAllEntries();
+ return hr;
+ }
+
+ HRESULT ClearStats()
+ {
+ m_statObj.ResetCounters();
+ return S_OK;
+ }
+
+ HRESULT RemoveAllEntries()
+ {
+ ATLASSUME(m_bInitialized);
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ m_hashTable.DisableAutoRehash();
+ POSITION pos = m_hashTable.GetStartPosition();
+ POSITION oldpos;
+ while (pos != NULL)
+ {
+ oldpos = pos;
+ m_hashTable.GetNext(pos);
+ RemoveAt(oldpos, TRUE);
+ }
+ m_hashTable.EnableAutoRehash();
+ m_syncObj.Unlock();
+
+ return S_OK;
+ }
+
+protected:
+
+ // Checks to see if the cache can accommodate any new entries within
+ // its allocation and entry count limits.
+ bool CanAddEntry(DWORD dwSizeToAdd)
+ {
+ return CheckAlloc(dwSizeToAdd) && CheckEntryCount(1);
+ }
+
+ // Checks to see if the cache can accommodate dwSizeToAdd additional
+ // allocation within its allocation limit.
+ bool CheckAlloc(DWORD dwSizeToAdd)
+ {
+ if (m_dwMaxAllocationSize == 0xFFFFFFFF)
+ return true; //max allocation size setting hasn't been set
+ DWORD dwNew = m_statObj.GetCurrentAllocSize() + dwSizeToAdd;
+ return dwNew < m_dwMaxAllocationSize;
+ }
+
+
+ // Checks to see if the cache can accommodate dwNumEntriesToAdd
+ // additional entries within its limits.
+ bool CheckEntryCount(DWORD dwNumEntriesToAdd)
+ {
+ if (m_dwMaxEntries == 0xFFFFFFFF)
+ return true; //max entry size hasn't been set
+ DWORD dwNew = m_statObj.GetCurrentEntryCount() + dwNumEntriesToAdd;
+ return dwNew < m_dwMaxEntries;
+
+ }
+
+protected:
+ // Takes the element at pos in the hash table and removes it from
+ // the cache. If there are no references, then the entry is
+ // deleted, otherwise it is deleted by ReleaseEntry when the
+ // refcount goes to zero.
+ HRESULT RemoveAt(POSITION pos, BOOL bDelete)
+ {
+ HRESULT hr = S_OK;
+ ATLASSERT(pos != NULL);
+ NodeType * pEntry = m_hashTable.GetValueAt(pos);
+ m_flusher.Remove(pEntry);
+ m_culler.Remove(pEntry);
+ if (bDelete)
+ m_hashTable.RemoveAtPos(pos);
+
+ if ((long)pEntry->dwRef == 0)
+ hr = InternalRemoveEntry(pEntry);
+ else
+ pEntry->pos = NULL;
+
+ return S_OK;
+ }
+
+ // Does the actual destruction of the node. Deletes the
+ // NodeType struct and calls the inherited class's
+ // OnDestroyEntry function, where other necessary destruction
+ // can take place. Also updates the cache statistics.
+ // Inherited classes should call RemoveAt unless the element's
+ // refcount is zero and it has been removed from the
+ // culler and flusher lists.
+ HRESULT InternalRemoveEntry(NodeType * pEntry)
+ {
+ ATLENSURE(pEntry != NULL);
+
+ T* pT = static_cast<T*>(this);
+
+ ATLASSERT((long)pEntry->dwRef == 0);
+
+ pT->OnDestroyEntry(pEntry);
+
+ m_statObj.ReleaseElement(pEntry->dwSize);
+
+ delete pEntry;
+
+ return S_OK;
+ }
+}; // CMemoryCacheBase
+
+class CCacheDataBase
+{
+};
+
+struct CCacheDataEx : public CCacheDataBase
+{
+ CCacheDataEx()
+ {
+ hInstance = NULL;
+ pClient = NULL;
+ }
+
+ HINSTANCE hInstance;
+ IMemoryCacheClient * pClient;
+};
+
+
+template <typename DataType,
+ class StatClass=CStdStatClass,
+ class FlushClass=COldFlusher,
+ class keyType=CFixedStringKey, class KeyTrait=CStringElementTraits<CFixedStringKey >,
+ class SyncClass=CComCriticalSection,
+ class CullClass=CExpireCuller >
+class CMemoryCache:
+ public CMemoryCacheBase<CMemoryCache<DataType, StatClass, FlushClass, keyType, KeyTrait, SyncClass, CullClass>, DataType, CCacheDataEx,
+ keyType, KeyTrait, FlushClass, CullClass, SyncClass, StatClass>
+{
+protected:
+ CComPtr<IServiceProvider> m_spServiceProv;
+ CComPtr<IDllCache> m_spDllCache;
+ typedef CMemoryCacheBase<CMemoryCache<DataType, StatClass, FlushClass, keyType, KeyTrait, SyncClass, CullClass>, DataType, CCacheDataEx,
+ keyType, KeyTrait, FlushClass, CullClass, SyncClass, StatClass> baseClass;
+public:
+ virtual ~CMemoryCache()
+ {
+ }
+
+ HRESULT Initialize(IServiceProvider * pProvider)
+ {
+ baseClass::Initialize();
+ m_spServiceProv = pProvider;
+ if (pProvider)
+ return m_spServiceProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
+ else
+ return S_OK;
+ }
+
+ HRESULT AddEntry(
+ const keyType &Key,
+ const DataType &data,
+ DWORD dwSize,
+ FILETIME * pftExpireTime = NULL,
+ HINSTANCE hInstance = NULL,
+ IMemoryCacheClient * pClient = NULL,
+ HCACHEITEM *phEntry = NULL
+ )
+ {
+ _ATLTRY
+ {
+ HRESULT hr;
+ NodeType * pEntry = NULL;
+ hr = baseClass::AddEntry(Key, data, dwSize, (HCACHEITEM *)&pEntry);
+ if (hr != S_OK)
+ return hr;
+
+ pEntry->hInstance = hInstance;
+ pEntry->pClient = pClient;
+ if (pftExpireTime)
+ pEntry->cftExpireTime = *pftExpireTime;
+
+ if (hInstance && m_spDllCache)
+ m_spDllCache->AddRefModule(hInstance);
+
+ baseClass::CommitEntry(static_cast<HCACHEITEM>(pEntry));
+
+ if (phEntry)
+ *phEntry = static_cast<HCACHEITEM>(pEntry);
+ else
+ baseClass::ReleaseEntry(static_cast<HCACHEITEM>(pEntry));
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+ }
+
+ virtual void OnDestroyEntry(const NodeType * pEntry)
+ {
+ ATLASSERT(pEntry);
+ if (!pEntry)
+ return;
+
+ if (pEntry->pClient)
+ pEntry->pClient->Free((void *)&pEntry->Data);
+ if (pEntry->hInstance && m_spDllCache)
+ m_spDllCache->ReleaseModule(pEntry->hInstance);
+ }
+}; // CMemoryCache
+
+// CStdStatData - contains the data that CStdStatClass keeps track of
+#define ATL_PERF_CACHE_OBJECT 100
+
+struct CPerfStatObject : public CPerfObject
+{
+ DECLARE_PERF_CATEGORY(CPerfStatObject, ATL_PERF_CACHE_OBJECT, IDS_PERFMON_CACHE, IDS_PERFMON_CACHE_HELP, -1);
+
+ BEGIN_COUNTER_MAP(CPerfStatObject)
+ DEFINE_COUNTER(m_nHitCount, IDS_PERFMON_HITCOUNT, IDS_PERFMON_HITCOUNT_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_nMissCount, IDS_PERFMON_MISSCOUNT, IDS_PERFMON_MISSCOUNT_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_nCurrentAllocations, IDS_PERFMON_CURRENTALLOCATIONS, IDS_PERFMON_CURRENTALLOCATIONS_HELP, PERF_COUNTER_RAWCOUNT, -3)
+ DEFINE_COUNTER(m_nMaxAllocations, IDS_PERFMON_MAXALLOCATIONS, IDS_PERFMON_MAXALLOCATIONS_HELP, PERF_COUNTER_RAWCOUNT, -3)
+ DEFINE_COUNTER(m_nCurrentEntries, IDS_PERFMON_CURRENTENTRIES, IDS_PERFMON_CURRENTENTRIES_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_nMaxEntries, IDS_PERFMON_MAXENTRIES, IDS_PERFMON_MAXENTRIES_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ END_COUNTER_MAP()
+
+ long m_nHitCount;
+ long m_nMissCount;
+ long m_nCurrentAllocations;
+ long m_nMaxAllocations;
+ long m_nCurrentEntries;
+ long m_nMaxEntries;
+};
+
+// CCachePerfMon - the interface to CPerfMon, with associated definitions
+class CCachePerfMon : public CPerfMon
+{
+public:
+ BEGIN_PERF_MAP(_T("ATL Server:Cache"))
+ CHAIN_PERF_CATEGORY(CPerfStatObject)
+ END_PERF_MAP()
+};
+
+//
+//CStdStatClass
+// Description
+// This class provides the implementation of a standard cache statistics accounting class
+class CStdStatClass
+{
+protected:
+ CPerfStatObject* m_pStats;
+ CPerfStatObject m_stats;
+
+public:
+
+ CStdStatClass()
+ {
+ m_pStats = &m_stats;
+ }
+
+ // This function is not thread safe by design
+ HRESULT Initialize(CPerfStatObject* pStats = NULL)
+ {
+ if (pStats)
+ m_pStats = pStats;
+ else
+ m_pStats = &m_stats;
+
+ ResetCounters();
+ return S_OK;
+ }
+
+ // This function is not thread safe by design
+ HRESULT Uninitialize()
+ {
+ m_pStats = &m_stats;
+ return S_OK;
+ }
+
+ void Hit()
+ {
+ InterlockedIncrement(&m_pStats->m_nHitCount);
+ }
+
+ void Miss()
+ {
+ InterlockedIncrement(&m_pStats->m_nMissCount);
+ }
+
+ void AddElement(DWORD dwBytes)
+ {
+ DWORD nCurrentEntries = InterlockedIncrement(&m_pStats->m_nCurrentEntries);
+ AtlInterlockedUpdateMax(nCurrentEntries, &m_pStats->m_nMaxEntries);
+
+ DWORD nCurrentAllocations = dwBytes + AtlInterlockedExchangeAdd(&m_pStats->m_nCurrentAllocations, dwBytes);
+ AtlInterlockedUpdateMax(nCurrentAllocations, &m_pStats->m_nMaxAllocations);
+ }
+
+ void ReleaseElement(DWORD dwBytes)
+ {
+ InterlockedDecrement(&m_pStats->m_nCurrentEntries);
+ AtlInterlockedExchangeAdd(&m_pStats->m_nCurrentAllocations, -((long)dwBytes));
+ }
+
+ DWORD GetHitCount()
+ {
+ return m_pStats->m_nHitCount;
+ }
+
+ DWORD GetMissCount()
+ {
+ return m_pStats->m_nMissCount;
+ }
+
+ DWORD GetCurrentAllocSize()
+ {
+ return m_pStats->m_nCurrentAllocations;
+ }
+
+ DWORD GetMaxAllocSize()
+ {
+ return m_pStats->m_nMaxAllocations;
+ }
+
+ DWORD GetCurrentEntryCount()
+ {
+ return m_pStats->m_nCurrentEntries;
+ }
+
+ DWORD GetMaxEntryCount()
+ {
+ return m_pStats->m_nMaxEntries;
+ }
+
+ void ResetCounters()
+ {
+ m_pStats->m_nHitCount = 0;
+ m_pStats->m_nMissCount = 0;
+ m_pStats->m_nCurrentAllocations = 0;
+ m_pStats->m_nMaxAllocations = 0;
+ m_pStats->m_nCurrentEntries = 0;
+ m_pStats->m_nMaxEntries = 0;
+ }
+}; // CStdStatClass
+
+//
+// CNoStatClass
+// This is a noop stat class
+class CNoStatClass
+{
+public:
+ HRESULT Initialize(){ return S_OK; }
+ HRESULT Uninitialize(){ return S_OK; }
+ void Hit(){ }
+ void Miss(){ }
+ void AddElement(DWORD){ }
+ void ReleaseElement(DWORD){ }
+ DWORD GetHitCount(){ return 0; }
+ DWORD GetMissCount(){ return 0; }
+ DWORD GetCurrentAllocSize(){ return 0; }
+ DWORD GetMaxAllocSize(){ return 0; }
+ DWORD GetCurrentEntryCount(){ return 0; }
+ DWORD GetMaxEntryCount(){ return 0; }
+ void ResetCounters(){ }
+}; // CNoStatClass
+
+//
+//CPerfStatClass
+// Description
+// This class provides the implementation of a cache statistics gathering class
+// with PerfMon support
+class CPerfStatClass : public CStdStatClass
+{
+ CPerfStatObject * m_pPerfObject;
+ CCachePerfMon m_PerfMon;
+
+public:
+
+ HRESULT Initialize(__in_z_opt LPWSTR szName=NULL)
+ {
+ HRESULT hr;
+ WCHAR szPath[MAX_PATH];
+
+ if (!szName)
+ {
+ // default name is the name of the module
+ // we don't care about possible truncation if longer than max_path
+ // we just need an identifier
+ HINSTANCE hInst = _AtlBaseModule.GetModuleInstance();
+ if (::GetModuleFileNameW(hInst, szPath, MAX_PATH) == 0)
+ {
+ return E_FAIL;
+ }
+ szPath[MAX_PATH-1] = 0;
+ szName = szPath;
+ }
+
+ m_pPerfObject = NULL;
+ ATLTRACE(atlTraceCache, 2, _T("Initializing m_PerfMon\n"));
+ hr = m_PerfMon.Initialize();
+ if (SUCCEEDED(hr))
+ {
+ CPerfLock lock(&m_PerfMon);
+ if (FAILED(hr = lock.GetStatus()))
+ {
+ return hr;
+ }
+
+ hr = m_PerfMon.CreateInstance(ATL_PERF_CACHE_OBJECT, 0, szName, reinterpret_cast<CPerfObject**>(&m_pPerfObject));
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ CStdStatClass::Initialize(m_pPerfObject);
+ }
+ else
+ ATLASSUME(m_pPerfObject == NULL);
+
+ return hr;
+ }
+
+ HRESULT Uninitialize()
+ {
+ CStdStatClass::Uninitialize();
+
+ if (m_pPerfObject != NULL) // Initialized m_pPerfObject successfully above
+ {
+ HRESULT hr = m_PerfMon.ReleaseInstance(m_pPerfObject);
+ if (hr != S_OK)
+ return hr;
+
+ m_PerfMon.UnInitialize();
+ }
+
+ return S_OK;
+ }
+}; // CPerfStatClass
+
+#ifndef ATL_BLOB_CACHE_TIMEOUT
+#ifdef _DEBUG
+#define ATL_BLOB_CACHE_TIMEOUT 1000
+#else
+#define ATL_BLOB_CACHE_TIMEOUT 5000
+#endif // _DEBUG
+#endif // ATL_BLOB_CACHE_TIMEOUT
+
+//
+//CBlobCache
+// Description:
+// Implements a cache that stores pointers to void. Uses the generic CMemoryCacheBase class
+// as the implementation.
+template <class MonitorClass,
+ class StatClass=CStdStatClass,
+ class SyncObj=CComCriticalSection,
+ class FlushClass=COldFlusher,
+ class CullClass=CExpireCuller >
+class CBlobCache : public CMemoryCache<void*, StatClass, FlushClass, CFixedStringKey,
+ CStringElementTraits<CFixedStringKey >, SyncObj, CullClass>,
+ public IMemoryCache,
+ public IMemoryCacheControl,
+ public IMemoryCacheStats,
+ public IWorkerThreadClient
+{
+ typedef CMemoryCache<void*, StatClass, FlushClass, CFixedStringKey,
+ CStringElementTraits<CFixedStringKey>, SyncObj, CullClass> cacheBase;
+
+ MonitorClass m_Monitor;
+
+protected:
+ HANDLE m_hTimer;
+
+public:
+ CBlobCache() : m_hTimer(NULL)
+ {
+ }
+
+ HRESULT Initialize(IServiceProvider *pProv)
+ {
+ HRESULT hr = cacheBase::Initialize(pProv);
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize();
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(ATL_BLOB_CACHE_TIMEOUT,
+ static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
+ }
+
+ template <class ThreadTraits>
+ HRESULT Initialize(IServiceProvider *pProv, CWorkerThread<ThreadTraits> *pWorkerThread)
+ {
+ ATLASSERT(pWorkerThread);
+
+ HRESULT hr = cacheBase::Initialize(pProv);
+ if (FAILED(hr))
+ return hr;
+
+ hr = m_Monitor.Initialize(pWorkerThread);
+ if (FAILED(hr))
+ return hr;
+
+ return m_Monitor.AddTimer(ATL_BLOB_CACHE_TIMEOUT,
+ static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
+ }
+
+ HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/)
+ {
+ CBlobCache* pCache = (CBlobCache*)dwParam;
+
+ if (pCache)
+ pCache->Flush();
+ return S_OK;
+ }
+
+ HRESULT CloseHandle(HANDLE hObject)
+ {
+ ATLASSUME(m_hTimer == hObject);
+ m_hTimer = NULL;
+ ::CloseHandle(hObject);
+ return S_OK;
+ }
+
+ virtual ~CBlobCache()
+ {
+ if (m_hTimer)
+ {
+ ATLENSURE(SUCCEEDED(m_Monitor.RemoveHandle(m_hTimer)));
+ }
+ }
+
+ HRESULT Uninitialize()
+ {
+ HRESULT hrMonitor=S_OK;
+ if (m_hTimer)
+ {
+ hrMonitor=m_Monitor.RemoveHandle(m_hTimer);
+ m_hTimer = NULL;
+ }
+ HRESULT hrShut=m_Monitor.Shutdown();
+ HRESULT hrCache=cacheBase::Uninitialize();
+ if(FAILED(hrMonitor))
+ {
+ return hrMonitor;
+ }
+ if(FAILED(hrShut))
+ {
+ return hrShut;
+ }
+ return hrCache;
+ }
+ // IUnknown methods
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
+ {
+ HRESULT hr = E_NOINTERFACE;
+ if (!ppv)
+ hr = E_POINTER;
+ else
+ {
+ if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
+ InlineIsEqualGUID(riid, __uuidof(IMemoryCache)))
+ {
+ *ppv = (IUnknown *) (IMemoryCache *) this;
+ AddRef();
+ hr = S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheStats)))
+ {
+ *ppv = (IUnknown *) (IMemoryCacheStats*)this;
+ AddRef();
+ hr = S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheControl)))
+ {
+ *ppv = (IUnknown *) (IMemoryCacheControl*)this;
+ AddRef();
+ hr = S_OK;
+ }
+
+ }
+ return hr;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ // IMemoryCache Methods
+ HRESULT STDMETHODCALLTYPE Add(LPCSTR szKey, void *pvData, DWORD dwSize,
+ FILETIME *pftExpireTime,
+ HINSTANCE hInstClient,
+ HCACHEITEM *phEntry,
+ IMemoryCacheClient *pClient)
+ {
+ HRESULT hr = E_FAIL;
+ //if it's a multithreaded cache monitor we'll let the monitor take care of
+ //cleaning up the cache so we don't overflow our configuration settings.
+ //if it's not a threaded cache monitor, we need to make sure we don't
+ //overflow the configuration settings by adding a new element
+ if (m_Monitor.GetThreadHandle()==NULL)
+ {
+ if (!cacheBase::CanAddEntry(dwSize))
+ {
+ //flush the entries and check again to see if we can add
+ cacheBase::FlushEntries();
+ if (!cacheBase::CanAddEntry(dwSize))
+ return E_OUTOFMEMORY;
+ }
+ }
+ _ATLTRY
+ {
+ hr = cacheBase::AddEntry(szKey, pvData, dwSize,
+ pftExpireTime, hInstClient, pClient, phEntry);
+ return hr;
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+ }
+
+ HRESULT STDMETHODCALLTYPE LookupEntry(LPCSTR szKey, HCACHEITEM * phEntry)
+ {
+ return cacheBase::LookupEntry(szKey, phEntry);
+ }
+
+ HRESULT STDMETHODCALLTYPE GetData(const HCACHEITEM hKey, void **ppvData, DWORD *pdwSize) const
+ {
+ return cacheBase::GetEntryData(hKey, ppvData, pdwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE ReleaseEntry(const HCACHEITEM hKey)
+ {
+ return cacheBase::ReleaseEntry(hKey);
+ }
+
+ HRESULT STDMETHODCALLTYPE RemoveEntry(const HCACHEITEM hKey)
+ {
+ return cacheBase::RemoveEntry(hKey);
+ }
+
+ HRESULT STDMETHODCALLTYPE RemoveEntryByKey(LPCSTR szKey)
+ {
+ return cacheBase::RemoveEntryByKey(szKey);
+ }
+
+ HRESULT STDMETHODCALLTYPE Flush()
+ {
+ return cacheBase::FlushEntries();
+ }
+
+
+ HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize)
+ {
+ return cacheBase::SetMaxAllowedSize(dwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize)
+ {
+ return cacheBase::GetMaxAllowedSize(pdwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize)
+ {
+ return cacheBase::SetMaxAllowedEntries(dwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize)
+ {
+ return cacheBase::GetMaxAllowedEntries(pdwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE ResetCache()
+ {
+ return cacheBase::ResetCache();
+ }
+
+ // IMemoryCacheStats methods
+ HRESULT STDMETHODCALLTYPE ClearStats()
+ {
+ m_statObj.ResetCounters();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetHitCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMissCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMaxAllocSize();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetCurrentAllocSize();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMaxEntryCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetCurrentEntryCount();
+ return S_OK;
+ }
+
+}; // CBlobCache
+
+
+//
+// CDllCache
+// This class manages a cache to handle calls to LoadLibrary
+// and FreeLibrary.
+// It keeps dlls loaded even after the last call to free library
+// a worker thread then calls FreeLibrary on unused dlls
+//
+#ifndef ATL_DLL_CACHE_TIMEOUT
+ #ifdef _DEBUG
+ #define ATL_DLL_CACHE_TIMEOUT 1000 // 1 sec default for debug builds
+ #else
+ #define ATL_DLL_CACHE_TIMEOUT 10*60000 // 10 minute default for retail builds
+ #endif
+#endif
+
+class CNoDllCachePeer
+{
+public:
+ struct DllInfo
+ {
+ };
+
+ BOOL Add(HINSTANCE /*hInst*/, DllInfo * /*pInfo*/)
+ {
+ return TRUE;
+ }
+
+ void Remove(HINSTANCE /*hInst*/, DllInfo * /*pInfo*/)
+ {
+ }
+};
+
+// CDllCache
+// Implements IDllCache, an interface that is used to load and unload Dlls.
+// To use it, construct an instance of a CDllCache and call Initialize.
+// The Initialize call has to match with the type of monitor class you
+// templatize on. The monitor thread will call IWorkerThreadClient::Execute
+// after its timeout expires. Make sure to Uninitialize the object before
+// it is destroyed by calling Uninitialize
+//
+template <class MonitorClass, class Peer=CNoDllCachePeer>
+class CDllCache : public IDllCache,
+ public IWorkerThreadClient
+{
+protected:
+ CComCriticalSection m_critSec;
+ CSimpleArray<DLL_CACHE_ENTRY> m_Dlls;
+ CSimpleArray<typename Peer::DllInfo> m_DllInfos;
+ MonitorClass m_Monitor;
+ HANDLE m_hTimer;
+
+ void RemoveDllEntry(DLL_CACHE_ENTRY& entry)
+ {
+ ::FreeLibrary(entry.hInstDll);
+ entry.hInstDll = NULL;
+ m_Dlls.RemoveAt(m_Dlls.GetSize()-1);
+ }
+
+public:
+ Peer m_Peer;
+
+ CDllCache() :
+ m_hTimer(NULL)
+ {
+
+ }
+
+ HRESULT Initialize(DWORD dwTimeout=ATL_DLL_CACHE_TIMEOUT)
+ {
+ HRESULT hr = m_critSec.Init();
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize();
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(dwTimeout, this, 0, &m_hTimer);
+ }
+
+ template <class ThreadTraits>
+ HRESULT Initialize(CWorkerThread<ThreadTraits> *pWorkerThread,
+ DWORD dwTimeout=ATL_DLL_CACHE_TIMEOUT)
+ {
+ HRESULT hr = m_critSec.Init();
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize(pWorkerThread);
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(dwTimeout, this, 0, &m_hTimer);
+ }
+
+ HRESULT Uninitialize()
+ {
+ HRESULT hr = S_OK;
+ HRESULT hrLatest = S_OK;
+ if (m_hTimer)
+ {
+ hrLatest=m_Monitor.RemoveHandle(m_hTimer);
+ if(FAILED(hrLatest) && SUCCEEDED(hr))
+ {
+ hr=hrLatest;
+ }
+ m_hTimer = NULL;
+ }
+ m_Monitor.Shutdown();
+
+ // free all the libraries we've cached
+ int nLen = m_Dlls.GetSize();
+ for (int i=0; i<nLen; i++)
+ {
+ DLL_CACHE_ENTRY& entry = m_Dlls[i];
+ ATLASSERT(entry.dwRefs == 0);
+ BOOL bRet = ::FreeLibrary(entry.hInstDll);
+
+ if (!bRet)
+ {
+ hrLatest = AtlHresultFromLastError();
+ if(FAILED(hrLatest) && SUCCEEDED(hr))
+ {
+ hr=hrLatest;
+ }
+ ATLTRACE(atlTraceCache, 0, _T("Free library failed on shutdown of dll cache : hr = 0x%08x)"), hr);
+ }
+ }
+ m_Dlls.RemoveAll();
+ m_critSec.Term();
+ return hr;
+
+ }
+
+ // IUnknown methods
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+ if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
+ InlineIsEqualGUID(riid, __uuidof(IDllCache)))
+ {
+ *ppv = (IUnknown *) this;
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ // IDllCache methods
+ HINSTANCE Load(LPCSTR szDllName, void *pPeerInfo) throw(...)
+ {
+ HRESULT hr = m_critSec.Lock();
+ if (FAILED(hr))
+ return NULL;
+
+ int nLen = m_Dlls.GetSize();
+ for (int i=0; i<nLen; i++)
+ {
+ DLL_CACHE_ENTRY& entry = m_Dlls[i];
+ if (!_stricmp(entry.szDllName, szDllName))
+ {
+ entry.dwRefs++;
+ m_critSec.Unlock();
+ if (pPeerInfo)
+ {
+ Peer::DllInfo *pl = (Peer::DllInfo*)pPeerInfo;
+ *pl = m_DllInfos[i];
+ }
+ return entry.hInstDll;
+ }
+ }
+ DLL_CACHE_ENTRY entry;
+ entry.hInstDll = ::LoadLibraryA(szDllName);
+ if (!entry.hInstDll)
+ {
+ m_critSec.Unlock();
+ return NULL;
+ }
+ if (!SafeStringCopy(entry.szDllName, szDllName))
+ {
+ ::FreeLibrary(entry.hInstDll);
+ m_critSec.Unlock();
+ return NULL;
+ }
+ entry.dwRefs = 1;
+ entry.bAlive = TRUE;
+ m_Dlls.Add(entry);
+
+ Peer::DllInfo *pdllInfo = (Peer::DllInfo*)pPeerInfo;
+
+ // m_Peer could throw an exception from user code. We
+ // pass that exception from here to a higher context (we
+ // won't deal with user exception here).
+ if (!m_Peer.Add(entry.hInstDll, pdllInfo))
+ {
+ RemoveDllEntry(entry);
+ }
+
+
+ if ((entry.hInstDll != NULL) && (!m_DllInfos.Add(*pdllInfo)))
+ {
+ RemoveDllEntry(entry);
+ }
+
+ m_critSec.Unlock();
+ return entry.hInstDll;
+ }
+
+ BOOL Free(HINSTANCE hInstDll)
+ {
+ HRESULT hr = m_critSec.Lock();
+ if (FAILED(hr))
+ return FALSE;
+
+ int nLen = m_Dlls.GetSize();
+ for (int i=0; i<nLen; i++)
+ {
+ DLL_CACHE_ENTRY &entry = m_Dlls[i];
+ if (entry.hInstDll == hInstDll)
+ {
+ ATLASSERT(entry.dwRefs > 0);
+ entry.bAlive = TRUE;
+ entry.dwRefs--;
+ m_critSec.Unlock();
+ return TRUE;
+ }
+ }
+
+ m_critSec.Unlock();
+ // the dll wasn't found
+ // in the cache, so just
+ // pass along to ::FreeLibrary
+ return ::FreeLibrary(hInstDll);
+ }
+
+ BOOL AddRefModule(HINSTANCE hInstDll)
+ {
+ HRESULT hr = m_critSec.Lock();
+ if (FAILED(hr))
+ return FALSE;
+
+ int nLen = m_Dlls.GetSize();
+ for (int i=0; i<nLen; i++)
+ {
+ DLL_CACHE_ENTRY &entry = m_Dlls[i];
+ if (entry.hInstDll == hInstDll)
+ {
+ ATLASSERT(entry.dwRefs > 0);
+ entry.dwRefs++;
+ m_critSec.Unlock();
+ return TRUE;
+ }
+ }
+
+ m_critSec.Unlock();
+ return FALSE;
+ }
+
+ BOOL ReleaseModule(HINSTANCE hInstDll)
+ {
+ HRESULT hr = m_critSec.Lock();
+ if (FAILED(hr))
+ return FALSE;
+
+ int nLen = m_Dlls.GetSize();
+ for (int i=0; i<nLen; i++)
+ {
+ DLL_CACHE_ENTRY &entry = m_Dlls[i];
+ if (entry.hInstDll == hInstDll)
+ {
+ ATLASSERT(entry.dwRefs > 0);
+ entry.bAlive = TRUE;
+ entry.dwRefs--;
+ m_critSec.Unlock();
+ return TRUE;
+ }
+ }
+ m_critSec.Unlock();
+ return FALSE;
+ }
+
+ HRESULT GetEntries(DWORD dwCount, DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied)
+ {
+ if (!pdwCopied)
+ return E_POINTER;
+
+ HRESULT hr = m_critSec.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ if (dwCount==0 || pEntries==NULL)
+ {
+ // just return the required size
+ *pdwCopied = m_Dlls.GetSize();
+ m_critSec.Unlock();
+ return S_OK;
+ }
+
+ if (dwCount > (DWORD) m_Dlls.GetSize())
+ dwCount = m_Dlls.GetSize();
+ Checked::memcpy_s(pEntries, dwCount*sizeof(DLL_CACHE_ENTRY), m_Dlls.GetData(), dwCount*sizeof(DLL_CACHE_ENTRY));
+ *pdwCopied = dwCount;
+ m_critSec.Unlock();
+ return S_OK;
+ }
+
+ HRESULT Flush()
+ {
+ HRESULT hr = m_critSec.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ int nLen = m_Dlls.GetSize();
+ for (int i=0; i<nLen; i++)
+ {
+ DLL_CACHE_ENTRY &entry = m_Dlls[i];
+ if (entry.dwRefs == 0 && !entry.bAlive)
+ {
+ _ATLTRY
+ {
+ m_Peer.Remove(entry.hInstDll, &m_DllInfos[i]);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE(atlTraceCache, 2, _T("Exception thrown from user code in CDllCache::Flush\n"));
+ }
+
+ ::FreeLibrary(entry.hInstDll);
+ m_Dlls.RemoveAt(i);
+ m_DllInfos.RemoveAt(i);
+ i--;
+ nLen--;
+ }
+ entry.bAlive = FALSE;
+ }
+
+ m_critSec.Unlock();
+ return S_OK;
+ }
+
+ HRESULT Execute(DWORD_PTR /*dwParam*/, HANDLE /*hObject*/)
+ {
+ Flush();
+ return S_OK;
+ }
+
+ HRESULT CloseHandle(HANDLE hObject)
+ {
+ ATLASSUME(m_hTimer == hObject);
+ m_hTimer = NULL;
+ ::CloseHandle(hObject);
+ return S_OK;
+ }
+}; // CDllCache
+
+
+//
+//IStencilCache
+//IStencilCache is used by a stencil processor to cache pointers to CStencil
+//derived objects.
+
+
+// {8702269B-707D-49cc-AEF8-5FFCB3D6891B}
+extern "C" __declspec(selectany) const IID IID_IStencilCache = { 0x8702269b, 0x707d, 0x49cc, { 0xae, 0xf8, 0x5f, 0xfc, 0xb3, 0xd6, 0x89, 0x1b } };
+
+__interface ATL_NO_VTABLE __declspec(uuid("8702269B-707D-49cc-AEF8-5FFCB3D6891B"))
+ IStencilCache : public IUnknown
+{
+ // IStencilCache methods
+ STDMETHOD(CacheStencil)(LPCSTR szName, //a name for this cache entry
+ void *pStencil, //a pointer to a CStencil derived object
+ DWORD dwSize, //sizeof pStencil
+ HCACHEITEM *pHandle, //out pointer to a handle to the this cache entry
+ HINSTANCE hInst, //HINSTANCE of the module putting this entry
+ //in the cache.
+ IMemoryCacheClient *pClient //Interface used to free this instance
+ );
+ STDMETHOD(LookupStencil)(LPCSTR szName, HCACHEITEM * phStencil);
+ STDMETHOD(GetStencil)(const HCACHEITEM hStencil, void ** ppStencil) const;
+ STDMETHOD(AddRefStencil)(const HCACHEITEM hStencil);
+ STDMETHOD(ReleaseStencil)(const HCACHEITEM hStencil);
+};
+
+// {55DEF119-D7A7-4eb7-A876-33365E1C5E1A}
+extern "C" __declspec(selectany) const IID IID_IStencilCacheControl = { 0x55def119, 0xd7a7, 0x4eb7, { 0xa8, 0x76, 0x33, 0x36, 0x5e, 0x1c, 0x5e, 0x1a } };
+__interface ATL_NO_VTABLE __declspec(uuid("55DEF119-D7A7-4eb7-A876-33365E1C5E1A"))
+IStencilCacheControl : public IUnknown
+{
+ //IStencilCacheControl
+ STDMETHOD(RemoveStencil)(const HCACHEITEM hStencil); // Removes the stencil if there are no references,
+ // otherwise detaches it
+ STDMETHOD(RemoveStencilByName)(LPCSTR szStencil); //removes a stencil if there are no
+ //references to it
+ STDMETHOD(RemoveAllStencils)(); //removes all stencils that don't have references on them
+ STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan); //sets the lifespan for all stencils
+ //in the cache (in 100 nanosecond units (10,000,000=1 second)).
+ STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifespan);
+};
+
+#ifndef ATL_STENCIL_CACHE_TIMEOUT
+#ifdef _DEBUG
+ #define ATL_STENCIL_CACHE_TIMEOUT 1000
+#else
+ #define ATL_STENCIL_CACHE_TIMEOUT 5000
+#endif // _DEBUG
+#endif // ATL_STENCIL_CACHE_TIMEOUT
+
+#ifndef ATL_STENCIL_LIFESPAN
+#ifdef _DEBUG
+ #define ATL_STENCIL_LIFESPAN CFileTime::Second
+#else
+ #define ATL_STENCIL_LIFESPAN CFileTime::Hour
+#endif
+#endif
+
+// timeout before we check if the file
+// has changed in m.s.
+#ifndef ATL_STENCIL_CHECK_TIMEOUT
+ #define ATL_STENCIL_CHECK_TIMEOUT 1000
+#endif
+
+template <class MonitorClass,
+ class StatClass=CStdStatClass,
+ class SyncClass=CComCriticalSection,
+ class FlushClass=COldFlusher,
+ class CullClass=CLifetimeCuller >
+class CStencilCache :
+ public CMemoryCacheBase<CStencilCache<MonitorClass, StatClass, SyncClass, FlushClass, CullClass>, void *, CCacheDataEx,
+ CFixedStringKey, CStringElementTraitsI<CFixedStringKey >,
+ FlushClass, CullClass, SyncClass, StatClass>,
+ public IStencilCache,
+ public IStencilCacheControl,
+ public IWorkerThreadClient,
+ public IMemoryCacheStats,
+ public CComObjectRootEx<CComGlobalsThreadModel>
+{
+protected:
+ typedef CMemoryCacheBase<CStencilCache<MonitorClass, StatClass, SyncClass, FlushClass, CullClass>, void *, CCacheDataEx,
+ CFixedStringKey, CStringElementTraitsI<CFixedStringKey >,
+ FlushClass, CullClass, SyncClass, StatClass> cacheBase;
+ unsigned __int64 m_dwdwStencilLifespan;
+
+ MonitorClass m_Monitor;
+ HANDLE m_hTimer;
+ CComPtr<IDllCache> m_spDllCache;
+
+public:
+
+ CStencilCache() :
+ m_dwdwStencilLifespan(ATL_STENCIL_LIFESPAN),
+ m_hTimer(NULL)
+ {
+
+ }
+
+ ~CStencilCache()
+ {
+ if (m_hTimer)
+ {
+ ATLENSURE(SUCCEEDED(m_Monitor.RemoveHandle(m_hTimer)));
+ }
+ }
+
+ HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/)
+ {
+ CStencilCache* pCache = (CStencilCache*)dwParam;
+ if (pCache)
+ pCache->FlushEntries();
+ return S_OK;
+ }
+
+ HRESULT CloseHandle(HANDLE hObject)
+ {
+ ATLASSUME(m_hTimer == hObject);
+ m_hTimer = NULL;
+ ::CloseHandle(hObject);
+ return S_OK;
+ }
+
+ HRESULT Initialize(IServiceProvider *pProv, DWORD dwStencilCacheTimeout=ATL_STENCIL_CACHE_TIMEOUT,
+ __int64 dwdwStencilLifespan=ATL_STENCIL_LIFESPAN)
+ {
+ m_dwdwStencilLifespan = dwdwStencilLifespan;
+ HRESULT hr = cacheBase::Initialize();
+ if (FAILED(hr))
+ return hr;
+ hr = E_FAIL;
+ if (pProv)
+ hr = pProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize();
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(dwStencilCacheTimeout, this, (DWORD_PTR) this, &m_hTimer);
+ }
+
+ template <class ThreadTraits>
+ HRESULT Initialize(IServiceProvider *pProv, CWorkerThread<ThreadTraits> *pWorkerThread,
+ DWORD dwStencilCacheTimeout=ATL_STENCIL_CACHE_TIMEOUT, __int64 dwdwStencilLifespan=ATL_STENCIL_LIFESPAN)
+ {
+ m_dwdwStencilLifespan = dwdwStencilLifespan;
+ HRESULT hr = cacheBase::Initialize();
+ if (FAILED(hr))
+ return hr;
+ hr = E_FAIL;
+ if (pProv)
+ hr = pProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize(pWorkerThread);
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(dwStencilCacheTimeout, this, (DWORD_PTR) this, &m_hTimer);
+ }
+
+
+ BEGIN_COM_MAP(CStencilCache)
+ COM_INTERFACE_ENTRY(IMemoryCacheStats)
+ COM_INTERFACE_ENTRY(IStencilCache)
+ COM_INTERFACE_ENTRY(IStencilCacheControl)
+ END_COM_MAP()
+//IStencilCache methods
+ STDMETHOD(CacheStencil)(LPCSTR szName, void *pStencil, DWORD dwSize, HCACHEITEM *phEntry,
+ HINSTANCE hInstance, IMemoryCacheClient *pClient)
+ {
+ NodeType * pEntry = NULL;
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ _ATLTRY
+ {
+ hr = cacheBase::AddEntry(szName, pStencil, dwSize, (HCACHEITEM *)&pEntry);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_FAIL;
+ }
+ if (hr != S_OK)
+ {
+ m_syncObj.Unlock();
+ return hr;
+ }
+
+ pEntry->hInstance = hInstance;
+ pEntry->pClient = pClient;
+ pEntry->nLifespan = m_dwdwStencilLifespan;
+ if (hInstance && m_spDllCache)
+ m_spDllCache->AddRefModule(hInstance);
+
+ cacheBase::CommitEntry(static_cast<HCACHEITEM>(pEntry));
+
+ if (phEntry)
+ *phEntry = static_cast<HCACHEITEM>(pEntry);
+ else
+ cacheBase::ReleaseEntry(static_cast<HCACHEITEM>(pEntry));
+
+ m_syncObj.Unlock();
+ return hr;
+ }
+
+ STDMETHOD(LookupStencil)(LPCSTR szName, HCACHEITEM * phStencil)
+ {
+ return cacheBase::LookupEntry(szName, phStencil);
+ }
+
+ STDMETHOD(GetStencil)(const HCACHEITEM hStencil, void ** pStencil) const
+ {
+ return cacheBase::GetEntryData(hStencil, pStencil, NULL);
+ }
+
+ STDMETHOD(AddRefStencil)(const HCACHEITEM hStencil)
+ {
+ return cacheBase::AddRefEntry(hStencil);
+ }
+
+ STDMETHOD(ReleaseStencil)(const HCACHEITEM hStencil)
+ {
+ return cacheBase::ReleaseEntry(hStencil);
+ }
+
+ //IStencilCacheControl
+
+ STDMETHOD(RemoveStencil)(const HCACHEITEM hStencil)
+ {
+ return cacheBase::RemoveEntry(hStencil);
+ }
+
+ STDMETHOD(RemoveStencilByName)(LPCSTR szStencil)
+ {
+ return cacheBase::RemoveEntryByKey(szStencil);
+ }
+
+ STDMETHOD(RemoveAllStencils)()
+ {
+ return cacheBase::RemoveAllEntries();
+ }
+
+ STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan)
+ {
+ m_dwdwStencilLifespan = dwdwLifespan;
+ return S_OK;
+ }
+
+ STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifepsan)
+ {
+ HRESULT hr = E_POINTER;
+ if (pdwdwLifepsan)
+ {
+ *pdwdwLifepsan = m_dwdwStencilLifespan;
+ hr = S_OK;
+ }
+ return hr;
+ }
+
+ virtual void OnDestroyEntry(const NodeType * pEntry)
+ {
+ ATLASSERT(pEntry);
+ if (!pEntry)
+ return;
+
+ if (pEntry->pClient)
+ pEntry->pClient->Free((void *)&pEntry->Data);
+ if (pEntry->hInstance && m_spDllCache)
+ m_spDllCache->ReleaseModule(pEntry->hInstance);
+ }
+
+ HRESULT Uninitialize()
+ {
+ HRESULT hrMonitor=S_OK;
+ if (m_hTimer)
+ {
+ hrMonitor=m_Monitor.RemoveHandle(m_hTimer);
+ m_hTimer = NULL;
+ }
+ m_Monitor.Shutdown();
+ HRESULT hrCache=cacheBase::Uninitialize();
+ if(FAILED(hrMonitor))
+ {
+ return hrMonitor;
+ }
+ return hrCache;
+ }
+ // IMemoryCacheStats methods
+ HRESULT STDMETHODCALLTYPE ClearStats()
+ {
+ m_statObj.ResetCounters();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetHitCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMissCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMaxAllocSize();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetCurrentAllocSize();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMaxEntryCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetCurrentEntryCount();
+ return S_OK;
+ }
+}; // CStencilCache
+
+// {105A8866-4059-45fe-86AE-FA0EABBFBBB4}
+extern "C" __declspec(selectany) const IID IID_IFileCache = { 0x105a8866, 0x4059, 0x45fe, { 0x86, 0xae, 0xfa, 0xe, 0xab, 0xbf, 0xbb, 0xb4 } };
+
+__interface ATL_NO_VTABLE __declspec(uuid("105A8866-4059-45fe-86AE-FA0EABBFBBB4"))
+ IFileCache : public IUnknown
+{
+ // IFileCache Methods
+
+ STDMETHOD(AddFile)(
+ LPCSTR szFileName,
+ LPCSTR szTempFileName,
+ FILETIME *pftExpireTime,
+ void *pPeerInfo,
+ HCACHEITEM * phKey);
+ STDMETHOD(LookupFile)(LPCSTR szFileName, HCACHEITEM * phKey);
+ STDMETHOD(GetFile)(const HCACHEITEM hKey, LPSTR * pszFileName, void **ppPeerInfo);
+ STDMETHOD(ReleaseFile)(const HCACHEITEM hKey);
+ STDMETHOD(RemoveFile)(const HCACHEITEM hKey);
+ STDMETHOD(RemoveFileByName)(LPCSTR szFileName);
+ STDMETHOD(Flush)();
+};
+
+#ifndef ATL_FILE_CACHE_TIMEOUT
+ #define ATL_FILE_CACHE_TIMEOUT 1000
+#endif
+
+class CNoFileCachePeer
+{
+public:
+ struct PeerInfo
+ {
+ };
+
+ static BOOL Add(PeerInfo* /*pDest*/, PeerInfo * /*pSrc*/)
+ {
+ return TRUE;
+ }
+
+ static BOOL Remove(const PeerInfo* /*pFileInfo*/)
+ {
+ return TRUE;
+ }
+};
+
+template <class Peer>
+struct CCacheDataPeer : public CCacheDataBase
+{
+ typename Peer::PeerInfo PeerData;
+};
+
+// A class to keep track of files, with maintenance -- maximum size of cache,
+// maximum number of entries, expiration of entries, etc. -- inherits from
+// CMemoryCacheBase
+template <
+ class MonitorClass,
+ class StatClass=CStdStatClass,
+ class FileCachePeer=CNoFileCachePeer,
+ class FlushClass=COldFlusher,
+ class SyncClass=CComCriticalSection,
+ class CullClass=CExpireCuller >
+class CFileCache:
+ public CMemoryCacheBase<CFileCache<MonitorClass, StatClass, FileCachePeer, FlushClass, SyncClass, CullClass>, LPSTR, CCacheDataPeer<FileCachePeer>,
+ CFixedStringKey, CStringElementTraits<CFixedStringKey >,
+ FlushClass, CullClass, SyncClass, StatClass>,
+ public IWorkerThreadClient,
+ public IFileCache,
+ public IMemoryCacheControl,
+ public IMemoryCacheStats
+{
+ typedef CMemoryCacheBase<CFileCache<MonitorClass, StatClass, FileCachePeer, FlushClass, SyncClass, CullClass>, LPSTR, CCacheDataPeer<FileCachePeer>,
+ CFixedStringKey, CStringElementTraits<CFixedStringKey >,
+ FlushClass, CullClass, SyncClass, StatClass> cacheBase;
+
+ MonitorClass m_Monitor;
+
+protected:
+ HANDLE m_hTimer;
+
+public:
+
+ CFileCache() : m_hTimer(NULL)
+ {
+ }
+
+ HRESULT Initialize()
+ {
+ HRESULT hr = cacheBase::Initialize();
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize();
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(ATL_FILE_CACHE_TIMEOUT,
+ static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
+ }
+
+ template <class ThreadTraits>
+ HRESULT Initialize(CWorkerThread<ThreadTraits> *pWorkerThread)
+ {
+ ATLASSERT(pWorkerThread);
+
+ HRESULT hr = cacheBase::Initialize();
+ if (FAILED(hr))
+ return hr;
+ hr = m_Monitor.Initialize(pWorkerThread);
+ if (FAILED(hr))
+ return hr;
+ return m_Monitor.AddTimer(ATL_FILE_CACHE_TIMEOUT,
+ static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
+ }
+
+
+ // Callback for CWorkerThread
+ HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/)
+ {
+ CFileCache* pCache = (CFileCache*)dwParam;
+
+ if (pCache)
+ pCache->Flush();
+ return S_OK;
+ }
+
+ HRESULT CloseHandle(HANDLE hObject)
+ {
+ ATLASSUME(m_hTimer == hObject);
+ m_hTimer = NULL;
+ ::CloseHandle(hObject);
+ return S_OK;
+ }
+
+ ~CFileCache()
+ {
+ if (m_hTimer)
+ {
+ ATLENSURE(SUCCEEDED(m_Monitor.RemoveHandle(m_hTimer)));
+ m_hTimer = NULL;
+ }
+ }
+
+ HRESULT Uninitialize()
+ {
+ HRESULT hrMonitor=S_OK;
+ if (m_hTimer)
+ {
+ hrMonitor=m_Monitor.RemoveHandle(m_hTimer);
+ m_hTimer = NULL;
+ }
+ m_Monitor.Shutdown();
+ HRESULT hrCache=cacheBase::Uninitialize();
+ if(FAILED(hrMonitor))
+ {
+ return hrMonitor;
+ }
+ return hrCache;
+ }
+
+
+ // IUnknown methods
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
+ {
+ HRESULT hr = E_NOINTERFACE;
+ if (!ppv)
+ hr = E_POINTER;
+ else
+ {
+ if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
+ InlineIsEqualGUID(riid, __uuidof(IFileCache)))
+ {
+ *ppv = (IUnknown *) (IFileCache *) this;
+ AddRef();
+ hr = S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheStats)))
+ {
+ *ppv = (IMemoryCacheStats*)this;
+ AddRef();
+ hr = S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheControl)))
+ {
+ *ppv = (IMemoryCacheControl*)this;
+ AddRef();
+ hr = S_OK;
+ }
+
+ }
+ return hr;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ // Adds a file to the cache. A file is created with a
+ // temporary name, and then Add is called with the temp
+ // file name and the final file name, along with expiration data,
+ // etc. A search on the file name will return the name of
+ // the file on disk (i.e. the temporary file)
+ HRESULT STDMETHODCALLTYPE AddFile(
+ LPCSTR szFileName,
+ LPCSTR szTempFileName,
+ FILETIME *pftExpireTime,
+ void* pPeerInfo,
+ HCACHEITEM * phKey = NULL)
+ {
+ WIN32_FILE_ATTRIBUTE_DATA fadData;
+ BOOL bRet = GetFileAttributesExA(szTempFileName, GetFileExInfoStandard, &fadData);
+ if (!bRet)
+ return AtlHresultFromLastError();
+
+ __int64 ddwFileSize = (static_cast<__int64>(fadData.nFileSizeHigh) << 32) + fadData.nFileSizeLow;
+
+ DWORD dwRecordedFileSize = (DWORD) (ddwFileSize >> 10);
+ // Round the file size up to 1K if it is < 1K
+ if (dwRecordedFileSize == 0)
+ dwRecordedFileSize = 1;
+
+ if (m_Monitor.GetThreadHandle()==NULL)
+ {
+ if (!cacheBase::CanAddEntry(dwRecordedFileSize))
+ {
+ cacheBase::FlushEntries();
+ if (!cacheBase::CanAddEntry(dwRecordedFileSize))
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ NodeType *pEntry = NULL;
+ HRESULT hr = m_syncObj.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ hr = E_FAIL;
+
+ // Make a private copy of the file name
+ CHeapPtr<char> szTempFileCopy;
+ if (szTempFileCopy.Allocate(MAX_PATH))
+ {
+ if (strlen(szTempFileName) >= MAX_PATH)
+ {
+ m_syncObj.Unlock();
+ return E_FAIL;
+ }
+ Checked::strncpy_s(szTempFileCopy, MAX_PATH, szTempFileName, _TRUNCATE);
+
+ _ATLTRY
+ {
+ hr = cacheBase::AddEntry(szFileName, szTempFileCopy, dwRecordedFileSize, (HCACHEITEM*)&pEntry);
+ szTempFileCopy.Detach();
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_FAIL;
+ }
+ }
+
+ if (hr != S_OK)
+ {
+ m_syncObj.Unlock();
+ return hr;
+ }
+
+
+ hr = (TRUE == FileCachePeer::Add(&pEntry->PeerData,
+ static_cast<FileCachePeer::PeerInfo*>(pPeerInfo)) ? S_OK : E_FAIL);
+ if (hr == S_OK)
+ hr = cacheBase::CommitEntry(static_cast<HCACHEITEM>(pEntry));
+
+ if (hr != S_OK)
+ {
+ cacheBase::RemoveEntry(static_cast<HCACHEITEM>(pEntry));
+ m_syncObj.Unlock();
+ return hr;
+ }
+
+
+ if (pftExpireTime)
+ pEntry->cftExpireTime = *pftExpireTime;
+
+ if (phKey)
+ *phKey = static_cast<HCACHEITEM>(pEntry);
+ else
+ cacheBase::ReleaseEntry(pEntry);
+
+ m_syncObj.Unlock();
+ return hr;
+ }
+
+ // Action to take when the entry is removed from the cache
+ virtual void OnDestroyEntry(const NodeType * pEntry)
+ {
+ ATLASSERT(pEntry);
+ if (!pEntry)
+ return;
+ FileCachePeer::Remove(&pEntry->PeerData);
+ if (pEntry->Data)
+ {
+ DeleteFileA(pEntry->Data);
+ free(pEntry->Data);
+ const_cast<NodeType*>(pEntry)->Data = NULL;
+ }
+ }
+
+ // Looks up a file by name. Must be released after use
+ HRESULT STDMETHODCALLTYPE LookupFile(LPCSTR szFileName, HCACHEITEM * phKey)
+ {
+ return cacheBase::LookupEntry(szFileName, phKey);
+ }
+
+ // Gets the name of the file on disk
+ HRESULT STDMETHODCALLTYPE GetFile(__in const HCACHEITEM hKey, __deref_out_z_opt LPSTR * pszFileName, __deref_out_opt void **ppPeerInfo)
+ {
+ NodeType *pEntry = (NodeType *)hKey;
+ if (ppPeerInfo)
+ *ppPeerInfo = &pEntry->PeerData;
+ return cacheBase::GetEntryData(hKey, pszFileName, NULL);
+ }
+
+ // Releases a file
+ HRESULT STDMETHODCALLTYPE ReleaseFile(const HCACHEITEM hKey)
+ {
+ return cacheBase::ReleaseEntry(hKey);
+ }
+
+ // Releases a file and marks it for deletion
+ HRESULT STDMETHODCALLTYPE RemoveFile(const HCACHEITEM hKey)
+ {
+ return cacheBase::RemoveEntry(hKey);
+ }
+
+ // Removes a file by name -- this calls IMemoryCacheClient->Free
+ // on the file name, which by default (for CFileCache) deletes the
+ // file.
+ HRESULT STDMETHODCALLTYPE RemoveFileByName(LPCSTR szFileName)
+ {
+ return cacheBase::RemoveEntryByKey(szFileName);
+ }
+
+ // Flushes the entries in the cache, eliminates expired entries,
+ // or if the cache exceeds the parameters (alloc size, num entries),
+ // culls items based on the sweep mode
+ HRESULT STDMETHODCALLTYPE Flush()
+ {
+ return cacheBase::FlushEntries();
+ }
+
+ // IMemoryCacheControl methods
+ HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize)
+ {
+ return cacheBase::SetMaxAllowedSize(dwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize)
+ {
+ return cacheBase::GetMaxAllowedSize(pdwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize)
+ {
+ return cacheBase::SetMaxAllowedEntries(dwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize)
+ {
+ return cacheBase::GetMaxAllowedEntries(pdwSize);
+ }
+
+ HRESULT STDMETHODCALLTYPE ResetCache()
+ {
+ return cacheBase::ResetCache();
+ }
+
+
+ // IMemoryCacheStats methods
+ HRESULT STDMETHODCALLTYPE ClearStats()
+ {
+ m_statObj.ResetCounters();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetHitCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMissCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMaxAllocSize();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetCurrentAllocSize();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetMaxEntryCount();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize)
+ {
+ if (!pdwSize)
+ return E_POINTER;
+ *pdwSize = m_statObj.GetCurrentEntryCount();
+ return S_OK;
+ }
+}; // CFileCache
+
+class CDataConnection; // see atldbcli.h
+__interface __declspec(uuid("52E7759B-D6CC-4a03-BDF3-80A6BDCA1F94"))
+IDataSourceCache : public IUnknown
+{
+ // Method: Add
+ // Params:
+ // szConn: Connection string of data source to connect to
+ // ppDS: Out pointer to the newly added data source
+ // Comments:
+ // Attempts to open a connection to the specified data source
+ // using a CDataSource object. Once the connection is open, the
+ // CDatasource is cached.
+ STDMETHOD(Add)(LPCTSTR szID, LPCOLESTR szConn, CDataConnection *pDS);
+
+ // Method: Remove
+ // Params:
+ // szConn: Specifies the connection string of the connection to close
+ // Comments:
+ // Closes the specified connection and removes it's entry from the cache
+ STDMETHOD(Remove)(LPCTSTR szID);
+
+ // Method: Lookup
+ // Params:
+ // szConn: Specifies the connection string of the connection to look up
+ // ppDS: Out pointer to CDataSource object that is connected to the specified
+ // data source.
+ STDMETHOD(Lookup)(LPCTSTR szID, CDataConnection *pDS);
+
+ // Method: Uninitialize
+ // Params:
+ // None
+ // Comments:
+ // Closes removes all connections from the cache.
+ STDMETHOD(Uninitialize)();
+
+};
+#ifndef ATL_DS_CONN_STRING_LEN
+ #define ATL_DS_CONN_STRING_LEN 512
+#endif
+
+template <>
+class CElementTraits< CDataConnection > :
+ public CElementTraitsBase< CDataConnection >
+{
+public:
+ static ULONG Hash( INARGTYPE t )
+ {
+ return( ULONG( ULONG_PTR( &t ) ) );
+ }
+
+ static bool CompareElements( INARGTYPE element1, INARGTYPE element2 )
+ {
+ return( element1.m_session.m_spOpenRowset == element2.m_session.m_spOpenRowset);
+ }
+
+ static int CompareElementsOrdered( INARGTYPE /*element1*/, INARGTYPE /*element2*/ )
+ {
+ ATLASSERT(FALSE);
+ return -1;
+ }
+
+};
+
+typedef CFixedStringT<CString, ATL_DS_CONN_STRING_LEN> atlDataSourceKey;
+typedef CAtlMap<atlDataSourceKey, CDataConnection,
+ CStringElementTraits<atlDataSourceKey>, CElementTraits<CDataConnection> > atlDataSourceCacheMap;
+
+template <class TCritSec=CComFakeCriticalSection>
+class CDataSourceCache :
+ public IDataSourceCache,
+ public CComObjectRootEx<CComGlobalsThreadModel>
+{
+public:
+ BEGIN_COM_MAP(CDataSourceCache)
+ COM_INTERFACE_ENTRY(IDataSourceCache)
+ END_COM_MAP()
+
+ CDataSourceCache()
+ {
+ m_cs.Init();
+ }
+
+ virtual ~CDataSourceCache ()
+ {
+ Uninitialize();
+ }
+
+ STDMETHOD(Uninitialize)()
+ {
+ HRESULT hr = m_cs.Lock();
+ if (SUCCEEDED(hr))
+ {
+ m_ConnectionMap.RemoveAll();
+ m_cs.Unlock();
+ }
+
+ return hr;
+ }
+
+ STDMETHOD(Add)(LPCTSTR szID, LPCOLESTR szConn, CDataConnection *pSession)
+ {
+ HRESULT hr = E_FAIL;
+
+ if (!szID)
+ return E_INVALIDARG; // must have session name
+
+ // Do a lookup to make sure we don't add multiple entries
+ // with the same name. Adding multiple entries with the same name
+ // could cause some entries to get orphaned.
+ hr = m_cs.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ const atlDataSourceCacheMap::CPair *pPair =
+ m_ConnectionMap.Lookup(szID);
+ if (!pPair)
+ {
+ // try to open connection
+ CDataConnection DS;
+ hr = DS.Open(szConn);
+ if (hr == S_OK)
+ {
+ _ATLTRY
+ {
+ if (m_ConnectionMap.SetAt(szID, DS))
+ {
+ if (pSession) // we allow NULL here
+ *pSession = DS; // copy connection to output.
+ hr = S_OK;
+ }
+ else
+ hr = E_FAIL; // map add failed
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_FAIL;
+ }
+ }
+ }
+ else // lookup succeeded, entry is already in cache
+ {
+ // Instead of opening a new connection, just copy
+ // the one we already have in the cache.
+ if (pSession)
+ *pSession = pPair->m_value;
+ hr = S_OK;
+ }
+ m_cs.Unlock();
+ return hr;
+ }
+
+ STDMETHOD(Remove)(LPCTSTR szID)
+ {
+ HRESULT hr = E_INVALIDARG;
+ if (!szID)
+ return hr; // must have session name
+
+ hr = m_cs.Lock();
+ if (SUCCEEDED(hr))
+ {
+ hr = m_ConnectionMap.RemoveKey(szID) ? S_OK : E_FAIL;
+ m_cs.Unlock();
+ }
+
+ return hr;
+ }
+
+ STDMETHOD(Lookup)(LPCTSTR szID, CDataConnection *pSession)
+ {
+ if (!szID||!pSession)
+ return E_POINTER;
+
+ HRESULT hr = m_cs.Lock();
+ bool bRet = true;
+ if (SUCCEEDED(hr))
+ {
+ bRet = m_ConnectionMap.Lookup(szID, *pSession);
+ m_cs.Unlock();
+ }
+
+ return (bRet && (bool)*pSession)? hr : E_FAIL;
+ }
+
+protected:
+ atlDataSourceCacheMap m_ConnectionMap;
+ TCritSec m_cs;
+};
+
+
+// Some helpers for using the datasource cache.
+//
+// Function: GetDataSource
+// Params:
+// pProvider: Pointer to IServiceProvider that provides the
+// data source cache service
+// szID: The name of the connection (can be same as szDS)
+// szDS: OLEDB connection string for data source
+// ppDS: Out pointer to CDataSource. The CDataSource will be connected
+// to the OLEDB provider specified by szDS on successful return.
+// RetVal:
+// Returns S_OK on success.
+static HRESULT ATL_NOINLINE GetDataSource(IServiceProvider *pProvider,
+ LPCTSTR szID, LPCOLESTR szConn,
+ CDataConnection *pSession)
+{
+ if (!pProvider || !szID || !szConn)
+ return E_POINTER;
+
+ CComPtr<IDataSourceCache> spDSCache;
+ HRESULT hr;
+ hr = pProvider->QueryService(__uuidof(IDataSourceCache), __uuidof(IDataSourceCache), (void**)&spDSCache);
+ if (hr == S_OK && spDSCache)
+ {
+ hr = spDSCache->Add(szID, szConn, pSession);
+ }
+ return hr;
+}
+
+//
+// Function: RemoveDataSource
+// Params:
+// pProvider: Pointer to IServiceProvider that provides the
+// data source cache service
+// szID: Name of the datasource connection to remove from the cache
+// RetVal:
+// none
+// Comments:
+// Removes the datasource entry from the datasource cache. Since entries are
+// copied to the client on calls to lookup and add, removing an entry will not
+// release the connections of existing clients.
+static HRESULT ATL_NOINLINE RemoveDataSource(IServiceProvider *pProvider, LPCTSTR szID)
+{
+ if (!pProvider || !szID)
+ return E_POINTER;
+
+ CComPtr<IDataSourceCache> spDSCache;
+ HRESULT hr = pProvider->QueryService(__uuidof(IDataSourceCache), __uuidof(IDataSourceCache), (void**)&spDSCache);
+ if (spDSCache)
+ hr = spDSCache->Remove(szID);
+ return hr;
+}
+
+} // namespace ATL
+#pragma pack(pop)
+
+#pragma warning (pop)
+
+#endif // __ATLCACHE_H__
diff --git a/include/atl/atlcrypt.h b/include/atl/atlcrypt.h
new file mode 100644
index 000000000..8e8ac7d4e
--- /dev/null
+++ b/include/atl/atlcrypt.h
@@ -0,0 +1,376 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLCRYPT_H__
+#define __ATLCRYPT_H__
+
+#pragma once
+
+#include <atlchecked.h>
+#include <wincrypt.h>
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL
+{
+
+class CCryptKey;
+
+class CCryptProv
+{
+protected:
+ HCRYPTPROV m_hProv;
+
+public:
+ CCryptProv() throw();
+ CCryptProv( const CCryptProv& prov ) throw();
+ explicit CCryptProv( HCRYPTPROV hProv, BOOL bTakeOwnership = FALSE ) throw();
+ ~CCryptProv() throw();
+
+ CCryptProv& operator=( const CCryptProv& prov ) throw();
+
+ HRESULT AddRef() throw();
+ void Attach( HCRYPTPROV hProv, BOOL bTakeOwnership = FALSE ) throw();
+ HCRYPTPROV Detach() throw();
+ HRESULT Release() throw();
+
+
+ HRESULT Initialize(DWORD dwProviderType = PROV_RSA_FULL,
+ LPCTSTR szContainer = NULL, LPCTSTR szProvider = MS_DEF_PROV,
+ DWORD dwFlags = 0) throw();
+ HRESULT InitVerifyContext(DWORD dwProviderType = PROV_RSA_FULL,
+ LPCTSTR szProvider = MS_DEF_PROV, DWORD dwFlags = 0) throw();
+ HRESULT InitCreateKeySet(DWORD dwProviderType = PROV_RSA_FULL,
+ LPCTSTR szContainer = NULL, LPCTSTR szProvider = MS_DEF_PROV,
+ DWORD dwFlags = 0) throw();
+
+ HRESULT DeleteKeySet(DWORD dwProviderType = PROV_RSA_FULL,
+ LPCTSTR szContainer = NULL, LPCTSTR szProvider = MS_DEF_PROV,
+ DWORD dwFlags = 0) throw();
+
+ HRESULT Uninitialize();
+
+ HRESULT GetParam(DWORD dwParam, BYTE * pbData, DWORD * pdwDataLen, DWORD dwFlags = 0) throw();
+ HRESULT SetParam( DWORD dwParam, BYTE* pbData, DWORD dwFlags = 0) throw();
+ HRESULT GetName(__out_ecount_part_z(*pdwLength, *pdwLength) LPSTR szBuf, __inout DWORD * pdwLength) throw();
+ HRESULT GetContainer(__out_ecount_part_z(*pdwLength, *pdwLength) LPSTR szBuf, __inout DWORD * pdwLength) throw();
+ HRESULT GetImpType(DWORD * pdwImpType) throw();
+ HRESULT GetVersion(DWORD * pdwVersion) throw();
+ HRESULT GetProvType(DWORD * pdwType) throw();
+ HRESULT GetSecurityDesc(SECURITY_INFORMATION * pSecInfo) throw();
+ HRESULT SetSecurityDesc(SECURITY_INFORMATION SecInfo) throw();
+
+ HRESULT GenRandom(ULONG nLength, BYTE* pbBuffer ) throw();
+
+ inline HCRYPTPROV GetHandle() throw()
+ {
+ return m_hProv;
+ }
+}; // class CCryptProv
+
+
+// class CCryptHash
+// Provides base functionality of hashes.
+class CCryptHash
+{
+protected:
+ HCRYPTHASH m_hHash;
+
+public:
+ CCryptHash() throw();
+ CCryptHash( const CCryptHash& hash ) throw();
+ explicit CCryptHash( HCRYPTHASH hHash, BOOL bTakeOwnership = FALSE ) throw();
+ ~CCryptHash() throw();
+
+ void Attach( HCRYPTHASH hHash, BOOL bTakeOwnership = FALSE ) throw();
+ void Destroy() throw();
+ HCRYPTHASH Detach() throw();
+ HCRYPTHASH Duplicate() const throw();
+
+ HRESULT Uninitialize() throw();
+ HRESULT Detach(HCRYPTHASH * phHash) throw();
+ HRESULT AddData(const BYTE * pbData, DWORD dwDataLen, DWORD dwFlags = 0) throw();
+ HRESULT AddString(LPCTSTR szData, DWORD dwFlags = 0) throw();
+ HRESULT GetParam(DWORD dwParam, BYTE * pbData, DWORD * pdwDataLen, DWORD dwFlags = 0) throw();
+ HRESULT SetParam(DWORD dwParam, BYTE * pbData, DWORD dwFlags = 0) throw();
+ HRESULT GetAlgId(ALG_ID * pAlgId) throw();
+ HRESULT GetSize(DWORD * pdwSize) throw();
+ HRESULT GetValue(BYTE * pBuf, DWORD * pdwSize) throw();
+ HRESULT SetValue(BYTE * pBuf) throw();
+ HRESULT Sign(
+ BYTE * pbSignature,
+ DWORD * pdwSigLen,
+ DWORD dwFlags = 0,
+ DWORD dwKeySpec = AT_SIGNATURE) throw();
+ HRESULT VerifySignature(
+ const BYTE * pbSignature,
+ DWORD pdwSignLen,
+ CCryptKey &PubKey,
+ DWORD dwFlags = 0) throw();
+
+ inline HCRYPTHASH GetHandle()
+ {
+ return m_hHash;
+ }
+ static CCryptHash EmptyHash;
+
+}; // class CCryptHash
+
+// class CCryptKey
+// Provides the functionality for cryptographic keys, i.e. encrypting, decrypting.
+class CCryptKey
+{
+protected:
+ HCRYPTKEY m_hKey;
+
+public:
+ CCryptKey() throw();
+ CCryptKey( const CCryptKey& key ) throw();
+ explicit CCryptKey( HCRYPTKEY hKey, BOOL bTakeOwnership = FALSE ) throw();
+ ~CCryptKey() throw();
+
+ void Attach( HCRYPTKEY hKey, BOOL bTakeOwnership = FALSE ) throw();
+ void Destroy() throw();
+ HCRYPTKEY Detach() throw();
+ HCRYPTKEY Duplicate() const throw();
+
+ HRESULT Uninitialize() throw();
+ HRESULT Encrypt(
+ BOOL final,
+ BYTE * pbData,
+ DWORD * pdwDataLen,
+ DWORD dwBufLen,
+ CCryptHash &Hash = CCryptHash::EmptyHash) throw();
+
+ HRESULT Encrypt(
+ const BYTE * pbPlainText,
+ DWORD dwPlainTextLen,
+ BYTE * pbCipherText,
+ DWORD * pdwCipherTextLen,
+ CCryptHash &Hash = CCryptHash::EmptyHash) throw();
+
+ HRESULT Decrypt(
+ BOOL final,
+ BYTE * pbData,
+ DWORD * pdwDataLen,
+ CCryptHash &Hash = CCryptHash::EmptyHash) throw();
+ HRESULT Decrypt(
+ const BYTE * pbCipherText,
+ DWORD dwCipherTextLen,
+ BYTE * pbPlainText,
+ DWORD * pdwPlainTextLen,
+ CCryptHash &Hash = CCryptHash::EmptyHash) throw();
+ HRESULT EncryptString(
+ LPCTSTR szPlainText,
+ BYTE * pbCipherText,
+ DWORD * pdwCipherTextLen,
+ CCryptHash &Hash = CCryptHash::EmptyHash) throw();
+ HRESULT ExportSimpleBlob(
+ CCryptKey &ExpKey,
+ DWORD dwFlags,
+ BYTE * pbData,
+ DWORD * pdwDataLen) throw();
+ HRESULT ExportPublicKeyBlob(
+ CCryptKey &ExpKey,
+ DWORD dwFlags,
+ BYTE * pbData,
+ DWORD * pdwDataLen) throw();
+ HRESULT ExportPrivateKeyBlob(
+ CCryptKey &ExpKey,
+ DWORD dwFlags,
+ BYTE * pbData,
+ DWORD * pdwDataLen) throw();
+ HRESULT GetParam(DWORD dwParam, BYTE * pbData, DWORD * pdwDataLen, DWORD dwFlags = 0) throw();
+ HRESULT SetParam(DWORD dwParam, BYTE * pbData, DWORD dwFlags = 0) throw();
+ HRESULT GetAlgId(ALG_ID * pAlgId) throw();
+ HRESULT SetAlgId(ALG_ID AlgId, DWORD dwFlags) throw();
+ HRESULT GetBlockLength(DWORD * pdwBlockLen) throw();
+ HRESULT GetKeyLength(DWORD * pdwKeyLen) throw();
+ HRESULT GetSalt(BYTE * pbSalt, DWORD * pdwLength) throw();
+ HRESULT SetSalt(BYTE * pbSalt) throw();
+ HRESULT SetSaltEx(_CRYPTOAPI_BLOB * pBlobSalt) throw();
+ HRESULT GetPermissions(DWORD * pdwPerms) throw();
+ HRESULT SetPermissions(DWORD dwPerms) throw();
+ HRESULT GetP(BYTE * pbP, DWORD * pdwLength) throw();
+ HRESULT SetP(_CRYPTOAPI_BLOB * pBlobP) throw();
+ HRESULT SetP(BYTE * pbP, DWORD dwLength) throw();
+ HRESULT GetQ(BYTE * pbQ, DWORD * pdwLength) throw();
+ HRESULT SetQ(_CRYPTOAPI_BLOB * pBlobQ) throw();
+ HRESULT SetQ(BYTE * pbQ, DWORD dwLength) throw();
+ HRESULT GetG(BYTE * pbG, DWORD * pdwLength) throw();
+ HRESULT SetG(_CRYPTOAPI_BLOB * pBlobG) throw();
+ HRESULT SetG(BYTE * pbG, DWORD dwLength) throw();
+ HRESULT SetX() throw();
+ HRESULT GetEffKeyLen(DWORD * pdwEffKeyLen) throw();
+ HRESULT SetEffKeyLen(DWORD dwEffKeyLen) throw();
+ HRESULT GetPadding(DWORD * pdwPadding) throw();
+ HRESULT SetPadding(DWORD dwPadding) throw();
+ HRESULT GetIV(BYTE * pbIV, DWORD * pdwLength) throw();
+ HRESULT SetIV(BYTE * pbIV) throw();
+ HRESULT GetMode(DWORD * pdwMode) throw();
+ HRESULT SetMode(DWORD dwMode) throw();
+ HRESULT GetModeBits(DWORD * pdwModeBits) throw();
+ HRESULT SetModeBits(DWORD dwModeBits) throw();
+
+ inline HCRYPTKEY GetHandle() throw()
+ {
+ return m_hKey;
+ }
+
+ static CCryptKey EmptyKey;
+}; // class CCryptKey
+
+
+
+// Specific instances of Keys and Hashes
+
+// class CCryptDerivedKey
+// A key that is derived from a hashed password. Two keys derived
+// from the same password will be identical.
+class CCryptDerivedKey : public CCryptKey
+{
+public:
+ HRESULT Initialize(
+ CCryptProv &Prov,
+ CCryptHash &Hash,
+ ALG_ID algid = CALG_RC4,
+ DWORD dwFlags = CRYPT_EXPORTABLE) throw();
+}; // class CCryptDerivedKey
+
+// class CCryptRandomKey
+// A randomly generated key. Can be used internally by a program
+// to protect data during execution, or can be exported with Crypt.Export
+//
+// Currently it is possible to pass in AT_KEYEXCHANGE or AT_SIGNATURE
+// for algid, but these two will generate keys for the current key set, and
+// the resulting handle can only be used for exporting and importing keys or
+// signing hashes.
+class CCryptRandomKey : public CCryptKey
+{
+public:
+ HRESULT Initialize(
+ CCryptProv &Prov,
+ ALG_ID algid = CALG_RC4,
+ DWORD dwFlags = CRYPT_EXPORTABLE) throw();
+}; // class CCryptRandomKey
+
+// class CCryptUserExKey
+// Obtains the user's key exchange key pair.
+class CCryptUserExKey : public CCryptKey
+{
+public:
+ HRESULT Initialize(CCryptProv &Prov) throw();
+ HRESULT Create(CCryptProv &Prov) throw();
+}; // class CCryptUserExKey
+
+// class CCryptUserSigKey
+// Obtains the user's signature key pair
+class CCryptUserSigKey : public CCryptKey
+{
+public:
+ HRESULT Initialize(CCryptProv &Prov) throw();
+ HRESULT Create(CCryptProv &Prov) throw();
+}; // class CCryptUserSigKey
+
+// class CCryptImportKey
+// Forms a key from an imported key blob
+class CCryptImportKey : public CCryptKey
+{
+public:
+ HRESULT Initialize(
+ CCryptProv &Prov,
+ BYTE * pbData,
+ DWORD dwDataLen,
+ CCryptKey &PubKey,
+ DWORD dwFlags) throw();
+}; // class CCryptImportKey
+
+
+// class CCryptHash
+// A generic hash that may or may not take a key.
+class CCryptKeyedHash : public CCryptHash
+{
+public:
+
+ HRESULT Initialize(CCryptProv &Prov, ALG_ID Algid, CCryptKey &Key, DWORD dwFlags) throw();
+}; // class CCryptKeyedHash
+
+// class CCryptMD5Hash
+// RSA's MD5 hash (RSA's most recent hash as of 9/7/99);
+class CCryptMD5Hash : public CCryptHash
+{
+public:
+
+ HRESULT Initialize(CCryptProv &Prov, LPCTSTR szText = NULL) throw();
+}; // class CCryptMD5Hash
+
+// class CCryptMD4Hash
+// RSA's MD4 hash
+class CCryptMD4Hash : public CCryptHash
+{
+public:
+
+ HRESULT Initialize(CCryptProv &Prov, LPCTSTR szText = NULL) throw();
+}; // class CCryptMD4Hash
+
+
+// class CCryptMD2Hash
+// RSA's MD2 hash
+class CCryptMD2Hash : public CCryptHash
+{
+public:
+
+ HRESULT Initialize(CCryptProv &Prov, LPCTSTR szText = NULL) throw();
+}; // class CCryptMD2Hash
+
+
+// class CCryptSHAHash
+// The Secure Hash Algorithm hash, from NIST and NSA. Technically, SHA-1.
+class CCryptSHAHash : public CCryptHash
+{
+public:
+
+ HRESULT Initialize(CCryptProv &Prov, LPCTSTR szText = NULL) throw();
+}; // class CCryptSHAHash
+
+// The Secure Hash Algorithm, from NIST and NSA. Identical to CCryptSHA
+typedef CCryptSHAHash CCryptSHA1Hash;
+
+
+// class CCryptHMACHash
+// Hash-base Message Authentication Code keyed hash
+class CCryptHMACHash : public CCryptHash
+{
+public:
+ HRESULT Initialize(CCryptProv &Prov, CCryptKey &Key, LPCTSTR szText = NULL) throw();
+}; // class CCryptHMACHash
+
+// class CCryptMACHash
+// Message Authentication Code keyed hash. Believed to be less secure than HMAC
+class CCryptMACHash : public CCryptHash
+{
+public:
+ HRESULT Initialize(CCryptProv &Prov, CCryptKey &Key, LPCTSTR szText = NULL) throw();
+}; // class CCryptMACHash
+
+// class CCryptSSL3SHAMD5Hash
+// Hash algorithm used by Secure Socket Layer
+class CCryptSSL3SHAMD5Hash : public CCryptHash
+{
+public:
+ HRESULT Initialize(CCryptProv &Prov, CCryptKey &Key, LPCTSTR szText = NULL) throw();
+}; // class CCryptSSl3SHAMD5Hash
+
+}; // namespace ATL
+
+
+#include <atlcrypt.inl>
+#pragma pack(pop)
+#endif // __ATLCRYPT_H__
diff --git a/include/atl/atlcrypt.inl b/include/atl/atlcrypt.inl
new file mode 100644
index 000000000..bfd119f87
--- /dev/null
+++ b/include/atl/atlcrypt.inl
@@ -0,0 +1,1062 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+
+#ifndef __ATLCRYPT_INL__
+#define __ATLCRYPT_INL__
+
+#pragma once
+
+#ifndef __ATLCRYPT_H__
+ #error atlcrypt.inl requires atlcrypt.h to be included first
+#endif
+
+
+namespace ATL
+{
+
+inline CCryptProv::CCryptProv( const CCryptProv& prov ) throw()
+{
+ m_hProv = prov.m_hProv;
+ if (m_hProv)
+ AddRef();
+}
+
+inline CCryptProv::CCryptProv( HCRYPTPROV hProv, BOOL bTakeOwnership ) throw()
+{
+ m_hProv = hProv;
+ if (m_hProv && !bTakeOwnership)
+ AddRef();
+}
+
+inline CCryptProv::~CCryptProv() throw()
+{
+ Release();
+}
+
+inline CCryptProv& CCryptProv::operator=( const CCryptProv& prov ) throw()
+{
+ if(this!=&prov)
+ {
+ Release();
+
+ m_hProv = prov.m_hProv;
+ if( m_hProv != NULL )
+ {
+ AddRef();
+ }
+ }
+ return( *this );
+}
+
+inline HRESULT CCryptProv::AddRef() throw()
+{
+ ATLASSUME( m_hProv != NULL );
+
+ if (!CryptContextAddRef( m_hProv, NULL, 0))
+ {
+ return AtlHresultFromLastError();
+ }
+ return S_OK;
+}
+
+inline void CCryptProv::Attach( HCRYPTPROV hProv, BOOL bTakeOwnership ) throw()
+{
+ ATLASSUME( m_hProv == NULL );
+
+ m_hProv = hProv;
+ if (m_hProv && !bTakeOwnership)
+ AddRef();
+}
+
+inline HCRYPTPROV CCryptProv::Detach() throw()
+{
+ HCRYPTPROV hProv;
+
+ hProv = m_hProv;
+ m_hProv = NULL;
+
+ return( hProv );
+}
+
+
+inline CCryptProv::CCryptProv() throw() :
+ m_hProv( NULL )
+{
+}
+
+inline HRESULT CCryptProv::Release() throw()
+{
+ if( m_hProv != NULL )
+ {
+ if (!CryptReleaseContext( m_hProv, 0 ))
+ {
+ return AtlHresultFromLastError();
+ }
+ m_hProv = NULL;
+ }
+ return S_OK;
+}
+
+inline HRESULT CCryptProv::Initialize(
+ DWORD dwProviderType,
+ LPCTSTR szContainer,
+ LPCTSTR szProvider,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hProv == NULL);
+
+ if (!CryptAcquireContext(&m_hProv, szContainer, szProvider, dwProviderType, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptProv::InitVerifyContext(
+ DWORD dwProviderType,
+ LPCTSTR szProvider,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hProv == NULL);
+
+ if (!CryptAcquireContext(&m_hProv, NULL, szProvider, dwProviderType, CRYPT_VERIFYCONTEXT | dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptProv::InitCreateKeySet(
+ DWORD dwProviderType,
+ LPCTSTR szContainer,
+ LPCTSTR szProvider,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hProv == NULL);
+
+ if (!CryptAcquireContext(&m_hProv, szContainer, szProvider, dwProviderType, CRYPT_NEWKEYSET | dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptProv::DeleteKeySet(
+ DWORD dwProviderType,
+ LPCTSTR szContainer,
+ LPCTSTR szProvider,
+ DWORD dwFlags) throw()
+{
+ HCRYPTPROV hProv = NULL;
+ if (!CryptAcquireContext(&hProv, szContainer, szProvider, dwProviderType, CRYPT_DELETEKEYSET | dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+
+inline HRESULT CCryptProv::Uninitialize() throw()
+{
+ ATLASSUME(m_hProv != NULL);
+
+ if (!CryptReleaseContext(m_hProv, 0))
+ {
+ return AtlHresultFromLastError();
+ }
+ else
+ {
+ m_hProv = NULL;
+ return S_OK;
+ }
+}
+
+inline HRESULT CCryptProv::GetParam(DWORD dwParam, BYTE * pbData, DWORD * pdwDataLen, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hProv != NULL);
+
+ if (!CryptGetProvParam(m_hProv, dwParam, pbData, pdwDataLen, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptProv::SetParam( DWORD dwParam, BYTE* pbData, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hProv != NULL);
+
+ if (!CryptSetProvParam(m_hProv, dwParam, pbData, dwFlags ))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptProv::GetName(__out_bcount_part(*pdwLength, *pdwLength) LPSTR szBuf, __inout DWORD * pdwLength) throw()
+{
+ return GetParam(PP_NAME, (BYTE *)szBuf, pdwLength);
+}
+
+inline HRESULT CCryptProv::GetContainer(__out_bcount_part(*pdwLength, *pdwLength) LPSTR szBuf, __inout DWORD * pdwLength) throw()
+{
+ return GetParam(PP_CONTAINER, (BYTE *)szBuf, pdwLength);
+}
+
+inline HRESULT CCryptProv::GetImpType(DWORD * pdwImpType) throw()
+{
+ DWORD dwLength = sizeof(DWORD);
+ return GetParam(PP_IMPTYPE, (BYTE *)pdwImpType, &dwLength);
+}
+
+inline HRESULT CCryptProv::GetVersion(DWORD * pdwVersion) throw()
+{
+ DWORD dwLength = sizeof(DWORD);
+ return GetParam(PP_VERSION, (BYTE *)pdwVersion, &dwLength);
+}
+
+inline HRESULT CCryptProv::GetProvType(DWORD * pdwType) throw()
+{
+ DWORD dwLength = sizeof(DWORD);
+ return GetParam(PP_PROVTYPE, (BYTE * )pdwType, &dwLength);
+}
+
+inline HRESULT CCryptProv::GetSecurityDesc(SECURITY_INFORMATION * pSecInfo) throw()
+{
+ DWORD dwSize = sizeof(SECURITY_INFORMATION);
+ return GetParam(PP_KEYSET_SEC_DESCR, (BYTE *)pSecInfo, &dwSize);
+}
+
+inline HRESULT CCryptProv::SetSecurityDesc(SECURITY_INFORMATION SecInfo) throw()
+{
+ return SetParam(PP_KEYSET_SEC_DESCR, (BYTE *)&SecInfo);
+}
+
+inline HRESULT CCryptProv::GenRandom(ULONG nLength, BYTE* pbBuffer ) throw()
+{
+ ATLASSUME(m_hProv != NULL);
+
+ if (!CryptGenRandom( m_hProv, nLength, pbBuffer ))
+ {
+ return AtlHresultFromLastError();
+ }
+
+ return S_OK;
+}
+
+inline CCryptHash::CCryptHash() throw() :
+ m_hHash( NULL )
+{
+}
+
+inline CCryptHash::CCryptHash( const CCryptHash& hash ) throw()
+{
+ m_hHash = hash.Duplicate();
+}
+
+inline CCryptHash::CCryptHash( HCRYPTHASH hHash, BOOL bTakeOwnership ) throw()
+{
+ if (bTakeOwnership)
+ m_hHash = hHash;
+ else
+ {
+ m_hHash = NULL;
+ BOOL bRet = ::CryptDuplicateHash( hHash, NULL, 0, &m_hHash );
+ if (!bRet)
+ m_hHash = NULL;
+ }
+}
+
+inline CCryptHash::~CCryptHash() throw()
+{
+ Destroy();
+}
+
+inline void CCryptHash::Attach( HCRYPTHASH hHash, BOOL bTakeOwnership ) throw()
+{
+ ATLASSUME( m_hHash == NULL );
+
+ if (bTakeOwnership)
+ m_hHash = hHash;
+ else
+ {
+ m_hHash = NULL;
+ BOOL bRet = ::CryptDuplicateHash( hHash, NULL, 0, &m_hHash );
+ if (!bRet)
+ m_hHash = NULL;
+ }
+}
+
+inline void CCryptHash::Destroy() throw()
+{
+ if( m_hHash != NULL )
+ {
+ BOOL bSuccess;
+
+ bSuccess = ::CryptDestroyHash( m_hHash );
+
+ // can fail if the cryptographic service provider
+ // (managed by CCryptProv) has already been destroyed
+ ATLASSERT( bSuccess );
+ m_hHash = NULL;
+ }
+}
+
+inline HCRYPTHASH CCryptHash::Detach() throw()
+{
+ HCRYPTHASH hHash;
+
+ hHash = m_hHash;
+ m_hHash = NULL;
+
+ return hHash;
+}
+
+inline HCRYPTHASH CCryptHash::Duplicate() const throw()
+{
+ BOOL bSuccess;
+ HCRYPTHASH hHash;
+
+ ATLASSUME( m_hHash != NULL );
+
+ hHash = NULL;
+ bSuccess = ::CryptDuplicateHash( m_hHash, NULL, 0, &hHash );
+ if( !bSuccess )
+ {
+ return NULL;
+ }
+
+ return hHash;
+}
+
+inline HRESULT CCryptHash::Uninitialize() throw()
+{
+ ATLASSUME(m_hHash != NULL);
+
+ if (!CryptDestroyHash(m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ else
+ {
+ m_hHash = NULL;
+ return S_OK;
+ }
+}
+
+inline HRESULT CCryptHash::Detach(HCRYPTHASH * phHash) throw()
+{
+ ATLASSERT(phHash);
+ if (!phHash)
+ return E_INVALIDARG;
+
+ *phHash = m_hHash;
+ m_hHash = NULL;
+
+ return S_OK;
+}
+
+inline HRESULT CCryptHash::AddData(const BYTE * pbData, DWORD dwDataLen, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hHash != NULL);
+
+ if (!CryptHashData(m_hHash, pbData, dwDataLen, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+
+}
+
+inline HRESULT CCryptHash::AddString(LPCTSTR szData, DWORD dwFlags) throw()
+{
+ return AddData((BYTE *)szData, (DWORD)_tcslen(szData) * sizeof(TCHAR), dwFlags);
+}
+
+inline HRESULT CCryptHash::GetParam(DWORD dwParam, BYTE * pbData, DWORD * pdwDataLen, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hHash != NULL);
+
+ if (!CryptGetHashParam(m_hHash, dwParam, pbData, pdwDataLen, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptHash::SetParam(DWORD dwParam, BYTE * pbData, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hHash != NULL);
+
+ if (!CryptSetHashParam(m_hHash, dwParam, pbData, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptHash::GetAlgId(ALG_ID * pAlgId) throw()
+{
+ DWORD dwSize = sizeof(ALG_ID);
+ return GetParam(HP_ALGID, (BYTE *)pAlgId, &dwSize);
+}
+
+inline HRESULT CCryptHash::GetSize(DWORD * pdwSize) throw()
+{
+ DWORD dwLength = sizeof(DWORD);
+ return GetParam(HP_HASHSIZE, (BYTE *)pdwSize, &dwLength);
+}
+
+inline HRESULT CCryptHash::GetValue(BYTE * pBuf, DWORD * pdwSize) throw()
+{
+ return GetParam(HP_HASHVAL, pBuf, pdwSize);
+}
+
+inline HRESULT CCryptHash::SetValue(BYTE * pBuf) throw()
+{
+ return SetParam(HP_HASHVAL, pBuf);
+}
+
+inline HRESULT CCryptHash::Sign(
+ BYTE * pbSignature,
+ DWORD * pdwSigLen,
+ DWORD dwFlags,
+ DWORD dwKeySpec) throw()
+{
+ ATLASSUME(m_hHash != NULL);
+
+ if (!CryptSignHash(m_hHash, dwKeySpec, NULL, dwFlags, pbSignature, pdwSigLen))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptHash::VerifySignature(
+ const BYTE * pbSignature,
+ DWORD dwSigLen,
+ CCryptKey &PubKey,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hHash != NULL);
+
+ if (!CryptVerifySignature(m_hHash, pbSignature, dwSigLen, PubKey.GetHandle(), NULL, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+__declspec(selectany) CCryptHash CCryptHash::EmptyHash = CCryptHash();
+__declspec(selectany) CCryptKey CCryptKey::EmptyKey = CCryptKey();
+inline CCryptKey::CCryptKey() throw() :
+ m_hKey( NULL )
+{
+}
+
+inline CCryptKey::CCryptKey( const CCryptKey& key ) throw()
+{
+ m_hKey = key.Duplicate();
+}
+
+inline CCryptKey::CCryptKey( HCRYPTKEY hKey, BOOL bTakeOwnership ) throw()
+{
+ if (bTakeOwnership)
+ m_hKey = hKey;
+ else
+ {
+ BOOL bSuccess = ::CryptDuplicateKey( hKey, NULL, 0, &m_hKey );
+ if( !bSuccess )
+ m_hKey = NULL;
+ }
+}
+
+inline CCryptKey::~CCryptKey() throw()
+{
+ Destroy();
+}
+
+inline void CCryptKey::Attach( HCRYPTKEY hKey, BOOL bTakeOwnership ) throw()
+{
+ ATLASSUME( m_hKey == NULL );
+ if (bTakeOwnership)
+ m_hKey = hKey;
+ else
+ {
+ BOOL bSuccess = ::CryptDuplicateKey( hKey, NULL, 0, &m_hKey );
+ if( !bSuccess )
+ m_hKey = NULL;
+ }
+}
+
+inline void CCryptKey::Destroy() throw()
+{
+ if( m_hKey != NULL )
+ {
+ BOOL bSuccess;
+
+ bSuccess = ::CryptDestroyKey( m_hKey );
+
+ // can fail if the cryptographic service provider
+ // (managed by CCryptProv) has already been destroyed
+ ATLASSERT( bSuccess );
+ m_hKey = NULL;
+ }
+}
+
+inline HCRYPTKEY CCryptKey::Detach() throw()
+{
+ HCRYPTKEY hKey;
+
+ hKey = m_hKey;
+ m_hKey = NULL;
+
+ return( hKey );
+}
+
+inline HCRYPTKEY CCryptKey::Duplicate() const throw()
+{
+ BOOL bSuccess;
+
+ ATLASSUME( m_hKey != NULL );
+
+ HCRYPTKEY hKey = NULL;
+ bSuccess = ::CryptDuplicateKey( m_hKey, NULL, 0, &hKey );
+ if( !bSuccess )
+ return NULL;
+
+ return hKey;
+}
+
+inline HRESULT CCryptKey::Uninitialize() throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!CryptDestroyKey(m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else
+ {
+ m_hKey = NULL;
+ return S_OK;
+ }
+}
+
+inline HRESULT CCryptKey::Encrypt(
+ BOOL final,
+ BYTE * pbData,
+ DWORD * pdwDataLen,
+ DWORD dwBufLen,
+ CCryptHash &Hash) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!::CryptEncrypt(m_hKey, Hash.GetHandle(), final, 0, pbData, pdwDataLen, dwBufLen))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+
+}
+
+inline HRESULT CCryptKey::Decrypt(BOOL final, BYTE * pbData, DWORD * pdwDataLen, CCryptHash &Hash) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!::CryptDecrypt(m_hKey, Hash.GetHandle(), final, 0, pbData, pdwDataLen))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+
+inline HRESULT CCryptKey::Encrypt(
+ const BYTE * pbPlainText,
+ DWORD dwPlainTextLen,
+ BYTE * pbCipherText,
+ DWORD * pdwCipherTextLen,
+ CCryptHash &Hash) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (*pdwCipherTextLen < dwPlainTextLen)
+ return ERROR_MORE_DATA;
+
+ Checked::memcpy_s(pbCipherText, dwPlainTextLen, pbPlainText, dwPlainTextLen);
+ DWORD dwSize = dwPlainTextLen;
+ if (!::CryptEncrypt(m_hKey, Hash.GetHandle(), TRUE, 0, pbCipherText, &dwSize, *pdwCipherTextLen))
+ {
+ return AtlHresultFromLastError();
+ }
+
+ *pdwCipherTextLen = dwSize;
+ return S_OK;
+
+}
+
+inline HRESULT CCryptKey::Decrypt(
+ const BYTE * pbCipherText,
+ DWORD dwCipherTextLen,
+ BYTE * pbPlainText,
+ DWORD * pdwPlainTextLen,
+ CCryptHash &Hash) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (*pdwPlainTextLen < dwCipherTextLen)
+ return ERROR_MORE_DATA;
+
+ Checked::memcpy_s(pbPlainText, dwCipherTextLen, pbCipherText, dwCipherTextLen);
+ DWORD dwSize = dwCipherTextLen;
+ if (!::CryptDecrypt(m_hKey, Hash.GetHandle(), TRUE, 0, pbPlainText, &dwSize))
+ {
+ return AtlHresultFromLastError();
+ }
+
+ *pdwPlainTextLen = dwSize;
+ return S_OK;
+}
+
+inline HRESULT CCryptKey::EncryptString(
+ LPCTSTR szPlainText,
+ BYTE * pbCipherText,
+ DWORD * pdwCipherTextLen,
+ CCryptHash &Hash) throw()
+{
+ DWORD dwSize = ((DWORD)_tcslen(szPlainText) + 1) * sizeof(TCHAR);
+ return Encrypt((BYTE *)szPlainText, dwSize, pbCipherText, pdwCipherTextLen, Hash);
+}
+
+inline HRESULT CCryptKey::ExportSimpleBlob(
+ CCryptKey &ExpKey,
+ DWORD dwFlags,
+ BYTE * pbData,
+ DWORD * pdwDataLen) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!CryptExportKey(m_hKey, ExpKey.GetHandle(), SIMPLEBLOB, dwFlags, pbData, pdwDataLen))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptKey::ExportPublicKeyBlob(
+ CCryptKey &ExpKey,
+ DWORD dwFlags,
+ BYTE * pbData,
+ DWORD * pdwDataLen) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!CryptExportKey(m_hKey, ExpKey.GetHandle(), PUBLICKEYBLOB, dwFlags, pbData, pdwDataLen))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptKey::ExportPrivateKeyBlob(
+ CCryptKey &ExpKey,
+ DWORD dwFlags,
+ BYTE * pbData,
+ DWORD * pdwDataLen) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!CryptExportKey(m_hKey, ExpKey.GetHandle(), PRIVATEKEYBLOB, dwFlags, pbData, pdwDataLen))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptKey::GetParam(DWORD dwParam, BYTE * pbData, DWORD * pdwDataLen, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!CryptGetKeyParam(m_hKey, dwParam, pbData, pdwDataLen, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptKey::SetParam(DWORD dwParam, BYTE * pbData, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hKey != NULL);
+
+ if (!CryptSetKeyParam(m_hKey, dwParam, pbData, dwFlags))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptKey::GetAlgId(ALG_ID * pAlgId) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_ALGID, (BYTE *)pAlgId, &dwSize);
+}
+
+inline HRESULT CCryptKey::SetAlgId(ALG_ID AlgId, DWORD dwFlags) throw()
+{
+ return SetParam(KP_ALGID, (BYTE *)&AlgId, dwFlags);
+}
+
+inline HRESULT CCryptKey::GetBlockLength(DWORD * pdwBlockLen) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_BLOCKLEN, (BYTE *)pdwBlockLen, &dwSize);
+}
+
+inline HRESULT CCryptKey::GetKeyLength(DWORD * pdwKeyLen) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_KEYLEN, (BYTE *)pdwKeyLen, &dwSize);
+}
+
+inline HRESULT CCryptKey::GetSalt(BYTE * pbSalt, DWORD * pdwLength) throw()
+{
+ return GetParam(KP_SALT, pbSalt, pdwLength);
+}
+
+inline HRESULT CCryptKey::SetSalt(BYTE * pbSalt) throw()
+{
+ return SetParam(KP_SALT, pbSalt);
+}
+
+inline HRESULT CCryptKey::SetSaltEx(_CRYPTOAPI_BLOB * pBlobSalt) throw()
+{
+ return SetParam(KP_SALT_EX, (BYTE *)pBlobSalt);
+}
+
+inline HRESULT CCryptKey::GetPermissions(DWORD * pdwPerms) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_PERMISSIONS, (BYTE *)pdwPerms, &dwSize);
+}
+
+inline HRESULT CCryptKey::SetPermissions(DWORD dwPerms) throw()
+{
+ return SetParam(KP_PERMISSIONS, (BYTE *)&dwPerms);
+}
+
+inline HRESULT CCryptKey::GetP(BYTE * pbP, DWORD * pdwLength) throw()
+{
+ return GetParam(KP_P, (BYTE *)pbP, pdwLength);
+}
+
+inline HRESULT CCryptKey::SetP(_CRYPTOAPI_BLOB * pBlobP) throw()
+{
+ return SetParam(KP_P, (BYTE *)pBlobP);
+}
+
+inline HRESULT CCryptKey::SetP(BYTE * pbP, DWORD dwLength) throw()
+{
+ _CRYPTOAPI_BLOB blob = { dwLength, pbP };
+ return SetParam(KP_P, (BYTE *)&blob);
+}
+
+inline HRESULT CCryptKey::GetQ(BYTE * pbQ, DWORD * pdwLength) throw()
+{
+ return GetParam(KP_Q, (BYTE *)pbQ, pdwLength);
+}
+
+inline HRESULT CCryptKey::SetQ(_CRYPTOAPI_BLOB * pBlobQ) throw()
+{
+ return SetParam(KP_Q, (BYTE *)pBlobQ);
+}
+
+inline HRESULT CCryptKey::SetQ(BYTE * pbQ, DWORD dwLength) throw()
+{
+ _CRYPTOAPI_BLOB blob = { dwLength, pbQ };
+ return SetParam(KP_Q, (BYTE *)&blob);
+}
+
+inline HRESULT CCryptKey::GetG(BYTE * pbG, DWORD * pdwLength) throw()
+{
+ return GetParam(KP_G, (BYTE *)pbG, pdwLength);
+}
+
+inline HRESULT CCryptKey::SetG(_CRYPTOAPI_BLOB * pBlobG) throw()
+{
+ return SetParam(KP_G, (BYTE *)pBlobG);
+}
+
+inline HRESULT CCryptKey::SetG(BYTE * pbG, DWORD dwLength) throw()
+{
+ _CRYPTOAPI_BLOB blob = { dwLength, pbG };
+ return SetParam(KP_G, (BYTE *)&blob);
+}
+
+inline HRESULT CCryptKey::SetX() throw()
+{
+ return SetParam(KP_X, NULL);
+}
+
+inline HRESULT CCryptKey::GetEffKeyLen(DWORD * pdwEffKeyLen) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_EFFECTIVE_KEYLEN, (BYTE *)pdwEffKeyLen, &dwSize);
+}
+
+inline HRESULT CCryptKey::SetEffKeyLen(DWORD dwEffKeyLen) throw()
+{
+ return SetParam(KP_EFFECTIVE_KEYLEN, (BYTE *)&dwEffKeyLen);
+}
+
+inline HRESULT CCryptKey::GetPadding(DWORD * pdwPadding) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_PADDING, (BYTE *)pdwPadding, &dwSize);
+}
+
+inline HRESULT CCryptKey::SetPadding(DWORD dwPadding) throw()
+{
+ return SetParam(KP_PADDING, (BYTE *)&dwPadding);
+}
+
+inline HRESULT CCryptKey::GetIV(BYTE * pbIV, DWORD * pdwLength) throw()
+{
+ return GetParam(KP_IV, pbIV, pdwLength);
+}
+
+inline HRESULT CCryptKey::SetIV(BYTE * pbIV) throw()
+{
+ return SetParam(KP_IV, pbIV);
+}
+
+inline HRESULT CCryptKey::GetMode(DWORD * pdwMode) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_MODE, (BYTE *)pdwMode, &dwSize);
+}
+
+inline HRESULT CCryptKey::SetMode(DWORD dwMode) throw()
+{
+ return SetParam(KP_MODE, (BYTE *)&dwMode);
+}
+
+inline HRESULT CCryptKey::GetModeBits(DWORD * pdwModeBits) throw()
+{
+ DWORD dwSize = sizeof(DWORD);
+ return GetParam(KP_MODE_BITS, (BYTE *)pdwModeBits, &dwSize);
+}
+
+inline HRESULT CCryptKey::SetModeBits(DWORD dwModeBits) throw()
+{
+ return SetParam(KP_MODE_BITS, (BYTE *)&dwModeBits);
+}
+
+inline HRESULT CCryptDerivedKey::Initialize(
+ CCryptProv &Prov,
+ CCryptHash &Hash,
+ ALG_ID algid,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptDeriveKey(Prov.GetHandle(), algid, Hash.GetHandle(), dwFlags, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptRandomKey::Initialize(CCryptProv &Prov, ALG_ID algid, DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptGenKey(Prov.GetHandle(), algid, dwFlags, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+
+}
+
+inline HRESULT CCryptUserExKey::Initialize(CCryptProv &Prov) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptGetUserKey(Prov.GetHandle(), AT_KEYEXCHANGE, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptUserExKey::Create(CCryptProv &Prov) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptGenKey(Prov.GetHandle(), AT_KEYEXCHANGE, 0, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptUserSigKey::Initialize(CCryptProv &Prov) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptGetUserKey(Prov.GetHandle(), AT_SIGNATURE, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptUserSigKey::Create(CCryptProv &Prov) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptGenKey(Prov.GetHandle(), AT_SIGNATURE, 0, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptImportKey::Initialize(
+ CCryptProv &Prov,
+ BYTE * pbData,
+ DWORD dwDataLen,
+ CCryptKey &PubKey,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hKey == NULL);
+
+ if (!CryptImportKey(Prov.GetHandle(), pbData, dwDataLen, PubKey.GetHandle(), dwFlags, &m_hKey))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptKeyedHash::Initialize(
+ CCryptProv &Prov,
+ ALG_ID Algid,
+ CCryptKey &Key,
+ DWORD dwFlags) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), Algid, Key.GetHandle(), dwFlags, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ else return S_OK;
+}
+
+inline HRESULT CCryptMD5Hash::Initialize(CCryptProv &Prov, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_MD5, 0, 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+}
+
+inline HRESULT CCryptMD4Hash::Initialize(CCryptProv &Prov, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_MD4, 0, 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+}
+
+inline HRESULT CCryptMD2Hash::Initialize(CCryptProv &Prov, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_MD2, 0, 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+}
+
+inline HRESULT CCryptSHAHash::Initialize(CCryptProv &Prov, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_SHA, 0, 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+}
+
+inline HRESULT CCryptHMACHash::Initialize(CCryptProv &Prov, CCryptKey &Key, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_HMAC, Key.GetHandle(), 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+
+}
+
+inline HRESULT CCryptMACHash::Initialize(CCryptProv &Prov, CCryptKey &Key, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_MAC, Key.GetHandle(), 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+
+}
+
+inline HRESULT CCryptSSL3SHAMD5Hash::Initialize(CCryptProv &Prov, CCryptKey &Key, LPCTSTR szText) throw()
+{
+ ATLASSUME(m_hHash == NULL);
+
+ if (!CryptCreateHash(Prov.GetHandle(), CALG_SSL3_SHAMD5, Key.GetHandle(), 0, &m_hHash))
+ {
+ return AtlHresultFromLastError();
+ }
+ if (szText!=NULL)
+ return AddString(szText);
+ else return S_OK;
+
+}
+
+}; // namespace ATL
+
+#endif //__ATLCRYPT_INL__
diff --git a/include/atl/atlextmgmt.h b/include/atl/atlextmgmt.h
new file mode 100644
index 000000000..59f8d4353
--- /dev/null
+++ b/include/atl/atlextmgmt.h
@@ -0,0 +1,1279 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLEXTMGMT_H__
+#define __ATLEXTMGMT_H__
+
+#pragma once
+#pragma warning(push)
+#pragma warning(disable: 4702)
+#include <atlsoap.h>
+#include <atlutil.h>
+#include <atlsrvres.h>
+#include <atlsecurity.h>
+
+//
+// You can change the local group that is used for authorizing
+// site administrators by #define'ing ATL_DEFAULT_AUTH group
+// to something else before including this header file. For
+// example:
+// #define ATL_DEFAULT_AUTHGRP CSid(_T("My Heros"))
+// Verify that the logged on user is a member of
+// the local group 'My Heros' before allowing them to
+// administrate this site.
+//
+// #define ATL_DEFAULT_AUTHGRP Sids::World
+// Allow everyone access
+//
+// #define ATL_DEFAULT_AUTHGRP Sids::Null
+// Allow no one access
+//
+#ifndef ATL_DEFAULT_AUTHGRP
+ #define ATL_DEFAULT_AUTHGRP Sids::Admins()
+#endif
+
+// If you #define ATL_NO_DEFAULT_AUTHORITY then there will be no authorization
+// check before allowing access to management functions. You can also #define
+// ATL_NO_DEFAULT_AUTHORITY and then declare you own instance of _Authority
+// before #include-ing atlextmgmt.h to use a different authorization scheme.
+#ifndef ATL_NO_DEFAULT_AUTHORITY
+ __declspec(selectany) CDefaultAuth _Authority;
+#endif
+
+// You can choose which of the management handlers actually get used by
+// #defining the following constants before including this header
+// _ATL_THREADPOOL_MANAGEMENT (The thread pool manager web service and web based UI)
+// _ATL_STENCILCACHE_MANAGEMENT (The stencil cache manager web service and web based UI)
+// _ATL_DLLCACHE_MANAGEMENT (The DLL cache manager service and web based UI)
+
+// You can use the following constants to remove the web based UI if you don't
+// want to use it.
+// _ATL_THREADPOOL_NOUI (removes the thread pool mgr's stencil handler)
+// _ATL_STENCILCACHE_NOUI (removes the stencil cache mgr's stencil handler)
+// _ATL_DLLCACHE_NOUI (removes the dll cache mgr's stencil handler)
+
+// You can use the following constants to remove the web service management
+// components individually
+// _ATL_THREADPOOL_NOWEBSERVICE (removes the thread pool mgr's stencil handler)
+// _ATL_STENCILCACHE_NOWEBSERVICE (removes the stencil cache mgr's stencil handler)
+// _ATL_DLLCACHE_NOWEBSERVICE (removes the dll cache mgr's stencil handler)
+
+
+// The following constants declare resource names of stencils included
+// as resources in the module that uses this header. These stencils are
+// used for the web based UI for the management objects. You can provide
+// stencils of your own by including them as resources and redefining these
+// constants before including this header.
+#ifndef IDR_THREADMGR_SRF
+ #define IDR_THREADMGR_SRF "THREADMGR.SRF"
+#endif
+
+#ifndef IDR_STENCILMGR_SRF
+ #define IDR_STENCILMGR_SRF "STENCILMGR.SRF"
+#endif
+
+#ifndef IDR_DLLMGR_SRF
+ #define IDR_DLLMGR_SRF "DLLMGR.SRF"
+#endif
+
+// A warning so users using the web based UI to manage their extension
+// will remember to include the stencil resources in their projects
+#if (defined(_ATL_THREADPOOL_MANAGEMENT) && !defined(_ATL_THREADPOOL_NOUI)) || (defined(_ATL_STENCILCACHE_MANAGEMENT) && !defined(_ATL_STENCILCACHE_NOUI)) || (defined(_ATL_DLLCACHE_MANAGEMENT) && !defined(_ATL_DLLCACHE_NOUI))
+#ifndef NO_ATL_MGMT_STENCIL_WARNING
+ #pragma message("*************** Please Note ***************")
+ #pragma message("Your usage of atlextmgmt.h requires you to include management")
+ #pragma message("stencil resources in your module's resource file.")
+ #pragma message("Please make sure you include atlsrv.rc in your resource file.\r\n")
+#endif
+#endif
+
+// These constants define the names used for the handler objects for the
+// various services. You can change the names by redefining these constants
+// before including this header
+
+#ifndef ID_THREADMGR_WEBSERVICE_NAME
+ #define ID_THREADMGR_WEBSERVICE_NAME "ThreadPoolManager"
+#endif
+
+#ifndef ID_THREADMGR_WEBSERVICE_URL
+ #define ID_THREADMGR_WEBSERVICE_URL "http://www.microsoft.com/vc/atlserver/soap/ThreadPoolManager"
+#endif
+
+#ifndef ID_THREADMGR_WEBSERVICE_WSDL
+ #define ID_THREADMGR_WEBSERVICE_WSDL "GenThreadPoolManagerWSDL"
+#endif
+
+#ifndef ID_THREADMGR_SRFHANDLER_NAME
+ #define ID_THREADMGR_SRFHANDLER_NAME "ThreadMgrSrf"
+#endif
+
+#ifndef ID_STENCILCACHEMGR_WEBSERVICE_NAME
+ #define ID_STENCILCACHEMGR_WEBSERVICE_NAME "StencilCacheManager"
+#endif
+
+#ifndef ID_STENCILCACHEMGR_WEBSERVICE_URL
+ #define ID_STENCILCACHEMGR_WEBSERVICE_URL "http://www.microsoft.com/vc/atlserver/soap/StencilCacheManager"
+#endif
+
+#ifndef ID_STENCILCACHEMGR_WEBSERVICE_WSDL
+ #define ID_STENCILCACHEMGR_WEBSERVICE_WSDL "GenStencilCacheManagerWSDL"
+#endif
+
+#ifndef ID_STENCILCACHEMGR_SRFHANDLER_NAME
+ #define ID_STENCILCACHEMGR_SRFHANDLER_NAME "StencilMgrSrf"
+#endif
+
+#ifndef ID_DLLCACHEMGR_WEBSERVICE_NAME
+ #define ID_DLLCACHEMGR_WEBSERVICE_NAME "DllCacheManager"
+#endif
+
+#ifndef ID_DLLCACHEMGR_WEBSERVICE_URL
+ #define ID_DLLCACHEMGR_WEBSERVICE_URL "http://www.microsoft.com/vc/atlserver/soap/DllCacheManager"
+#endif
+
+#ifndef ID_DLLCACHEMGR_WEBSERVICE_WSDL
+ #define ID_DLLCACHEMGR_WEBSERVICE_WSDL "GenDllCacheManagerWSDL"
+#endif
+
+
+#ifndef ID_DLLCACHEMGR_SRFHANDLER_NAME
+ #define ID_DLLCACHEMGR_SRFHANDLER_NAME "DllMgrSrf"
+#endif
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+[emitidl(restricted)];
+
+#define ATL_COLOR_TR1 RGB(0xd2, 0xff, 0xff)
+#define ATL_COLOR_TR2 RGB(0xd2, 0xff, 0xd2)
+#define ATL_COLOR_BODYBG RGB(0xec, 0xf9, 0xec)
+
+// _AtlRedirectToPage builds up a redirect URL from the
+// current request plus a Handler= specification and
+// redirects the user's browser to that page.
+inline HTTP_CODE _AtlRedirectToPage(
+ IHttpServerContext *pContext,
+ CHttpRequest& request,
+ CHttpResponse& response,
+ const char *szHandler)
+{
+ ATLENSURE(pContext);
+ CStringA strRedirect("http://");
+
+ char buff[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwLen = static_cast<DWORD>(_countof(buff));
+ if (!pContext->GetServerVariable("SERVER_NAME", buff, &dwLen))
+ {
+ return HTTP_FAIL;
+ }
+ buff[_countof(buff)-1]='\0';
+ strRedirect+=buff;
+
+ dwLen = static_cast<DWORD>(_countof(buff));
+ if (!request.GetUrl(buff, &dwLen))
+ {
+ return HTTP_FAIL;
+ }
+ buff[_countof(buff)-1]='\0';
+ strRedirect+=buff;
+ strRedirect+=szHandler;
+
+ if (strRedirect.GetLength() >= ATL_URL_MAX_URL_LENGTH)
+ {
+ return HTTP_FAIL;
+ }
+
+ BOOL bOK=response.Redirect(strRedirect.GetString());
+
+ return bOK ? HTTP_SUCCESS_NO_PROCESS : HTTP_FAIL;
+}
+
+#ifdef _ATL_THREADPOOL_MANAGEMENT
+///////////////////////////////////////////////////////////////////////
+// Thread pool management
+
+[ uuid("44e9962a-5207-4d2a-a466-5f08a76e0e5d"), object ]
+__interface IThreadPoolMgr
+{
+ [id(0)] STDMETHOD(SetSize)([in] int nNumThreads);
+ [id(1)] STDMETHOD(GetSize)([out,retval] int *pnNumThreads);
+
+};
+
+
+class CThreadPoolMgrObject
+{
+public:
+ CThreadPoolMgrObject() throw()
+ {
+ }
+
+ HRESULT SetSize(int nNumThreads) throw()
+ {
+ if (!m_spThreadPoolConfig)
+ return E_UNEXPECTED;
+
+ CRevertThreadToken revert;
+ if (!revert.Initialize())
+ return E_FAIL;
+
+ HRESULT hr = m_spThreadPoolConfig->SetSize(nNumThreads);
+
+ DWORD dwErr = revert.Restore();
+ if (dwErr)
+ return AtlHresultFromWin32(dwErr);
+
+ return hr;
+ }
+
+
+ HRESULT GetSize(int *pnNumThreads) throw()
+ {
+ if (!m_spThreadPoolConfig)
+ return E_UNEXPECTED;
+
+ return m_spThreadPoolConfig->GetSize(pnNumThreads);
+
+ }
+
+ HTTP_CODE Initialize(IServiceProvider *pProvider) throw()
+ {
+ ATLASSERT(pProvider); // should never be NULL
+ if (!pProvider)
+ return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+
+ if (m_spThreadPoolConfig)
+ return HTTP_SUCCESS; // already initialized
+
+ pProvider->QueryService(__uuidof(IThreadPoolConfig), &m_spThreadPoolConfig);
+ return m_spThreadPoolConfig ? HTTP_SUCCESS : HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+private:
+ CComPtr<IThreadPoolConfig> m_spThreadPoolConfig;
+};
+
+#ifndef _ATL_THREADPOOL_NOWEBSERVICE
+#pragma warning(push)
+#pragma warning(disable:4199)
+[
+ soap_handler(
+ name= ID_THREADMGR_WEBSERVICE_NAME,
+ namespace= ID_THREADMGR_WEBSERVICE_URL,
+ protocol= "soap"
+ ),
+ request_handler(
+ name= ID_THREADMGR_WEBSERVICE_NAME,
+ sdl= ID_THREADMGR_WEBSERVICE_WSDL
+ )
+]
+class CThreadPoolManager :
+ public IThreadPoolMgr
+{
+#pragma warning(pop)
+public:
+ [soap_method]
+ STDMETHOD(SetSize)(int nNumThreads)
+ {
+ return m_PoolMgr.SetSize(nNumThreads);
+ }
+
+ [soap_method]
+ STDMETHOD(GetSize)(int *pnNumThreads)
+ {
+ return m_PoolMgr.GetSize(pnNumThreads);
+ }
+
+ // override HandleRequest to Initialize our m_spServiceProvider
+ // and to handle authorizing the client.
+ HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
+ {
+ HTTP_CODE hcErr = m_PoolMgr.Initialize(pProvider);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+
+ // Make sure caller is authorized on this system
+__if_exists(_Authority)
+{
+ hcErr = HTTP_FAIL;
+ ATLTRY(hcErr = _Authority.IsAuthorized(pRequestInfo, ATL_DEFAULT_AUTHGRP))
+}
+ if (hcErr == HTTP_SUCCESS)
+ {
+ hcErr = __super::HandleRequest(pRequestInfo, pProvider);
+ }
+ return hcErr;
+ }
+private:
+ CThreadPoolMgrObject m_PoolMgr;
+};
+#endif //_ATL_THREADPOOL_NOWEBSERVICE
+
+#ifndef _ATL_THREADPOOL_NOUI
+#define INVALID_COMMAND_ID -1
+#define MAX_COMMAND_ID 64
+
+[request_handler(name=ID_THREADMGR_SRFHANDLER_NAME)]
+class CThreadMgrStencil
+{
+public:
+ CThreadMgrStencil() :
+ m_nColor(ATL_COLOR_TR1)
+ {
+
+ }
+
+ [tag_name("GetSize")]
+ HTTP_CODE GetSize()
+ {
+ int nSize = 0;
+ HRESULT hr = m_PoolMgr.GetSize(&nSize);
+ if (SUCCEEDED(hr))
+ {
+ m_HttpResponse << nSize;
+ }
+ else
+ m_HttpResponse << "size not found";
+
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetTRColor")]
+ HTTP_CODE GetTRColor()
+ {
+ m_nColor = (m_nColor == ATL_COLOR_TR1) ? ATL_COLOR_TR2 : ATL_COLOR_TR1;
+ TCHAR cr[8];
+ if (RGBToHtml(m_nColor, cr, sizeof(cr)))
+ m_HttpResponse << cr;
+
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetBodyColor")]
+ HTTP_CODE GetBodyColor()
+ {
+ TCHAR cr[8];
+ if (RGBToHtml(ATL_COLOR_BODYBG, cr, sizeof(cr)))
+ m_HttpResponse << cr;
+ return HTTP_SUCCESS;
+ }
+
+
+ HTTP_CODE ValidateAndExchange() throw()
+ {
+ _ATLTRY
+ {
+ // Initialize the thread pool manager instance. Internally
+ // the initialize function will only intialize it's data structures
+ // once.
+ HTTP_CODE hcErr = m_PoolMgr.Initialize(m_spServiceProvider);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+
+__if_exists(_Authority)
+{
+ // Make sure caller is authorized on this system
+ hcErr = HTTP_FAIL;
+ ATLTRY(hcErr = _Authority.IsAuthorized(m_pRequestInfo, ATL_DEFAULT_AUTHGRP))
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+}
+
+
+ m_HttpResponse.SetContentType("text/html");
+
+ CString strHandler, strOptParam;
+ int nCmdToExec = INVALID_COMMAND_ID;
+
+ if (m_HttpRequest.GetMethod() == CHttpRequest::HTTP_METHOD_POST)
+ {
+ // check to see if we have a "Method" form variable and can execute a command
+ DWORD dwErr = m_HttpRequest.FormVars.Exchange("Method", &strHandler);
+ if (dwErr == VALIDATION_S_OK)
+ {
+ if (strHandler == _T("ExecuteCommand"))
+ {
+ // get the value of the command parameter so we can execute it
+ dwErr = m_HttpRequest.FormVars.Validate("command", &nCmdToExec, 0, MAX_COMMAND_ID);
+ if (dwErr == VALIDATION_S_OK)
+ {
+ // get the optional parameter if it's there.
+ m_HttpRequest.FormVars.Validate("DynValue", &strOptParam, 0, MAX_COMMAND_ID);
+
+ hcErr = ExecCommand(nCmdToExec, strOptParam);
+ return hcErr;
+ }
+ }
+ }
+ }
+
+ // If we had a proper command to execute, we would have done it by now.
+ // Just handle like it's a normal request to view the thread count.
+ hcErr = LoadStencilResource(m_hInstHandler, IDR_THREADMGR_SRF);
+ return hcErr;
+
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+ }
+
+ HTTP_CODE ExecCommand(int nCmdToExec, CString& strOptParam)
+ {
+ switch (nCmdToExec)
+ {
+ case 0:
+ TCHAR *pStop = NULL;
+ int nValue = _tcstoul(strOptParam, &pStop, 10);
+ m_PoolMgr.SetSize(nValue);
+ break;
+ };
+
+ return _AtlRedirectToPage(
+ m_spServerContext,
+ m_HttpRequest,
+ m_HttpResponse,
+ "?Handler=" ID_THREADMGR_SRFHANDLER_NAME
+ );
+ }
+private:
+ CThreadPoolMgrObject m_PoolMgr;
+ long m_nColor;
+ CString m_strUrl;
+
+};
+
+
+#endif // _ATL_THREADPOOL_NOUI
+#endif // _ATL_THREADPOOL_MANAGEMENT
+
+#ifdef _ATL_STENCILCACHE_MANAGEMENT
+//////////////////////////////////////////////////////////////////////
+// Stencil cache management
+class CStencilCacheMgrObject
+{
+public:
+ CStencilCacheMgrObject()
+ {
+
+ }
+
+ HRESULT GetCurrentEntryCount(__int64 *pdwSize)
+ {
+ ATLASSUME(m_spMemCacheStats);
+ if (!pdwSize)
+ return E_INVALIDARG;
+
+ DWORD dwValue;
+ HRESULT hr = m_spMemCacheStats->GetCurrentEntryCount(&dwValue);
+ if (hr == S_OK)
+ {
+ *pdwSize = dwValue;
+ }
+ return hr;
+ }
+
+ HRESULT ClearStats()
+ {
+ ATLENSURE(m_spMemCacheStats);
+ return m_spMemCacheStats->ClearStats();
+ }
+
+ HRESULT GetHitCount(__int64 *pdwSize)
+ {
+ ATLENSURE(m_spMemCacheStats);
+ if (!pdwSize)
+ return E_INVALIDARG;
+
+ DWORD dwValue;
+ HRESULT hr = m_spMemCacheStats->GetHitCount(&dwValue);
+ if (hr == S_OK)
+ {
+ *pdwSize = dwValue;
+ }
+ return hr;
+ }
+
+ HRESULT GetMissCount(__int64 *pdwSize)
+ {
+ ATLENSURE(m_spMemCacheStats);
+ if (!pdwSize)
+ return E_INVALIDARG;
+
+ DWORD dwValue;
+
+ HRESULT hr = m_spMemCacheStats->GetMissCount(&dwValue);
+ if (hr == S_OK)
+ {
+ *pdwSize = dwValue;
+ }
+ return hr;
+ }
+
+ HRESULT GetCurrentAllocSize(__int64 *pdwSize)
+ {
+ ATLENSURE(m_spMemCacheStats);
+ if (!pdwSize)
+ return E_INVALIDARG;
+
+ DWORD dwValue;
+
+ HRESULT hr = m_spMemCacheStats->GetCurrentAllocSize(&dwValue);
+ if (hr == S_OK)
+ {
+ *pdwSize = dwValue;
+ }
+ return hr;
+ }
+
+ HRESULT GetMaxAllocSize(__int64 *pdwSize)
+ {
+ ATLENSURE(m_spMemCacheStats);
+ if (!pdwSize)
+ return E_INVALIDARG;
+
+ DWORD dwValue;
+
+ HRESULT hr = m_spMemCacheStats->GetMaxAllocSize(&dwValue);
+ if (hr == S_OK)
+ {
+ *pdwSize = dwValue;
+ }
+ return hr;
+ }
+
+
+ HRESULT GetMaxEntryCount(__int64 *pdwSize)
+ {
+ ATLENSURE(m_spMemCacheStats);
+ if (!pdwSize)
+ return E_INVALIDARG;
+
+ DWORD dwValue;
+
+ HRESULT hr = m_spMemCacheStats->GetMaxEntryCount(&dwValue);
+ if (hr == S_OK)
+ {
+ *pdwSize = dwValue;
+ }
+ return hr;
+ }
+
+
+ HRESULT RemoveStencil(__int64 hStencil)
+ {
+ ATLENSURE(m_spStencilCacheControl);
+ return m_spStencilCacheControl->RemoveStencil((const HCACHEITEM)hStencil);
+ }
+
+ HRESULT RemoveStencilByName(BSTR szStencil) throw()
+ {
+ ATLENSURE_RETURN(m_spStencilCacheControl);
+ return m_spStencilCacheControl->RemoveStencilByName(CW2A(szStencil));
+ }
+
+
+ HRESULT RemoveAllStencils()
+ {
+ ATLENSURE(m_spStencilCacheControl);
+ return m_spStencilCacheControl->RemoveAllStencils();
+ }
+
+ // we show lifespan in milliseconds in the UI so we have to
+ // do the conversion to 100ns intervals here.
+ HRESULT SetDefaultLifespan(unsigned __int64 dwdwLifespan)
+ {
+ ATLENSURE(m_spStencilCacheControl);
+ // convert to 100ns intervals
+ return m_spStencilCacheControl->SetDefaultLifespan(dwdwLifespan * CFileTime::Millisecond);
+ }
+
+ HRESULT GetDefaultLifespan(unsigned __int64 *pdwdwLifespan)
+ {
+ ATLENSURE(m_spStencilCacheControl);
+ ATLENSURE(pdwdwLifespan!=NULL);
+ *pdwdwLifespan = 0;
+ unsigned __int64 dwls = 0;
+ HRESULT hr = m_spStencilCacheControl->GetDefaultLifespan(&dwls);
+
+ // convert to milliseconds
+ if (SUCCEEDED(hr))
+ {
+ dwls /= CFileTime::Millisecond;
+ *pdwdwLifespan = dwls;
+ }
+
+ return hr;
+ }
+
+ HTTP_CODE Initialize(IServiceProvider *pProvider) throw()
+ {
+
+ ATLASSERT(pProvider); // should never be NULL
+ if (!pProvider)
+ return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+
+
+ if (m_spMemCacheStats && m_spStencilCacheControl)
+ return HTTP_SUCCESS; // already initialized
+
+ CComPtr<IStencilCache> spStencilCache;
+ pProvider->QueryService(__uuidof(IStencilCache), &spStencilCache);
+ if (spStencilCache)
+ {
+ if (!m_spMemCacheStats)
+ {
+ spStencilCache->QueryInterface(__uuidof(IMemoryCacheStats),
+ (void**)&m_spMemCacheStats);
+ }
+ if (!m_spStencilCacheControl)
+ {
+ spStencilCache->QueryInterface(__uuidof(IStencilCacheControl),
+ (void**)&m_spStencilCacheControl);
+ }
+ }
+
+ return (m_spMemCacheStats && m_spStencilCacheControl)
+ ? HTTP_SUCCESS : HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+private:
+ CComPtr<IMemoryCacheStats> m_spMemCacheStats;
+ CComPtr<IStencilCacheControl> m_spStencilCacheControl;
+};
+
+
+#ifndef _ATL_STENCILCACHE_NOWEBSERVICE
+
+[ uuid("3813895C-4C4C-41df-95F4-12220140B164"), object ]
+__interface IStencilCacheMgr
+{
+ // data access
+ [id(0)] STDMETHOD(GetCurrentEntryCount)([out,retval] __int64 *pdwSize);
+ [id(1)] STDMETHOD(GetHitCount)([out,retval] __int64 *pdwSize);
+ [id(2)] STDMETHOD(GetMissCount)([out,retval] __int64 *pdwSize);
+ [id(3)] STDMETHOD(GetCurrentAllocSize)([out,retval] __int64 *pdwSize);
+ [id(4)] STDMETHOD(GetMaxAllocSize)([out,retval] __int64 *pdwSize);
+ [id(5)] STDMETHOD(GetMaxEntryCount)([out,retval] __int64 *pdwSize);
+ [id(6)] STDMETHOD(GetDefaultLifespan)([out,retval] unsigned __int64 *pdwdwLifespan);
+
+ // commands
+ [id(7)] STDMETHOD(ClearStats)();
+ [id(8)] STDMETHOD(RemoveStencil)([in] __int64 hStencil);
+ [id(9)] STDMETHOD(RemoveStencilByName)([in] BSTR szStencil);
+ [id(10)] STDMETHOD(RemoveAllStencils)();
+ [id(11)] STDMETHOD(SetDefaultLifespan)([in] unsigned __int64 dwdwLifespan);
+};
+
+#pragma warning(push)
+#pragma warning(disable:4199)
+[
+ soap_handler( name= ID_STENCILCACHEMGR_WEBSERVICE_NAME,
+ namespace= ID_STENCILCACHEMGR_WEBSERVICE_URL,
+ protocol= "soap"
+ ),
+ request_handler(
+ name= ID_STENCILCACHEMGR_WEBSERVICE_NAME,
+ sdl= ID_STENCILCACHEMGR_WEBSERVICE_WSDL )
+]
+class CStencilCacheManager :
+ public IStencilCacheMgr
+{
+#pragma warning(pop)
+public:
+ [ soap_method ]
+ STDMETHOD(GetCurrentEntryCount)(__int64 *pdwSize)
+ {
+ return m_MgrObj.GetCurrentEntryCount(pdwSize);
+ }
+
+ [ soap_method ]
+ STDMETHOD(ClearStats)()
+ {
+ return m_MgrObj.ClearStats();
+ }
+
+ [ soap_method ]
+ STDMETHOD(GetHitCount)(__int64 *pdwSize)
+ {
+ return m_MgrObj.GetHitCount(pdwSize);
+ }
+
+ [ soap_method ]
+ STDMETHOD(GetMissCount)(__int64 *pdwSize)
+ {
+ return m_MgrObj.GetMissCount(pdwSize);
+ }
+
+ [ soap_method ]
+ STDMETHOD(GetCurrentAllocSize)(__int64 *pdwSize)
+ {
+ return m_MgrObj.GetCurrentAllocSize(pdwSize);
+ }
+
+ [ soap_method ]
+ STDMETHOD(GetMaxAllocSize)(__int64 *pdwSize)
+ {
+ return m_MgrObj.GetMaxAllocSize(pdwSize);
+ }
+
+ [ soap_method ]
+ STDMETHOD(GetMaxEntryCount)(__int64 *pdwSize)
+ {
+ return m_MgrObj.GetMaxEntryCount(pdwSize);
+ }
+
+ [ soap_method ]
+ STDMETHOD(RemoveStencil)(__int64 hStencil)
+ {
+ return m_MgrObj.RemoveStencil(hStencil);
+ }
+
+ [ soap_method ]
+ STDMETHOD(RemoveStencilByName)(BSTR bstrStencil)
+ {
+ return m_MgrObj.RemoveStencilByName(bstrStencil);
+ }
+
+ [ soap_method ]
+ STDMETHOD(RemoveAllStencils)()
+ {
+ return m_MgrObj.RemoveAllStencils();
+ }
+
+ // we show lifespan in milliseconds in the UI.
+ // m_MgrObj handles the conversion to 100ns intervals.
+ [ soap_method ]
+ STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan)
+ {
+ return m_MgrObj.SetDefaultLifespan(dwdwLifespan);
+ }
+
+ [ soap_method ]
+ STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifespan)
+ {
+ return m_MgrObj.GetDefaultLifespan(pdwdwLifespan);
+ }
+
+ HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
+ {
+ HTTP_CODE hcErr = m_MgrObj.Initialize(pProvider);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+
+__if_exists(_Authority)
+{
+ // Make sure caller is authorized on this system
+ hcErr = HTTP_FAIL;
+ ATLTRY(hcErr = _Authority.IsAuthorized(pRequestInfo, ATL_DEFAULT_AUTHGRP))
+}
+ if (hcErr == HTTP_SUCCESS)
+ {
+ hcErr = __super::HandleRequest(pRequestInfo, pProvider);
+ }
+ return hcErr;
+ }
+private:
+ CStencilCacheMgrObject m_MgrObj;
+};
+#endif //_ATL_STENCILCACHE_NOWEBSERVICE
+#ifndef _ATL_STENCILCACHE_NOUI
+typedef HRESULT (CStencilCacheMgrObject::*PFNGETDATA)(__int64 *pdwSize);
+
+struct CCache_data
+{
+ PFNGETDATA m_pfn;
+ char m_sz[128];
+};
+
+#define INVALID_DATA_PTR ((DWORD_PTR) -1)
+#define INVALID_COMMAND_ID -1
+#define MAX_COMMAND_ID 64
+#define ATL_STENCILCACHECMD_CLEARALLSTATS 0
+#define ATL_STENCILCACHECMD_REMOVESTENCIL 1
+#define ATL_STENCILCACHECMD_REMOVEALLSTENCILS 2
+#define ATL_STENCILCACHECMD_SETDEFLIFESPAN 3
+
+[request_handler(name=ID_STENCILCACHEMGR_SRFHANDLER_NAME)]
+class CStencilMgr
+{
+public:
+ CStencilMgr()
+ {
+ m_pData = (CCache_data*)INVALID_DATA_PTR;
+ m_nColor = ATL_COLOR_TR1;
+ }
+
+ HTTP_CODE ValidateAndExchange() throw()
+ {
+ _ATLTRY
+ {
+ HTTP_CODE hcErr = m_MgrObj.Initialize(m_spServiceProvider);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+
+__if_exists(_Authority)
+{
+ // Make sure caller is authorized on this system
+ hcErr = HTTP_FAIL;
+ ATLTRY(hcErr = _Authority.IsAuthorized(m_pRequestInfo, ATL_DEFAULT_AUTHGRP))
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+}
+ m_HttpResponse.SetContentType("text/html");
+
+ // check to see if we have a "Handler" form variable
+ CString strHandler, strOptParam;
+ int nCmdToExec;
+
+ if (m_HttpRequest.GetMethod() == CHttpRequest::HTTP_METHOD_POST)
+ {
+ DWORD dwErr = m_HttpRequest.FormVars.Exchange("Method", &strHandler);
+ if (dwErr == VALIDATION_S_OK)
+ {
+ if (strHandler == _T("ExecuteCommand"))
+ {
+ // get the value of the command parameter so we can execute it
+ dwErr = m_HttpRequest.FormVars.Validate("command", &nCmdToExec, 0, MAX_COMMAND_ID);
+ if (dwErr == VALIDATION_S_OK)
+ {
+ // get the optional parameter if it's there.
+ m_HttpRequest.FormVars.Validate("DynValue", &strOptParam, 0, MAX_COMMAND_ID);
+ hcErr = ExecCommand(nCmdToExec, strOptParam);
+ return hcErr;
+ }
+ }
+ }
+ }
+ hcErr = LoadStencilResource(m_hInstHandler, IDR_STENCILMGR_SRF);
+ return hcErr;
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+ }
+
+ HTTP_CODE ExecCommand(int nCmdToExec, CString& strOptParam)
+ {
+ switch (nCmdToExec)
+ {
+ case ATL_STENCILCACHECMD_CLEARALLSTATS:
+ m_MgrObj.ClearStats();
+ break;
+
+ case ATL_STENCILCACHECMD_REMOVESTENCIL:
+ m_MgrObj.RemoveStencilByName(strOptParam.AllocSysString());
+ break;
+
+ case ATL_STENCILCACHECMD_REMOVEALLSTENCILS:
+ m_MgrObj.RemoveAllStencils();
+ break;
+
+ case ATL_STENCILCACHECMD_SETDEFLIFESPAN:
+ TCHAR *pStop = NULL;
+ m_MgrObj.SetDefaultLifespan(_tcstoul(strOptParam, &pStop, 10));
+ break;
+ };
+
+ return _AtlRedirectToPage(
+ m_spServerContext,
+ m_HttpRequest,
+ m_HttpResponse,
+ "?Handler=" ID_STENCILCACHEMGR_SRFHANDLER_NAME
+ );
+
+ }
+
+ [tag_name("GetNextStencilCacheStats")]
+ HTTP_CODE GetNextStencilCacheStats()
+ {
+ if (m_pData == (CCache_data*)INVALID_DATA_PTR)
+ {
+ m_pData = GetCacheData();
+ return HTTP_SUCCESS;
+ }
+ m_pData++;
+
+ if (m_pData->m_pfn != NULL)
+ return HTTP_SUCCESS;
+
+ m_pData = (CCache_data*)INVALID_DATA_PTR;
+ return HTTP_S_FALSE;
+
+ }
+
+ [tag_name("GetCacheValue")]
+ HTTP_CODE GetCacheValue()
+ {
+ ATLENSURE(m_pData);
+ ATLENSURE(m_pData != (CCache_data*)INVALID_DATA_PTR);
+ m_HttpResponse << m_pData->m_sz;
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetCacheQuantity")]
+ HTTP_CODE GetCacheQuantity()
+ {
+ ATLENSURE(m_pData);
+ ATLENSURE(m_pData != (CCache_data*)INVALID_DATA_PTR);
+ __int64 dwValue = 0;
+ PFNGETDATA pfn = m_pData->m_pfn;
+ ATLENSURE(pfn);
+ CStencilCacheMgrObject *pMgr = &m_MgrObj;
+ (pMgr->*pfn)(&dwValue);
+
+ m_HttpResponse << dwValue;
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetTRColor")]
+ HTTP_CODE GetTRColor()
+ {
+ m_nColor = (m_nColor == ATL_COLOR_TR1) ? ATL_COLOR_TR2 : ATL_COLOR_TR1;
+ TCHAR cr[8];
+ if (RGBToHtml(m_nColor, cr, sizeof(cr)))
+ m_HttpResponse << cr;
+
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetBodyColor")]
+ HTTP_CODE GetBodyColor()
+ {
+ TCHAR cr[8];
+ if (RGBToHtml(ATL_COLOR_BODYBG, cr, sizeof(cr)))
+ m_HttpResponse << cr;
+ return HTTP_SUCCESS;
+ }
+private:
+ static CCache_data* GetCacheData()
+ {
+ static CCache_data cache_data[] =
+ {
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetCurrentEntryCount, "Current Cache Entry Count(stencils)"},
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetHitCount, "Cache Hit Count(stencils)"},
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetMissCount, "Cache Miss Count(stencils)"},
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetCurrentAllocSize, "Cache memory allocation(bytes)"},
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetMaxAllocSize, "Cache maximum allocation size(bytes)"},
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetMaxEntryCount, "Cache maximum entry count(stencils)"},
+ {(PFNGETDATA)&CStencilCacheMgrObject::GetDefaultLifespan, "Default stencil lifespan(ms)"},
+ {NULL, NULL}
+ };
+ return cache_data;
+ }
+
+ CStencilCacheMgrObject m_MgrObj;
+ CCache_data *m_pData;
+ long m_nColor;
+};
+//__declspec(selectany) CComObjectGlobal<CStencilCacheManager> CStencilMgr::m_cachemgr;
+#endif // _ATL_STENCILCACHE_NOUI
+#endif // _ATL_STENCILCACHE_MANAGEMENT
+
+//////////////////////////////////////////////////////////////////////
+// DLL cache management
+#ifdef _ATL_DLLCACHE_MANAGEMENT
+
+
+#ifndef _ATL_DLLCACHE_NOWEBSERVICE
+[export]
+#endif
+struct _DLL_CACHE_ENTRY
+{
+ DWORD hInstDll;
+ DWORD dwRefs;
+ BSTR szDllName;
+};
+
+
+class CDllMgrObject
+{
+public:
+ HRESULT GetEntries(DWORD dwCount, _DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied)
+ {
+ ATLASSUME(m_spDllCache);
+ HRESULT hr = E_FAIL;
+ DLL_CACHE_ENTRY *pe = NULL;
+
+ if (!m_spDllCache)
+ return E_UNEXPECTED;
+
+ if (dwCount != 0 && pEntries == NULL)
+ return E_UNEXPECTED; // asking for entries but no place to put them
+
+ if (!pdwCopied)
+ return E_POINTER;
+ *pdwCopied = 0;
+
+ if (dwCount)
+ {
+ pe = new DLL_CACHE_ENTRY[dwCount];
+ if (!pe)
+ return E_OUTOFMEMORY;
+ }
+
+ hr = m_spDllCache->GetEntries(dwCount, pe, pdwCopied);
+ if (hr == S_OK && dwCount != 0 && pEntries != NULL)
+ {
+ // SysAllocString our path strings
+ for (DWORD i = 0; i<*pdwCopied; i++)
+ {
+ pEntries[i].hInstDll = (DWORD)(DWORD_PTR)pe[i].hInstDll;
+ pEntries[i].dwRefs = pe[i].dwRefs;
+ pEntries[i].szDllName = ::SysAllocString(CA2W(pe[i].szDllName));
+ }
+ }
+
+ delete [] pe;
+ return hr;
+ }
+
+
+ HRESULT GetEntryCount(DWORD *pdwCount)
+ {
+ ATLASSUME(m_spDllCache);
+ if (!m_spDllCache)
+ return E_UNEXPECTED;
+
+ return m_spDllCache->GetEntries(0, NULL, pdwCount);
+ }
+
+ HTTP_CODE Initialize(IServiceProvider *pProvider)
+ {
+ ATLASSERT(pProvider); // should never be NULL
+ if (!pProvider)
+ return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+
+ if (m_spDllCache)
+ return HTTP_SUCCESS; // already initialized
+
+ pProvider->QueryService(__uuidof(IDllCache), &m_spDllCache);
+ return m_spDllCache ? HTTP_SUCCESS : HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+private:
+ CComPtr<IDllCache> m_spDllCache;
+
+}; // CDllMgrObject
+
+
+#ifndef _ATL_DLLCACHE_NOWEBSERVICE
+// _DLL_CACHE_ENTRY is our own version of DLL_CACHE_ENTRY(atlcache.h) that
+// uses a BSTR instead of a fixed length string for the szDllName for compatiblility
+// with our SOAP implementation.
+[ uuid("A0C00AF8-CEA5-46b9-97ED-FDEE55B583EF"), object ]
+__interface IDllCacheMgr
+{
+ [id(0)] STDMETHOD(GetEntries)([in] DWORD dwCount, [out] _DLL_CACHE_ENTRY *pEntries, [out, retval] DWORD *pdwCopied);
+ [id(1)] STDMETHOD(GetEntryCount)([out, retval] DWORD *pdwCount);
+
+};
+
+
+#pragma warning(push)
+#pragma warning(disable:4199)
+[
+ soap_handler(
+ name= ID_DLLCACHEMGR_WEBSERVICE_NAME,
+ namespace= ID_DLLCACHEMGR_WEBSERVICE_URL,
+ protocol= "soap"
+ ),
+ request_handler(
+ name= ID_DLLCACHEMGR_WEBSERVICE_NAME,
+ sdl= ID_DLLCACHEMGR_WEBSERVICE_WSDL
+ )
+]
+class CDllCacheManager :
+ public IDllCacheMgr
+{
+#pragma warning(pop)
+public:
+ [soap_method]
+ HRESULT GetEntries(DWORD dwCount, _DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied)
+ {
+ return m_MgrObj.GetEntries(dwCount, pEntries, pdwCopied);
+ }
+
+ [soap_method]
+ STDMETHOD(GetEntryCount)(DWORD *pdwCount)
+ {
+ return m_MgrObj.GetEntries(0, NULL, pdwCount);
+ }
+
+ HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
+ {
+ HTTP_CODE hcErr = m_MgrObj.Initialize(pProvider);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+
+__if_exists(_Authority)
+{
+ // Make sure caller is authorized on this system
+ hcErr = HTTP_FAIL;
+ ATLTRY(hcErr = _Authority.IsAuthorized(pRequestInfo, ATL_DEFAULT_AUTHGRP))
+}
+ if (hcErr == HTTP_SUCCESS)
+ {
+ hcErr = __super::HandleRequest(pRequestInfo, pProvider);
+ }
+ return hcErr;
+ }
+
+protected:
+ CDllMgrObject m_MgrObj;
+};
+#endif _ATL_DLLCACHE_NOWEBSERVICE
+
+#ifndef _ATL_DLLCACHE_NOUI
+#define INVALID_INDEX -1
+
+[
+ request_handler(name=ID_DLLCACHEMGR_SRFHANDLER_NAME)
+]
+class CDllCacheMgr
+{
+public:
+ CDllCacheMgr() : m_nColor(ATL_COLOR_TR1),
+ m_nEnumCount(INVALID_INDEX),
+ m_nEnumIndex(INVALID_INDEX),
+ m_pEntries(NULL)
+ {
+
+ }
+
+ [tag_name("GetTRColor")]
+ HTTP_CODE GetTRColor()
+ {
+ m_nColor = (m_nColor == ATL_COLOR_TR1) ? ATL_COLOR_TR2 : ATL_COLOR_TR1;
+ TCHAR cr[8];
+ if (RGBToHtml(m_nColor, cr, sizeof(cr)))
+ m_HttpResponse << cr;
+
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetBodyColor")]
+ HTTP_CODE GetBodyColor()
+ {
+ TCHAR cr[8];
+ if (RGBToHtml(ATL_COLOR_BODYBG, cr, sizeof(cr)))
+ m_HttpResponse << cr;
+ return HTTP_SUCCESS;
+ }
+
+
+ [tag_name("GetNumEntries")]
+ HTTP_CODE GetNumEntries()
+ {
+ DWORD dwEntries = 0;
+ m_MgrObj.GetEntryCount(&dwEntries);
+ m_HttpResponse << dwEntries;
+ return HTTP_SUCCESS;
+ }
+
+
+ [tag_name("EnumEntries")]
+ HTTP_CODE EnumEntries()
+ {
+ // we lock the cache while we enum entries so no entries
+ // will be removed during the enumeration request.
+ if (m_nEnumIndex == INVALID_INDEX)
+ {
+ // set up for the iteration
+ m_MgrObj.GetEntryCount((DWORD*)&m_nEnumCount);
+ if (!m_nEnumCount)
+ return HTTP_S_FALSE; // nothing to enum
+
+ m_pEntries = new _DLL_CACHE_ENTRY[m_nEnumCount];
+ if (!m_pEntries)
+ return HTTP_ERROR(500, ISE_SUBERR_OUTOFMEM);
+
+ DWORD dwFetched = INVALID_INDEX;
+
+ if (S_OK != m_MgrObj.GetEntries(m_nEnumCount, m_pEntries, &dwFetched))
+ return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+
+ m_nEnumIndex = 0;
+ return HTTP_SUCCESS;
+ }
+
+ m_nEnumIndex++;
+ if (m_nEnumIndex < m_nEnumCount)
+ return HTTP_SUCCESS; // continue iterating
+
+ else
+ {
+ // done, clean up
+ for (int i = 0; i< m_nEnumCount; i++)
+ {
+ ::SysFreeString(m_pEntries[i].szDllName);
+ }
+ delete [] m_pEntries;
+ m_pEntries = NULL;
+ m_nEnumCount = INVALID_INDEX;
+ m_nEnumIndex = INVALID_INDEX;
+ return HTTP_S_FALSE; // terminate iterations.
+ }
+ }
+
+ [tag_name("GetDllName")]
+ HTTP_CODE GetDllName()
+ {
+ m_HttpResponse << m_pEntries[m_nEnumIndex].szDllName;
+ return HTTP_SUCCESS;
+ }
+
+ [tag_name("GetDllReferences")]
+ HTTP_CODE GetDllReferences()
+ {
+ m_HttpResponse << m_pEntries[m_nEnumIndex].dwRefs;
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE ValidateAndExchange()
+ {
+
+ HTTP_CODE hcErr = m_MgrObj.Initialize(m_spServiceProvider);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+
+__if_exists(_Authority)
+{
+ // Make sure caller is authorized on this system
+ hcErr = HTTP_FAIL;
+ ATLTRY(hcErr = _Authority.IsAuthorized(m_pRequestInfo, ATL_DEFAULT_AUTHGRP))
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+}
+ hcErr = LoadStencilResource(m_hInstHandler, IDR_DLLMGR_SRF);
+ m_HttpResponse.SetContentType("text/html");
+ return hcErr;
+
+ }
+
+ CDllMgrObject m_MgrObj;
+ long m_nColor;
+ int m_nEnumCount;
+ int m_nEnumIndex;
+ _DLL_CACHE_ENTRY *m_pEntries;
+
+};
+
+#endif // _ATL_DLLCACHE_NOUI
+#endif // _ATL_DLLCACHE_MANAGEMENT
+
+}; // ATL
+
+#pragma pack(pop)
+#pragma warning(pop)
+#endif // __ATLEXTMGMT_H__
diff --git a/include/atl/atlhtml.h b/include/atl/atlhtml.h
new file mode 100644
index 000000000..4922e580d
--- /dev/null
+++ b/include/atl/atlhtml.h
@@ -0,0 +1,1682 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLHTML_H__
+#define __ATLHTML_H__
+
+#pragma once
+
+#include <atlstr.h>
+#include <atlsiface.h>
+#include <atlconv.h>
+
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+#define TAGF_NONE 0
+#define TAGF_HASEND 1
+#define TAGF_BLOCK 2
+
+
+struct ATL_HTML_TAG
+{
+ LPCTSTR szTagName;
+ UINT uFlags;
+};
+
+enum ATL_HTML_TAGS {
+ ATL_HTML_TAG_BODY,
+ ATL_HTML_TAG_A,
+ ATL_HTML_TAG_B,
+ ATL_HTML_TAG_I,
+ ATL_HTML_TAG_U,
+ ATL_HTML_TAG_FONT,
+ ATL_HTML_TAG_IMG,
+ ATL_HTML_TAG_HR,
+ ATL_HTML_TAG_BR,
+ ATL_HTML_TAG_DIV,
+ ATL_HTML_TAG_BLOCKQUOTE,
+ ATL_HTML_TAG_ADDRESS,
+ ATL_HTML_TAG_P,
+ ATL_HTML_TAG_H1,
+ ATL_HTML_TAG_H2,
+ ATL_HTML_TAG_H3,
+ ATL_HTML_TAG_H4,
+ ATL_HTML_TAG_H5,
+ ATL_HTML_TAG_H6,
+ ATL_HTML_TAG_PRE,
+ ATL_HTML_TAG_Q,
+ ATL_HTML_TAG_SUB,
+ ATL_HTML_TAG_SUP,
+ ATL_HTML_TAG_INS,
+ ATL_HTML_TAG_DEL,
+ ATL_HTML_TAG_EM,
+ ATL_HTML_TAG_STRONG,
+ ATL_HTML_TAG_DFN,
+ ATL_HTML_TAG_CODE,
+ ATL_HTML_TAG_SAMP,
+ ATL_HTML_TAG_KBD,
+ ATL_HTML_TAG_VAR,
+ ATL_HTML_TAG_CITE,
+ ATL_HTML_TAG_ABBR,
+ ATL_HTML_TAG_ACRONYM,
+ ATL_HTML_TAG_OL,
+ ATL_HTML_TAG_UL,
+ ATL_HTML_TAG_LI,
+ ATL_HTML_TAG_DL,
+ ATL_HTML_TAG_DT,
+ ATL_HTML_TAG_DD,
+ ATL_HTML_TAG_TABLE,
+ ATL_HTML_TAG_TR,
+ ATL_HTML_TAG_TD,
+ ATL_HTML_TAG_FORM,
+ ATL_HTML_TAG_INPUT,
+ ATL_HTML_TAG_SELECT,
+ ATL_HTML_TAG_OPTION,
+ ATL_HTML_TAG_HEAD,
+ ATL_HTML_TAG_HTML,
+ ATL_HTML_TAG_MAP,
+ ATL_HTML_TAG_AREA,
+ ATL_HTML_TAG_BASE,
+ ATL_HTML_TAG_BDO,
+ ATL_HTML_TAG_BIG,
+ ATL_HTML_TAG_BUTTON,
+ ATL_HTML_TAG_IFRAME,
+ ATL_HTML_TAG_LABEL,
+ ATL_HTML_TAG_LINK,
+ ATL_HTML_TAG_META,
+ ATL_HTML_TAG_NOFRAMES,
+ ATL_HTML_TAG_NOSCRIPT,
+ ATL_HTML_TAG_COL,
+ ATL_HTML_TAG_COLGROUP,
+ ATL_HTML_TAG_FIELDSET,
+ ATL_HTML_TAG_LEGEND,
+ ATL_HTML_TAG_TBODY,
+ ATL_HTML_TAG_TEXTAREA,
+ ATL_HTML_TAG_TFOOT,
+ ATL_HTML_TAG_TH,
+ ATL_HTML_TAG_TITLE,
+ ATL_HTML_TAG_TT,
+ ATL_HTML_TAG_SMALL,
+ ATL_HTML_TAG_SPAN,
+ ATL_HTML_TAG_OBJECT,
+ ATL_HTML_TAG_PARAM,
+ ATL_HTML_TAG_LAST };
+
+extern __declspec(selectany) const ATL_HTML_TAG s_tags[] =
+{
+ { _T("body"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("a"), TAGF_HASEND },
+ { _T("b"), TAGF_HASEND },
+ { _T("i"), TAGF_HASEND },
+ { _T("u"), TAGF_HASEND },
+ { _T("font"), TAGF_HASEND },
+ { _T("img"), TAGF_NONE },
+ { _T("hr"), TAGF_NONE },
+ { _T("br"), TAGF_NONE },
+ { _T("div"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("blockquote"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("adress"), TAGF_HASEND },
+ { _T("p"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("h1"), TAGF_HASEND | TAGF_BLOCK},
+ { _T("h2"), TAGF_HASEND | TAGF_BLOCK},
+ { _T("h3"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("h4"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("h5"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("h6"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("pre"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("q"), TAGF_HASEND },
+ { _T("sub"), TAGF_HASEND },
+ { _T("sup"), TAGF_HASEND },
+ { _T("ins"), TAGF_HASEND },
+ { _T("del"), TAGF_HASEND },
+ { _T("em"), TAGF_HASEND },
+ { _T("strong"), TAGF_HASEND },
+ { _T("dfn"), TAGF_HASEND },
+ { _T("code"), TAGF_HASEND },
+ { _T("samp"), TAGF_HASEND },
+ { _T("kbd"), TAGF_HASEND },
+ { _T("var"), TAGF_HASEND },
+ { _T("cite"), TAGF_HASEND },
+ { _T("abbr"), TAGF_HASEND },
+ { _T("acronym"), TAGF_HASEND },
+ { _T("ol"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("ul"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("li"), TAGF_HASEND },
+ { _T("dl"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("dt"), TAGF_HASEND },
+ { _T("dd"), TAGF_HASEND },
+ { _T("table"), TAGF_HASEND },
+ { _T("tr"), TAGF_HASEND },
+ { _T("td"), TAGF_HASEND },
+ { _T("form"), TAGF_HASEND },
+ { _T("input"), TAGF_HASEND },
+ { _T("select"), TAGF_HASEND },
+ { _T("option"), TAGF_HASEND },
+ { _T("head"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("html"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("map"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("area"), TAGF_BLOCK },
+ { _T("base"), TAGF_BLOCK },
+ { _T("bdo"), TAGF_HASEND },
+ { _T("big"), TAGF_HASEND },
+ { _T("button"), TAGF_HASEND },
+ { _T("iframe"), TAGF_HASEND },
+ { _T("label"), TAGF_HASEND },
+ { _T("link"), TAGF_NONE },
+ { _T("meta"), TAGF_BLOCK },
+ { _T("noframes"), TAGF_BLOCK },
+ { _T("noscript"), TAGF_BLOCK },
+ { _T("col"), TAGF_BLOCK },
+ { _T("colgroup"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("fieldset"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("legend"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("tbody"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("textarea"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("tfoot"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("th"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("title"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("tt"), TAGF_HASEND },
+ { _T("small"), TAGF_HASEND },
+ { _T("span"), TAGF_HASEND },
+ { _T("object"), TAGF_HASEND | TAGF_BLOCK },
+ { _T("param"), TAGF_NONE },
+};
+
+class AtlHtmlAttrs
+{
+public:
+ CString m_strAttrs;
+
+ AtlHtmlAttrs()
+ {
+
+ }
+
+#pragma warning(push)
+#pragma warning(disable : 4793)
+ AtlHtmlAttrs(int nCount, ...)
+ {
+ va_list args;
+
+ va_start(args, nCount);
+ for (int i=0; i<nCount; i++)
+ {
+ LPCTSTR szName = va_arg(args, LPCTSTR);
+ LPCTSTR szVal = va_arg(args, LPCTSTR);
+ Add(szName, szVal);
+ }
+ va_end(args);
+ }
+#pragma warning(pop)
+
+#pragma warning(push)
+#pragma warning(disable : 4793)
+ AtlHtmlAttrs(LPCTSTR szFormat, ...)
+ {
+ if (!szFormat || !*szFormat)
+ return;
+
+ va_list args;
+
+ va_start(args, szFormat);
+
+ CString strTmp;
+ strTmp.FormatV(szFormat, args);
+ va_end(args);
+
+ m_strAttrs += _T(" ");
+ m_strAttrs += strTmp;
+ }
+#pragma warning(pop)
+
+ BOOL Add(LPCTSTR szName, LPCTSTR szValue)
+ {
+ if (szValue)
+ m_strAttrs.AppendFormat(_T(" %s=\"%s\""), szName, szValue);
+ else
+ m_strAttrs.AppendFormat(_T(" %s"), szName);
+ return TRUE;
+ }
+
+#pragma warning(push)
+#pragma warning(disable : 4793)
+ void AddFormat(LPCTSTR szFormat, ...)
+ {
+ va_list args;
+
+ va_start(args, szFormat);
+
+ CString strTmp;
+ strTmp.FormatV(szFormat, args);
+ va_end(args);
+
+ m_strAttrs += _T(" ");
+ m_strAttrs += strTmp;
+ }
+#pragma warning(pop)
+
+ void Set(LPCTSTR szAttrs)
+ {
+ if (szAttrs)
+ {
+ m_strAttrs.Empty();
+#ifndef UNICODE
+ if (!isspace(static_cast<unsigned char>(szAttrs[0])))
+#else
+ if (!iswspace(szAttrs[0]))
+#endif
+ m_strAttrs = _T(" ");
+ m_strAttrs += szAttrs;
+ }
+ }
+
+ operator LPCTSTR()
+ {
+ return m_strAttrs;
+ }
+
+};
+
+class CStreamOnWriteStream : public IStream
+{
+public:
+ IWriteStream *m_pWriteStream;
+
+ CStreamOnWriteStream()
+ {
+ m_pWriteStream = NULL;
+ }
+
+ void Init(IWriteStream *pWriteStream)
+ {
+ m_pWriteStream = pWriteStream;
+ }
+
+ // IUnknown methods
+ STDMETHOD(QueryInterface)(REFIID riid, void **ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+
+ if (IsEqualGUID(riid, IID_IUnknown) ||
+ IsEqualGUID(riid, IID_IStream) ||
+ IsEqualGUID(riid, IID_ISequentialStream))
+ {
+ *ppv = (IStream *) this;
+ }
+ if (!*ppv)
+ return E_NOINTERFACE;
+ return S_OK;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+ // ISequentialStream methods
+ HRESULT STDMETHODCALLTYPE Read(void * /*pDest*/, ULONG /*dwMaxLen*/, ULONG * /*pdwRead*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten)
+ {
+ ATLASSUME(m_pWriteStream);
+ HRESULT hr = m_pWriteStream->WriteStream((const char *) pv, cb, pcbWritten);
+ return (hr==S_OK) ? S_OK : STG_E_WRITEFAULT;
+ }
+
+ // IStream methods
+ HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER /*dlibMove*/, DWORD /*dwOrigin*/, ULARGE_INTEGER * /*plibNewPosition*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER /*libNewSize*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE CopyTo(IStream * /*pstm*/, ULARGE_INTEGER /*cb*/, ULARGE_INTEGER * /*pcbRead*/, ULARGE_INTEGER * /*pcbWritten*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Commit(DWORD /*grfCommitFlags*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Revert(void)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE Stat(STATSTG * /*pstatstg*/, DWORD /*grfStatFlag*/)
+ {
+ return E_NOTIMPL;
+ }
+
+
+ HRESULT STDMETHODCALLTYPE Clone(IStream ** /*ppstm*/)
+ {
+ return E_NOTIMPL;
+ }
+};
+
+class CStreamFormatter
+{
+protected:
+ CStreamOnWriteStream m_sows;
+ IStream *m_pStream;
+ BOOL m_bAddCRLF;
+ BOOL m_bEmitUnicode;
+ UINT m_nConversionCodepage;
+
+public:
+ CStreamFormatter()
+ {
+ m_pStream = NULL;
+ m_bAddCRLF = TRUE;
+ m_bEmitUnicode = FALSE;
+ m_nConversionCodepage = _AtlGetConversionACP();
+ }
+
+ void Initialize(IStream *pStream, BOOL bAddCRLF=TRUE)
+ {
+ m_pStream = pStream;
+ m_bAddCRLF = bAddCRLF;
+ }
+
+ void Initialize(IWriteStream *pWriteStream, BOOL bAddCRLF=TRUE)
+ {
+ m_bAddCRLF = bAddCRLF;
+ m_sows.Init(pWriteStream);
+ m_pStream = &m_sows;
+ }
+
+ void EmitUnicode(BOOL bEmitUnicode)
+ {
+ m_bEmitUnicode = bEmitUnicode;
+ }
+
+ void SetConversionCodepage(UINT nConversionCodepage)
+ {
+ m_nConversionCodepage = nConversionCodepage;
+ }
+
+ void AddCRLF(bool bNewVal)
+ {
+ m_bAddCRLF = bNewVal;
+ }
+
+ HRESULT WriteRaw(LPCTSTR szString, int nCount=-1)
+ {
+ ATLENSURE_RETURN(szString != NULL);
+ if (!m_pStream)
+ return E_FAIL;
+
+ if (m_bEmitUnicode)
+ {
+#ifdef _UNICODE
+ LPCWSTR sz = szString;
+ if (nCount == -1)
+ nCount = (int) wcslen(szString);
+#else
+ CA2W sz(szString, m_nConversionCodepage);
+ nCount = (int) wcslen(sz);
+#endif
+ DWORD dwWritten;
+ return m_pStream->Write(sz, (DWORD) nCount*sizeof(WCHAR), &dwWritten);
+ }
+ else
+ {
+#ifdef _UNICODE
+ CW2A sz(szString, m_nConversionCodepage);
+ nCount = (int) strlen(sz);
+#else
+ LPCSTR sz = szString;
+ if (nCount == -1)
+ nCount = (int) strlen(szString);
+#endif
+ DWORD dwWritten;
+ return m_pStream->Write(sz, (DWORD) nCount, &dwWritten);
+ }
+ }
+
+ HRESULT StartTag(int nTagIndex, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ if (nTagIndex < 0 || nTagIndex >= ATL_HTML_TAG_LAST)
+ return E_INVALIDARG;
+ if (m_bAddCRLF && (s_tags[nTagIndex].uFlags & TAGF_BLOCK))
+ WriteRaw(_T("\r\n"));
+ HRESULT hr = StartTag(s_tags[nTagIndex].szTagName, szContent, szAttrs);
+ if (FAILED(hr))
+ return hr;
+ if (m_bAddCRLF && (s_tags[nTagIndex].uFlags & TAGF_BLOCK))
+ WriteRaw(_T("\r\n"));
+ return S_OK;
+ }
+
+ HRESULT StartTag(LPCTSTR szTag, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ HRESULT hr;
+ hr = WriteRaw(_T("<"));
+ if (FAILED(hr))
+ return hr;
+ hr = WriteRaw(szTag);
+ if (FAILED(hr))
+ return hr;
+ hr = WriteAttributes(szAttrs);
+ if (FAILED(hr))
+ return hr;
+ hr = WriteRaw(_T(">"));
+ if (FAILED(hr))
+ return hr;
+ if (szContent && *szContent)
+ {
+ WriteRaw(szContent);
+ EndTag(szTag);
+ }
+ return S_OK;
+ }
+
+ HRESULT EndTag(int nTagIndex)
+ {
+ if (nTagIndex < 0 || nTagIndex >= ATL_HTML_TAG_LAST)
+ return E_INVALIDARG;
+ if (m_bAddCRLF && (s_tags[nTagIndex].uFlags & TAGF_BLOCK))
+ WriteRaw(_T("\r\n"));
+ HRESULT hr = EndTag(s_tags[nTagIndex].szTagName);
+ if (FAILED(hr))
+ return hr;
+ if (m_bAddCRLF && (s_tags[nTagIndex].uFlags & TAGF_BLOCK))
+ WriteRaw(_T("\r\n"));
+ return S_OK;
+ }
+
+ HRESULT EndTag(LPCTSTR szTag)
+ {
+ HRESULT hr = WriteRaw(_T("</"));
+ if (FAILED(hr))
+ return hr;
+ hr = WriteRaw(szTag);
+ if (FAILED(hr))
+ return hr;
+ return WriteRaw(_T(">"));
+ }
+
+ HRESULT WriteAttributes(LPCTSTR szAttrs)
+ {
+ if (szAttrs && szAttrs[0])
+ {
+#ifndef UNICODE
+ if (!isspace(static_cast<unsigned char>(szAttrs[0])))
+#else
+ if (!iswspace(szAttrs[0]))
+#endif
+ WriteRaw(_T(" "));
+ return WriteRaw(szAttrs);
+ }
+
+ return S_OK;
+ }
+
+#pragma warning(push)
+#pragma warning(disable : 4793)
+ HRESULT WriteFormatted(LPCTSTR szFormat, ...)
+ {
+ ATLASSERT(szFormat != NULL);
+ if (!m_pStream)
+ return E_FAIL;
+
+ va_list args;
+ va_start(args, szFormat);
+
+
+ TCHAR buffFixed[1024];
+ CTempBuffer<TCHAR> buffHeap;
+ TCHAR *szTemp = buffFixed;
+ int nCount = _vstprintf_s((LPTSTR)szTemp, _countof(buffFixed), szFormat, args);
+ if (nCount < 0)
+ {
+ // we'll have to dynamically allocate the buffer
+ nCount = _vsctprintf(szFormat, args);
+ szTemp = NULL;
+ ATLTRY(szTemp = buffHeap.Allocate(nCount + 1));
+ if (!szTemp)
+ return E_OUTOFMEMORY;
+ nCount = _vstprintf_s(szTemp, nCount+1, szFormat, args);
+ }
+
+ va_end(args);
+
+ if (nCount > 0)
+ return WriteRaw(szTemp, (DWORD) nCount);
+ return E_UNEXPECTED;
+ }
+#pragma warning(pop)
+};
+
+template <typename TData, int nMax=64>
+class CSimpleStack
+{
+public:
+ int m_nTop;
+ TData m_Data[nMax];
+
+ CSimpleStack()
+ {
+ m_nTop = -1;
+ }
+
+ bool IsEmpty()
+ {
+ return (m_nTop == -1);
+ }
+
+ bool Push(const TData *pData)
+ {
+ if (m_nTop + 1 >= nMax)
+ return false;
+
+ m_nTop++;
+
+ m_Data[m_nTop] = *pData;
+ return true;
+ }
+
+ bool Pop(TData *pData)
+ {
+ if (m_nTop < 0)
+ return false;
+
+ *pData = m_Data[m_nTop];
+ m_nTop--;
+ return true;
+ }
+};
+
+
+struct HTML_SCHEME
+{
+ CString strBgColor;
+ CString strLinkColor;
+ CString strVLinkColor;
+ CString strALinkColor;
+ CString strBackground;
+ int nTopMargin;
+ int nLeftMargin;
+
+ CString strTdBgColor;
+ CString strTableBgColor;
+ CString strTrBgColor;
+
+ HTML_SCHEME()
+ {
+ nTopMargin = -1;
+ nLeftMargin = -1;
+ }
+};
+
+template <class T>
+class CHtmlGenBase : public CStreamFormatter
+{
+public:
+ T* GetOuter()
+ {
+ return static_cast<T*>(this);
+ }
+
+ enum ATL_HTML_FORM_METHOD { ATL_HTML_FORM_METHOD_NONE=0, ATL_HTML_FORM_METHOD_GET, ATL_HTML_FORM_METHOD_POST, ATL_HTML_FORM_METHOD_MULTIPART };
+
+ CHtmlGenBase()
+ {
+ m_nWidthPercent = -1;
+ m_nHeightPercent = -1;
+ m_nFormMethod = ATL_HTML_FORM_METHOD_NONE;
+ m_pScheme = NULL;
+ }
+
+ void SetScheme(HTML_SCHEME *pScheme)
+ {
+ m_pScheme = pScheme;
+ }
+
+ HRESULT body(LPCTSTR szBgColor=NULL, LPCTSTR szBackground=NULL, LPCTSTR szTopMargin=NULL, LPCTSTR szLeftMargin=NULL,
+ LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szBgColor && *szBgColor)
+ Attrs.Add(_T("bgColor"), szBgColor);
+ else if (m_pScheme && m_pScheme->strBgColor.GetLength())
+ Attrs.Add(_T("bgColor"), m_pScheme->strBgColor);
+
+ if (szBackground && *szBackground)
+ Attrs.Add(_T("background"), szBackground);
+ else if (m_pScheme && m_pScheme->strBackground.GetLength())
+ Attrs.Add(_T("background"), m_pScheme->strBackground);
+
+ if (m_pScheme && m_pScheme->strLinkColor.GetLength())
+ Attrs.Add(_T("link"), m_pScheme->strLinkColor);
+
+ if (m_pScheme && m_pScheme->strALinkColor.GetLength())
+ Attrs.Add(_T("alink"), m_pScheme->strALinkColor);
+
+ if (m_pScheme && m_pScheme->strVLinkColor.GetLength())
+ Attrs.Add(_T("vlink"), m_pScheme->strVLinkColor);
+
+ if (szTopMargin && *szTopMargin)
+ Attrs.Add(_T("topmargin"), szTopMargin);
+ else if (m_pScheme && m_pScheme->nTopMargin != -1)
+ Attrs.AddFormat(_T("topmargin=\"%d\""), m_pScheme->nTopMargin);
+
+ if (szLeftMargin && *szLeftMargin)
+ Attrs.Add(_T("leftmargin"), szLeftMargin);
+ else if (m_pScheme && m_pScheme->nLeftMargin != -1)
+ Attrs.AddFormat(_T("leftmargin=\"%d\""), m_pScheme->nLeftMargin);
+
+ return GetOuter()->StartTag(ATL_HTML_TAG_BODY, NULL, Attrs);
+ }
+
+ HRESULT bodyEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_BODY);
+ }
+
+ HRESULT a(LPCTSTR szHref, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (m_strState.GetLength()==0)
+ {
+ if (szHref && *szHref)
+ Attrs.Add(_T("href"), szHref);
+ return GetOuter()->StartTag(ATL_HTML_TAG_A, szContent, Attrs);
+ }
+
+ const TCHAR *szQuestion = NULL;
+ if(szHref)
+ szQuestion = _tcschr(szHref, '?');
+
+ CString strHref = szHref;
+ if (!szQuestion)
+ strHref.Append("?");
+ else
+ strHref.Append("&");
+
+ strHref += m_strState;
+
+ if (szHref && *szHref)
+ Attrs.Add(_T("href"), strHref);
+
+ return GetOuter()->StartTag(ATL_HTML_TAG_A, szContent, Attrs);
+ }
+
+ HRESULT aEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_A);
+ }
+
+ HRESULT b(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_B, szContent, szAttrs);
+ }
+
+ HRESULT bEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_B);
+ }
+
+ HRESULT i(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_I, szContent, szAttrs);
+ }
+
+ HRESULT iEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_I);
+ }
+
+ HRESULT u(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_U, szContent, szAttrs);
+ }
+
+ HRESULT uEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_U);
+ }
+
+ HRESULT font(LPCTSTR szFace, LPCTSTR szSize=NULL, LPCTSTR szColor=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+ if (szFace && *szFace)
+ Attrs.Add(_T("face"), szFace);
+ if (szSize && *szSize)
+ Attrs.Add(_T("size"), szSize);
+ if (szColor && *szColor)
+ Attrs.Add(_T("color"), szColor);
+ return GetOuter()->StartTag(ATL_HTML_TAG_FONT, NULL, Attrs);
+ }
+
+ HRESULT font(COLORREF clrColor, LPCTSTR szAttrs=NULL)
+ {
+ TCHAR szColor[8];
+ _stprintf_s(szColor, _countof(szColor), _T("#%02x%02x%02x"), GetRValue(clrColor),
+ GetGValue(clrColor), GetBValue(clrColor));
+ return GetOuter()->font(NULL, NULL, szColor, szAttrs);
+ }
+
+ HRESULT fontEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_FONT);
+ }
+
+ HRESULT img(LPCTSTR szSrc, LPCTSTR szAttrs=NULL)
+ {
+ ATLASSERT(szSrc && *szSrc);
+
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ Attrs.Add(_T("src"), szSrc);
+
+ return GetOuter()->StartTag(ATL_HTML_TAG_IMG, NULL, Attrs);
+ }
+
+ HRESULT br(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_BR, NULL, szAttrs);
+ }
+
+ HRESULT hr(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_HR, NULL, szAttrs);
+ }
+
+ HRESULT div(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_DIV, szContent, szAttrs);
+ }
+
+ HRESULT divEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_DIV);
+ }
+
+ HRESULT blockquote(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_BLOCKQUOTE, szContent, szAttrs);
+ }
+
+ HRESULT blockquoteEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_BLOCKQUOTE);
+ }
+
+ HRESULT address(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_ADDRESS, szContent, szAttrs);
+ }
+
+ HRESULT addressEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_ADDRESS);
+ }
+
+ HRESULT p(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_P, szContent, szAttrs);
+ }
+
+ HRESULT pEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_P);
+ }
+
+ HRESULT h(int nLevel=1, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ if (nLevel < 1 || nLevel > 6)
+ return E_INVALIDARG;
+ return GetOuter()->StartTag(ATL_HTML_TAG_H1+nLevel-1, szContent, szAttrs);
+ }
+
+ HRESULT hEnd(int nLevel=1)
+ {
+ if (nLevel < 1 || nLevel > 6)
+ return E_INVALIDARG;
+ return GetOuter()->EndTag(ATL_HTML_TAG_H1+nLevel-1);
+ }
+
+ HRESULT pre(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_PRE, szContent, szAttrs);
+ }
+
+ HRESULT preEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_PRE);
+ }
+
+ HRESULT q(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_Q, szContent, szAttrs);
+ }
+
+ HRESULT qEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_Q);
+ }
+
+ HRESULT sub(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_SUB, szContent, szAttrs);
+ }
+
+ HRESULT subEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_SUB);
+ }
+
+ HRESULT sup(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_SUP, szContent, szAttrs);
+ }
+
+ HRESULT supEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_SUP);
+ }
+
+ HRESULT ins(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_INS, szContent, szAttrs);
+ }
+
+ HRESULT insEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_INS);
+ }
+
+ HRESULT del(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_DEL, szContent, szAttrs);
+ }
+
+ HRESULT delEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_DEL);
+ }
+
+
+ HRESULT em(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_EM, szContent, szAttrs);
+ }
+
+ HRESULT emEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_EM);
+ }
+
+ HRESULT strong(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_STRONG, szContent, szAttrs);
+ }
+
+ HRESULT strongEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_STRONG);
+ }
+
+ HRESULT dfn(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_DFN, szContent, szAttrs);
+ }
+
+ HRESULT dfnEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_DFN);
+ }
+
+ HRESULT code(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_CODE, szContent, szAttrs);
+ }
+
+ HRESULT codeEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_CODE);
+ }
+
+ HRESULT samp(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_SAMP, szContent, szAttrs);
+ }
+
+ HRESULT sampEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_SAMP);
+ }
+
+ HRESULT kbd(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_KBD, szContent, szAttrs);
+ }
+
+ HRESULT kbdEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_KBD);
+ }
+
+ HRESULT var(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_VAR, szContent, szAttrs);
+ }
+
+ HRESULT varEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_VAR);
+ }
+
+ HRESULT cite(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_CITE, szContent, szAttrs);
+ }
+
+ HRESULT citeEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_CITE);
+ }
+
+ HRESULT abbr(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_ABBR, szContent, szAttrs);
+ }
+
+ HRESULT abbrEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_ABBR);
+ }
+
+ HRESULT acronym(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_ACRONYM, szContent, szAttrs);
+ }
+
+ HRESULT acronymEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_ACRONYM);
+ }
+
+
+ HRESULT ol(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_OL, NULL, szAttrs);
+ }
+
+ HRESULT ul(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_UL, NULL, szAttrs);
+ }
+
+ HRESULT olEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_OL);
+ }
+
+ HRESULT ulEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_UL);
+ }
+
+ HRESULT li(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_LI, szContent, szAttrs);
+ }
+
+ HRESULT liEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_LI);
+ }
+
+ HRESULT dl(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_DL, szContent, szAttrs);
+ }
+
+ HRESULT dlEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_DL);
+ }
+
+ HRESULT dt(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_DT, szContent, szAttrs);
+ }
+
+ HRESULT dtEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_DT);
+ }
+
+ HRESULT dd(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_DD, szContent, szAttrs);
+ }
+
+ HRESULT ddEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_DD);
+ }
+
+ void SetSizePercent(int nWidth, int nHeight)
+ {
+ m_nWidthPercent = nWidth;
+ m_nHeightPercent = nHeight;
+ }
+
+ HRESULT table(int nBorderWidth=0, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ m_RowStack.Push(&m_tableState);
+ m_tableState.Clear();
+
+ Attrs.AddFormat(_T("border=\"%d\""), nBorderWidth);
+
+ if (m_nWidthPercent != -1)
+ Attrs.AddFormat(_T("width=\"%d%%\""), m_nWidthPercent);
+ if (m_nHeightPercent != -1)
+ Attrs.AddFormat(_T("height=\"%d%%\""), m_nHeightPercent);
+
+ if (m_pScheme && m_pScheme->strTableBgColor.GetLength())
+ Attrs.Add(_T("bgcolor"), m_pScheme->strTableBgColor);
+
+ m_nWidthPercent = -1;
+ m_nHeightPercent = -1;
+ return GetOuter()->StartTag(ATL_HTML_TAG_TABLE, NULL, Attrs);
+ }
+
+ HRESULT tableEnd()
+ {
+ if (m_tableState.m_bRowOpen)
+ GetOuter()->trEnd();
+ m_RowStack.Pop(&m_tableState);
+ return GetOuter()->EndTag(ATL_HTML_TAG_TABLE);
+ }
+
+ HRESULT tr(LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (m_RowStack.IsEmpty())
+ GetOuter()->table();
+ if (m_tableState.m_bRowOpen)
+ GetOuter()->trEnd();
+ m_tableState.m_bRowOpen = true;
+
+ if (m_pScheme && m_pScheme->strTrBgColor.GetLength())
+ Attrs.Add(_T("bgcolor"), m_pScheme->strTrBgColor);
+ return GetOuter()->StartTag(ATL_HTML_TAG_TR, NULL, Attrs);
+ }
+
+ HRESULT td(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (!m_tableState.m_bRowOpen)
+ GetOuter()->tr();
+ m_tableState.m_bDataOpen = true;
+ if (m_pScheme && m_pScheme->strTdBgColor.GetLength())
+ Attrs.Add(_T("bgColor"), m_pScheme->strTdBgColor);
+
+ HRESULT hr = GetOuter()->StartTag(ATL_HTML_TAG_TD, szContent, Attrs);
+ if (FAILED(hr))
+ return hr;
+ if (szContent)
+ m_tableState.m_bDataOpen = false;
+ return S_OK;
+ }
+
+ HRESULT tdEnd()
+ {
+ if (!m_tableState.m_bDataOpen)
+ return S_OK;
+ m_tableState.m_bDataOpen = false;
+ return GetOuter()->EndTag(ATL_HTML_TAG_TD);
+ }
+
+ HRESULT trEnd()
+ {
+ if (!m_tableState.m_bRowOpen)
+ return S_OK;
+ if (m_tableState.m_bDataOpen)
+ GetOuter()->tdEnd();
+ m_tableState.m_bRowOpen = false;
+ return GetOuter()->EndTag(ATL_HTML_TAG_TR);
+ }
+
+ HRESULT form(LPCTSTR szAction, ATL_HTML_FORM_METHOD nMethod=ATL_HTML_FORM_METHOD_GET, LPCTSTR szAttrs=NULL)
+ {
+ static const LPCTSTR s_szFormMethods[] = { NULL, _T("get"), _T("post"), _T("multipart-www-url-encoded") };
+ return GetOuter()->form(szAction, s_szFormMethods[nMethod], szAttrs);
+ }
+
+ HRESULT form(LPCTSTR szAction, LPCTSTR szMethod, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szAction && *szAction)
+ Attrs.Add(_T("action"), szAction);
+ if (szMethod && *szMethod)
+ Attrs.Add(_T("method"), szMethod);
+
+ return GetOuter()->StartTag(ATL_HTML_TAG_FORM, NULL, Attrs);
+ }
+
+ HRESULT input(LPCTSTR szType, LPCTSTR szName, LPCTSTR szValue, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szType && *szType)
+ Attrs.Add(_T("type"), szType);
+ if (szName && *szName)
+ Attrs.Add(_T("name"), szName);
+ if (szValue && *szValue)
+ Attrs.Add(_T("value"), szValue);
+ return GetOuter()->StartTag(ATL_HTML_TAG_INPUT, NULL, Attrs);
+ }
+
+ HRESULT submit(LPCTSTR szValue=NULL, LPCTSTR szName=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return input(_T("submit"), szName, szValue, szAttrs);
+ }
+
+ HRESULT textarea(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TEXTAREA, szContent, szAttrs);
+ }
+
+ HRESULT textareaEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_TEXTAREA);
+ }
+
+ HRESULT formEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_FORM);
+ }
+
+
+ HRESULT select(LPCTSTR szName, BOOL bMultiple=FALSE, LPCTSTR szAttrs=NULL)
+ {
+ ATLASSERT(szName && *szName);
+
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ Attrs.Add(_T("name"), szName);
+ if (bMultiple)
+ Attrs.Add(_T("multiple"), NULL);
+ return GetOuter()->StartTag(ATL_HTML_TAG_SELECT, NULL, Attrs);
+ }
+
+ HRESULT option(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_OPTION, szContent, szAttrs);
+ }
+
+ HRESULT optionEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_OPTION);
+ }
+
+ HRESULT selectEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_SELECT);
+ }
+
+
+ HRESULT head(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_HEAD, NULL, szAttrs);
+ }
+
+ HRESULT headEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_HEAD);
+ }
+
+ HRESULT html(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_HTML, NULL, szAttrs);
+ }
+
+ HRESULT htmlEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_HTML);
+ }
+
+ HRESULT map(LPCTSTR szName, LPCTSTR szAttrs=NULL)
+ {
+ ATLASSERT(szName && *szName);
+
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ Attrs.Add(_T("name"), szName);
+ return GetOuter()->StartTag(ATL_HTML_TAG_MAP, NULL, Attrs);
+ }
+
+ HRESULT mapEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_MAP);
+ }
+
+ HRESULT area(LPCTSTR szAlt, LPCTSTR szHref=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szAlt && *szAlt)
+ Attrs.Add(_T("alt"), szAlt);
+ if (szHref && *szHref)
+ Attrs.Add(_T("href"), szHref);
+ return GetOuter()->StartTag(ATL_HTML_TAG_AREA, NULL, Attrs);
+ }
+
+ HRESULT base(LPCTSTR szHref, LPCTSTR szAttrs=NULL)
+ {
+ ATLASSERT(szHref && *szHref);
+
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ Attrs.Add(_T("href"), szHref);
+ return GetOuter()->StartTag(ATL_HTML_TAG_BASE, NULL, Attrs);
+ }
+
+ HRESULT bdo(LPCTSTR szDir, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ ATLASSERT(szDir&& *szDir);
+
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ Attrs.Add(_T("dir"), szDir);
+ return GetOuter()->StartTag(ATL_HTML_TAG_BDO, szContent, Attrs);
+ }
+
+ HRESULT bdoEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_BDO);
+ }
+
+ HRESULT big(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_BIG, szContent, szAttrs);
+ }
+
+ HRESULT bigEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_BIG);
+ }
+
+ HRESULT button(LPCTSTR szName=NULL, LPCTSTR szValue=NULL, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szName && *szName)
+ Attrs.Add(_T("name"), szName);
+ if (szValue && *szValue)
+ Attrs.Add(_T("value"), szValue);
+ return GetOuter()->StartTag(ATL_HTML_TAG_BUTTON, szContent, Attrs);
+ }
+
+ HRESULT buttonEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_BUTTON);
+ }
+
+ HRESULT iframe(LPCTSTR szSrc=NULL, LPCTSTR szWidth=NULL, LPCTSTR szHeight=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szSrc && *szSrc)
+ Attrs.Add(_T("src"), szSrc);
+ if (szWidth && *szWidth)
+ Attrs.Add(_T("width"), szWidth);
+ if (szHeight && *szHeight)
+ Attrs.Add(_T("height"), szHeight);
+ return GetOuter()->StartTag(ATL_HTML_TAG_IFRAME, NULL, Attrs);
+ }
+
+ HRESULT iframeEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_IFRAME);
+ }
+
+ HRESULT label(LPCTSTR szFor=NULL, LPCTSTR szAccessKey=NULL, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szFor && *szFor)
+ Attrs.Add(_T("for"), szFor);
+ if (szAccessKey && *szAccessKey)
+ Attrs.Add(_T("accesskey"), szAccessKey);
+ return GetOuter()->StartTag(ATL_HTML_TAG_LABEL, szContent, Attrs);
+ }
+
+ HRESULT labelEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_LABEL);
+ }
+
+ HRESULT link(LPCTSTR szRel=NULL, LPCTSTR szHref=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szRel && *szRel)
+ Attrs.Add(_T("rel"), szRel);
+ if (szHref && *szHref)
+ Attrs.Add(_T("href"), szHref);
+ return GetOuter()->StartTag(ATL_HTML_TAG_LINK, NULL, Attrs);
+ }
+
+ HRESULT meta(LPCTSTR szName=NULL, LPCTSTR szContent=NULL, LPCTSTR szHttpEquiv=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (szName && *szName)
+ Attrs.Add(_T("name"), szName);
+ if (szContent && *szContent)
+ Attrs.Add(_T("content"), szContent);
+ if (szHttpEquiv && *szHttpEquiv)
+ Attrs.Add(_T("http-equiv"), szHttpEquiv);
+ return GetOuter()->StartTag(ATL_HTML_TAG_META, NULL, Attrs);
+ }
+
+ HRESULT noframes(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_NOFRAMES, szContent, szAttrs);
+ }
+
+ HRESULT noframesEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_NOFRAMES);
+ }
+
+ HRESULT noscript(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_NOSCRIPT, szContent, szAttrs);
+ }
+
+ HRESULT noscriptEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_NOSCRIPT);
+ }
+
+ HRESULT col(int nSpan=1, LPCTSTR szWidth=NULL, LPCTSTR szHeight=NULL, LPCTSTR szVAlign=NULL,
+ LPCTSTR szHAlign=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+
+ if (nSpan != 1)
+ Attrs.AddFormat(_T("span"), _T("\"%d\""), nSpan);
+ if (szWidth && *szWidth)
+ Attrs.Add(_T("width"), szWidth);
+ if (szHeight && *szHeight)
+ Attrs.Add(_T("height"), szHeight);
+ if (szVAlign && *szVAlign)
+ Attrs.Add(_T("valign"), szVAlign);
+ if (szHAlign && *szHAlign)
+ Attrs.Add(_T("align"), szHAlign);
+ return GetOuter()->StartTag(ATL_HTML_TAG_COL, NULL, Attrs);
+ }
+
+ HRESULT colgroup(int nSpan=1, LPCTSTR szWidth=NULL, LPCTSTR szHeight=NULL, LPCTSTR szVAlign=NULL,
+ LPCTSTR szHAlign=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ if (nSpan != 1)
+ Attrs.AddFormat(_T("span"), _T("\"%d\""), nSpan);
+ if (szWidth && *szWidth)
+ Attrs.Add(_T("width"), szWidth);
+ if (szHeight && *szHeight)
+ Attrs.Add(_T("height"), szHeight);
+ if (szVAlign && *szVAlign)
+ Attrs.Add(_T("valign"), szVAlign);
+ if (szHAlign && *szHAlign)
+ Attrs.Add(_T("align"), szHAlign);
+ return GetOuter()->StartTag(ATL_HTML_TAG_COL, NULL, Attrs);
+ }
+
+ HRESULT colgroupEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_COLGROUP);
+ }
+
+ HRESULT fieldset(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_FIELDSET, NULL, szAttrs);
+ }
+
+ HRESULT fieldsetEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_FIELDSET);
+ }
+
+ HRESULT legend(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_LEGEND, szContent, szAttrs);
+ }
+
+ HRESULT legendEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_LEGEND);
+ }
+
+ HRESULT tbody(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TBODY, NULL, szAttrs);
+ }
+
+ HRESULT tbodyEnd()
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TBODY);
+ }
+
+ HRESULT tfoot(LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TFOOT, NULL, szAttrs);
+ }
+
+ HRESULT tfootEnd()
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TFOOT);
+ }
+
+ HRESULT th(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ if (!m_tableState.m_bRowOpen)
+ GetOuter()->tr();
+ m_tableState.m_bDataOpen = true;
+ return GetOuter()->StartTag(ATL_HTML_TAG_TH, szContent, szAttrs);
+ }
+
+ HRESULT thEnd()
+ {
+ ATLASSUME(m_tableState.m_bDataOpen);
+ m_tableState.m_bDataOpen = false;
+ return GetOuter()->EndTag(ATL_HTML_TAG_TH);
+ }
+
+ HRESULT title(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TITLE, szContent, szAttrs);
+ }
+
+ HRESULT titleEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_TITLE);
+ }
+
+ HRESULT tt(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_TT, szContent, szAttrs);
+ }
+
+ HRESULT ttEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_TT);
+ }
+
+ // unfortunately, we can't use small since it is defined as char
+ // in rpcndr.h!
+ HRESULT _small(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_SMALL, szContent, szAttrs);
+ }
+
+ HRESULT _smallEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_SMALL);
+ }
+
+ HRESULT span(LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ return GetOuter()->StartTag(ATL_HTML_TAG_SPAN, szContent, szAttrs);
+ }
+
+ HRESULT spanEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_SPAN);
+ }
+
+ HRESULT object(LPCTSTR szClassId, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+ if (szClassId && *szClassId)
+ Attrs.Add(_T("classid"), szClassId);
+ return GetOuter()->StartTag(ATL_HTML_TAG_OBJECT, szContent, Attrs);
+ }
+
+ HRESULT object(REFCLSID rclsid, LPCTSTR szContent=NULL, LPCTSTR szAttrs=NULL)
+ {
+ USES_CONVERSION_EX;
+ OLECHAR szClsid[64];
+ CString strClassId;
+ int i = StringFromGUID2(rclsid, szClsid, 64);
+ if (!i)
+ return E_FAIL;
+ szClsid[i-2] = 0; // don't want curly braces
+ strClassId.Format(_T("clsid:%s"), OLE2T_EX_DEF(szClsid+1));
+ return object(strClassId, szContent, szAttrs);
+ }
+
+ HRESULT objectEnd()
+ {
+ return GetOuter()->EndTag(ATL_HTML_TAG_OBJECT);
+ }
+
+ HRESULT param(LPCTSTR szName, LPCTSTR szValue, LPCTSTR szAttrs=NULL)
+ {
+ ATLASSERT(szName && *szName);
+
+ AtlHtmlAttrs Attrs;
+ Attrs.Set(szAttrs);
+
+ Attrs.Add(_T("name"), szName);
+ if (szValue && *szValue)
+ Attrs.Add(_T("value"), szValue);
+ return GetOuter()->StartTag(ATL_HTML_TAG_PARAM, NULL, Attrs);
+ }
+
+private:
+ CString m_strState;
+ HTML_SCHEME *m_pScheme;
+
+ struct TableState
+ {
+ TableState() : m_bRowOpen(false), m_bDataOpen(false)
+ {
+
+ }
+
+ void Clear()
+ {
+ m_bRowOpen = false;
+ m_bDataOpen = false;
+ }
+
+ bool m_bRowOpen;
+ bool m_bDataOpen;
+ };
+
+ ATL_HTML_FORM_METHOD m_nFormMethod;
+
+ TableState m_tableState;
+ CSimpleStack<TableState> m_RowStack;
+
+ int m_nWidthPercent;
+ int m_nHeightPercent;
+};
+
+class CHtmlGen : public CHtmlGenBase<CHtmlGen>
+{
+public:
+};
+
+} // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLHTML_H__
diff --git a/include/atl/atlhttp.h b/include/atl/atlhttp.h
new file mode 100644
index 000000000..e8a4321e6
--- /dev/null
+++ b/include/atl/atlhttp.h
@@ -0,0 +1,725 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLHTTP_H__
+#define __ATLHTTP_H__
+
+#pragma once
+#ifndef __CPPUNWIND
+#pragma warning(push)
+#pragma warning(disable: 4702)
+#endif
+#ifndef _WINSOCKAPI_
+ #include <winsock2.h>
+#endif
+#include <atlutil.h>
+#include <atlisapi.h>
+#include <atlcoll.h>
+#include <atlfile.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <atlenc.h>
+#ifndef _ATL_NO_DEFAULT_LIBS
+#pragma comment(lib, "ws2_32.lib")
+#pragma comment(lib, "SECUR32.LIB")
+#endif // !_ATL_NO_DEFAULT_LIBS
+
+#include <atlspriv.h>
+
+#pragma warning(push)
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+template <class TSocketClass>
+class CAtlHttpClientT;
+class CAtlBaseAuthObject;
+
+enum status_headerparse{
+ ATL_HEADER_PARSE_COMPLETE=0,
+ ATL_HEADER_PARSE_HEADERNOTCOMPLETE,
+ ATL_HEADER_PARSE_HEADERERROR
+};
+
+enum readstate{rs_init=0, rs_readheader, rs_scanheader, rs_readbody, rs_complete};
+
+#define ATL_HEADER_END "\r\n\r\n"
+#define ATL_HEADER_END_LEN 4
+#define ATL_DW_HEADER_END 0x0a0d0a0d
+#define ATL_FIELDNAME_DELIMITER _T(':')
+#define ATL_MAX_FIELDNAME_LEN 1024
+#define ATL_MAX_VALUE_LEN 1024
+#define ATL_AUTH_HDR_SIZE 1024
+#define ATL_READ_BUFF_SIZE 2048
+#define ATL_INVALID_STATUS -1
+#define ATL_HTTP_HEADER _T(" HTTP/1.1\r\n")
+#define ATL_HTTP_HEADER_PROXY _T(" HTTP/1.1\r\n")
+#ifndef ATL_HTTP_USERAGENT
+ #define ATL_HTTP_USERAGENT _T("User-Agent: Microsoft-ATL-Native/") _T(_ATL_VER_RBLD) _T("\r\n")
+#endif
+
+#define ATL_IS_INVALIDCREDHANDLE(x) ((x.dwLower==0xFFFFFFFF) && (x.dwUpper==0xFFFFFFFF))
+#define ATL_HTTP_AUTHTYPE_NTLM _T("NTLM")
+#define ATL_HTTP_AUTHTYPE_BASIC _T("BASIC")
+#define ATL_HTTP_METHOD_GET _T("GET")
+#define ATL_HTTP_METHOD_POST _T("POST")
+
+#ifndef MAX_REALM_LEN
+ #define MAX_REALM_LEN 1024
+#endif
+
+#ifndef _ATL_MAX_AUTH_BUFF
+ #define _ATL_MAX_AUTH_BUFF 512
+#endif
+
+__interface IAuthInfo;
+typedef bool (WINAPI *PFNATLCHUNKEDCB)(BYTE** ppData, DWORD *pdwSize, DWORD_PTR dwParam);
+typedef bool (WINAPI *PFNATLSTATUSCALLBACK)(DWORD dwBytesSent, DWORD_PTR dwParam);
+
+#define ATL_HTTP_FLAG_AUTO_REDIRECT 0x1
+#define ATL_HTTP_FLAG_PROCESS_RESULT 0x2
+#define ATL_HTTP_FLAG_SEND_CALLBACK 0x4
+#define ATL_HTTP_FLAG_SEND_BLOCKS 0x8
+#define ATL_HTTP_FLAG_INVALID_FLAGS 0xFFFFFFFF
+
+#ifndef ATL_HTTP_DEFAULT_BLOCK_SIZE
+ #define ATL_HTTP_DEFAULT_BLOCK_SIZE 4096
+#endif
+
+#define ATL_HTTP_CLIENT_EMPTY_READ_RETRIES 5
+
+struct ATL_NAVIGATE_DATA
+{
+ LPCTSTR szExtraHeaders;
+ LPCTSTR szMethod;
+ LPCTSTR szDataType;
+ DWORD dwDataLen;
+ DWORD dwFlags;
+ DWORD dwTimeout;
+ DWORD dwSendBlockSize;
+ DWORD dwReadBlockSize;
+ DWORD_PTR m_lParamSend;
+ DWORD_PTR m_lParamRead;
+ DWORD_PTR m_lParamChunkCB;
+ short nPort;
+ BYTE *pData;
+ PFNATLCHUNKEDCB pfnChunkCallback;
+ PFNATLSTATUSCALLBACK pfnSendStatusCallback;
+ PFNATLSTATUSCALLBACK pfnReadStatusCallback;
+};
+
+class CAtlNavigateData : public ATL_NAVIGATE_DATA
+{
+public:
+ CAtlNavigateData() throw(); // public construction
+ CAtlNavigateData(const CAtlNavigateData &rhs);
+ CAtlNavigateData(const ATL_NAVIGATE_DATA &rhs);
+ CAtlNavigateData& operator=(const CAtlNavigateData &rhs);
+ CAtlNavigateData& operator=(const ATL_NAVIGATE_DATA &rhs);
+ DWORD SetFlags(DWORD dwNewFlags) throw(); // set all flags
+ DWORD GetFlags() throw(); // get value of flags
+ DWORD AddFlags(DWORD dwFlagsToAdd) throw(); // add one or more flags to existing flags
+ DWORD RemoveFlags(DWORD dwFlagsToRemove) throw(); // remove one or more flags from existing flags
+ LPCTSTR SetExtraHeaders(LPCTSTR szNewHeaders) throw(); // set the extra request headers
+ LPCTSTR GetExtraHeaders() throw(); // get the extra request headers
+ LPCTSTR SetMethod(LPCTSTR szNewMethod) throw(); // set the HTTP request method
+ LPCTSTR GetMethod() throw(); // get the HTTP request method
+ short SetPort(short newPort) throw(); // set the TCP port for this request
+ short GetPort() throw(); // get the TCP port for this request
+ void SetPostData(BYTE *pData, DWORD dwDataLen, LPCTSTR szDataType) throw(); // Set data to be sent as the reqeust entity body
+ DWORD SetSocketTimeout(DWORD dwNewTimeout) throw(); // Set the timeout for this socket
+ DWORD GetSocketTimeout() throw(); // Get the timeout for this socket
+ DWORD SetSendBlockSize(DWORD dwBlockSize) throw(); // Set the size of the blocks used to send data
+ DWORD GetSendBlockSize() throw(); // get the size of the blocks used to send data
+ DWORD SetReadBlockSize(DWORD dwBlockSize) throw(); // Set the size of the blocks used to send data
+ DWORD GetReadBlockSize() throw(); // get the size of the blocks used to send data
+ PFNATLCHUNKEDCB SetChunkCallback(PFNATLCHUNKEDCB pfn, DWORD_PTR dwParam) throw(); // set the callback function used for sending chunked data
+ PFNATLCHUNKEDCB GetChunkCallback() throw(); // get the chunked callback function
+ PFNATLSTATUSCALLBACK SetSendStatusCallback(PFNATLSTATUSCALLBACK pfn, DWORD_PTR dwData) throw(); // sets a function pointer to be called after bytes are sent over the socket
+ PFNATLSTATUSCALLBACK GetSendStatusCallback() throw(); // returns current status callback function
+ PFNATLSTATUSCALLBACK SetReadStatusCallback(PFNATLSTATUSCALLBACK pfn, DWORD_PTR dwData) throw();
+ PFNATLSTATUSCALLBACK GetReadStatusCallback() throw();
+};
+
+template <class TSocketClass>
+class CAtlHttpClientT :
+ private TSocketClass
+{
+public:
+ CAtlHttpClientT() throw();
+ virtual ~CAtlHttpClientT()
+ {
+ }
+
+ // Use these functions to send an HTTP request and retrieve
+ // the response.
+ bool Navigate(
+ const CUrl* pUrl,
+ ATL_NAVIGATE_DATA *pNavData = NULL
+ ) throw(...);
+
+ bool Navigate(
+ LPCTSTR szServer,
+ LPCTSTR szPath,
+ ATL_NAVIGATE_DATA *pNavData = NULL
+ ) throw(...);
+
+ bool Navigate(
+ LPCTSTR szURL,
+ ATL_NAVIGATE_DATA *pNavData = NULL
+ ) throw(...);
+
+
+ // Performs navigation, sending data with Transfer-Coding: chunked
+ bool NavigateChunked(
+ const CUrl *pUrl,
+ ATL_NAVIGATE_DATA *pData
+ ) throw();
+
+ bool NavigateChunked(
+ LPCTSTR szServer,
+ LPCTSTR szPath,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw();
+
+ bool NavigateChunked(
+ LPCTSTR szURL,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw();
+
+ // Use to set/retrieve information about the proxy server used
+ // when making this request via a proxy server.
+ bool SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 0) throw();
+ void RemoveProxy() throw();
+ LPCTSTR GetProxy() const throw();
+ short GetProxyPort() const throw();
+
+ // Use these functions to add/remove/find objects that will
+ // be used to authorize request when a 401 Not Authorized response
+ // is received. This class maps these objects by scheme name in map.
+ // Override NegotiateAuth to change the way authorization negotiation occurs.
+ bool AddAuthObj(LPCTSTR szScheme, CAtlBaseAuthObject *pObject, IAuthInfo *pInfo=NULL) throw();
+ const CAtlBaseAuthObject* FindAuthObject(LPCTSTR szScheme) throw();
+ bool RemoveAuthObject(LPCTSTR szScheme) throw();
+ virtual bool NegotiateAuth(bool bProxy) throw();
+
+
+ // Retrieve the value of a response header
+ bool GetHeaderValue(LPCTSTR szName, CString& strValue) const throw();
+ bool GetHeaderValue(__in_z LPCTSTR szName, __out_ecount_part_z_opt(*pdwLen, *pdwLen) LPTSTR szBuffer, __inout DWORD *pdwLen) const throw();
+
+ DWORD GetResponseLength() throw(); // Get the number of bytes in the response
+ const BYTE* GetResponse() throw(); // Get the entire response
+ DWORD GetBodyLength() const throw(); // Get the length of the body of the response (everything after the \r\n\r\n)
+ const BYTE* GetBody() throw(); // Get the body of the response (length is determined by GetBodyLength())
+ DWORD GetRawResponseHeaderLength() throw(); // Get the length of the raw request headers
+ bool GetRawResponseHeader(LPBYTE szBuffer, DWORD *pdwLen) throw(); // Get the raw request headers
+ LPCURL GetCurrentUrl() const throw(); // Get a pointer to the current URL for this request
+ DWORD GetFlags() const throw(); // Retrieve flags used for processing this request
+ int GetStatus() throw(); // Get the HTTP status code that resulted from making this request
+ LPCTSTR GetMethod() throw(); // Get the HTTP method used for making this request
+ BYTE* GetPostData() throw(); // Get a pointer to raw data being sent with this request
+ DWORD GetPostDataLen() throw(); // Get the length of the raw data sent with this request
+ LPCTSTR GetPostDataType() throw(); // Get the data type (sent as Content-Type header) for this request
+ DWORD GetLastError() throw(); // Retrieves errors from the underlying socket
+ const SOCKET& GetSocket() throw(); // Retrieves the underlying socket. Be careful!
+ void Close() throw(); // Close the connection
+ DWORD SetSocketTimeout(DWORD dwNewTimeout) throw(); // Sets a new socket timeout, returns the old timeout.
+ DWORD GetSocketTimeout() throw(); // retrieves the current socket timeout
+ void AuthProtocolFailed(LPCTSTR szProto) throw(); // notifies us of failure to connect with the named protocol
+ const ATL_NAVIGATE_DATA* GetCurrentNavdata();
+ enum HTTP_RESPONSE_READ_STATUS
+ {
+ RR_OK = 0, // response was successfully processed
+ RR_FAIL, // an unknown error occurred reading the HTTP response
+ RR_STATUS_INVALID, // could not parse the status line
+ RR_PARSEHEADERS_FAILED, // failed to parse HTTP response headers
+ RR_READSOCKET_FAILED, // failed to read response data from socket
+ RR_READBODY_FAILED, // failed to successfully read the entity body of the HTTP response
+ RR_READCHUNKEDBODY_FAILED, // failed to read a 'Transfer-Encoding: chunked' response body
+ RR_NOT_READ // we haven't started reading the response.
+ };
+ HTTP_RESPONSE_READ_STATUS GetResponseStatus();
+
+
+// Implementation
+ HTTP_RESPONSE_READ_STATUS ReadHttpResponse() throw();
+ void ResetConnection() throw();
+ bool ProcessStatus(DWORD dwFlags) throw();
+ bool BuildRequest(/*out*/CString *pstrRequest,
+ LPCTSTR szDataType=NULL,
+ LPCTSTR szExtraHeaders=NULL) throw();
+
+ void SetSilentLogonOk(bool bSet)
+ {
+ m_bSilentLogonOk = bSet;
+ }
+protected:
+ DWORD WriteWithNoData(LPCSTR pRequest, DWORD dwRequestLen);
+ DWORD WriteWithCallback(LPCSTR pRequest, DWORD dwRequestLen);
+ DWORD WriteWithChunks(LPCSTR pRequest, DWORD dwRequestLen);
+ DWORD WriteWithData(LPCSTR pRequest, DWORD dwRequestLen);
+ bool SetDefaultUrl(LPCTSTR szUrl, short nPortNumber=ATL_URL_DEFAULT_HTTP_PORT) throw();
+ bool SetDefaultUrl(LPCURL pUrl, short nPortNumber=ATL_URL_DEFAULT_HTTP_PORT) throw();
+ bool SetDefaultMethod(LPCTSTR szMethod) throw();
+ void InitializeObject() throw();
+ void ResetRequest() throw();
+ bool ReadSocket() throw();
+ unsigned char* FindHeaderEnd(unsigned char** ppBegin) throw();
+ bool LookupRegProxy() throw();
+ bool DisconnectIfRequired() throw();
+ bool ConnectSocket() throw();
+
+ long GetContentLength() throw();
+ LPCSTR NextLine(BYTE* pCurr) throw();
+ bool IsMsgBodyChunked() throw();
+ LPCSTR FindEndOfHeader(LPCSTR pszStart) throw();
+ bool DecodeHeader(LPCSTR pHeaderStart, LPCSTR pHeaderEnd) throw();
+ virtual void OnSetCookie(LPCTSTR /*szCookie*/) throw();
+ LPCSTR ParseStatusLine(BYTE* pBuffer) throw();
+ int CrackResponseHeader(LPCSTR pBuffer, /*out*/ LPCSTR *pEnd) throw();
+ bool ReadBody(int nContentLen, int nCurrentBodyLen) throw();
+ bool ReadChunkedBody() throw();
+ bool ReconnectIfRequired() throw();
+ bool CompleteURL(CString& strURL) throw();
+ bool ProcessObjectMoved() throw();
+ bool _SetDefaultUrl(LPCTSTR szURL, short nPort) throw();
+
+ enum CHUNK_STATE{
+ READ_CHUNK_SIZE, // need to read the size of a chunk.
+ READ_CHUNK_SIZE_FOOTER,
+ READ_CHUNK_DATA, // need to read the actual data
+ READ_CHUNK_DATA_FOOTER, // need to read the chunk footer.
+ READ_CHUNK_TRAILER, // Read the trailer headers at the end of the chunk data
+ READ_CHUNK_TRAILER_FOOTER, // read the final crlf
+ CHUNK_READ_DATA_COMPLETE, // done reading chunk data.
+ };
+
+ enum CHUNK_LEX_RESULT{
+ LEX_OK,
+ LEX_OUTOFDATA,
+ LEX_ERROR,
+ LEX_TRAILER_COMPLETE
+ };
+
+ CHUNK_LEX_RESULT get_chunked_size(__deref_inout char *&pBuffStart, __deref_inout char *&pBuffEnd, __inout long* pnChunkSize) throw();
+ bool move_leftover_bytes(__in_ecount(nLen) char *pBufferStart, __in int nLen, __deref_inout char *&pBuffStart, __deref_inout char *&pBuffEnd) throw();
+ CHUNK_LEX_RESULT get_chunked_data(__deref_inout char *&pBufferStart, __deref_inout char *&pBufferEnd, long nChunkSize,
+ __deref_out_ecount_part(*pnDataLen, *pnDataLen) char **ppDataStart, __inout long *pnDataLen) throw();
+ CHUNK_LEX_RESULT consume_chunk_trailer(__deref_inout char *&pBufferStart, __deref_inout char *pBufferEnd) throw();
+ CHUNK_LEX_RESULT consume_chunk_footer(__deref_inout char *&pBufferStart, __deref_inout char *&pBufferEnd) throw();
+
+ typedef CAtlMap<
+ CString,
+ CString,
+ CStringElementTraitsI<CString>,
+ CStringElementTraitsI<CString>
+ > HeaderMapType;
+
+ typedef CAtlMap <
+ CString,
+ CAtlBaseAuthObject*,
+ CStringElementTraitsI<CString>
+ > AuthMapType;
+
+ typedef CAtlArray<
+ CString,
+ CStringElementTraitsI<CString>
+ > AuthListType;
+
+ HeaderMapType m_HeaderMap; // Map of response headers
+ AuthMapType m_AuthMap; // Map of pointers to authorization objects.
+ AuthListType m_AuthTypes; // list of authorization types the server is willing to use.
+ BOOL m_bSilentLogonOk;
+ CAtlIsapiBuffer<> m_current; // The entire response
+ CUrl m_urlCurrent; // URL of current request
+
+ CString m_strMethod; // Current request method.
+ CString m_strProxy; // Path to current proxy server.
+
+ long m_nStatus; // Current response status (from status line)
+ short m_nProxyPort; // Port used on current proxy server
+ DWORD m_dwBodyLen; // Length of body
+ DWORD m_dwHeaderLen; // Length of current raw headers
+ DWORD m_dwHeaderStart;
+ BYTE *m_pCurrent;
+ BYTE *m_pEnd; // the end of the data we've read fromt he socket;
+ ATL_NAVIGATE_DATA *m_pNavData;
+ HTTP_RESPONSE_READ_STATUS m_LastResponseParseError;
+}; //CAtlHttpClientT
+typedef CAtlHttpClientT<ZEvtSyncSocket> CAtlHttpClient;
+
+
+// Interface used to acquire authentication information from clients
+__interface IAuthInfo
+{
+ HRESULT GetPassword(__out_ecount_part_z_opt(*pdwBuffSize, *pdwBuffSize) LPTSTR szPwd, __inout DWORD *pdwBuffSize);
+ HRESULT GetUsername(__out_ecount_part_z_opt(*pdwBuffSize, *pdwBuffSize) LPTSTR szUid, __inout DWORD *pdwBuffSize);
+ HRESULT GetDomain(__out_ecount_part_z_opt(*pdwBuffSize, *pdwBuffSize) LPTSTR szDomain, __inout DWORD *pdwBuffSize);
+};
+typedef HRESULT (IAuthInfo::*PFNAUTHFUNC)(LPTSTR szPwd, DWORD *pdwSize);
+
+// pure virtual class that describes required functions for authoriztion
+// objects
+class CAtlBaseAuthObject
+{
+public:
+ CAtlBaseAuthObject();
+ virtual bool Authenticate(LPCTSTR szAuthTypes, bool bProxy) = 0;
+ virtual void Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo) = 0;
+ bool m_bFailed;
+};
+
+// strings used for authentication.
+extern __declspec(selectany)const TCHAR * const g_pszWWWAuthenticate = _T("www-authenticate");
+extern __declspec(selectany)const TCHAR * const g_pszProxyAuthenticate = _T("proxy-authenticate");
+
+// Performs NTLM authentication
+class CNTLMAuthObject :
+ public CAtlBaseAuthObject
+{
+public:
+ virtual ~CNTLMAuthObject() throw();
+ CNTLMAuthObject() throw();
+ CNTLMAuthObject(IAuthInfo *pAuthInfo) throw();
+ void SetAuthInfo(IAuthInfo *pAuthInfo) throw();
+ bool GetCredentialNames(CString& theName);
+
+// Implementation
+ // Called by the CAtlHttpClient class to authenticate a user.
+ virtual void Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo=NULL) throw();
+
+ // Called by the CAtlHttpClient class to initialize this authentication object.
+ virtual bool Authenticate(LPCTSTR szAuthTypes, bool bProxy) throw();
+protected:
+ bool AcquireCredHandle() throw();
+ // This function creates an NTML Authorization header
+ // and sends it to the HTTP server.
+ bool SendSecurityInfo(SecBuffer *pSecBuffer, LPSTR *pszBuffer) throw();
+ bool DoNTLMAuthenticate() throw();
+
+ IAuthInfo *m_pAuthInfo;
+ CAtlHttpClient *m_pSocket;
+ CredHandle m_hCredentials;
+ int m_nMaxTokenSize;
+ TimeStamp m_ts;
+ bool m_bProxy;
+ static const char * const m_pszFmtWWW;
+ static const char * const m_pszFmtProxy;
+ CAtlNavigateData m_CurrentRequestData;
+
+}; // CNTLMAuthObject
+
+// Performs BASIC authentication for an CAtlHttpClient
+// object. Caller must implement an IAuthInfo interface
+// and pass it to this object before this object attempts
+// to authenticate or authentication will fail.
+class CBasicAuthObject :
+ public CAtlBaseAuthObject
+{
+public:
+ CBasicAuthObject() throw();
+ CBasicAuthObject(IAuthInfo *pAuthInfo) throw();
+ void SetAuthInfo(IAuthInfo *pAuthInfo) throw();
+ LPCTSTR GetRealm() throw(); // Retrieve's the realm being used.
+
+// Implementation
+ // Called by the CAtlHttpClient class to authenticate a user.
+ virtual bool Authenticate(LPCTSTR szAuthTypes, bool bProxy) throw();
+
+ // Called by the CAtlHttpClient class to initialize this authentication object.
+ virtual void Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo=NULL) throw();
+protected:
+ bool DoBasicAuthenticate() throw();
+ bool CrackRealm(LPCTSTR szHeader) throw();
+
+ IAuthInfo *m_pAuthInfo;
+ CAtlHttpClient *m_pClient;
+ TCHAR m_szRealm[MAX_REALM_LEN];
+ bool m_bProxy;
+ static const char * const m_pszFmtWWW;
+ static const char * const m_pszFmtProxy;
+}; // CBasicAuthObject
+
+__declspec(selectany)const char * const CBasicAuthObject::m_pszFmtWWW = "Authorization: Basic ";
+__declspec(selectany)const char * const CBasicAuthObject::m_pszFmtProxy = "Proxy-Authorization: Basic ";
+__declspec(selectany)const char * const CNTLMAuthObject::m_pszFmtWWW = "Authorization: NTLM %s\r\n";
+__declspec(selectany)const char * const CNTLMAuthObject::m_pszFmtProxy = "Proxy-Authorization: NTLM %s\r\n";
+
+typedef CTempBuffer<TCHAR, _ATL_MAX_AUTH_BUFF> CAuthInfoBuffType;
+inline bool _AtlGetAuthInfoHelper(IAuthInfo *pObj, PFNAUTHFUNC pFunc, CAuthInfoBuffType& buff, DWORD *dwLen) throw()
+{
+ ATLENSURE_RETURN_VAL(pObj, false);
+ ATLENSURE_RETURN_VAL(pFunc, false);
+ DWORD dwSize = _ATL_MAX_AUTH_BUFF;
+ bool bRet = true;
+ TCHAR *szValue = NULL;
+ _ATLTRY
+ {
+ szValue = buff.Allocate(_ATL_MAX_AUTH_BUFF);
+ HRESULT hr = E_FAIL;
+ if (szValue)
+ {
+ hr = (pObj->*pFunc)(szValue, &dwSize);
+ if (hr != S_OK)
+ {
+ if (hr == E_OUTOFMEMORY)
+ {
+ // buffer not big enough, try to allocate
+ szValue = buff.Reallocate(dwSize);
+ if (szValue)
+ {
+ // retry the call
+ if (S_OK != (pObj->*pFunc)(szValue, &dwSize))
+ bRet = false;
+ }
+ else
+ bRet = false;
+ }
+ else
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+
+ }
+ _ATLCATCHALL()
+ {
+ bRet = false;
+ }
+ if (bRet)
+ *dwLen = (DWORD)_tcslen(szValue);
+ else
+ *dwLen = 0;
+ return bRet;
+}
+
+//
+// Security Service Provider Interface (sspi) Helper classes
+// These classes are used as helpers for structures used in
+// SSPI functions.
+//
+class CSecAuthIdentity : public SEC_WINNT_AUTH_IDENTITY_EX
+{
+public:
+ CSecAuthIdentity() throw()
+ {
+ Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
+ Length = sizeof(SEC_WINNT_AUTH_IDENTITY_EX);
+#ifdef _UNICODE
+ Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else
+ Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+ }
+
+
+ bool Init(IAuthInfo *pAuthInfo) throw()
+ {
+ if (!pAuthInfo)
+ return false;
+
+ if (!_AtlGetAuthInfoHelper(pAuthInfo, &IAuthInfo::GetUsername, buffUserName, &UserLength))
+ return false;
+
+ if (!_AtlGetAuthInfoHelper(pAuthInfo, &IAuthInfo::GetPassword, buffPassword, &PasswordLength))
+ return false;
+
+ if (!_AtlGetAuthInfoHelper(pAuthInfo, &IAuthInfo::GetDomain, buffDomain, &DomainLength))
+ return false;
+
+#ifndef _UNICODE
+ User = (unsigned char*)(char*)buffUserName;
+ Domain = DomainLength > 0 ? (unsigned char*)(char*)buffDomain : 0;
+ Password = PasswordLength > 0 ? (unsigned char*)(char*)buffPassword : 0;
+#else
+ // have to cast to unsigned short *, because SEC_WINNT_AUTH_IDENTITY_EXW
+ // uses unsigned short instead of wchar_t
+ User = (unsigned short *)(wchar_t*)buffUserName;
+ Domain = DomainLength > 0 ? (unsigned short *)(wchar_t*)buffDomain : 0;
+ Password = PasswordLength > 0 ? (unsigned short *)(wchar_t*)buffPassword : 0;
+#endif
+ return true;
+ }
+
+protected:
+ CAuthInfoBuffType buffUserName;
+ CAuthInfoBuffType buffPassword;
+ CAuthInfoBuffType buffDomain;
+}; // CSecAuthIdentity
+
+class CSecBuffer : public SecBuffer
+{
+public:
+ CSecBuffer() throw()
+ {
+ cbBuffer = 0;
+ BufferType = 0;
+ pvBuffer = NULL;
+ m_cbAlloc = 0;
+ }
+
+ ~CSecBuffer() throw()
+ {
+ delete [] static_cast<unsigned char*>(pvBuffer);
+ }
+
+ bool SetSize(unsigned long nSize) throw()
+ {
+ if (!nSize)
+ return false;
+
+ if (pvBuffer)
+ {
+ delete [] static_cast<unsigned char*>(pvBuffer);
+ pvBuffer = NULL;
+ cbBuffer = 0;
+ m_cbAlloc = 0;
+ }
+
+ ATLTRY(pvBuffer = static_cast<void*>(new unsigned char[nSize]));
+ if (pvBuffer)
+ {
+ cbBuffer = nSize;
+ BufferType = SECBUFFER_TOKEN;
+ m_cbAlloc = cbBuffer;
+ return true;
+ }
+ return false;
+ }
+
+ bool ClearBuffer(unsigned long nSize) throw()
+ {
+ if(nSize > m_cbAlloc)
+ return false;
+
+ ZeroMemory(pvBuffer, nSize);
+ cbBuffer = nSize;
+ return true;
+ }
+
+ unsigned long Size()
+ {
+ return cbBuffer;
+ }
+
+ unsigned char *Buffer() throw()
+ {
+ return static_cast<unsigned char*>(pvBuffer);
+ }
+
+ operator SecBuffer*() throw()
+ {
+ return (SecBuffer*)this;
+ }
+
+protected:
+ unsigned long m_cbAlloc;
+
+}; // CSecBuffer
+
+class CSecBufferDesc : public SecBufferDesc
+{
+public:
+ CSecBufferDesc() throw()
+ {
+ ulVersion = SECBUFFER_VERSION;
+ cBuffers = 0;
+ pBuffers = NULL;
+ }
+
+ ~CSecBufferDesc() throw()
+ {
+ cBuffers = 0;
+
+ if (pBuffers)
+ {
+ CSecBuffer *psb = (CSecBuffer*)pBuffers;
+ delete [] psb;
+ }
+ }
+
+ // index is 0 based
+ CSecBuffer* Buffers(unsigned int i) throw()
+ {
+ if (i < cBuffers)
+ {
+ return (CSecBuffer*)(&pBuffers[i]);
+ }
+
+ return NULL;
+ }
+
+ bool AddBuffers(unsigned int nCount, unsigned int nBufferSize) throw()
+ {
+ if (!nCount)
+ return true;
+
+ if (cBuffers == 0)
+ {
+ CSecBuffer *pSecBuffer = NULL;
+ ATLTRY(pSecBuffer = new CSecBuffer[nCount]);
+ if (!pSecBuffer)
+ return false;
+ CAutoVectorPtr<CSecBuffer> spSecBuffer(pSecBuffer);
+
+ for (unsigned int i=0; i<nCount; i++)
+ {
+ if (!pSecBuffer[i].SetSize(nBufferSize))
+ return false;
+ }
+ cBuffers = nCount;
+ pBuffers = (SecBuffer*)spSecBuffer.Detach();
+ }
+ else // realloc
+ {
+ CSecBuffer *pSecBuffer = NULL;
+ ATLTRY(pSecBuffer = new CSecBuffer[nCount + cBuffers]);
+ if (!pSecBuffer)
+ return false;
+ CAutoVectorPtr<CSecBuffer> spSecBuffer(pSecBuffer);
+ Checked::memcpy_s(pSecBuffer, (nCount + cBuffers)*sizeof(CSecBuffer), pBuffers, cBuffers*sizeof(CSecBuffer));
+ delete [] pBuffers;
+ pBuffers=NULL;
+
+ // initialize new buffers
+ for (unsigned int i=0; i<nCount; i++)
+ {
+ if (!pSecBuffer[cBuffers+i].SetSize(nBufferSize))
+ return false;
+ }
+ pBuffers = spSecBuffer.Detach();
+ cBuffers += nCount;
+ }
+ return true;
+ }
+
+ operator PSecBufferDesc() throw()
+ {
+ return static_cast<PSecBufferDesc>(this);
+ }
+}; // CSecBufferDesc
+
+} // namespace ATL
+
+
+#include <atlhttp.inl>
+
+#pragma pack(pop)
+#pragma warning(pop)
+
+#ifndef __CPPUNWIND
+#pragma warning(pop)
+#endif
+
+#endif // __ATLHTTP_H__
diff --git a/include/atl/atlhttp.inl b/include/atl/atlhttp.inl
new file mode 100644
index 000000000..71294b49b
--- /dev/null
+++ b/include/atl/atlhttp.inl
@@ -0,0 +1,3144 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLHTTP_INL__
+#define __ATLHTTP_INL__
+
+#include <errno.h>
+
+#pragma warning(push)
+#pragma warning(disable: 4061) // enumerate 'enum value' in switch of enum 'enum type' is not explicitly handled by a case label
+#pragma warning(disable: 4062) // enumerate 'enum value' in switch of enum 'enum type' is not handled
+
+namespace ATL
+{
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// CAtlHttpClient
+// Implementation of CAtlHttpClient member functions
+//
+/////////////////////////////////////////////////////////////////////////////////
+template <class TSocketClass>
+inline CAtlHttpClientT<TSocketClass>::CAtlHttpClientT() throw()
+{
+ InitializeObject();
+}
+
+// Sets this object to a known state.
+template <class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::InitializeObject() throw()
+{
+ Close(); // will close the socket if it's already open
+ ResetRequest();
+ SetSilentLogonOk(FALSE);
+}
+
+template <class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::ResetRequest() throw()
+{
+ // reset all data that has to do with the current request
+ m_HeaderMap.RemoveAll();
+ m_current.Empty();
+ m_urlCurrent.Clear();
+ m_strMethod.Empty();
+ m_nStatus = ATL_INVALID_STATUS;
+ m_dwBodyLen = 0;
+ m_dwHeaderLen = 0;
+ m_dwHeaderStart = 0;
+ m_pCurrent = NULL;
+ m_pNavData = NULL;
+ m_LastResponseParseError = RR_NOT_READ;
+ m_pEnd = NULL;
+
+}
+
+
+// Use this function to retrieve an entity from a server via an HTTP
+// request. This function will either request a connection from the
+// server specified in the szURL parameter or request a connection from
+// the proxy server. If a proxy server is to be used, you must call
+// SetProxy prior to calling this function to specify the proxy server
+// being used. Once the connection is established, an HTTP request
+// is built and sent to the HTTP server. An attempt to read the HTTP
+// response is then made. If the response is successfully read, the
+// response will be parsed and stored in this class instance. The
+// headers can be parsed via the LookupHeader function and the body
+// of the response can be retrieved using the GetBody function. You
+// can also retrieve the contents of the entire response by calling
+// GetResponse.
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::Navigate(
+
+ LPCTSTR szUrl,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw(...)
+{
+ if (!szUrl || *szUrl == _T('\0'))
+ return false;
+
+ CUrl url;
+ TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
+ if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
+ return false;
+
+ if(!url.CrackUrl(szTmp))
+ return false;
+
+ // Navigate
+ return Navigate(&url, pNavData);
+}
+
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::Navigate(
+ LPCTSTR szServer,
+ LPCTSTR szPath,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw(...)
+{
+ // Create a URL
+ if (!szServer || *szServer == _T('\0'))
+ return false;
+ if (!szPath || *szPath == _T('\0'))
+ return false;
+ CUrl url;
+ url.SetScheme(ATL_URL_SCHEME_HTTP);
+ url.SetHostName(szServer);
+ url.SetUrlPath(szPath);
+ if (pNavData)
+ url.SetPortNumber(pNavData->nPort);
+ else
+ url.SetPortNumber(ATL_URL_DEFAULT_HTTP_PORT);
+
+ TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
+ if (!url.CreateUrl(szUrl, &dwMaxLen))
+ return false;
+
+ // Navigate
+ return Navigate(szUrl, pNavData);
+}
+
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::Navigate(
+ const CUrl *pUrl,
+ ATL_NAVIGATE_DATA *pData
+ ) throw(...)
+{
+ bool bRet = false;
+ if (!pUrl)
+ return false;
+
+ ResetRequest();
+
+ CAtlNavigateData default_nav_data;
+ if (!pData)
+ m_pNavData = &default_nav_data;
+ else
+ m_pNavData = pData;
+
+ ATLASSUME(m_pNavData);
+
+ _ATLTRY
+ {
+ m_strMethod = m_pNavData->szMethod;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+
+ SetSocketTimeout(m_pNavData->dwTimeout);
+
+ // set m_urlCurrent
+ if (!SetDefaultUrl(pUrl, m_pNavData->nPort))
+ return false;
+ DWORD dwSent = 0;
+ CString strRequest;
+ CString strExtraInfo;
+
+ if (!BuildRequest(&strRequest,
+ m_pNavData->szMethod,
+ m_pNavData->szExtraHeaders))
+ {
+ return false;
+ }
+
+
+ if (!ConnectSocket())
+ return false;
+
+ LPCTSTR szTRequest = strRequest;
+ CT2CA strARequest(szTRequest);
+ DWORD dwRequestLen = (DWORD)strlen(strARequest);
+ DWORD dwAvailable = dwRequestLen + m_pNavData->dwDataLen;
+
+ if (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_CALLBACK)
+ {
+ dwSent = WriteWithCallback(strARequest, dwRequestLen);
+ }
+ else if (!m_pNavData->pData)
+ dwSent = WriteWithNoData(strARequest, dwRequestLen);
+ else if (m_pNavData->pData && (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_BLOCKS))
+ {
+ dwSent = WriteWithChunks(strARequest, dwRequestLen);
+ }
+ else if(m_pNavData->pData)
+ {
+ dwSent = WriteWithData(strARequest, dwRequestLen);
+ }
+
+
+ // make sure everything was sent
+ if (dwSent == dwAvailable)
+ {
+ // Read the response
+ if (RR_OK == ReadHttpResponse())
+ {
+ // if navigation isn't complete, try to complete
+ // it based on the status code and flags
+ if ((m_pNavData->dwFlags & ATL_HTTP_FLAG_PROCESS_RESULT)&&
+ !ProcessStatus(m_pNavData->dwFlags))
+ {
+ bRet = false;
+ }
+ else
+ bRet = true;
+ }
+ else
+ bRet = false;
+ }
+
+ if (!bRet)
+ Close(); // some kind of failure happened, close the socket.
+
+ m_pNavData = NULL;
+ return bRet;
+}
+
+template <class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithNoData(LPCSTR pRequest, DWORD dwRequestLen)
+{
+ ATLASSUME(m_pNavData);
+ WSABUF Buffer;
+ Buffer.buf = (char*)pRequest;
+ Buffer.len = (int)dwRequestLen;
+ DWORD dwWritten = 0;
+ Write(&Buffer, 1, &dwWritten);
+ if (m_pNavData->pfnSendStatusCallback)
+ m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend);
+ return dwWritten;
+}
+
+// The entity body will be retrieved from the client by calling their
+// callback function.
+template <class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithCallback(LPCSTR pRequest, DWORD dwRequestLen)
+{
+ ATLASSUME(m_pNavData);
+ if (!(m_pNavData->pfnChunkCallback &&
+ (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_CALLBACK)))
+ return 0; // error, must have flag set and callback function
+
+ // write the request
+ DWORD dwTotalWritten = 0;
+ WSABUF Buffer;
+ Buffer.buf = (char*)pRequest;
+ Buffer.len = (int)dwRequestLen;
+ DWORD dwWritten = 0;
+ Write(&Buffer, 1, &dwWritten);
+ if (m_pNavData->pfnSendStatusCallback)
+ if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
+ return 0;
+ if (!dwWritten)
+ return 0; // failure
+ dwTotalWritten += dwWritten;
+
+ // start writing data;
+ while (m_pNavData->pfnChunkCallback((BYTE**)&Buffer.buf, (DWORD*)&Buffer.len, m_pNavData->m_lParamChunkCB) &&
+ Buffer.len > 0 &&
+ Buffer.buf != NULL)
+ {
+ Write(&Buffer, 1, &dwWritten);
+ if (dwWritten != Buffer.len)
+ return 0;
+ if (m_pNavData->pfnSendStatusCallback)
+ if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
+ return 0;
+ dwTotalWritten += dwWritten;
+ }
+ return dwTotalWritten;
+}
+
+template <class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithChunks(LPCSTR pRequest, DWORD dwRequestLen)
+{
+ ATLASSUME(m_pNavData);
+ if (!(m_pNavData->dwSendBlockSize > 0 && (m_pNavData->dwFlags & ATL_HTTP_FLAG_SEND_BLOCKS)))
+ return 0; // error, must have flag set and callback function
+
+ // write the request
+ DWORD dwTotalWritten = 0;
+ WSABUF Buffer;
+ Buffer.buf = (char*)pRequest;
+ Buffer.len = (int)dwRequestLen;
+ DWORD dwWritten = 0;
+ Write(&Buffer, 1, &dwWritten);
+ if (m_pNavData->pfnSendStatusCallback)
+ if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
+ return 0;
+ if (!dwWritten)
+ return 0; // failure
+ dwTotalWritten += dwWritten;
+
+ // start writing data;
+ DWORD dwDataWritten = 0;
+ DWORD dwDataLeft = m_pNavData->dwDataLen;
+ while (dwDataLeft)
+ {
+ Buffer.buf = (char*)(m_pNavData->pData + dwDataWritten);
+ Buffer.len = __min(dwDataLeft, m_pNavData->dwSendBlockSize);
+ Write(&Buffer, 1, &dwWritten);
+ if (dwWritten != Buffer.len)
+ return 0;
+ if (m_pNavData->pfnSendStatusCallback)
+ if (!m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend))
+ return false;
+ dwTotalWritten += dwWritten;
+ dwDataWritten += dwWritten;
+ dwDataLeft -= dwWritten;
+ }
+ return dwTotalWritten;
+}
+
+template <class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::WriteWithData(LPCSTR pRequest, DWORD dwRequestLen)
+{
+ WSABUF Buffers[2];
+ Buffers[0].buf = (char*)pRequest;
+ Buffers[0].len = dwRequestLen;
+ Buffers[1].buf = (char*)m_pNavData->pData;
+ Buffers[1].len = m_pNavData->dwDataLen;
+
+ DWORD dwWritten = 0;
+ Write(Buffers, 2, &dwWritten);
+ if (m_pNavData->pfnSendStatusCallback)
+ m_pNavData->pfnSendStatusCallback(dwWritten, m_pNavData->m_lParamSend);
+
+ return dwWritten;
+}
+
+template <class TSocketClass>
+bool CAtlHttpClientT<TSocketClass>::NavigateChunked(
+ LPCTSTR szServer,
+ LPCTSTR szPath,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw()
+{
+ // Create a URL
+ if (!szServer || *szServer == _T('\0'))
+ return false;
+ if (!szPath || *szPath == _T('\0'))
+ return false;
+
+ if (!pNavData)
+ {
+ // To do chunked navigation you must specify an
+ // ATL_NAVIGATE_DATA structure that has the pfnChunkCallback
+ // member filled out.
+ ATLASSERT(FALSE);
+ return false;
+ }
+ CUrl url;
+ url.SetScheme(ATL_URL_SCHEME_HTTP);
+ url.SetHostName(szServer);
+ url.SetUrlPath(szPath);
+ if (pNavData)
+ url.SetPortNumber(pNavData->nPort);
+ else
+ url.SetPortNumber(ATL_URL_DEFAULT_HTTP_PORT);
+
+ TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
+ if (!url.CreateUrl(szUrl, &dwMaxLen))
+ return false;
+
+ // Navigate
+ return NavigateChunked(szUrl, pNavData);
+}
+
+template <class TSocketClass>
+bool CAtlHttpClientT<TSocketClass>::NavigateChunked(
+ LPCTSTR szURL,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw()
+{
+ if (!szURL || *szURL == _T('\0'))
+ return false;
+
+ ResetRequest();
+
+ ATLASSERT(pNavData);
+
+ CUrl url;
+ if (!url.CrackUrl(szURL))
+ return false;
+
+ // Navigate
+ return NavigateChunked(&url, pNavData);
+}
+
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::NavigateChunked(
+ const CUrl *pUrl,
+ ATL_NAVIGATE_DATA *pNavData
+ ) throw()
+{
+ if (!pUrl)
+ return false;
+
+ if (!pNavData)
+ {
+ // To do chunked navigation you must specify an
+ // ATL_NAVIGATE_DATA structure that has the pfnChunkCallback
+ // member filled out.
+ ATLASSERT(FALSE);
+ return false;
+ }
+
+ m_pNavData = pNavData;
+ if (!pNavData->pfnChunkCallback)
+ return false;
+
+ bool bRet = true;
+
+ _ATLTRY
+ {
+ m_strMethod = m_pNavData->szMethod;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+
+ SetSocketTimeout(m_pNavData->dwTimeout);
+
+ // set m_urlCurrent
+ if (!SetDefaultUrl(pUrl, m_pNavData->nPort))
+ return false;
+
+
+ DWORD dwSent = 0;
+ CString strRequest;
+ CString strExtraInfo;
+
+ if (!BuildRequest(&strRequest,
+ m_pNavData->szMethod,
+ m_pNavData->szExtraHeaders // extra headers
+ ))
+ {
+ return false;
+ }
+
+ if (!ConnectSocket())
+ return false;
+
+ WSABUF Buffers[3];
+
+ _ATLTRY
+ {
+ CT2A pRequest(strRequest);
+
+ Buffers[0].buf = (char*)pRequest;
+ Buffers[0].len = strRequest.GetLength();
+
+ // send the first buffer which is the request
+ if (!Write(Buffers, 1, &dwSent))
+ {
+ Close();
+ return false;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ Close();
+ return false;
+ }
+ Buffers[0].buf = NULL;
+ Buffers[0].len = 0;
+
+ CStringA strChunkSize;
+
+ Buffers[2].buf = "\r\n";
+ Buffers[2].len = 2;
+ int z = 0;
+
+ // start sending the chunks
+ do
+ {
+ z++;
+ Buffers[1].buf = NULL;
+ Buffers[1].len = 0;
+ if (m_pNavData->pfnChunkCallback((BYTE**)&Buffers[1].buf, &Buffers[1].len,
+ m_pNavData->m_lParamChunkCB))
+ {
+ _ATLTRY
+ {
+ if (Buffers[1].len > 0)
+ {
+ // send the chunk
+ strChunkSize.Format("%x\r\n", Buffers[1].len);
+ Buffers[0].buf = (char*)(LPCSTR)strChunkSize;
+ Buffers[0].len = strChunkSize.GetLength();
+ if (!Write(Buffers, 3, &dwSent))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ else if (Buffers[1].len == 0)
+ {
+ strChunkSize = "0\r\n\r\n\r\n";
+ Buffers[0].buf = (char*)(LPCSTR)strChunkSize;
+ Buffers[0].len = strChunkSize.GetLength();
+ if (!Write(Buffers, 1, &dwSent))
+ {
+ bRet = false;
+ break;
+ }
+ break;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ bRet = false;
+ break;
+ }
+ }
+ else
+ {
+ bRet = false;
+ break; // something went wrong in callback
+ }
+ }while (Buffers[1].len > 0);
+
+ strRequest.ReleaseBuffer();
+
+ if (bRet)
+ {
+ // Read the response
+ if (RR_OK == ReadHttpResponse())
+ {
+ // if navigation isn't complete, try to complete
+ // it based on the status code and flags
+ if ((m_pNavData->dwFlags & ATL_HTTP_FLAG_PROCESS_RESULT)
+ && !ProcessStatus(m_pNavData->dwFlags))
+ {
+ bRet = false;
+ }
+ bRet = true;
+ }
+ else
+ bRet = false;
+ }
+
+ if (!bRet)
+ Close();
+
+ return bRet;
+}
+
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ConnectSocket() throw()
+{
+ bool bRet=false;
+ // connect to the correct server
+ if (GetProxy())
+ {
+ //if we're using a proxy connect to the proxy
+ bRet=Connect(m_strProxy, m_nProxyPort);
+ }
+ else
+ {
+ bRet=Connect(m_urlCurrent.GetHostName(),m_urlCurrent.GetPortNumber()); // connect to the server
+ }
+ return bRet;
+}
+
+
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::BuildRequest(/*out*/CString *pstrRequest,
+ LPCTSTR szMethod,
+ LPCTSTR szExtraHeaders) throw()
+{
+ if (!m_pNavData)
+ return false;
+ _ATLTRY
+ {
+ // build up the request
+ CString strRequest = szMethod;
+ strRequest += _T(" ");
+ if (GetProxy())
+ {
+ TCHAR buffURL[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwSize = ATL_URL_MAX_URL_LENGTH;
+ m_urlCurrent.CreateUrl(buffURL, &dwSize);
+ strRequest += buffURL;
+
+ strRequest += ATL_HTTP_HEADER_PROXY;
+ CString strHost;
+ if (m_urlCurrent.GetPortNumber() != ATL_URL_DEFAULT_HTTP_PORT)
+ strHost.Format(_T("Host: %s:%d\r\n"), m_urlCurrent.GetHostName(), m_urlCurrent.GetPortNumber());
+ else
+ strHost.Format(_T("Host: %s\r\n"), m_urlCurrent.GetHostName());
+ strRequest += strHost;
+
+ if (m_pNavData->dwDataLen>0)
+ {
+ CString strCL;
+ strCL.Format(_T("Content-Length: %d\r\n"), m_pNavData->dwDataLen);
+ strRequest += strCL;
+ }
+
+ if (m_pNavData->szDataType)
+ {
+ strRequest += _T("Content-Type: ");
+ strRequest += m_pNavData->szDataType;
+ strRequest += _T("\r\n");
+ }
+
+ if (m_pNavData->szExtraHeaders)
+ strRequest += szExtraHeaders;
+ strRequest += ATL_HTTP_USERAGENT;
+ }
+ else
+ {
+ strRequest += m_urlCurrent.GetUrlPath();
+ strRequest += m_urlCurrent.GetExtraInfo();
+ strRequest += ATL_HTTP_HEADER;
+
+ if (m_pNavData->dwDataLen > 0)
+ {
+ CString strCL;
+ strCL.Format(_T("Content-Length: %d\r\n"), m_pNavData->dwDataLen);
+ strRequest += strCL;
+ }
+
+ if (m_pNavData->szDataType &&
+ *m_pNavData->szDataType)
+ {
+ strRequest += _T("Content-Type: ");
+ strRequest += m_pNavData->szDataType;
+ strRequest += _T("\r\n");
+ }
+
+ if (szExtraHeaders)
+ strRequest += szExtraHeaders;
+
+
+ CString strHost;
+ strHost.Format(_T("Host: %s\r\n"), m_urlCurrent.GetHostName());
+ strRequest += strHost;
+ strRequest += ATL_HTTP_USERAGENT;
+ }
+ strRequest += _T("\r\n");
+
+
+ *pstrRequest = strRequest;
+ return true;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+}
+
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ReadSocket() throw()
+{
+ bool bRet = false;
+ unsigned char read_buff[ATL_READ_BUFF_SIZE];
+ int dwSize = ATL_READ_BUFF_SIZE;
+
+ // read some data
+ for (int i = 0; i < ATL_HTTP_CLIENT_EMPTY_READ_RETRIES; ++i)
+ {
+ bRet = Read(read_buff, (DWORD*)&dwSize);
+ if (!bRet)
+ return bRet;
+
+ // notify user
+ if (m_pNavData)
+ {
+ if (m_pNavData->pfnReadStatusCallback)
+ bRet = m_pNavData->pfnReadStatusCallback(dwSize, m_pNavData->m_lParamRead);
+ if (!bRet)
+ return bRet;
+ }
+
+ if (dwSize > 0)
+ {
+ // append the data to our internal buffer
+ // m_current holds bytes (not UNICODE!)
+
+ if (!m_current.Append((LPCSTR)read_buff, dwSize))
+ return FALSE;
+
+ m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
+ m_pCurrent = (BYTE*)(LPCSTR)m_current;
+ break;
+ }
+ bRet = false; // nothing was read
+ }
+
+ return bRet;
+}
+
+// Starts searching for a complete header set at
+// m_pCurrent. This function will only move m_pCurrent
+// if a complete set is found. Returns the header beginning
+// optionally.
+template <class TSocketClass>
+inline unsigned char* CAtlHttpClientT<TSocketClass>::FindHeaderEnd(unsigned char** ppBegin) throw()
+{
+ if (!m_pCurrent)
+ return NULL;
+
+ BYTE *pCurr = m_pCurrent;
+ BYTE *pBegin = m_pCurrent;
+ int nLen = m_current.GetLength();
+
+ if (pCurr >= (BYTE*)(LPCSTR)m_current + m_current.GetLength())
+ return NULL; // no more chars in buffer
+ // look for the end of the header (the \r\n\r\n)
+ while (pCurr <= (pBegin + nLen - ATL_HEADER_END_LEN))
+ {
+
+ if (* ((UNALIGNED DWORD*)pCurr)==ATL_DW_HEADER_END)
+ {
+ // set m_pCurrent pointer to the end of the header
+ m_pCurrent = pCurr + ATL_HEADER_END_LEN;
+ if (ppBegin)
+ *ppBegin = pBegin;
+ return m_pCurrent;
+ }
+ pCurr++;
+ }
+ return NULL;
+}
+
+// Call this function after sending an HTTP request over the socket. The complete
+// HTTP response will be read. This function will also parse
+// response headers into the response header map.
+template <class TSocketClass>
+inline typename CAtlHttpClientT<TSocketClass>::HTTP_RESPONSE_READ_STATUS CAtlHttpClientT<TSocketClass>::ReadHttpResponse()
+{
+ // Read until we at least have the response headers
+ HTTP_RESPONSE_READ_STATUS result = RR_OK;
+ readstate state = rs_init;
+ unsigned char *pBodyBegin = NULL;
+ unsigned char *pHeaderBegin = NULL;
+ m_current.Empty();
+ m_pCurrent = NULL;
+ m_LastResponseParseError = RR_OK;
+
+ while (state != rs_complete)
+ {
+ switch(state)
+ {
+ case rs_init:
+ m_HeaderMap.RemoveAll();
+ m_nStatus = ATL_INVALID_STATUS;
+ m_dwHeaderLen = 0;
+ m_dwBodyLen = 0;
+ state = rs_readheader;
+ // fall through
+
+ case rs_readheader:
+
+ // read from the socket until we have a complete set of headers.
+ pBodyBegin = FindHeaderEnd(&pHeaderBegin);
+ if (!pBodyBegin)
+ {
+ if (!ReadSocket())
+ {
+ // Either reading from the socket failed, or there
+ // was not data to read. Set the nav status to error
+ // and change the state to complete.
+ state = rs_complete;
+ result = RR_READSOCKET_FAILED;
+ break;
+ }
+ else
+ break; // loop back and FindHeaderEnd again.
+ }
+ // we have a complete set of headers
+ m_dwHeaderLen = (DWORD)(pBodyBegin-pHeaderBegin);
+ m_dwHeaderStart = (DWORD)(pHeaderBegin - (BYTE*)(LPCSTR)m_current);
+ // fall through
+ state = rs_scanheader;
+
+ case rs_scanheader:
+ // set m_nStatus and check for valid status
+ ParseStatusLine(pHeaderBegin);
+ // failed to set m_nStatus;
+ if (m_nStatus == ATL_INVALID_STATUS)
+ {
+ state = rs_complete;
+ result = RR_STATUS_INVALID;
+ break;
+ }
+
+ else if (m_nStatus == 100) // continue
+ {
+ state = rs_init;
+ break;
+ }
+
+ // crack all the headers and put them into a header map. We've already
+ // done the check to make sure we have a complete set of headers in
+ // rs_readheader above
+ if (ATL_HEADER_PARSE_COMPLETE != CrackResponseHeader((LPCSTR)pHeaderBegin,
+ (LPCSTR*)&pBodyBegin))
+ {
+ // something bad happened while parsing the headers!
+ state = rs_complete;
+ result = RR_PARSEHEADERS_FAILED;
+ break;
+ }
+ state = rs_readbody;
+ // fall through
+
+ case rs_readbody:
+ // headers are parsed and cracked, we're ready to read the rest
+ // of the response.
+ if (IsMsgBodyChunked())
+ {
+ if (!ReadChunkedBody())
+ {
+ result = RR_READCHUNKEDBODY_FAILED;
+ state = rs_complete;
+ break;
+ }
+ }
+ else
+ if (!ReadBody(GetContentLength(), m_current.GetLength()-(m_dwHeaderStart+m_dwHeaderLen)))
+ result = RR_READBODY_FAILED;
+ state = rs_complete;
+ //fall through
+
+ case rs_complete:
+ // clean up the connection if the server requested a close;
+ DisconnectIfRequired();
+ break;
+ }
+ }
+ m_LastResponseParseError = result;
+ return result;
+}
+
+template <class TSocketClass>
+inline typename CAtlHttpClientT<TSocketClass>::HTTP_RESPONSE_READ_STATUS CAtlHttpClientT<TSocketClass>::GetResponseStatus()
+{
+ return m_LastResponseParseError;
+}
+
+// Checks to see if the server has closed the connection.
+// If it has, we create a new socket and reconnect it to
+// the current server. This also clears the contents of the
+// current response buffer.
+template <class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::ResetConnection() throw()
+{
+ ReconnectIfRequired();
+ m_HeaderMap.RemoveAll();
+ m_current.Empty();
+ m_nStatus = ATL_INVALID_STATUS;
+ m_AuthTypes.RemoveAll(); // the server will keep sending back www-authenticate
+ // headers until the connection is authorized
+}
+
+// Takes action based on the flags passed and the current
+// status for this object.
+template <class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ProcessStatus(DWORD dwFlags) throw()
+{
+ switch(m_nStatus)
+ {
+ case 200: // In all these cases there is no further action
+ case 201: // to take. Any additional informaion is returned
+ case 202: // in the entity body.
+ case 203:
+ case 204:
+ case 205:
+ case 206:
+ case 304:
+ case 305:
+ return true;
+ break;
+ case 301:
+ case 302:
+ case 303:
+ if (dwFlags & ATL_HTTP_FLAG_AUTO_REDIRECT)
+ return ProcessObjectMoved();
+ break;
+ case 401: // auth required
+ return NegotiateAuth(false);
+ break;
+ case 407: // proxy auth required
+ return NegotiateAuth(true);
+ break;
+
+ }
+ return false;
+}
+
+// Looks up the value of a response header in the header map. Call with
+// NULL szBuffer to have length of the required buffer placed in
+// pdwLen on output.
+
+// szName is the name of the header to look up.
+// szBuffer is the buffer that will contain the looked up string.
+// pdwLen contains the length of szBuffer in characters on input and the length
+// of the string including NULL terminator in characters on output.
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::GetHeaderValue(LPCTSTR szName, CString& strValue) const throw()
+{
+ _ATLTRY
+ {
+ return m_HeaderMap.Lookup(szName, strValue);
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::GetHeaderValue(__in_z LPCTSTR szName, __out_ecount_part_z_opt(*pdwLen, *pdwLen) LPTSTR szBuffer, __inout DWORD *pdwLen) const throw()
+{
+ CString strValue;
+ bool bRet = GetHeaderValue(szName, strValue);
+ DWORD nLen = strValue.GetLength();
+ if (!bRet)
+ return false;
+
+ if ((pdwLen && *pdwLen < nLen+1) ||
+ (!szBuffer && pdwLen) )
+ {
+ *pdwLen = nLen+1;
+ return true;
+ }
+
+ if (!szBuffer)
+ return false;
+
+ Checked::tcsncpy_s(szBuffer, nLen+1, (LPCTSTR)strValue, _TRUNCATE);
+ if (pdwLen)
+ *pdwLen = nLen+1;
+ return true;
+}
+
+// Adds an authorization object to use for a particular scheme.
+// This will overwrite an existing entry if an object for the
+// same scheme has already been set.
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::AddAuthObj(LPCTSTR szScheme,
+ CAtlBaseAuthObject *pObject, IAuthInfo *pInfo/*=NULL*/) throw()
+{
+ if (!pObject)
+ return false;
+
+ pObject->Init(this, pInfo);
+
+ _ATLTRY
+ {
+ POSITION pos = m_AuthMap.SetAt(szScheme, pObject);
+ if (!pos)
+ return false;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Tries to find an authorization object to use for a particular
+// scheme
+template<class TSocketClass>
+inline const CAtlBaseAuthObject* CAtlHttpClientT<TSocketClass>::FindAuthObject(LPCTSTR szScheme) throw()
+{
+ CAtlBaseAuthObject *pObject = NULL;
+ if (m_AuthMap.Lookup(szScheme, pObject))
+ {
+ return const_cast<const CAtlBaseAuthObject*>(pObject);
+ }
+ return NULL;
+}
+
+// Removes an existing authorization object from the map.
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::RemoveAuthObject(LPCTSTR szScheme) throw()
+{
+ return m_AuthMap.RemoveKey(szScheme);
+}
+
+// Sets the current proxy server and port
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::SetProxy(LPCTSTR szProxy, short nProxyPort) throw()
+{
+ if (!szProxy)
+ {
+ if (!LookupRegProxy())
+ return false;
+ }
+ else
+ {
+ _ATLTRY
+ {
+ m_strProxy = szProxy;
+ m_nProxyPort = nProxyPort;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Removes the current proxy settings.
+template<class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::RemoveProxy() throw()
+{
+ m_strProxy.Empty();
+ m_nProxyPort = ATL_URL_INVALID_PORT_NUMBER;
+}
+
+// retrieves the current proxy
+template<class TSocketClass>
+inline LPCTSTR CAtlHttpClientT<TSocketClass>::GetProxy() const throw()
+{
+ if (m_strProxy.GetLength())
+ return (LPCTSTR)m_strProxy;
+ return NULL;
+}
+
+template<class TSocketClass>
+inline short CAtlHttpClientT<TSocketClass>::GetProxyPort() const throw()
+{
+ return m_nProxyPort;
+}
+
+// Gets the contents of the entire response buffer.
+template<class TSocketClass>
+inline const BYTE* CAtlHttpClientT<TSocketClass>::GetResponse() throw()
+{
+ return (const BYTE*)(LPCSTR)m_current;
+}
+
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetResponseLength() throw()
+{
+ return m_current.GetLength();
+}
+
+// Gets the length in bytes of the body of the
+// current response
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetBodyLength() const throw()
+{
+ return m_dwBodyLen;
+}
+
+// Gets the contents of the body of the current response. This
+// is the response without the headers.
+template<class TSocketClass>
+inline const BYTE* CAtlHttpClientT<TSocketClass>::GetBody() throw()
+{
+ return (BYTE*)((LPCSTR)m_current + m_dwHeaderLen + m_dwHeaderStart);
+}
+
+// Get the length of the header part of the response in bytes.
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetRawResponseHeaderLength() throw()
+{
+ return m_dwHeaderLen >= 2 ? m_dwHeaderLen-2 : 0; // m_dwHeaderLen includes the final \r\n
+}
+
+// buffer must include space for null terminator.
+// on input, pdwLen specifies the size of szBuffer,
+// on output, pdwLen holds the number of bytes copied
+// to szBuffer, or the required size of szBuffer if
+// szBuffer wasn't big enough
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::GetRawResponseHeader(LPBYTE szBuffer, DWORD *pdwLen) throw()
+{
+ if (!pdwLen)
+ return false;
+
+ DWORD header_len = GetRawResponseHeaderLength();
+ if (header_len == 0)
+ return false;
+
+ if (!szBuffer || *pdwLen < header_len+1)
+ {
+ *pdwLen = header_len+1;
+ return false;
+ }
+
+ Checked::memcpy_s(szBuffer, *pdwLen, (BYTE*)(LPCSTR)m_current, header_len);
+ szBuffer[header_len]='\0';
+
+ *pdwLen = header_len+1;
+ return true;
+}
+
+// Gets the current URL object.
+template<class TSocketClass>
+inline LPCURL CAtlHttpClientT<TSocketClass>::GetCurrentUrl() const throw()
+{
+ return (LPCURL)&m_urlCurrent;
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::SetDefaultUrl( LPCTSTR szUrl,
+ short nPortNumber) throw()
+{
+ return _SetDefaultUrl(szUrl,nPortNumber);
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::SetDefaultUrl( LPCURL pUrl,
+ short nPortNumber) throw()
+{
+ m_urlCurrent = *pUrl;
+ return _SetDefaultUrl(NULL, nPortNumber);
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::SetDefaultMethod(LPCTSTR szMethod) throw()
+
+{
+ _ATLTRY
+ {
+ m_strMethod = szMethod;
+ return true;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+}
+
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetFlags() const throw()
+{
+ if (m_pNavData)
+ return m_pNavData->dwFlags;
+ else
+ return ATL_HTTP_FLAG_INVALID_FLAGS;
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::LookupRegProxy() throw()
+{
+ // attempt to look it up from the registry
+ CRegKey rkProxy;
+ ULONG nChars = ATL_URL_MAX_URL_LENGTH+1;
+ TCHAR szUrl[ATL_URL_MAX_URL_LENGTH+1] = { 0 };
+
+ DWORD dwErr = rkProxy.Open(HKEY_CURRENT_USER,
+ _T("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"), KEY_READ);
+ if (dwErr == ERROR_SUCCESS)
+ {
+ dwErr = rkProxy.QueryStringValue(_T("ProxyServer"), szUrl, &nChars);
+ }
+ if (dwErr == ERROR_SUCCESS)
+ {
+ CUrl url;
+ if (url.CrackUrl(szUrl))
+ {
+ if (url.GetScheme()==ATL_URL_SCHEME_UNKNOWN)
+ {
+ // without the scheme name (e.g. proxy:80)
+ m_strProxy = url.GetSchemeName();
+ m_nProxyPort = (short)_ttoi(url.GetHostName());
+ return true;
+ }
+ else if (url.GetHostName())
+ {
+ // with the scheme (e.g. http://proxy:80)
+ m_strProxy = url.GetHostName();
+ m_nProxyPort = url.GetPortNumber();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::DisconnectIfRequired() throw()
+{
+ CString strValue;
+ if (GetHeaderValue(_T("Connection"), strValue) && !strValue.CompareNoCase(_T("close")))
+ {
+ Close();
+ }
+
+ return true;
+}
+
+class CInitializeCOMThread
+{
+public:
+ CInitializeCOMThread()
+ : m_bCoInit(FALSE),m_bShouldUninit(FALSE)
+ {
+ //At this point the Thread can be uninit, init to STA or init to MTA.
+ //CoInitialize can always fail unexpectedly.
+ HRESULT hr = ::CoInitialize(NULL);
+ if (SUCCEEDED(hr))
+ {
+ m_bCoInit=TRUE;
+ m_bShouldUninit=TRUE;
+ } else if (hr == RPC_E_CHANGED_MODE)
+ {
+ m_bCoInit=TRUE;
+ }
+ }
+ ~CInitializeCOMThread()
+ {
+ if (m_bShouldUninit)
+ {
+ ::CoUninitialize();
+ }
+ }
+ BOOL IsInitialized() { return m_bCoInit; }
+protected:
+ BOOL m_bCoInit;
+ BOOL m_bShouldUninit;
+};
+// Tries to find an authorization object that meets
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::NegotiateAuth(bool bProxy) throw()
+{
+ //Test if can silently pass user credentials to server.
+ if (!m_bSilentLogonOk)
+ {
+ //Call CoInit, because ATL Http code cannot assume it has already been called by the user.
+ CInitializeCOMThread initThread;
+ if (initThread.IsInitialized())
+ {
+ HRESULT hr = S_OK;
+ CComPtr<IInternetSecurityManager> spSecurityMgr;
+
+ hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
+ IID_IInternetSecurityManager, (void**)&spSecurityMgr);
+ if (SUCCEEDED(hr))
+ {
+ TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
+ if (!m_urlCurrent.CreateUrl(szUrl, &dwMaxLen))
+ {
+ return false;
+ }
+
+ CStringW strUrlW(szUrl);
+ DWORD dwPolicy=0xFFFF;
+ hr=spSecurityMgr->ProcessUrlAction(strUrlW.GetString(),
+ URLACTION_CREDENTIALS_USE,
+ reinterpret_cast<BYTE*>(&dwPolicy),
+ sizeof(dwPolicy),
+ NULL,
+ 0,
+ PUAF_NOUI,
+ NULL);
+
+ if (FAILED(hr) || dwPolicy != URLPOLICY_CREDENTIALS_SILENT_LOGON_OK)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // CoCreateInstance failed, return false
+ return false;
+ }
+ }
+ else
+ {
+ // CoInit failed, return false
+ return false;
+ }
+ }
+
+ // szAuthHeaderValue should contain a comma separated list
+ // of authentication types
+ CAtlBaseAuthObject *pAuthObj = NULL;
+ bool bRet = false;
+ for (size_t i = 0; i<m_AuthTypes.GetCount(); i++)
+ {
+ _ATLTRY
+ {
+ CString strName = m_AuthTypes[i];
+ int nSpace = strName.Find(_T(' '));
+ if (nSpace!=-1)
+ strName.SetAt(nSpace,0);
+
+ if (m_AuthMap.Lookup(strName, pAuthObj) &&
+ !pAuthObj->m_bFailed)
+ bRet = pAuthObj->Authenticate(m_AuthTypes[i], bProxy);
+
+ if (bRet)
+ return bRet;
+ }
+ _ATLCATCHALL()
+ {
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+template<class TSocketClass>
+inline long CAtlHttpClientT<TSocketClass>::GetContentLength() throw()
+{
+ CString strValue;
+ if (GetHeaderValue(_T("Content-Length"), strValue))
+ {
+ TCHAR *pStop = NULL;
+ return _tcstol(strValue, &pStop, 10);
+ }
+ else
+ return -1;
+}
+
+template<class TSocketClass>
+inline LPCSTR CAtlHttpClientT<TSocketClass>::NextLine(BYTE* pCurr) throw()
+{
+ if (!pCurr)
+ return NULL;
+
+ while ( pCurr < m_pEnd && *pCurr && !(*pCurr == '\r' && *(pCurr+1) == '\n'))
+ pCurr++;
+
+ if (pCurr >= m_pEnd)
+ return NULL;
+
+// if (pCurr < m_pEnd-4)
+// if (!memcmp(pCurr, ATL_HEADER_END, 4))
+ //return NULL;
+
+ return (LPCSTR)(pCurr+2);
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::IsMsgBodyChunked() throw()
+{
+ CString strValue;
+ return (
+ GetHeaderValue(_T("Transfer-Encoding"), strValue) &&
+ !strValue.CompareNoCase(_T("chunked"))
+ );
+
+}
+
+// finds the end of an individual header field pointed to by
+// pszStart. Header fields can be multi-line with multi-line
+// header fields being a line that starts with some kind of
+// white space.
+template<class TSocketClass>
+inline LPCSTR CAtlHttpClientT<TSocketClass>::FindEndOfHeader(LPCSTR pszStart) throw()
+{
+ // move through all the lines until we come to one
+ // that doesn't start with white space
+ LPCSTR pLineStart = pszStart;
+ LPCSTR pHeaderEnd = NULL;
+
+ do
+ {
+ pLineStart = NextLine((BYTE*)pLineStart);
+ }while (pLineStart && isspace(static_cast<unsigned char>(*pLineStart)) && strncmp(pLineStart-2, ATL_HEADER_END, ATL_HEADER_END_LEN));
+
+ if (pLineStart > (LPCSTR)m_pEnd)
+ return NULL; // ran out of data in the buffer without finding the end of a line
+ // or the end of the headers.
+
+ if (pLineStart)
+ pHeaderEnd = pLineStart-2;
+ else
+ pHeaderEnd = NULL;
+
+ return pHeaderEnd;
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::DecodeHeader(LPCSTR pHeaderStart, LPCSTR pHeaderEnd) throw()
+{
+ _ATLTRY
+ {
+ if (!pHeaderStart || !pHeaderEnd)
+ return false;
+ LPCSTR pTemp = pHeaderStart;
+ while (*pTemp != ATL_FIELDNAME_DELIMITER && pTemp < pHeaderEnd)
+ pTemp++;
+ if (*pTemp == ATL_FIELDNAME_DELIMITER)
+ {
+ char szName[ATL_MAX_FIELDNAME_LEN];
+ char szValue[ATL_MAX_VALUE_LEN];
+ int nLen = (int)(pTemp-pHeaderStart) ;
+ ATLASSERT(nLen < ATL_MAX_FIELDNAME_LEN);
+ if (nLen >= ATL_MAX_FIELDNAME_LEN)
+ return false; // won't fit in the buffer.
+ Checked::memcpy_s(szName, ATL_MAX_FIELDNAME_LEN, pHeaderStart, nLen);
+ szName[nLen]=0;
+
+ pTemp++; // move past delimiter;
+ while (isspace(static_cast<unsigned char>(*pTemp)) && pTemp < pHeaderEnd)
+ pTemp++;
+
+ nLen = (int)(pHeaderEnd-pTemp);
+ ATLASSERT(nLen < ATL_MAX_VALUE_LEN);
+ if (nLen >= ATL_MAX_VALUE_LEN)
+ return false; // won't fit in the buffer
+ Checked::memcpy_s(szValue, ATL_MAX_VALUE_LEN, pTemp, nLen);
+ szValue[nLen]=0;
+
+ CString strExist;
+ CA2T pszName(szName);
+ CA2T pszValue(szValue);
+
+ if (!_tcsicmp(pszName, _T("www-authenticate")) ||
+ !_tcsicmp(pszName, _T("proxy-authenticate")))
+ {
+ m_AuthTypes.Add(pszValue);
+ }
+
+ if (!m_HeaderMap.Lookup(pszName, strExist))
+ m_HeaderMap.SetAt(pszName, pszValue);
+ else
+ {
+ // field-values for headers with the same name can be appended
+ // per rfc2068 4.2, we do the appending so we don't have to
+ // store/lookup duplicate keys.
+ strExist += ',';
+ strExist += pszValue;
+ m_HeaderMap.SetAt(pszName, (LPCTSTR)strExist);
+ }
+
+ // if it's a set-cookie header notify users so they can do
+ // somthing with it.
+ if (!_tcsicmp(pszName, _T("set-cookie")))
+ OnSetCookie(pszValue);
+ }
+
+ return true;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+}
+
+template<class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::OnSetCookie(LPCTSTR) throw()
+{
+ return;
+}
+
+template<class TSocketClass>
+inline LPCSTR CAtlHttpClientT<TSocketClass>::ParseStatusLine(BYTE* pBuffer) throw()
+{
+ if (!pBuffer)
+ return NULL;
+ if (m_pEnd <= pBuffer)
+ return NULL;
+
+ // find the first space'
+ while (pBuffer < m_pEnd && !isspace(static_cast<unsigned char>(*pBuffer)))
+ pBuffer++;
+
+ if (pBuffer >= m_pEnd)
+ return NULL;
+
+ // move past the space
+ while (pBuffer < m_pEnd && isspace(static_cast<unsigned char>(*pBuffer)))
+ pBuffer++;
+
+ if (pBuffer >= m_pEnd)
+ return NULL;
+
+ // pBuffer better be pointing at the status code now
+ LPCSTR pEnd = NULL;
+ if (*pBuffer >= '0' && *pBuffer <= '9')
+ {
+ // probably a good status code
+ errno_t errnoValue = AtlStrToNum(&m_nStatus, (LPSTR)pBuffer, (LPSTR*)&pEnd, 10);
+ if (errnoValue == ERANGE)
+ return NULL; // bad status code
+ }
+ else
+ return FALSE; // bad status code;
+
+ if (!pEnd)
+ return FALSE; // bad status code;
+
+ pBuffer = (BYTE*)pEnd;
+ // move to end of line
+ while (pBuffer < m_pEnd && *pBuffer != '\n')
+ pBuffer++;
+
+ if (pBuffer >= m_pEnd)
+ return NULL;
+
+ // set the return pointing to the first
+ // character after our status line.
+ return (LPCSTR)++pBuffer;
+}
+
+
+// pBuffer should start at the first character
+// after the status line.
+template<class TSocketClass>
+inline int CAtlHttpClientT<TSocketClass>::CrackResponseHeader(LPCSTR pBuffer, /*out*/ LPCSTR *pEnd) throw()
+{
+ // read up to the double /r/n
+ LPCSTR pszStartSearch = pBuffer;
+ if (!pEnd)
+ return ATL_HEADER_PARSE_HEADERERROR;
+
+ *pEnd = NULL;
+ if (pszStartSearch == NULL)
+ return ATL_HEADER_PARSE_HEADERERROR;
+
+ // start parsing headers
+ LPCSTR pHeaderStart = ParseStatusLine((BYTE*)pBuffer);
+ if (!pHeaderStart)
+ return ATL_HEADER_PARSE_HEADERERROR;
+ LPCSTR pHeaderEnd = NULL;
+
+ while (pHeaderStart && *pHeaderStart && pHeaderStart < (LPCSTR)m_pEnd)
+ {
+ pHeaderEnd = FindEndOfHeader(pHeaderStart);
+ if (!pHeaderEnd)
+ break; // error
+
+ DecodeHeader(pHeaderStart, pHeaderEnd);
+
+ if (!strncmp(pHeaderEnd, ATL_HEADER_END, strlen(ATL_HEADER_END)))
+ {
+ *pEnd = pHeaderEnd + strlen(ATL_HEADER_END);
+ break; // we're done
+ }
+ else
+ pHeaderStart = pHeaderEnd+2;
+ }
+
+ return ATL_HEADER_PARSE_COMPLETE;
+}
+
+// Reads the body if the encoding is not chunked.
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ReadBody(int nContentLen, int nCurrentBodyLen) throw()
+{
+ // nCurrentBodyLen is the length of the body that has already been read
+ // nContentLen is the value of Content-Length
+ // current is the buffer that will contain the entire response
+ bool bRet = true;
+ ATLASSUME(m_pNavData);
+ if (!m_pNavData)
+ return false;
+
+ CTempBuffer<BYTE, 512> readbuff;
+ DWORD dwReadBuffSize = 0;
+ DWORD dwRead = 0;
+ if (m_pNavData->dwReadBlockSize)
+ {
+ ATLTRY(readbuff.Allocate(m_pNavData->dwReadBlockSize));
+ dwReadBuffSize = m_pNavData->dwReadBlockSize;
+ }
+ else
+ {
+ ATLTRY(readbuff.Allocate(ATL_READ_BUFF_SIZE));
+ dwReadBuffSize = ATL_READ_BUFF_SIZE;
+ }
+
+ if (readbuff.operator BYTE*() == NULL)
+ return false;
+
+ if (nContentLen != -1) // We know the content length.
+ {
+ // read the rest of the body.
+ while (nCurrentBodyLen < nContentLen)
+ {
+ dwRead = dwReadBuffSize;
+ // loop while dwRead == 0
+ for (int nRetry = 0; nRetry < ATL_HTTP_CLIENT_EMPTY_READ_RETRIES; ++nRetry)
+ {
+ if (!Read(readbuff, &dwRead))
+ return false;
+
+ // notify user
+ if (m_pNavData)
+ {
+ if (m_pNavData->pfnReadStatusCallback)
+ if (!m_pNavData->pfnReadStatusCallback(dwRead, m_pNavData->m_lParamRead))
+ return false;
+ }
+
+ if (dwRead == 0)
+ continue;
+ nCurrentBodyLen += dwRead;
+ if (!m_current.Append((LPCSTR)(BYTE*)readbuff, dwRead))
+ {
+ ATLASSERT(0);
+ return false; // error!
+ }
+ m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
+ break;
+ }
+ if (dwRead == 0)
+ return false;
+ }
+ m_dwBodyLen = nCurrentBodyLen;
+ }
+ else // We don't know content length. All we can do is
+ { // read until there is nothing else to read.
+ int nRetries = 0;
+ while (1)
+ {
+ dwRead = dwReadBuffSize;
+ if (Read((BYTE*)readbuff, (DWORD*)&dwRead))
+ {
+ // notify user
+ if (m_pNavData)
+ {
+ if (m_pNavData->pfnReadStatusCallback)
+ bRet = m_pNavData->pfnReadStatusCallback(dwRead, m_pNavData->m_lParamRead);
+ if (!bRet)
+ return bRet;
+ }
+
+ if (dwRead == 0)
+ {
+ if (nRetries++ < ATL_HTTP_CLIENT_EMPTY_READ_RETRIES)
+ continue;
+ break;
+ }
+
+ nRetries = 0;
+ nCurrentBodyLen += dwRead;
+ if (!m_current.Append((LPCSTR)(BYTE*)readbuff, dwRead))
+ return false;
+ m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
+ }
+ else
+ {
+ // notify user
+ if (m_pNavData)
+ {
+ if (m_pNavData->pfnReadStatusCallback)
+ bRet = m_pNavData->pfnReadStatusCallback(dwRead, m_pNavData->m_lParamRead);
+ if (!bRet)
+ return bRet;
+ }
+
+ bRet = true;
+ break;
+ }
+ }
+ m_dwBodyLen = nCurrentBodyLen;
+ }
+ return bRet;
+}
+
+
+// This function moves pBuffStart only on success. On success, pBuffStart is moved
+// to the element past the last element we consumed.
+template<class TSocketClass>
+inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::get_chunked_size(char *&pBuffStart, char *&pBuffEnd, long* pnChunkSize) throw()
+{
+ CHUNK_LEX_RESULT result = LEX_ERROR;
+ char *pStop = NULL;
+
+ if (pBuffStart >= pBuffEnd)
+ result = LEX_OUTOFDATA;
+ else
+ {
+ long nResult = 0;
+ errno_t errnoValue = AtlStrToNum(&nResult, pBuffStart, &pStop, 16);
+ if (errnoValue != ERANGE &&
+ nResult >= 0 &&
+ nResult < 0xFFFFFFFF &&
+ pStop <= pBuffEnd &&
+ *pStop == '\r')
+ {
+ // move pBuffStart
+ // return chunk size
+ *pnChunkSize = nResult;
+ pBuffStart = pStop;
+ result = LEX_OK;
+ }
+ if (*pStop != '\r')
+ {
+ result = LEX_OUTOFDATA; // not enough data in the buffer
+ }
+ }
+ return result;
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::move_leftover_bytes( __in_ecount(nLen) char *pBufferStart,
+ __in int nLen,
+ __deref_inout_ecount(nLen) char *&pBuffStart,
+ __deref_inout char *& pBuffEnd) throw()
+{
+ bool bRet = true;
+ Checked::memcpy_s(pBufferStart, (pBuffEnd-pBuffStart), pBuffStart, nLen);
+ return bRet;
+}
+
+template<class TSocketClass>
+inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::get_chunked_data(char *&pBufferStart,
+ char *&pBufferEnd,
+ long nChunkSize,
+ char **ppDataStart,
+ long *pnDataLen) throw()
+{
+ CHUNK_LEX_RESULT result = LEX_ERROR;
+ if (pBufferStart + nChunkSize - 1 < pBufferEnd)
+ {
+ *ppDataStart = pBufferStart;
+ *pnDataLen = nChunkSize;
+ pBufferStart = pBufferStart + nChunkSize;
+ result = LEX_OK;
+ }
+ else if (pBufferStart + nChunkSize - 1 >= pBufferEnd)
+ result = LEX_OUTOFDATA;
+
+ return result;
+}
+
+template<class TSocketClass>
+inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::consume_chunk_trailer(char *&pBufferStart, char *pBufferEnd)
+{
+ CHUNK_LEX_RESULT result = LEX_ERROR;
+ if (pBufferStart >= pBufferEnd)
+ return result;
+
+ char *pHeaderEnd = NULL;
+ char *pTemp = pBufferStart;
+ // check for empty trailer, this means there are no more trailers
+ if ( (pTemp < pBufferEnd && *pTemp == '\r') &&
+ (pTemp+1 < pBufferEnd && *(pTemp+1) == '\n'))
+ {
+ pBufferStart += 2;
+ return LEX_TRAILER_COMPLETE;
+ }
+
+ while (pTemp <= pBufferEnd)
+ {
+ if ( (pTemp < pBufferEnd && *pTemp == '\r') &&
+ (pTemp+1 < pBufferEnd && *(pTemp+1) == '\n'))
+ {
+ pHeaderEnd = pTemp; // success case
+ result = LEX_OK;
+ break;
+ }
+ pTemp++;
+ }
+
+ if (result == LEX_OK)
+ {
+ DecodeHeader(pBufferStart, pHeaderEnd);
+ pBufferStart = pHeaderEnd + 2;
+ }
+ else if (result != LEX_OK &&
+ pTemp > pBufferEnd)
+ result = LEX_OUTOFDATA;
+ return result;
+}
+
+template<class TSocketClass>
+inline typename CAtlHttpClientT<TSocketClass>::CHUNK_LEX_RESULT CAtlHttpClientT<TSocketClass>::consume_chunk_footer(char *&pBufferStart, char *&pBufferEnd)
+{
+ CHUNK_LEX_RESULT result = LEX_ERROR;
+ if (pBufferStart < pBufferEnd &&
+ (pBufferStart+1) <= pBufferEnd)
+ {
+ if ( *pBufferStart == '\r' &&
+ *(pBufferStart+1) == '\n')
+ {
+ pBufferStart += 2;
+ result = LEX_OK;
+ }
+ }
+ else
+ result = LEX_OUTOFDATA;
+ return result;
+}
+
+#define CHUNK_BUFF_SIZE 2048
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ReadChunkedBody() throw()
+{
+ // At this point, m_current contains the headers, up to and including the \r\n\r\n,
+ // plus any additional data that might have been read off the socket. So, we need
+ // to copy off the additional data into our read buffer before we start parsing the
+ // chunks.
+#ifdef _DEBUG
+ // nReadCount, keeps track of how many socket reads we do.
+ int nReadCount = 0;
+#endif
+
+ // nChunkBuffCarryOver
+ // When we run out of data in the input buffer, this is the
+ // count of bytes that are left in the input that could not
+ // be lexed into anything useful. We copy this many bytes to
+ // the top of the input buffer before we fill the input buffer
+ // with more bytes from the socket
+ long nChunkBuffCarryOver = 0;
+
+ // nChunkSize
+ // The size of the next chunk to be read from the input buffer.
+ long nChunkSize = 0;
+
+ // t_chunk_buffer
+ // The heap allocated buffer that we holds data
+ // read from the socket. We will increase the size
+ // of this buffer to 2 times the max chunk size we
+ // need to read if we have to.
+ CHeapPtr<char> t_chunk_buffer;
+
+ // nTChunkBuffSize
+ // Keeps track of the allocated size of t_chunk_buffer.
+ // This size will change if we need to read chunks bigger
+ // than the currently allocated size of t_chunk_buffer.
+ long nTChunkBuffSize = CHUNK_BUFF_SIZE;
+
+ // chunk_buffer & chunk_buffer_end
+ // Keeps track of the current location
+ // in t_chunk_buffer that we are lexing input from.
+ // chunk_buffer_end is the end of the input buffer we
+ // are lexing from. chunk_buffer_end is used as a marker
+ // to make sure we don't read past the end of our input buffer
+ char *chunk_buffer, *chunk_buffer_end;
+
+ // cstate
+ // The current state of the chunk parsing state machine. We
+ // start out reading the size of the first chunk.
+ CHUNK_STATE cstate = READ_CHUNK_SIZE;
+
+ // cresult
+ // Holds the value of the result of a lexing operation performed
+ // on the input buffer.
+ CHUNK_LEX_RESULT cresult = LEX_OK;
+
+ CAtlIsapiBuffer<> result_buffer;
+
+ // Initialize pointers and allocate the chunk buffer.
+ chunk_buffer = chunk_buffer_end = NULL;
+ if( !t_chunk_buffer.Allocate(nTChunkBuffSize) )
+ return false;
+
+ // copy the headers into a temporary buffer.
+ result_buffer.Append(m_current + m_dwHeaderStart, m_dwHeaderLen);
+
+ // calculate number of bytes left in m_current past the headers
+ long leftover_in_m_current = m_current.GetLength() - (m_dwHeaderStart + m_dwHeaderLen);
+
+ // copy the extra bytes that might have been read into m_current into the chunk buffer
+ if (leftover_in_m_current > 0)
+ {
+ if (leftover_in_m_current > nTChunkBuffSize)
+ {
+ if( ! t_chunk_buffer.Reallocate(leftover_in_m_current) )
+ return false;
+ }
+
+ chunk_buffer = (char*)t_chunk_buffer;
+ Checked::memcpy_s(chunk_buffer, leftover_in_m_current, ((LPCSTR)m_current)+ m_dwHeaderStart + m_dwHeaderLen, leftover_in_m_current);
+ chunk_buffer_end = chunk_buffer + leftover_in_m_current;
+ }
+
+ m_current.Empty();
+ m_dwBodyLen = 0;
+ m_dwHeaderStart = 0;
+
+ // as we start the state machine, we should be either pointing at the first
+ // byte of chunked response or nothing, in which case we will need to get
+ // more data from the socket.
+ nChunkSize = 0;
+
+ bool bDone = false;
+
+ while(!bDone)
+ {
+ // if we run out of data during processing, chunk_buffer
+ // get set to null
+ if (!chunk_buffer ||
+ chunk_buffer >= chunk_buffer_end)
+ {
+ // we ran out of data in our input buffer, we need
+ // to read more from the socket.
+ DWORD dwReadBuffSize = nTChunkBuffSize - nChunkBuffCarryOver;
+ chunk_buffer = t_chunk_buffer;
+ if (!Read((const unsigned char*)(chunk_buffer+nChunkBuffCarryOver), &dwReadBuffSize))
+ {
+ ATLTRACE("ReadChunkedBody: Error reading from socket (%d)\n", GetLastError());
+ return false;
+ }
+ else if(dwReadBuffSize == 0)
+ {
+ ATLTRACE("ReadChunkedBody: The socket read timed out and no bytes were read from the socket.\n");
+ return false;
+ }
+#ifdef _DEBUG
+ ATLTRACE("ReadChunkedBody read %d bytes from socket. Reads %d \n", dwReadBuffSize, ++nReadCount);
+#endif
+ chunk_buffer_end = chunk_buffer + nChunkBuffCarryOver + dwReadBuffSize;
+ nChunkBuffCarryOver = 0;
+ }
+
+ switch(cstate)
+ {
+ case READ_CHUNK_SIZE:
+ {
+ cresult = get_chunked_size(chunk_buffer, chunk_buffer_end, &nChunkSize);
+ switch(cresult)
+ {
+ case LEX_ERROR:
+ ATLTRACE("ReadChunkedBody Failed retrieving chunk size\n");
+ return false;
+ break;
+ case LEX_OUTOFDATA:
+ nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
+ if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
+ chunk_buffer, chunk_buffer_end))
+ {
+ ATLTRACE("failed to move leftover chunk data to head of buffer\n");
+ return false;
+ }
+ chunk_buffer = chunk_buffer_end = NULL;
+ break;
+ case LEX_OK:
+ if (nChunkSize == 0)
+ {
+ cstate = CHUNK_READ_DATA_COMPLETE;
+ }
+ else if (nChunkSize + 2 > nTChunkBuffSize)
+ {
+ char *pBuffStart = (char*)t_chunk_buffer;
+ int nReadSoFar = (int)(chunk_buffer - pBuffStart);
+ int nTotal = (int)(chunk_buffer_end - pBuffStart);
+ if( FAILED(::ATL::AtlMultiply(&nTChunkBuffSize, nChunkSize, 2L)))
+ {
+ return false;
+ }
+ t_chunk_buffer.Reallocate(nTChunkBuffSize);
+ pBuffStart = (char*)t_chunk_buffer;
+ chunk_buffer = pBuffStart + nReadSoFar;
+ chunk_buffer_end = pBuffStart + nTotal;
+ cstate = READ_CHUNK_SIZE_FOOTER;
+ m_dwBodyLen += nChunkSize;
+ }
+ else
+ {
+ // everything is OK. move to next state
+ cstate = READ_CHUNK_SIZE_FOOTER;
+ m_dwBodyLen += nChunkSize;
+ }
+ break;
+ default:
+ ATLASSERT(0);
+ return false;
+ break;
+ }
+ }
+ break;
+ case READ_CHUNK_DATA:
+ {
+ char *pDataStart = NULL;
+ long nDataLen = 0;
+ cresult = LEX_OK;
+ cresult = get_chunked_data(chunk_buffer, chunk_buffer_end,
+ nChunkSize, &pDataStart, &nDataLen);
+ switch(cresult)
+ {
+ case LEX_ERROR:
+ ATLTRACE("ReadChunkedBody failed to retrieve chunk data\n");
+ return false;
+ break;
+ case LEX_OUTOFDATA:
+ nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
+ if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
+ chunk_buffer, chunk_buffer_end))
+ {
+ ATLTRACE("failed to move leftover chunk data to head of buffer\n");
+ return false;
+ }
+ chunk_buffer = chunk_buffer_end = NULL;
+ break;
+ case LEX_OK:
+ result_buffer.Append(pDataStart, nDataLen);
+ cstate = READ_CHUNK_DATA_FOOTER;
+ break;
+ default:
+ ATLASSERT(0);
+ return false;
+ }
+ }
+ break;
+ case READ_CHUNK_SIZE_FOOTER:
+ case READ_CHUNK_DATA_FOOTER:
+ {
+ cresult = consume_chunk_footer(chunk_buffer, chunk_buffer_end);
+ switch(cresult)
+ {
+ case LEX_OK:
+ cstate = (cstate == READ_CHUNK_SIZE_FOOTER) ? READ_CHUNK_DATA : READ_CHUNK_SIZE;
+ break;
+ case LEX_ERROR:
+ ATLTRACE("Error consuming chunk footer!\n");
+ return false;
+ break;
+ case LEX_OUTOFDATA:
+ nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
+ if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
+ chunk_buffer, chunk_buffer_end))
+ {
+ ATLTRACE("failed to move leftover chunk data to head of buffer\n");
+ return false;
+ }
+ chunk_buffer = chunk_buffer_end = NULL;
+ break;
+ default:
+ ATLASSERT(0);
+ return false;
+
+ }
+ }
+ break;
+ case CHUNK_READ_DATA_COMPLETE:
+ {
+ // We read the chunk of size 0
+ // consume the chunk footer.
+ DWORD dwLen = 0;
+ cresult = consume_chunk_footer(chunk_buffer, chunk_buffer_end);
+ if (GetHeaderValue((_T("Trailer")), NULL, &dwLen))
+ {
+ cstate = READ_CHUNK_TRAILER; // start reading trailer headers
+ break;
+ }
+ else
+ bDone = true;
+ }
+ break;
+ case READ_CHUNK_TRAILER:
+ cresult = consume_chunk_trailer(chunk_buffer, chunk_buffer_end);
+ switch(cresult)
+ {
+ case LEX_OK:
+ cstate = READ_CHUNK_TRAILER; // keep reading
+ break;
+ case LEX_ERROR:
+ ATLTRACE("Error consuming chunk trailers!\n");
+ return false;
+ break;
+ case LEX_OUTOFDATA:
+ nChunkBuffCarryOver = (long)(chunk_buffer_end - chunk_buffer);
+ if (!move_leftover_bytes((char*)t_chunk_buffer, nChunkBuffCarryOver,
+ chunk_buffer, chunk_buffer_end))
+ {
+ ATLTRACE("failed to move leftover chunk data to head of buffer\n");
+ return false;
+ }
+ chunk_buffer = chunk_buffer_end = NULL;
+ break;
+ case LEX_TRAILER_COMPLETE:
+ return true;
+ break;
+ default:
+ ATLASSERT(0);
+ return false;
+
+
+
+ }
+ break;
+
+ }
+ }
+ if (!m_current.Append((LPCSTR)result_buffer))
+ return false;
+
+ m_pEnd = ((BYTE*)(LPCSTR)m_current) + m_current.GetLength();
+
+ return true;
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ReconnectIfRequired() throw()
+{
+ CString strValue;
+ // if we have a keep-alive header then return true
+ // else we have to close and re-open the connection
+ if (GetHeaderValue(_T("Connection"), strValue))
+ {
+ if (!strValue.CompareNoCase(_T("keep-alive")))
+ return true; // server said keep connection open.
+ }
+ else
+ {
+ return true; // there was no 'Connection' header
+ }
+
+ if (!strValue.CompareNoCase(_T("close")))
+ {
+ Close();
+ ConnectSocket();
+ }
+ return false;
+}
+
+// Complete relative URLs and URLs
+// that have a missing path. These are common with redirect headers.
+// http://www.microsoft.com becomes http://www.microsoft.com/
+// localstart.asp becomes whatever our current (m_urlCurrent)
+// path is plus localstart.asp
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::CompleteURL(CString& strURL) throw()
+{
+ _ATLTRY
+ {
+ CString strUrlTemp = strURL;
+ strUrlTemp.Trim();
+ CUrl url;
+ bool bErr = false;
+ if (url.CrackUrl(strUrlTemp))
+ {
+ return true; // URL is already valid
+ }
+
+
+ // if we have a scheme and a host name but no
+ // path, then add the path of '/'
+ if (url.GetScheme() == ATL_URL_SCHEME_HTTP &&
+ url.GetHostNameLength() > 0 &&
+ !url.GetUrlPathLength() )
+ {
+ url.SetUrlPath(_T("/"));
+ bErr = true;
+ }
+ // if we have leading / (absolute path) (ex: /Test/bbb.asp) we can concatinate it
+ // to it to our current URL (m_urlCurrent) scheme and host
+ else if (strUrlTemp[0] == _T('/'))
+ {
+ url = m_urlCurrent;
+ url.SetUrlPath(strUrlTemp);
+ bErr = true;
+ }
+ // relative path (ex: bbb.asp) - we don't have a valid url
+ // and the first char is not /
+ // Get the url from our current URL (m_urlCurrent) and add
+ // our relative paths
+ else
+ {
+ CString szPath;
+ url = m_urlCurrent;
+
+ if (!url.GetUrlPathLength())
+ {
+ szPath = _T('/'); // current URL has no path!
+ }
+ else
+ {
+ szPath = url.GetUrlPath();
+ }
+
+ // back up to the first / and insert our current url
+ int pos = szPath.ReverseFind(_T('/'));
+ if(pos == -1)
+ {
+ return false;
+ }
+
+ szPath.GetBufferSetLength(pos+1);
+ szPath.ReleaseBuffer();
+
+ szPath += strURL;
+ url.SetUrlPath(szPath);
+ bErr = true;
+ }
+ if (!bErr)
+ {
+ return bErr;
+ }
+ DWORD dwLen = ATL_URL_MAX_PATH_LENGTH;
+
+ return url.CreateUrl(strURL.GetBuffer(ATL_URL_MAX_PATH_LENGTH),
+ &dwLen) ? true : false;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::ProcessObjectMoved() throw()
+{
+ _ATLTRY
+ {
+ // look for a location header
+ CString strValue;
+ CString strURLNew;
+ if (GetHeaderValue(_T("Location"), strValue))
+ {
+ CString strRedirectReqHeaders=m_pNavData->szExtraHeaders;
+ ReconnectIfRequired();
+ m_HeaderMap.RemoveAll();
+ m_current.Empty();
+
+
+ // create a new URL based on what is in the
+ // Location header and set it as this object's
+ // default Url
+ strURLNew = strValue;
+ CompleteURL(strURLNew);
+ CString strCurrHostName = m_urlCurrent.GetHostName();
+ ATL_URL_PORT nCurrPort=m_urlCurrent.GetPortNumber();
+
+ SetDefaultUrl((LPCTSTR)strURLNew, m_urlCurrent.GetPortNumber());
+ //If redirected (new url in strURLNew) to different host (server) or port, need a new socket.
+ if (m_urlCurrent.GetHostName()!=strCurrHostName || m_urlCurrent.GetPortNumber()!=nCurrPort)
+ {
+ Close();
+ ConnectSocket();
+ }
+ // build up a request.
+ CString strRequest;
+ BuildRequest(&strRequest,
+ m_strMethod,
+ strRedirectReqHeaders.GetString());
+
+ // send the request
+ DWORD dwSent = strRequest.GetLength();
+ DWORD dwAvailable = dwSent;
+ if (!Write((BYTE*)((LPCSTR)CT2A(strRequest.GetBuffer(dwAvailable))), &dwSent))
+ return false;
+ strRequest.ReleaseBuffer();
+
+ if (dwSent != dwAvailable)
+ return false;
+
+ // read the response
+ if (RR_OK == ReadHttpResponse())
+ {
+ if (m_pNavData)
+ ProcessStatus(m_pNavData->dwFlags);
+ }
+ }
+ return true;
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+}
+
+template<class TSocketClass>
+inline bool CAtlHttpClientT<TSocketClass>::_SetDefaultUrl(LPCTSTR szURL, short nPort) throw()
+{
+
+ if (szURL)
+ if (!m_urlCurrent.CrackUrl(szURL)) // re-inits the field of the CUrl first
+ return false;
+
+ ATL_URL_SCHEME currScheme = m_urlCurrent.GetScheme();
+ if ( currScheme != ATL_URL_SCHEME_HTTP &&
+ !TSocketClass::SupportsScheme(currScheme) )
+ return false; // only support HTTP
+
+ if (!m_urlCurrent.GetUrlPathLength())
+ {
+ // no path, default to /
+ m_urlCurrent.SetUrlPath(_T("/"));
+ }
+
+ if (!m_urlCurrent.GetHostNameLength())
+ {
+ // no server name
+ return false;
+ }
+
+ if (m_urlCurrent.GetPortNumber() == ATL_URL_INVALID_PORT_NUMBER)
+ m_urlCurrent.SetPortNumber(nPort);
+ return true;
+}
+
+template<class TSocketClass>
+inline int CAtlHttpClientT<TSocketClass>::GetStatus() throw()
+{
+ return m_nStatus;
+}
+
+template<class TSocketClass>
+inline LPCTSTR CAtlHttpClientT<TSocketClass>::GetMethod() throw()
+{
+ return m_strMethod;
+}
+
+template<class TSocketClass>
+inline BYTE* CAtlHttpClientT<TSocketClass>::GetPostData() throw()
+{
+ if (m_pNavData)
+ return m_pNavData->pData;
+ return NULL;
+}
+
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetPostDataLen() throw()
+{
+ if (m_pNavData)
+ return m_pNavData->dwDataLen;
+ return 0;
+}
+
+template<class TSocketClass>
+inline LPCTSTR CAtlHttpClientT<TSocketClass>::GetPostDataType() throw()
+{
+ if (m_pNavData)
+ return m_pNavData->szDataType;
+ return NULL;
+}
+
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetLastError() throw()
+{
+ return m_dwLastError;
+}
+
+template<class TSocketClass>
+inline const SOCKET& CAtlHttpClientT<TSocketClass>::GetSocket() throw()
+{
+ return const_cast<const SOCKET&>(m_socket);
+}
+
+template<class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::Close() throw()
+{
+ TSocketClass::Close();
+}
+
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::SetSocketTimeout(DWORD dwNewTimeout) throw()
+{
+ return TSocketClass::SetSocketTimeout(dwNewTimeout);
+}
+
+template<class TSocketClass>
+inline DWORD CAtlHttpClientT<TSocketClass>::GetSocketTimeout() throw()
+{
+ return TSocketClass::GetSocketTimeout();
+}
+
+template<class TSocketClass>
+inline void CAtlHttpClientT<TSocketClass>::AuthProtocolFailed(LPCTSTR szProto) throw()
+{
+ CAtlBaseAuthObject *pAuthObj = NULL;
+ _ATLTRY
+ {
+ if (m_AuthMap.Lookup(szProto, pAuthObj) && pAuthObj)
+ {
+ pAuthObj->m_bFailed = true;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+}
+
+template<class TSocketClass>
+inline const ATL_NAVIGATE_DATA* CAtlHttpClientT<TSocketClass>::GetCurrentNavdata()
+{
+ return m_pNavData;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// CNTLMAuthObject
+// NTLM Security Authorization functions
+//
+/////////////////////////////////////////////////////////////////////////////////
+inline CNTLMAuthObject::CNTLMAuthObject() throw() :
+ m_pSocket(NULL),
+ m_nMaxTokenSize(0),
+ m_pAuthInfo(NULL),
+ m_bProxy(false)
+{
+ SecInvalidateHandle(&m_hCredentials)
+}
+
+inline CNTLMAuthObject::CNTLMAuthObject(IAuthInfo *pAuthInfo) throw() :
+ m_pSocket(NULL),
+ m_nMaxTokenSize(0),
+ m_pAuthInfo(pAuthInfo)
+{
+ SecInvalidateHandle(&m_hCredentials)
+}
+
+inline CNTLMAuthObject::~CNTLMAuthObject() throw()
+{
+ if (!ATL_IS_INVALIDCREDHANDLE(m_hCredentials))
+ FreeCredentialsHandle(&m_hCredentials);
+}
+
+inline void CNTLMAuthObject::Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo) throw()
+{
+ m_pSocket = pSocket;
+ SetAuthInfo(pAuthInfo);
+}
+
+inline void CNTLMAuthObject::SetAuthInfo(IAuthInfo *pAuthInfo) throw()
+{
+ m_pAuthInfo = pAuthInfo;
+}
+
+inline bool CNTLMAuthObject::Authenticate(LPCTSTR /*szAuthTypes*/, bool bProxy) throw()
+{
+ m_bProxy = bProxy;
+ if (AcquireCredHandle())
+ return DoNTLMAuthenticate();
+ return false;
+}
+
+inline bool CNTLMAuthObject::AcquireCredHandle() throw()
+{
+ PSecPkgInfo pPackageInfo = NULL;
+ SECURITY_STATUS SecurityStatus = SEC_E_OK;
+
+ // Acquire a credentials handle on the NTLM security package
+ SecurityStatus = QuerySecurityPackageInfo(ATL_HTTP_AUTHTYPE_NTLM,
+ &pPackageInfo);
+
+ if (SecurityStatus != SEC_E_OK)
+ return false;
+
+ void *pAuthData = NULL;
+ CSecAuthIdentity CA;
+ if (m_pAuthInfo)
+ {
+ // if m_pAuthInfo has been set then the caller wants us
+ // to get credentials from them.
+ if (CA.Init(m_pAuthInfo))
+ pAuthData = static_cast<void*>(&CA);
+ }
+
+ SecurityStatus = AcquireCredentialsHandle(
+ 0,
+ pPackageInfo->Name,
+ SECPKG_CRED_OUTBOUND,
+ 0,
+ pAuthData,
+ 0,
+ 0,
+ &m_hCredentials,
+ &m_ts
+ );
+
+ m_nMaxTokenSize = pPackageInfo->cbMaxToken;
+ FreeContextBuffer(pPackageInfo);
+ return SecurityStatus == SEC_E_OK ? true : false;
+}
+
+inline bool CNTLMAuthObject::DoNTLMAuthenticate() throw()
+{
+ bool bRet = false;
+
+ m_CurrentRequestData = (*(const_cast<const ATL_NAVIGATE_DATA*>(m_pSocket->GetCurrentNavdata())));
+ // make sure we have a good credentials handle
+ ATLASSERT(!ATL_IS_INVALIDCREDHANDLE(m_hCredentials));
+ if (ATL_IS_INVALIDCREDHANDLE(m_hCredentials))
+ return false;
+
+ SECURITY_STATUS SecurityStatus = SEC_E_OK;
+
+ unsigned long ContextAttributes = 0;
+ CSecBufferDesc OutBufferDesc;
+ CtxtHandle SecurityContext;
+ SecInvalidateHandle(&SecurityContext);
+
+ // Create a SecBufferDesc with one buffer of m_nMaxTokenSize
+ if (!OutBufferDesc.AddBuffers(1, m_nMaxTokenSize))
+ return false;
+
+ SecurityStatus = InitializeSecurityContext(
+ &m_hCredentials,
+ 0,
+ NULL,
+ ISC_REQ_CONNECTION,
+ 0,
+ 0,
+ 0,
+ 0,
+ &SecurityContext,
+ OutBufferDesc,
+ &ContextAttributes,
+ &m_ts
+ );
+
+ if ( (SecurityStatus == SEC_I_COMPLETE_NEEDED) ||
+ (SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE) )
+ {
+ SecurityStatus = CompleteAuthToken( &SecurityContext, (PSecBufferDesc)OutBufferDesc);
+ }
+
+ if (IS_ERROR(SecurityStatus))
+ return false;
+
+ // create an Authentication header with the contents of the
+ // security buffer and send it to the HTTP server. The output
+ // buffer will be pointing to a buffer that contains the
+ // response from the HTTP server on return.
+ LPSTR pszbuff = NULL;
+ if (!SendSecurityInfo(OutBufferDesc.Buffers(0), &pszbuff) || !pszbuff)
+ return false;
+
+ CString strVal;
+ if (!m_pSocket->GetHeaderValue(m_bProxy ? g_pszProxyAuthenticate : g_pszWWWAuthenticate, strVal))
+ return false; // wrong authentication type
+
+ LPCTSTR szResponsecode = strVal;
+ TCHAR pszcode[ATL_AUTH_HDR_SIZE];
+ if (szResponsecode)
+ {
+ // first four characters better be 'NTLM'
+ if (_tcsncicmp(szResponsecode, _T("NTLM"), 4) != 0)
+ return false;
+
+ // skip NTLM
+ szResponsecode += 4;
+
+ // skip space
+ while (*szResponsecode && _AtlIsHttpSpace(*szResponsecode))
+ szResponsecode++;
+
+ // find end of header
+ LPCTSTR pszend = szResponsecode;
+ while (*pszend && *pszend != _T('\r'))
+ pszend++;
+ bRet = false;
+ if (pszend)
+ {
+ // copy authentication data to our buffer
+ // and base64decode it.
+ int nlen = (int)(pszend-szResponsecode);
+ Checked::memcpy_s(pszcode, ATL_AUTH_HDR_SIZE, szResponsecode, nlen*sizeof(TCHAR));
+ pszcode[pszend-szResponsecode]=0;
+
+ // re-use OutBufferDesc here since we'll need to need
+ // a SecBufferDesc to pass to the next call to InitializeSecurityContext
+ // anyways.
+ if(!OutBufferDesc.Buffers(0)->ClearBuffer(m_nMaxTokenSize))
+ return false;
+
+ _ATLTRY
+ {
+ CT2A pszcode_a(pszcode);
+ bRet = Base64Decode(pszcode_a,
+ (int) strlen(pszcode_a),
+ (BYTE*)OutBufferDesc.Buffers(0)->pvBuffer,
+ (int*) &OutBufferDesc.Buffers(0)->cbBuffer) != FALSE;
+ }
+ _ATLCATCHALL()
+ {
+ bRet = false;
+ }
+ }
+
+ if (!bRet)
+ return false;
+
+ // Create buffers for the challenge data
+ CSecBufferDesc *InBufferDesc = &OutBufferDesc;
+ CSecBufferDesc OutBufferDesc2;
+ if (!OutBufferDesc2.AddBuffers(1, m_nMaxTokenSize))
+ return false;
+
+ // Process the challenge response from the server
+ SecurityStatus = InitializeSecurityContext(
+ 0,
+ &SecurityContext,
+ NULL,
+ 0,
+ 0,
+ 0 ,
+ InBufferDesc,
+ 0,
+ &SecurityContext,
+ OutBufferDesc2,
+ &ContextAttributes,
+ &m_ts
+ );
+
+ if (IS_ERROR(SecurityStatus))
+ return false;
+
+ pszbuff = NULL;
+ if (SendSecurityInfo(OutBufferDesc2.Buffers(0), &pszbuff))
+ {
+ // at this point we should be authenticated and either have the page
+ // we requested or be getting re-directed to another page under our
+ // authorization. Either way, we don't want to go through authorization
+ // code again if we are not authorized to prevent recursive authorization
+ // so we tell the client not to try this protocol again.
+ if (m_pSocket->GetStatus() == 401 ||
+ m_pSocket->GetStatus() == 407)
+ {
+ // Authorization with this protocol failed.
+ // don't try it again.
+ m_pSocket->AuthProtocolFailed(_T("NTLM"));
+ }
+ bRet = m_pSocket->ProcessStatus(m_pSocket->GetFlags());
+ }
+ }
+
+ return bRet;
+}
+inline bool CNTLMAuthObject::GetCredentialNames(CString& theName)
+{
+ if (ATL_IS_INVALIDCREDHANDLE(m_hCredentials))
+ return false;
+
+ SecPkgCredentials_Names spcn;
+ if(!IS_ERROR(QueryCredentialsAttributes(&m_hCredentials,
+ SECPKG_CRED_ATTR_NAMES, (void*)&spcn)))
+ {
+ theName = spcn.sUserName;
+ return true;
+ }
+ return false;
+}
+
+inline bool CNTLMAuthObject::SendSecurityInfo(SecBuffer *pSecBuffer, LPSTR *pszBuffer) throw()
+{
+ ATLASSERT(pSecBuffer);
+ ATLASSUME(m_pSocket);
+ ATLASSERT(pszBuffer);
+
+ int nDest = ATL_AUTH_HDR_SIZE;
+ char auth_b64encoded[ATL_AUTH_HDR_SIZE];
+ char auth_header[ATL_AUTH_HDR_SIZE];
+ const char *pszFmtStr = m_bProxy ? m_pszFmtProxy : m_pszFmtWWW;
+
+ if (!pSecBuffer || !pSecBuffer->pvBuffer || !pszBuffer)
+ return false;
+ *pszBuffer = 0;
+
+ // Base64Encode will fail gracefully if buffer not big enough
+ if (Base64Encode((BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer,
+ auth_b64encoded, &nDest, ATL_BASE64_FLAG_NOCRLF))
+ {
+ if (nDest < ATL_AUTH_HDR_SIZE)
+ {
+ auth_b64encoded[nDest]=0;
+ // make sure we have enough room in our header buffer
+ if ( (strlen(pszFmtStr)-2 + nDest) < ATL_AUTH_HDR_SIZE)
+ sprintf_s(auth_header, ATL_AUTH_HDR_SIZE, pszFmtStr, auth_b64encoded);
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+
+ // reset the connection if required
+ m_pSocket->ResetConnection();
+
+ // Resend the request with the authorization information
+ LPCURL pUrl = m_pSocket->GetCurrentUrl();
+ bool bRet = false;
+
+ TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
+ if( ! pUrl->CreateUrl(szUrl, &dwMaxLen) )
+ return false;
+
+ _ATLTRY
+ {
+ CA2CT hdr(auth_header);
+ CAtlNavigateData navigate_data(m_CurrentRequestData);
+ // append authorization header to extra headers
+ CString strHeaders = navigate_data.GetExtraHeaders();
+ strHeaders += hdr;
+ navigate_data.SetExtraHeaders(strHeaders);
+ navigate_data.RemoveFlags(ATL_HTTP_FLAG_PROCESS_RESULT);
+
+ bRet = m_pSocket->Navigate( szUrl, &navigate_data);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = false;
+ }
+ if (bRet)
+ *pszBuffer = (LPSTR)m_pSocket->GetResponse();
+ return bRet;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// CBasicAuthObject
+// BASIC Security Authorization functions
+//
+/////////////////////////////////////////////////////////////////////////////////
+inline bool CBasicAuthObject::DoBasicAuthenticate() throw()
+{
+ bool bRet = false;
+ ATLASSUME(m_pClient);
+ ATLASSUME(m_pAuthInfo);
+ // Create an authentication string
+ CTempBuffer<TCHAR, (_ATL_MAX_AUTH_BUFF*2)+2> auth_string;
+ CAuthInfoBuffType buffUID;
+ CAuthInfoBuffType buffPWD;
+
+ DWORD dwUID=0,dwPWD=0;
+ if (!_AtlGetAuthInfoHelper(m_pAuthInfo, &IAuthInfo::GetPassword, buffPWD, &dwPWD) ||
+ !_AtlGetAuthInfoHelper(m_pAuthInfo, &IAuthInfo::GetUsername, buffUID, &dwUID))
+ return false;
+
+ _ATLTRY
+ {
+ if (!auth_string.Allocate((_ATL_MAX_AUTH_BUFF*2)+2))
+ return false;
+
+ Checked::tcscpy_s(auth_string, _ATL_MAX_AUTH_BUFF, buffUID);
+ Checked::tcscat_s(auth_string, _ATL_MAX_AUTH_BUFF, _T(":"));
+ Checked::tcscat_s(auth_string, _ATL_MAX_AUTH_BUFF, buffPWD);
+
+ // Base64 encode the auth string
+ char *auth_string_enc = NULL;
+ CTempBuffer<char, 512> auth_string_buff;
+ CT2A auth_string_a(auth_string);
+
+ int nLen = Base64EncodeGetRequiredLength((int)strlen((LPSTR)auth_string_a));
+ auth_string_buff.Allocate(nLen+1);
+ if (!((char*)auth_string_buff))
+ return false;
+
+ auth_string_enc = (char*)auth_string_buff;
+ if (!Base64Encode((const BYTE*)(LPSTR)auth_string_a, (int)strlen((LPSTR)auth_string_a),
+ auth_string_enc, &nLen, ATL_BASE64_FLAG_NOCRLF))
+ return false;
+ auth_string_buff[nLen]=0;
+
+ // Format the Authentication header
+ int nLenFmt = (m_bProxy ? (int)strlen(m_pszFmtProxy) : (int)strlen(m_pszFmtWWW)) + 2;
+ nLen += nLenFmt;
+ ++nLen; // Space for '\0'
+
+ CTempBuffer<char, 512> auth_header_buff;
+ ATLTRY(auth_header_buff.Allocate(nLen));
+ if (!((char*)auth_header_buff))
+ return false;
+
+ char *auth_header = (char*)auth_header_buff;
+ Checked::strcpy_s(auth_header, nLen, m_bProxy ? m_pszFmtProxy : m_pszFmtWWW);
+ Checked::strcat_s(auth_header, nLen, auth_string_enc);
+ Checked::strcat_s(auth_header, nLen, "\r\n");
+
+ // Resend the request with the authorization information
+ LPCURL pUrl = m_pClient->GetCurrentUrl();
+ TCHAR szUrl[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwMaxLen = ATL_URL_MAX_URL_LENGTH;
+ pUrl->CreateUrl(szUrl, &dwMaxLen);
+
+ // reset the connection if required
+ m_pClient->ResetConnection();
+
+ CA2T hdr(auth_header);
+ CAtlNavigateData navigate_data(*(const_cast<const ATL_NAVIGATE_DATA*>(m_pClient->GetCurrentNavdata())));
+ // append authorization header to extra headers
+ CString strHeaders = navigate_data.GetExtraHeaders();
+ strHeaders += hdr;
+ navigate_data.SetExtraHeaders(strHeaders);
+ navigate_data.RemoveFlags(ATL_HTTP_FLAG_PROCESS_RESULT);
+ bRet = m_pClient->Navigate( szUrl,
+ &navigate_data);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = false;
+ }
+
+ if (bRet)
+ {
+ // Request was successfully sent. Process the result.
+ if (m_pClient->GetStatus() == 401 ||
+ m_pClient->GetStatus() == 407)
+ {
+ // Authorization with this protocol failed.
+ // don't try it again.
+ m_pClient->AuthProtocolFailed(_T("basic"));
+ }
+ bRet = m_pClient->ProcessStatus(m_pClient->GetFlags());
+ }
+ return bRet;
+}
+
+inline CBasicAuthObject::CBasicAuthObject() throw()
+{
+ m_pClient = NULL;
+ m_pAuthInfo = NULL;
+ m_szRealm[0] = 0;
+ m_bProxy = false;
+}
+
+inline CBasicAuthObject::CBasicAuthObject(IAuthInfo *pAuthInfo) throw()
+{
+ m_pAuthInfo = pAuthInfo;
+ m_pClient = NULL;
+}
+
+inline void CBasicAuthObject::SetAuthInfo(IAuthInfo *pAuthInfo) throw()
+{
+ m_pAuthInfo = pAuthInfo;
+}
+
+// Called by the CAtlHttpClient class to
+// authenticate a user.
+inline bool CBasicAuthObject::Authenticate(LPCTSTR szAuthTypes, bool bProxy) throw()
+{
+ if (lstrlen(szAuthTypes) > ATL_AUTH_HDR_SIZE)
+ return false;
+
+ m_bProxy = bProxy;
+
+ if (!CrackRealm(szAuthTypes))
+ return false;
+ return DoBasicAuthenticate();
+}
+
+inline LPCTSTR CBasicAuthObject::GetRealm() throw()
+{
+ return const_cast<LPCTSTR>(m_szRealm);
+}
+
+// Called by the CAtlHttpClient class to initialize
+// this authentication object.
+inline void CBasicAuthObject::Init(CAtlHttpClient *pSocket, IAuthInfo *pAuthInfo) throw()
+{
+ ATLASSERT(pSocket);
+ m_pClient = pSocket;
+ if (pAuthInfo)
+ SetAuthInfo(pAuthInfo);
+}
+
+inline bool CBasicAuthObject::CrackRealm(LPCTSTR szHeader) throw()
+{
+ // szHeader is pointing at the
+ // "basic" in the header
+ // see if realm is available
+ const TCHAR *pStart = szHeader;
+
+ // skip "basic"
+ pStart += 5;
+
+ // skip space
+ while (*pStart && _AtlIsHttpSpace(*pStart))
+ pStart++;
+
+ // are we pointing at 'realm'?
+ if ((*pStart == 'r' || *pStart == 'R') &&
+ (*(pStart+1) == 'e' || *(pStart+1) == 'E') &&
+ (*(pStart+2) == 'a' || *(pStart+2) == 'A') &&
+ (*(pStart+3) == 'l' || *(pStart+3) == 'L') &&
+ (*(pStart+4) == 'm' || *(pStart+4) == 'M'))
+ {
+ // skip 'realm'
+ pStart += 5;
+
+ // skip space
+ while (*pStart && _AtlIsHttpSpace(*pStart))
+ pStart++;
+
+ // skip '='
+ if (*pStart && *pStart == _T('='))
+ pStart++;
+ else
+ return false; // invalid realm
+
+ // skip space
+ while (*pStart && _AtlIsHttpSpace(*pStart))
+ pStart++;
+
+ // skip quotes if they are there
+ if (*pStart == '\"')
+ pStart++;
+
+ const TCHAR *pEnd = pStart;
+ while (*pEnd && *pEnd != '\"')
+ {
+ if (*pEnd == '\\' && *(pEnd + 1)) // escaped character, skip it
+ pEnd += 2;
+ else
+ pEnd++;
+ }
+
+ if (*pEnd == '\"' && *(pEnd+1) != '\0')
+ return false; //trailing junk after the quoted realm
+
+ if (*pEnd=='\0' || *pEnd =='\"')
+ {
+ int nLen = (int)(pEnd-pStart);
+ if (nLen < MAX_REALM_LEN)
+ {
+ Checked::tcsncpy_s(m_szRealm, _countof(m_szRealm), pStart, nLen);
+ m_szRealm[nLen]=0;
+ if (!AtlUnescapeUrl(m_szRealm, m_szRealm, NULL, MAX_REALM_LEN))
+ return false; // error unescaping the string
+ }
+ else
+ return false;
+ }
+ }
+ return true;
+}
+
+inline CAtlBaseAuthObject::CAtlBaseAuthObject()
+{
+ m_bFailed = false;
+}
+
+
+inline CAtlNavigateData::CAtlNavigateData() throw()
+{
+ dwFlags = ATL_HTTP_FLAG_AUTO_REDIRECT|
+ ATL_HTTP_FLAG_PROCESS_RESULT|
+ ATL_HTTP_FLAG_SEND_BLOCKS;
+ szExtraHeaders = NULL;
+ szMethod = ATL_HTTP_METHOD_GET;
+ nPort = ATL_URL_DEFAULT_HTTP_PORT;
+ pData = NULL;
+ dwDataLen = 0;
+ szDataType = NULL;
+ dwTimeout = ATL_SOCK_TIMEOUT;
+ dwSendBlockSize = ATL_HTTP_DEFAULT_BLOCK_SIZE;
+ dwReadBlockSize = ATL_HTTP_DEFAULT_BLOCK_SIZE;
+ pfnChunkCallback = NULL;
+ pfnSendStatusCallback = NULL;
+ pfnReadStatusCallback = NULL;
+ m_lParamSend = 0;
+ m_lParamRead = 0;
+}
+
+inline CAtlNavigateData::CAtlNavigateData(const CAtlNavigateData &rhs)
+{
+ this->operator=(rhs);
+}
+
+inline CAtlNavigateData::CAtlNavigateData(const ATL_NAVIGATE_DATA &rhs)
+{
+ this->operator=(rhs);
+}
+
+inline CAtlNavigateData& CAtlNavigateData::operator=(const CAtlNavigateData &rhs)
+{
+ return this->operator=(static_cast<const ATL_NAVIGATE_DATA&>(rhs));
+}
+
+inline CAtlNavigateData& CAtlNavigateData::operator=(const ATL_NAVIGATE_DATA &rhs)
+{
+ dwFlags = rhs.dwFlags;
+ szExtraHeaders = rhs.szExtraHeaders;
+ szMethod = rhs.szMethod;
+ nPort = rhs.nPort;
+ pData = rhs.pData;
+ dwDataLen = rhs.dwDataLen;
+ szDataType = rhs.szDataType;
+ dwTimeout = rhs.dwTimeout;
+ dwSendBlockSize = rhs.dwSendBlockSize;
+ dwReadBlockSize = rhs.dwReadBlockSize;
+ pfnChunkCallback = rhs.pfnChunkCallback;
+ pfnSendStatusCallback = rhs.pfnSendStatusCallback;
+ pfnReadStatusCallback = rhs.pfnReadStatusCallback;
+ m_lParamSend = rhs.m_lParamSend;
+ m_lParamRead = rhs.m_lParamRead;
+ return *this;
+}
+
+inline DWORD CAtlNavigateData::SetFlags(DWORD dwNewFlags) throw()
+{
+ // check for mutually exclusive flags
+ if ((dwNewFlags & ATL_HTTP_FLAG_SEND_CALLBACK) &&
+ (dwNewFlags & ATL_HTTP_FLAG_SEND_BLOCKS))
+ {
+ ATLASSERT(0);
+ return ATL_HTTP_FLAG_INVALID_FLAGS;
+ }
+
+ DWORD dwOldFlags = dwFlags;
+ dwFlags = dwNewFlags;
+ return dwOldFlags;
+}
+
+inline DWORD CAtlNavigateData::GetFlags() throw()
+{
+ return dwFlags;
+}
+
+inline DWORD CAtlNavigateData::AddFlags(DWORD dwFlagsToAdd) throw()
+{
+ // check for mutually exclusive flags
+ if (
+ ((dwFlagsToAdd & ATL_HTTP_FLAG_SEND_CALLBACK) &&
+ (dwFlags & ATL_HTTP_FLAG_SEND_BLOCKS)) ||
+ ((dwFlagsToAdd & ATL_HTTP_FLAG_SEND_BLOCKS) &&
+ (dwFlags & ATL_HTTP_FLAG_SEND_CALLBACK))
+ )
+ {
+ ATLASSERT(0);
+ return ATL_HTTP_FLAG_INVALID_FLAGS;
+ }
+
+ DWORD dwOldFlags = dwFlags;
+ dwFlags |= dwFlagsToAdd;
+ return dwOldFlags;
+}
+
+inline DWORD CAtlNavigateData::RemoveFlags(DWORD dwFlagsToRemove) throw()
+{
+ DWORD dwOldFlags = dwFlags;
+ dwFlags &= ~dwFlagsToRemove;
+ return dwOldFlags;
+}
+
+inline LPCTSTR CAtlNavigateData::SetExtraHeaders(LPCTSTR szNewHeaders) throw()
+{
+ LPCTSTR szold = szExtraHeaders;
+ szExtraHeaders = szNewHeaders;
+ return szold;
+}
+
+inline LPCTSTR CAtlNavigateData::GetExtraHeaders() throw()
+{
+ return szExtraHeaders;
+}
+inline LPCTSTR CAtlNavigateData::SetMethod(LPCTSTR szNewMethod) throw()
+{
+ LPCTSTR szold = szMethod;
+ szMethod = szNewMethod;
+ return szold;
+}
+inline LPCTSTR CAtlNavigateData::GetMethod() throw()
+{
+ return szMethod;
+}
+inline short CAtlNavigateData::SetPort(short newPort) throw()
+{
+ short oldport = nPort;
+ nPort = newPort;
+ return oldport;
+}
+inline short CAtlNavigateData::GetPort() throw()
+{
+ return nPort;
+}
+inline void CAtlNavigateData::SetPostData(BYTE *pd, DWORD len, LPCTSTR type) throw()
+{
+ pData = pd;
+ dwDataLen = len;
+ szDataType = type;
+}
+
+inline DWORD CAtlNavigateData::SetSocketTimeout(DWORD dwNewTimeout) throw()
+{
+ DWORD dwold = dwTimeout;
+ dwTimeout = dwNewTimeout;
+ return dwold;
+}
+inline DWORD CAtlNavigateData::GetSocketTimeout() throw()
+{
+ return dwTimeout;
+}
+inline DWORD CAtlNavigateData::SetSendBlockSize(DWORD dwNewBlockSize) throw()
+{
+ DWORD dwold = dwSendBlockSize;
+ dwSendBlockSize = dwNewBlockSize;
+ return dwold;
+}
+inline DWORD CAtlNavigateData::GetSendBlockSize() throw()
+{
+ return dwSendBlockSize;
+}
+
+inline DWORD CAtlNavigateData::SetReadBlockSize(DWORD dwNewBlockSize) throw()
+{
+ DWORD dwold = dwReadBlockSize;
+ dwReadBlockSize = dwNewBlockSize;
+ return dwold;
+}
+
+inline DWORD CAtlNavigateData::GetReadBlockSize() throw()
+{
+ return dwReadBlockSize;
+}
+
+inline PFNATLCHUNKEDCB CAtlNavigateData::SetChunkCallback(PFNATLCHUNKEDCB pfn, DWORD_PTR dwParam) throw()
+{
+ PFNATLCHUNKEDCB pold = pfnChunkCallback;
+ pfnChunkCallback = pfn;
+ m_lParamChunkCB = dwParam;
+ return pold;
+}
+inline PFNATLCHUNKEDCB CAtlNavigateData::GetChunkCallback() throw()
+{
+ return pfnChunkCallback;
+}
+
+inline PFNATLSTATUSCALLBACK CAtlNavigateData::SetSendStatusCallback(PFNATLSTATUSCALLBACK pfn, DWORD_PTR dwData) throw()
+{
+ PFNATLSTATUSCALLBACK pold = pfnSendStatusCallback;
+ pfnSendStatusCallback = pfn;
+ m_lParamSend = dwData;
+ return pold;
+}
+
+inline PFNATLSTATUSCALLBACK CAtlNavigateData::GetSendStatusCallback() throw()
+{
+ return pfnSendStatusCallback;
+}
+
+inline PFNATLSTATUSCALLBACK CAtlNavigateData::SetReadStatusCallback(PFNATLSTATUSCALLBACK pfn, DWORD_PTR dwData) throw()
+{
+ PFNATLSTATUSCALLBACK pOld = pfnReadStatusCallback;
+ pfnReadStatusCallback = pfn;
+ m_lParamRead = dwData;
+ return pOld;
+}
+
+inline PFNATLSTATUSCALLBACK CAtlNavigateData::GetReadStatusCallback() throw()
+{
+ return pfnReadStatusCallback;
+}
+
+} // namespace ATL
+
+#pragma warning(pop)
+
+#endif // __ATLHTTP_INL__
diff --git a/include/atl/atlisapi.h b/include/atl/atlisapi.h
new file mode 100644
index 000000000..e812aecc5
--- /dev/null
+++ b/include/atl/atlisapi.h
@@ -0,0 +1,10609 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSTENCIL_H__
+#include <atlstencil.h>
+#endif
+
+#ifndef __ATLISAPI_H__
+#define __ATLISAPI_H__
+
+#pragma once
+#include <atlbase.h>
+#include <time.h> // needed for cookie support
+#include <httpext.h> // needed for ECB and IIS support
+#include <atlspriv.h>
+#include <atlserr.h>
+#include <atlfile.h>
+#include <atlstr.h>
+#include <atldbcli.h>
+#include <atlutil.h>
+#include <atlcache.h>
+#include <atlsrvres.h>
+#include <atlsiface.h>
+#include <objbase.h>
+#include <atlsecurity.h>
+#include <errno.h>
+#ifndef ATL_NO_SOAP
+ #include <msxml2.h>
+#endif
+#ifndef ATL_NO_ACLAPI
+ #include <aclapi.h>
+#endif
+#ifndef ATL_NO_MMSYS
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+#include <mmsystem.h>
+#pragma warning(pop)
+#ifndef _ATL_NO_DEFAULT_LIBS
+#pragma comment(lib, "winmm.lib")
+#ifndef ATL_NO_SOAP
+#pragma comment(lib, "msxml2.lib")
+#endif
+#endif // !_ATL_NO_DEFAULT_LIBS
+#endif
+#include <atlpath.h>
+
+#pragma warning(push)
+#pragma warning(disable: 4291) // allow placement new
+#pragma warning(disable: 4127) // conditional expression is constant
+#pragma warning(disable: 4511) // copy constructor could not be generated
+#pragma warning(disable: 4512) // assignment operator could not be generated
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+#pragma warning(disable: 4191) // unsafe conversion from 'functionptr1' to 'functionptr2'
+#pragma warning(disable: 4702) // unreachable code
+
+#include <initguid.h>
+#include <dbgautoattach.h>
+
+#ifndef SESSION_COOKIE_NAME
+ #define SESSION_COOKIE_NAME "SESSIONID"
+#endif
+
+// override this if you want to use a different CLSID for SAX
+#ifndef ATLS_SAXXMLREADER_CLSID
+ #define ATLS_SAXXMLREADER_CLSID __uuidof(SAXXMLReader)
+#endif // ATLS_SAXXMLREADER_CLSID
+
+
+// This function is used in CValidateObject to determine if an empty
+// request parameter really should be empty. You can
+// specialize this function in your own code such as
+// the following specialization for type long:
+// template <>
+// inline bool IsNullByType<long>(long type) throw()
+// {
+// return type == 0;
+// }
+// You should provide your own specialization for this
+// function if the comparison of type==0 is not adequate
+// to discover whether or not your type is 0.
+template <class TComp>
+inline bool IsNullByType(__in TComp type) throw()
+{
+ return type == 0;
+}
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+// Default file extension for server response files
+#ifndef ATL_DEFAULT_STENCIL_EXTENSION
+ #define ATL_DEFAULT_STENCIL_EXTENSION ".srf"
+#endif
+extern __declspec(selectany) const char * const c_AtlSRFExtension = ATL_DEFAULT_STENCIL_EXTENSION;
+extern __declspec(selectany) const TCHAR * const c_tAtlSRFExtension = _T(ATL_DEFAULT_STENCIL_EXTENSION);
+#define ATLS_EXTENSION_LEN (sizeof(ATL_DEFAULT_STENCIL_EXTENSION)-2)
+
+// Default file extension for handler DLLs
+#ifndef ATL_DEFAULT_DLL_EXTENSION
+ #define ATL_DEFAULT_DLL_EXTENSION ".dll"
+#endif
+extern __declspec(selectany) const char * const c_AtlDLLExtension = ATL_DEFAULT_DLL_EXTENSION;
+extern __declspec(selectany) const TCHAR * const c_tAtlDLLExtension = _T(ATL_DEFAULT_DLL_EXTENSION);
+#define ATLS_DLL_EXTENSION_LEN (sizeof(ATL_DEFAULT_DLL_EXTENSION)-2)
+
+// maximum handler name length
+#ifndef ATL_MAX_HANDLER_NAME_LEN
+ #define ATL_MAX_HANDLER_NAME_LEN 64
+#endif
+
+#ifndef ATL_HANDLER_NAME_DEFAULT
+#define ATL_HANDLER_NAME_DEFAULT "Default"
+#endif // ATL_DEFAULT_HANDLER_NAME
+
+
+// maximum timeout for async guard mutex
+#ifndef ATLS_ASYNC_MUTEX_TIMEOUT
+ #define ATLS_ASYNC_MUTEX_TIMEOUT 10000
+#endif
+
+#if defined(_M_IA64) || defined (_M_AMD64)
+#define ATLS_FUNCID_INITIALIZEHANDLERS "InitializeAtlHandlers"
+#define ATLS_FUNCID_GETATLHANDLERBYNAME "GetAtlHandlerByName"
+#define ATLS_FUNCID_UNINITIALIZEHANDLERS "UninitializeAtlHandlers"
+#elif defined(_M_IX86)
+#define ATLS_FUNCID_INITIALIZEHANDLERS "_InitializeAtlHandlers@8"
+#define ATLS_FUNCID_GETATLHANDLERBYNAME "_GetAtlHandlerByName@12"
+#define ATLS_FUNCID_UNINITIALIZEHANDLERS "_UninitializeAtlHandlers@0"
+#else
+#error Unknown Platform.
+#endif
+
+#define ATL_MAX_COOKIE_LEN 2048
+#define ATL_MAX_COOKIE_ELEM 1024
+
+
+// Defines a small value used for comparing the equality of floating point numbers.
+#ifndef ATL_EPSILON
+ #define ATL_EPSILON .0001
+#endif
+
+#ifndef ATL_DEFAULT_PRECISION
+ #define ATL_DEFAULT_PRECISION 6
+#endif
+
+// Call this function to URL-encode a buffer and have the result appended to a CString passed by reference.
+//
+// A space in the input string is encoded as a plus sign (+).
+// Other unsafe characters (as determined by AtlIsUnsafeUrlChar) are encoded as escaped octets.
+// An escaped octet is a percent sign (%) followed by two digits representing the hexadecimal code of the character.
+//
+// string A CStringA reference to which will be appended the encoded version of szBuf.
+//
+// szBuf The string to be URL-encoded.
+ATL_NOINLINE inline bool EscapeToCString(__inout CStringA& string, __in LPCSTR szBuf)
+{
+ ATLENSURE( szBuf != NULL );
+
+ _ATLTRY
+ {
+ CHAR szEscaped[512];
+ LPSTR pszStr = szEscaped;
+ DWORD dwLen = 0;
+
+ while (*szBuf)
+ {
+ if (dwLen+4 >= _countof(szEscaped))
+ {
+ *pszStr = '\0';
+ string.Append(szEscaped, dwLen);
+ pszStr = szEscaped;
+ dwLen = 0;
+ }
+ if (AtlIsUnsafeUrlChar(*szBuf))
+ {
+ if (*szBuf == ' ')
+ {
+ dwLen++;
+ *pszStr++ = '+';
+ }
+ else
+ {
+ LPSTR pszTmp = pszStr;
+ *pszTmp++ = '%';
+ unsigned char ch = (unsigned char)*szBuf;
+ if (ch < 16)
+ {
+ *pszTmp++ = '0';
+ }
+ Checked::ultoa_s((unsigned char)ch, pszTmp, szEscaped + _countof(szEscaped) - pszTmp, 16);
+ pszStr+= sizeof("%FF")-1;
+ dwLen+= sizeof("%FF")-1;
+ }
+ }
+ else
+ {
+ *pszStr++ = *szBuf;
+ dwLen++;
+ }
+ szBuf++;
+ }
+
+ *pszStr = '\0';
+ string.Append(szEscaped, dwLen);
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// UNICODE overload for EscapeToCString
+// follow specifications detailed in RFC document on
+// Internationalized Uniform Resource Identifiers (IURI)
+inline bool EscapeToCString(__inout CStringA& string, __in_z LPCWSTR wszBuf) throw()
+{
+ _ATLTRY
+ {
+ // convert string to UTF8
+ CFixedStringT<CStringA, 2048> strConvert;
+
+ // get the required length for conversion
+ int nSrcLen = (int) wcslen(wszBuf);
+ int nLen = AtlUnicodeToUTF8(wszBuf, nSrcLen, NULL, 0);
+ if (!nLen)
+ {
+ return false;
+ }
+
+ // allocate MBCS conversion string
+ LPSTR sz = strConvert.GetBuffer(nLen+1);
+ if (!sz)
+ {
+ return false;
+ }
+
+ // do the UNICODE to UTF8 conversion
+ nLen = AtlUnicodeToUTF8(wszBuf, nSrcLen, sz, nLen);
+ if (!nLen)
+ {
+ return false;
+ }
+
+ // null-terminate
+ sz[nLen] = '\0';
+
+ // delegate to ANSI version of EscapeToCString
+ if (!EscapeToCString(string, sz))
+ {
+ return false;
+ }
+
+ strConvert.ReleaseBuffer(nLen);
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+
+ return true;
+}
+
+struct CDefaultErrorProvider
+{
+ struct HTTP_ERROR_TEXT
+ {
+ UINT uHttpError; // the Http Error value
+ UINT uHttpSubError; // Allows for customization of error text based on srf specific errors.
+ LPCSTR szHeader; // the string that should appear in the http response header
+ UINT uResId; // the resource id of the string to send back as the body
+ };
+
+
+ // GetErrorText retrieves the http response header string
+ // and a resource id of the response body for a given
+ // http error code
+ // uError: Http error code to retrieve information for
+ // ppszHeader: pointer to LPCSTR that receives the response header string
+ // ppszHeader is optional
+ // puResId: pointer to UINT that receives the response body resource id
+ // puResId is optional
+ static BOOL GetErrorText(__in UINT uError, __in UINT uSubErr, __deref_out_opt LPCSTR *ppszHeader, __out_opt UINT *puResId) throw()
+ {
+ static const HTTP_ERROR_TEXT s_Errors[] =
+ {
+ { 200, SUBERR_NONE, "OK", 0 },
+ { 201, SUBERR_NONE, "Created", 0 },
+ { 202, SUBERR_NONE, "Accepted", 0 },
+ { 203, SUBERR_NONE, "Non-Authoritative Information", 0 },
+ { 204, SUBERR_NONE, "No Content", 0 },
+ { 204, DBG_SUBERR_ALREADY_DEBUGGING, "Already being debugged by another user", 0},
+ { 204, DBG_SUBERR_NOT_DEBUGGING, "Not currently debugging a process", 0},
+ { 204, DBG_SUBERR_INVALID_SESSION, "Requested DebugSessionID does not match current DebugSessionID", 0},
+ { 204, DBG_SUBERR_BAD_ID, "DebugSessionID corrupted or not provided", 0 },
+ { 204, DBG_SUBERR_COCREATE, "Could not CoCreate the debugger", 0 },
+ { 204, DBG_SUBERR_ATTACH, "Could not attach to process", 0 },
+ { 205, SUBERR_NONE, "Reset Content", 0 },
+ { 206, SUBERR_NONE, "Partial Content", 0 },
+ { 300, SUBERR_NONE, "Multiple Choices", 0 },
+ { 301, SUBERR_NONE, "Moved Permanently", 0 },
+ { 302, SUBERR_NONE, "Found", 0 },
+ { 303, SUBERR_NONE, "See Other", 0 },
+ { 304, SUBERR_NONE, "Not Modified", 0 },
+ { 305, SUBERR_NONE, "Use Proxy", 0 },
+ { 306, SUBERR_NONE, "(Unused)", 0 },
+ { 307, SUBERR_NONE, "Temporary Redirect", 0 },
+ { 400, SUBERR_NONE, "Bad Request", IDS_ATLSRV_BAD_REQUEST },
+ { 401, SUBERR_NONE, "Unauthorized", IDS_ATLSRV_AUTH_REQUIRED },
+ { 402, SUBERR_NONE, "Payment Required", 0 },
+ { 403, SUBERR_NONE, "Forbidden", IDS_ATLSRV_FORBIDDEN },
+ { 404, SUBERR_NONE, "Not Found", IDS_ATLSRV_NOT_FOUND },
+ { 405, SUBERR_NONE, "Method Not Allowed", 0 },
+ { 406, SUBERR_NONE, "Not Acceptable", 0 },
+ { 407, SUBERR_NONE, "Proxy Authentication Required", 0 },
+ { 408, SUBERR_NONE, "Request Timeout", 0 },
+ { 409, SUBERR_NONE, "Conflict", 0 },
+ { 410, SUBERR_NONE, "Gone", 0 },
+ { 411, SUBERR_NONE, "Length Required", 0 },
+ { 412, SUBERR_NONE, "Precondition Failed", 0 },
+ { 413, SUBERR_NONE, "Request Entity Too Long", 0 },
+ { 414, SUBERR_NONE, "Request-URI Too Long", 0 },
+ { 415, SUBERR_NONE, "Unsupported Media Type", 0 },
+ { 416, SUBERR_NONE, "Requested Range Not Satisfiable", 0 },
+ { 417, SUBERR_NONE, "Expectation Failed", 0 },
+ { 500, SUBERR_NONE, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR },
+ { 500, ISE_SUBERR_BADSRF, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_BADSRF },
+ { 500, ISE_SUBERR_HNDLFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HNDLFAIL },
+ { 500, ISE_SUBERR_SYSOBJFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL},
+ { 500, ISE_SUBERR_READFILEFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_READFILEFAIL},
+ { 500, ISE_SUBERR_LOADFILEFAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL},
+ { 500, ISE_SUBERR_LOADLIB, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LOADLIB},
+ { 500, ISE_SUBERR_HANDLERIF, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HANDLERIF},
+ { 500, ISE_SUBERR_OUTOFMEM, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_OUTOFMEM},
+ { 500, ISE_SUBERR_UNEXPECTED, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_UNEXPECTED},
+ { 500, ISE_SUBERR_STENCIL_PARSE_FAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL},
+ { 500, ISE_SUBERR_STENCIL_LOAD_FAIL, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL},
+ { 500, ISE_SUBERR_HANDLER_NOT_FOUND, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND},
+ { 500, ISE_SUBERR_BAD_HANDLER_TAG, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG},
+ { 500, ISE_SUBERR_LONGMETHODNAME, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME},
+ { 500, ISE_SUBERR_LONGHANDLERNAME, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME},
+ { 500, ISE_SUBERR_NO_HANDLER_TAG, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG},
+ { 500, ISE_SUBERR_IMPERSONATIONFAILED, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED},
+ { 500, ISE_SUBERR_ISAPISTARTUPFAILED, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED},
+ { 500, ISE_SUBERR_SOAPNOSOAPACTION, "Internal Server Error", IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION},
+
+ { 501, SUBERR_NONE, "Not Implemented", IDS_ATLSRV_NOT_IMPLEMENTED },
+ { 502, SUBERR_NONE, "Bad Gateway", IDS_ATLSRV_BAD_GATEWAY },
+ { 503, SUBERR_NONE, "Service Unavailable", IDS_ATLSRV_SERVICE_NOT_AVAILABLE },
+ { 504, SUBERR_NONE, "Gateway Timeout", 0 },
+ { 505, SUBERR_NONE, "HTTP Version Not Supported", 0 },
+ };
+
+ // look for the error
+ for (int i=0; i<sizeof(s_Errors)/sizeof(s_Errors[0]); i++)
+ {
+ if ((s_Errors[i].uHttpError == uError) && (s_Errors[i].uHttpSubError == uSubErr))
+ {
+ if (ppszHeader)
+ *ppszHeader = s_Errors[i].szHeader;
+ if (puResId)
+ *puResId = s_Errors[i].uResId;
+ return TRUE;
+ }
+ }
+
+ // not found
+ return FALSE;
+ }
+}; // CDefaultErrorProvider
+
+template<class HttpUserErrorTextProvider>
+void GetStatusHeader(__inout CStringA &strStatus, __in DWORD dwStatus, __in DWORD dwSubStatus, __in HttpUserErrorTextProvider* pErrorProvider, __out_opt UINT *puResId = NULL) throw(...)
+{
+ ATLENSURE( pErrorProvider != NULL );
+
+ LPCSTR szHeadErr = NULL;
+ // First, we check for the error text in the extension's user error text provider
+ BOOL bRet = pErrorProvider->GetErrorText(dwStatus, dwSubStatus, &szHeadErr, puResId);
+ if (!bRet)
+ {
+ szHeadErr = "";
+ }
+
+ char szBuf[512];
+ Checked::itoa_s(dwStatus, szBuf, _countof(szBuf), 10);
+
+ // add the space after the 3 digit response code
+ szBuf[3] = ' ';
+ szBuf[4] = '\0';
+ strStatus.SetString(szBuf, 4);
+ strStatus.Append(szHeadErr);
+}
+
+template<class HttpUserErrorTextProvider>
+void RenderError(__in IHttpServerContext *pServerContext, __in DWORD dwStatus, __in DWORD dwSubStatus, __in HttpUserErrorTextProvider* pErrorProvider)
+{
+ ATLENSURE( pServerContext != NULL );
+ ATLENSURE( pErrorProvider != NULL );
+ _ATLTRY
+ {
+ UINT uResId = 0;
+
+ CFixedStringT<CStringA, 256> strStatus;
+ GetStatusHeader(strStatus, dwStatus, dwSubStatus, pErrorProvider, &uResId);
+ pServerContext->SendResponseHeader(NULL, strStatus, FALSE);
+
+ LPCSTR szBody = strStatus;
+ DWORD dwBodyLen = strStatus.GetLength();
+ CFixedStringT<CStringA, 1024> strBody;
+ if (uResId)
+ {
+ // load the body string from a resource
+ if (strBody.LoadString(uResId))
+ {
+ szBody = strBody;
+ dwBodyLen = strBody.GetLength();
+ }
+ }
+
+ pServerContext->WriteClient((void *) szBody, &dwBodyLen);
+ }
+ _ATLCATCHALL()
+ {
+ // last resort message when low on memory
+ LPCSTR szError;
+ BOOL bRes;
+ bRes = CDefaultErrorProvider::GetErrorText(dwStatus, dwSubStatus, &szError, 0);
+ if (!bRes)
+ bRes = CDefaultErrorProvider::GetErrorText(dwStatus, SUBERR_NONE, &szError, 0);
+ if (!bRes)
+ bRes = CDefaultErrorProvider::GetErrorText(500, SUBERR_NONE, &szError, 0);
+ if(!szError)
+ {
+ szError="Unknown Error"; // last resort, can't localize
+ }
+ DWORD dwBodyLen = (DWORD) strlen(szError);
+ pServerContext->WriteClient((void *) szError, &dwBodyLen);
+ }
+}
+
+// Call this function to retrieve the full canonical physical path
+ // of a file relative to the current script.
+//
+// Returns TRUE on success, FALSE on error.
+//
+// szFile A file path relative to the current script directory for which
+// you are trying to retrieve the full path.
+//
+// szFullFileName A caller-allocated buffer of at least MAX_PATH characters in length.
+// On success, contains the the full canonical path of szFile.
+//
+// pServerContext The context for the current request. The context is used to obtain the
+// current script directory.
+inline BOOL GetScriptFullFileName(
+ __in LPCSTR szFile,
+ __in_ecount(MAX_PATH) LPSTR szFullFileName,
+ __in IHttpServerContext* pServerContext) throw(...)
+{
+ ATLENSURE( szFile != NULL );
+ ATLASSERT( szFullFileName != NULL );
+ ATLENSURE( pServerContext != NULL );
+
+ char szTmpScriptPath[MAX_PATH];
+ LPCSTR szTmp = pServerContext->GetScriptPathTranslated();
+
+ if (!szTmp)
+ {
+ return FALSE;
+ }
+
+ if (!SafeStringCopy(szTmpScriptPath, szTmp))
+ {
+ // path is too long
+ return FALSE;
+ }
+
+ CHAR *szScriptPath = szTmpScriptPath;
+
+ LPSTR szBackslash;
+ if (*szFile != '\\')
+ {
+ szBackslash = strrchr(szScriptPath, '\\');
+
+ ATLASSERT( *(szScriptPath+strlen(szScriptPath)) != '\\');
+
+ if (szBackslash)
+ szBackslash++;
+ }
+ else
+ {
+ // handle case where szFile is of the form \directory\etc\etc
+ szBackslash = strchr(szScriptPath, '\\');
+ }
+
+ if (szBackslash)
+ *szBackslash = '\0';
+
+ int nScriptPathLen = (int)(szBackslash ? strlen(szScriptPath) : 0);
+ int nFileLen = (int) strlen(szFile);
+ int newLen = nScriptPathLen + nFileLen;
+
+ if ((newLen < nScriptPathLen) || (newLen < nFileLen) || (newLen > MAX_PATH-1))
+ {
+ return FALSE;
+ }
+ CHAR szTemp[MAX_PATH];
+ if (nScriptPathLen)
+ {
+ Checked::memcpy_s(szTemp, MAX_PATH, szScriptPath, nScriptPathLen);
+ }
+ Checked::memcpy_s(szTemp + nScriptPathLen, MAX_PATH-nScriptPathLen, szFile, nFileLen);
+ *(szTemp + newLen) = 0;
+
+ return PathCanonicalizeA(szFullFileName, szTemp);
+}
+
+enum ATLSRV_STATE
+{
+ ATLSRV_STATE_BEGIN, // The request has just arrived, and the type has not been determined
+ ATLSRV_STATE_CONTINUE, // The request is a continuation of an async request
+ ATLSRV_STATE_DONE, // The request is a continuation of an async request, but the server is done with it
+ ATLSRV_STATE_CACHE_DONE // The request is the callback of a cached page
+};
+
+enum ATLSRV_REQUESTTYPE
+{
+ ATLSRV_REQUEST_UNKNOWN=-1, // The request type isn't known yet
+ ATLSRV_REQUEST_STENCIL, // The request is for a .srf file
+ ATLSRV_REQUEST_DLL // The request is for a .dll file
+};
+
+// Flags the InitRequest can return in dwStatus
+#define ATLSRV_INIT_USECACHE 1
+#define ATLSRV_INIT_USEASYNC 2
+#define ATLSRV_INIT_USEASYNC_EX 4 // required for use of NOFLUSH status
+
+typedef HTTP_CODE (IRequestHandler::*PFnHandleRequest)(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider);
+typedef void (*PFnAsyncComplete)(AtlServerRequest *pRequestInfo, DWORD cbIO, DWORD dwError);
+
+struct AtlServerRequest
+{
+ DWORD cbSize; // For future compatibility
+ IHttpServerContext *pServerContext; // Necessary because it wraps the ECB
+ ATLSRV_REQUESTTYPE dwRequestType; // See the ATLSRV variables above
+ // Indicates whether it was called through an .srf file or through a .dll file
+ ATLSRV_STATE dwRequestState; // See the ATLSRV variables above
+ // Indicates what state of completion the request is in
+ IRequestHandler *pHandler; // Necessary because the callback (for async calls) must know where to
+ // route the request
+ HINSTANCE hInstDll; // Necessary in order to release the dll properly (for async calls)
+ IIsapiExtension *pExtension; // Necessary to requeue the request (for async calls)
+ IDllCache* pDllCache; // Necessary to release the dll in async callback
+
+ HANDLE hFile;
+ HCACHEITEM hEntry;
+ IFileCache* pFileCache;
+
+ HANDLE m_hMutex; // necessary to syncronize calls to HandleRequest
+ // if HandleRequest could potientially make an
+ // async call before returning. only used
+ // if indicated with ATLSRV_INIT_USEASYNC_EX
+
+ DWORD dwStartTicks; // Tick count when the request was received
+ EXTENSION_CONTROL_BLOCK *pECB;
+ PFnHandleRequest pfnHandleRequest;
+ PFnAsyncComplete pfnAsyncComplete;
+ LPCSTR pszBuffer; // buffer to be flushed asyncronously
+ DWORD dwBufferLen; // length of data in pszBuffer
+ void* pUserData; // value that can be used to pass user data between parent and child handlers
+};
+
+inline void _ReleaseAtlServerRequest(__inout AtlServerRequest* pRequest)
+{
+ ATLENSURE(pRequest!=NULL);
+ if (pRequest->pHandler)
+ pRequest->pHandler->Release();
+ if (pRequest->pServerContext)
+ pRequest->pServerContext->Release();
+ if (pRequest->pDllCache && pRequest->hInstDll)
+ pRequest->pDllCache->ReleaseModule(pRequest->hInstDll);
+ if (pRequest->m_hMutex)
+ CloseHandle(pRequest->m_hMutex);
+}
+
+typedef BOOL (__stdcall *GETATLHANDLERBYNAME)(LPCSTR szHandlerName, IIsapiExtension *pExtension, IUnknown **ppHandler);
+typedef BOOL (__stdcall *INITIALIZEATLHANDLERS)(IHttpServerContext*, IIsapiExtension*);
+typedef void (__stdcall *UNINITIALIZEATLHANDLERS)();
+
+// initial size of thread worker heap (per thread)
+// The heap is growable. The default initial is 16KB
+#ifndef ATLS_WORKER_HEAP_SIZE
+#define ATLS_WORKER_HEAP_SIZE 16384
+#endif
+
+class CIsapiWorker
+{
+public:
+ typedef AtlServerRequest* RequestType;
+ HANDLE m_hHeap;
+#ifndef ATL_NO_SOAP
+ CComPtr<ISAXXMLReader> m_spReader;
+#endif
+
+ CIsapiWorker() throw()
+ {
+ m_hHeap = NULL;
+ }
+
+ virtual ~CIsapiWorker() throw()
+ {
+ ATLASSUME(m_hHeap == NULL);
+ }
+
+ virtual BOOL Initialize(__inout __crt_typefix(IIsapiExtension*) void *pvParam)
+ {
+ IIsapiExtension* pExtension = (IIsapiExtension*) pvParam;
+ ATLENSURE(pExtension);
+ if (!(pExtension->OnThreadAttach()))
+ return FALSE;
+
+ m_hHeap = HeapCreate(HEAP_NO_SERIALIZE, ATLS_WORKER_HEAP_SIZE, 0);
+ if (!m_hHeap)
+ return FALSE;
+#ifndef ATL_NO_SOAP
+ if (FAILED(m_spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER )))
+ {
+ ATLASSERT( FALSE );
+ ATLTRACE( atlTraceISAPI, 0, _T("MSXML3 is not installed -- web services will not work.") );
+ }
+#endif
+ return pExtension->SetThreadWorker(this);
+ }
+
+ virtual void Terminate(__inout_opt __crt_typefix(IIsapiExtension*) void* pvParam) throw()
+ {
+ if (m_hHeap)
+ {
+ if (HeapDestroy(m_hHeap))
+ m_hHeap = NULL;
+ else
+ {
+ ATLASSERT(FALSE);
+ }
+ }
+
+#ifndef ATL_NO_SOAP
+ m_spReader.Release();
+#endif
+ if (pvParam != NULL)
+ (static_cast<IIsapiExtension*>(pvParam))->OnThreadTerminate();
+ }
+
+ void Execute(__inout AtlServerRequest *pRequestInfo, __inout __crt_typefix(IIsapiExtension*) void *pvParam, __reserved OVERLAPPED *pOverlapped)
+ {
+ ATLENSURE(pRequestInfo != NULL);
+ ATLENSURE(pvParam != NULL);
+ (pOverlapped); // unused
+ ATLASSUME(m_hHeap != NULL);
+ // any exceptions thrown at this point should have been caught in an
+ // override of DispatchStencilCall. They will not be thrown out of this
+ // function.
+ _ATLTRY
+ {
+ (static_cast<IIsapiExtension*>(pvParam))->DispatchStencilCall(pRequestInfo);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE(_T("Warning. An uncaught exception was thrown from DispatchStencilCall\n"));
+ ATLASSERT(FALSE);
+ }
+ }
+
+ virtual BOOL GetWorkerData(DWORD /*dwParam*/, void ** /*ppvData*/) throw()
+ {
+ return FALSE;
+ }
+};
+
+inline void _AtlGetScriptPathTranslated(
+ __in LPCSTR szPathTranslated,
+ __inout CFixedStringT<CStringA, MAX_PATH>& strScriptPathTranslated)
+{
+ ATLENSURE(szPathTranslated!=NULL);
+ LPCSTR szEnd = szPathTranslated;
+
+ while (TRUE)
+ {
+ while (*szEnd != '.' && *szEnd != '\0')
+ szEnd++;
+ if (*szEnd == '\0')
+ break;
+
+ szEnd++;
+
+ size_t nLen(0);
+ if (!AsciiStrnicmp(szEnd, c_AtlDLLExtension+1, ATLS_DLL_EXTENSION_LEN))
+ nLen = ATLS_DLL_EXTENSION_LEN;
+ else if (!AsciiStrnicmp(szEnd, c_AtlSRFExtension+1, ATLS_EXTENSION_LEN))
+ nLen = ATLS_EXTENSION_LEN;
+
+ if (nLen)
+ {
+ szEnd += nLen;
+ if (!*szEnd || *szEnd == '/' || *szEnd == '\\' || *szEnd == '?' || *szEnd == '#')
+ break;
+ }
+ }
+
+ DWORD dwResult = (DWORD)(szEnd - szPathTranslated);
+ char *szScriptPathTranslated = NULL;
+ ATLTRY(szScriptPathTranslated = strScriptPathTranslated.GetBuffer(dwResult));
+ if (szScriptPathTranslated)
+ {
+ Checked::memcpy_s(szScriptPathTranslated, dwResult, szPathTranslated, dwResult);
+ szScriptPathTranslated[dwResult] = '\0';
+ strScriptPathTranslated.ReleaseBuffer(dwResult);
+ }
+}
+
+
+struct CStencilState
+{
+ CStencilState() throw()
+ {
+ dwIndex = 0;
+ locale = CP_ACP;
+ pIncludeInfo = NULL;
+ pParentInfo = NULL;
+ }
+
+ DWORD dwIndex;
+ LCID locale;
+ AtlServerRequest* pIncludeInfo;
+ AtlServerRequest* pParentInfo;
+};
+
+class CWrappedServerContext:
+ public IHttpServerContext
+{
+public:
+ CComPtr<IHttpServerContext> m_spParent;
+
+ CWrappedServerContext() throw()
+ {
+ }
+
+ virtual ~CWrappedServerContext() throw()
+ {
+ }
+
+ CWrappedServerContext(__in IHttpServerContext *pParent) throw()
+ {
+ m_spParent = pParent;
+ }
+
+ LPCSTR GetRequestMethod()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetRequestMethod();
+ }
+
+ LPCSTR GetQueryString()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetQueryString();
+ }
+
+ LPCSTR GetPathInfo()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetPathInfo();
+ }
+
+ LPCSTR GetScriptPathTranslated()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetScriptPathTranslated();
+ }
+
+ LPCSTR GetPathTranslated()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetPathTranslated();
+ }
+
+ DWORD GetTotalBytes()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetTotalBytes();
+ }
+
+ DWORD GetAvailableBytes()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetAvailableBytes();
+ }
+
+ BYTE *GetAvailableData()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetAvailableData();
+ }
+
+ LPCSTR GetContentType()
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetContentType();
+ }
+
+ __checkReturn BOOL GetServerVariable(__in_z LPCSTR pszVariableName, __out_ecount_part(*pdwSize,*pdwSize) LPSTR pvBuffer, __inout DWORD *pdwSize)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetServerVariable(pszVariableName, pvBuffer, pdwSize);
+ }
+
+ __checkReturn BOOL WriteClient(__in_bcount(*pdwBytes) void *pvBuffer, __inout DWORD *pdwBytes)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->WriteClient(pvBuffer, pdwBytes);
+ }
+
+ __checkReturn BOOL AsyncWriteClient(__in_bcount(*pdwBytes) void * pvBuffer, __inout DWORD * pdwBytes)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->AsyncWriteClient(pvBuffer, pdwBytes);
+ }
+
+ __checkReturn BOOL ReadClient(__out_bcount_part(*pdwSize,*pdwSize) void * pvBuffer, __inout DWORD * pdwSize)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->ReadClient(pvBuffer, pdwSize);
+ }
+
+ __checkReturn BOOL AsyncReadClient(__out_bcount_part(*pdwSize,*pdwSize) void * pvBuffer, __inout DWORD * pdwSize)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->AsyncReadClient(pvBuffer, pdwSize);
+ }
+
+ __checkReturn BOOL SendRedirectResponse(__in LPCSTR pszRedirectUrl)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->SendRedirectResponse(pszRedirectUrl);
+ }
+
+ __checkReturn BOOL GetImpersonationToken(__out HANDLE * pToken)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->GetImpersonationToken(pToken);
+ }
+
+ __checkReturn BOOL SendResponseHeader(__in LPCSTR pszHeader, __in LPCSTR pszStatusCode, __in BOOL fKeepConn)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->SendResponseHeader(pszHeader, pszStatusCode, fKeepConn);
+ }
+
+ __checkReturn BOOL DoneWithSession(__in DWORD dwHttpStatusCode)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->DoneWithSession(dwHttpStatusCode);
+ }
+
+ __checkReturn BOOL RequestIOCompletion(__in PFN_HSE_IO_COMPLETION pfn, DWORD * pdwContext)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->RequestIOCompletion(pfn, pdwContext);
+ }
+
+ BOOL TransmitFile(__in HANDLE hFile, __in_opt PFN_HSE_IO_COMPLETION pfn, void * pContext,
+ __in LPCSTR szStatusCode, __in DWORD dwBytesToWrite, __in DWORD dwOffset, __in_bcount_opt(dwHeadLen) void * pvHead,
+ __in DWORD dwHeadLen, __in_bcount_opt(dwTailLen) void * pvTail, __in DWORD dwTailLen, __in DWORD dwFlags)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->TransmitFile(hFile, pfn, pContext, szStatusCode,
+ dwBytesToWrite, dwOffset, pvHead, dwHeadLen, pvTail, dwTailLen,
+ dwFlags);
+ }
+
+ BOOL AppendToLog(__in LPCSTR szMessage, __in_opt DWORD* pdwLen)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->AppendToLog(szMessage, pdwLen);
+ }
+
+ BOOL MapUrlToPathEx(__in_bcount(dwLen) LPCSTR szLogicalPath, __in DWORD dwLen, __out HSE_URL_MAPEX_INFO *pumInfo)
+ {
+ ATLENSURE(m_spParent);
+ return m_spParent->MapUrlToPathEx(szLogicalPath, dwLen, pumInfo);
+ }
+}; // class CWrappedServerContext
+
+// Wraps the EXTENSION_CONTROL_BLOCK structure used by IIS to provide
+// an ISAPI extension with information about the current request and
+// access to the web server's functionality.
+class CServerContext :
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IHttpServerContext
+{
+public:
+ BEGIN_COM_MAP(CServerContext)
+ COM_INTERFACE_ENTRY(IHttpServerContext)
+ END_COM_MAP()
+
+ CServerContext() throw()
+ {
+ m_pECB = NULL;
+ m_bHeadersHaveBeenSent = false;
+ }
+ virtual ~CServerContext() throw()
+ {
+ }
+
+ void Initialize(__in EXTENSION_CONTROL_BLOCK *pECB)
+ {
+ ATLENSURE(pECB);
+ m_pECB = pECB;
+
+ // Initialize the translated script path
+ _AtlGetScriptPathTranslated(GetPathTranslated(), m_strScriptPathTranslated);
+ }
+
+ // Returns a nul-terminated string that contains the HTTP method of the current request.
+ // Examples of common HTTP methods include "GET" and "POST".
+ // Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
+ LPCSTR GetRequestMethod()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->lpszMethod;
+ }
+
+ // Returns a nul-terminated string that contains the query information.
+ // This is the part of the URL that appears after the question mark (?).
+ // Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
+ LPCSTR GetQueryString()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->lpszQueryString;
+ }
+
+ // Returns a nul-terminated string that contains the path of the current request.
+ // This is the part of the URL that appears after the server name, but before the query string.
+ // Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
+ LPCSTR GetPathInfo()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->lpszPathInfo;
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the physical path of the script.
+ //
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the
+ // buffer (including the nul-terminating byte).
+ // The script path is the same as GetPathTranslated up to the first .srf or .dll.
+ // For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
+ // then this function returns "c:\inetpub\vcisapi\hello.srf".
+ LPCSTR GetScriptPathTranslated()
+ {
+ ATLASSUME(m_pECB);
+ return m_strScriptPathTranslated;
+ }
+
+
+ // Returns a nul-terminated string that contains the translated path of the requested resource.
+ // This is the path of the resource on the local server.
+ // Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
+ LPCSTR GetPathTranslated()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->lpszPathTranslated;
+ }
+
+ // Returns the total number of bytes to be received from the client.
+ // If this value is 0xffffffff, then there are four gigabytes or more of available data.
+ // In this case, ReadClient or AsyncReadClient should be called until no more data is returned.
+ // Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
+ DWORD GetTotalBytes()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->cbTotalBytes;
+ }
+
+ // Returns the number of bytes available in the request buffer accessible via GetAvailableData.
+ // If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
+ // Otherwise, the remaining data should be read from the client using ReadClient or AsyncReadClient.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
+ DWORD GetAvailableBytes()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->cbAvailable;
+ }
+
+ // Returns a pointer to the request buffer containing the data sent by the client.
+ // The size of the buffer can be determined by calling GetAvailableBytes.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
+ BYTE *GetAvailableData()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->lpbData;
+ }
+
+ // Returns a nul-terminated string that contains the content type of the data sent by the client.
+ // Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
+ LPCSTR GetContentType()
+ {
+ ATLENSURE(m_pECB);
+ return m_pECB->lpszContentType;
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the requested server variable.
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ // Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
+ __checkReturn BOOL GetServerVariable(
+ __in LPCSTR pszVariableName,
+ __out_ecount_part(*pdwSize,*pdwSize) LPSTR pvBuffer,
+ __inout DWORD *pdwSize)
+ {
+ ATLENSURE(m_pECB);
+ ATLASSERT(pszVariableName);
+ ATLASSERT(pdwSize);
+
+ if (pszVariableName && pdwSize)
+ {
+ return m_pECB->GetServerVariable(m_pECB->ConnID, (LPSTR) pszVariableName,
+ pvBuffer, pdwSize);
+ }
+ return FALSE;
+ }
+
+ // Synchronously sends the data present in the given buffer to the client that made the request.
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_SYNC).
+ __checkReturn BOOL WriteClient(__in_bcount(*pdwBytes) void *pvBuffer, __inout DWORD *pdwBytes)
+ {
+ ATLENSURE(m_pECB);
+ ATLASSERT(pvBuffer);
+ ATLASSERT(pdwBytes);
+
+ if (pvBuffer && pdwBytes)
+ {
+ return m_pECB->WriteClient(m_pECB->ConnID, pvBuffer, pdwBytes, HSE_IO_SYNC | HSE_IO_NODELAY);
+ }
+ return FALSE;
+ }
+
+ // Asynchronously sends the data present in the given buffer to the client that made the request.
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::WriteClient(..., HSE_IO_ASYNC).
+ __checkReturn BOOL AsyncWriteClient(__in_bcount(*pdwBytes) void *pvBuffer, __inout DWORD *pdwBytes)
+ {
+ ATLENSURE(m_pECB);
+ ATLASSERT(pvBuffer);
+ ATLASSERT(pdwBytes);
+
+ if (pvBuffer && pdwBytes)
+ {
+ return m_pECB->WriteClient(m_pECB->ConnID, pvBuffer, pdwBytes, HSE_IO_ASYNC | HSE_IO_NODELAY);
+ }
+ return FALSE;
+ }
+
+ // Call this function to synchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::ReadClient.
+ __checkReturn BOOL ReadClient(__out_ecount_part(*pdwSize,*pdwSize) void *pvBuffer, __inout DWORD *pdwSize)
+ {
+ ATLENSURE(m_pECB);
+ ATLASSERT(pvBuffer);
+ ATLASSERT(pdwSize);
+
+ if (pvBuffer && pdwSize)
+ {
+ return m_pECB->ReadClient(m_pECB->ConnID, pvBuffer, pdwSize);
+ }
+ return FALSE;
+ }
+
+ // Call this function to asynchronously read information from the body of the web client's HTTP request into the buffer supplied by the caller.
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ // Equivalent to the HSE_REQ_ASYNC_READ_CLIENT server support function.
+ __checkReturn BOOL AsyncReadClient(__out_bcount_part(*pdwSize,*pdwSize) void *pvBuffer, __inout DWORD *pdwSize)
+ {
+ // To call this function successfully someone has to have already
+ // called RequestIOCompletion specifying the callback function
+ // to be used for IO completion.
+ ATLENSURE(m_pECB);
+ ATLASSERT(pvBuffer);
+ ATLASSERT(pdwSize);
+
+ if (pvBuffer && pdwSize)
+ {
+ DWORD dwFlag = HSE_IO_ASYNC;
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_ASYNC_READ_CLIENT, pvBuffer, pdwSize,
+ &dwFlag);
+ }
+ return FALSE;
+ }
+
+ // Call this function to redirect the client to the specified URL.
+ // The client receives a 302 (Found) HTTP status code.
+ // Returns TRUE on success, and FALSE on failure.
+ // Equivalent to the HSE_REQ_SEND_URL_REDIRECT_RESP server support function.
+ __checkReturn BOOL SendRedirectResponse(__in LPCSTR pszRedirectUrl)
+ {
+ ATLENSURE(m_pECB);
+ ATLENSURE(pszRedirectUrl);
+
+ if (pszRedirectUrl)
+ {
+ DWORD dwSize = (DWORD) strlen(pszRedirectUrl);
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_SEND_URL_REDIRECT_RESP,
+ (void *) pszRedirectUrl, &dwSize, NULL);
+ }
+ return FALSE;
+ }
+
+ // Call this function to retrieve a handle to the impersonation token for this request.
+ // An impersonation token represents a user context. You can use the handle in calls to ImpersonateLoggedOnUser or SetThreadToken.
+ // Do not call CloseHandle on the handle.
+ // Returns TRUE on success, and FALSE on failure.
+ // Equivalent to the HSE_REQ_GET_IMPERSONATION_TOKEN server support function.
+ __checkReturn BOOL GetImpersonationToken(__out HANDLE * pToken)
+ {
+ ATLENSURE(m_pECB);
+ if (pToken)
+ {
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_GET_IMPERSONATION_TOKEN, pToken,
+ NULL, NULL);
+ }
+ return FALSE;
+ }
+
+ // Call this function to send an HTTP response header to the client including the HTTP status, server version, message time, and MIME version.
+ // Returns TRUE on success, and FALSE on failure.
+ // Equivalent to the HSE_REQ_SEND_RESPONSE_HEADER_EX server support function.
+ __checkReturn BOOL SendResponseHeader(
+ __in LPCSTR pszHeader = "Content-Type: text/html\r\n\r\n",
+ __in LPCSTR pszStatusCode = "200 OK",
+ __in BOOL fKeepConn=FALSE)
+ {
+ ATLENSURE(m_pECB);
+
+ if (m_bHeadersHaveBeenSent)
+ return TRUE;
+
+ HSE_SEND_HEADER_EX_INFO hex;
+ hex.pszStatus = pszStatusCode;
+ hex.pszHeader = pszHeader;
+ hex.cchStatus = (DWORD)(pszStatusCode ? strlen(pszStatusCode) : 0);
+ hex.cchHeader = (DWORD)(pszHeader ? strlen(pszHeader) : 0);
+ hex.fKeepConn = fKeepConn;
+
+ m_bHeadersHaveBeenSent = true;
+
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_SEND_RESPONSE_HEADER_EX,
+ &hex, NULL, NULL);
+ }
+
+ // Call this function to terminate the session for the current request.
+ // Returns TRUE on success, and FALSE on failure.
+ // Equivalent to the HSE_REQ_DONE_WITH_SESSION server support function.
+ __checkReturn BOOL DoneWithSession(__in DWORD dwHttpStatusCode)
+ {
+ ATLENSURE(m_pECB);
+
+ m_pECB->dwHttpStatusCode = dwHttpStatusCode;
+
+ DWORD dwStatusCode = (dwHttpStatusCode >= 400) ? HSE_STATUS_ERROR : HSE_STATUS_SUCCESS;
+
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_DONE_WITH_SESSION, &dwStatusCode, NULL, NULL);
+ }
+
+ // Call this function to set a special callback function that will be used for handling the completion of asynchronous I/O operations.
+ // Returns TRUE on success, and FALSE on failure.
+ // Equivalent to the HSE_REQ_IO_COMPLETION server support function.
+ __checkReturn BOOL RequestIOCompletion(__in PFN_HSE_IO_COMPLETION pfn, DWORD *pdwContext)
+ {
+ ATLENSURE(m_pECB);
+ ATLASSERT(pfn);
+
+ if (pfn)
+ {
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_IO_COMPLETION, pfn, NULL, pdwContext);
+ }
+ return FALSE;
+ }
+
+ // Call this function to transmit a file asynchronously to the client.
+ // Returns TRUE on success, and FALSE on failure.
+ // Equivalent to the HSE_REQ_TRANSMIT_FILE server support function.
+ BOOL TransmitFile(
+ __in HANDLE hFile,
+ __in_opt PFN_HSE_IO_COMPLETION pfn,
+ void *pContext,
+ __in LPCSTR szStatusCode,
+ __in DWORD dwBytesToWrite,
+ __in DWORD dwOffset,
+ __in_bcount_opt(dwHeadLen) void *pvHead,
+ __in DWORD dwHeadLen,
+ __in_bcount_opt(dwTailLen) void *pvTail,
+ __in DWORD dwTailLen,
+ __in DWORD dwFlags)
+ {
+ ATLENSURE(m_pECB);
+
+ HSE_TF_INFO tf;
+ tf.hFile = hFile;
+ tf.BytesToWrite = dwBytesToWrite;
+ tf.Offset = dwOffset;
+ tf.pContext = pContext;
+ tf.pfnHseIO = pfn;
+ tf.pHead = pvHead;
+ tf.HeadLength = dwHeadLen;
+ tf.pTail = pvTail;
+ tf.TailLength = dwTailLen;
+ tf.pszStatusCode = szStatusCode;
+ tf.dwFlags = dwFlags;
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_REQ_TRANSMIT_FILE, &tf, NULL, NULL);
+ }
+
+ // Appends the string szMessage to the web server log for the current
+ // request.
+ // Returns TRUE on success, FALSE on failure.
+ // Equivalent to the HSE_APPEND_LOG_PARAMETER server support function.
+ BOOL AppendToLog(__in LPCSTR szMessage, __in_opt DWORD *pdwLen)
+ {
+ DWORD dwLen = 0;
+ if (!pdwLen)
+ {
+ if(!szMessage)
+ {
+ return FALSE;
+ }
+ dwLen = (DWORD)strlen(szMessage);
+ }
+ else
+ {
+ dwLen = *pdwLen;
+ }
+
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID,
+ HSE_APPEND_LOG_PARAMETER, (void *)szMessage,
+ &dwLen, NULL);
+ }
+
+ // Maps a logical Url Path to a physical path
+ // Returns TRUE on success, FALSE on failure.
+ // Equivalent to the HSE_REQ_MAP_URL_TO_PATH_EX server support function.
+ // you can pass 0 for dwLen if szLogicalPath is null terminated
+ BOOL MapUrlToPathEx(__in_bcount(dwLen) LPCSTR szLogicalPath, __in DWORD dwLen, __out HSE_URL_MAPEX_INFO *pumInfo)
+ {
+ ATLENSURE(m_pECB!=NULL);
+ if (dwLen == 0)
+ dwLen = (DWORD) strlen(szLogicalPath);
+ return m_pECB->ServerSupportFunction(m_pECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, (void *) szLogicalPath,
+ &dwLen, (DWORD *) pumInfo);
+ }
+
+protected:
+ // The pointer to the extension control block provided by IIS.
+ EXTENSION_CONTROL_BLOCK *m_pECB;
+ bool m_bHeadersHaveBeenSent;
+
+ // The translated script path
+ CFixedStringT<CStringA, MAX_PATH> m_strScriptPathTranslated;
+
+}; // class CServerContext
+
+class CPageCachePeer
+{
+public:
+
+ struct PeerInfo
+ {
+ CStringA strHeader;
+ CStringA strStatus;
+ };
+
+ static BOOL Add(__inout PeerInfo * pDest, __in PeerInfo * pSrc) throw()
+ {
+ _ATLTRY
+ {
+ PeerInfo *pIn = (PeerInfo *)pSrc;
+ pDest->strHeader = pIn->strHeader;
+ pDest->strStatus = pIn->strStatus;
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ static BOOL Remove(const PeerInfo * /*pDest*/) throw()
+ {
+ return TRUE;
+ }
+};
+
+
+class CCacheServerContext :
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public CWrappedServerContext,
+ public IPageCacheControl
+{
+private:
+
+ CAtlTemporaryFile m_cacheFile;
+ CComPtr<IFileCache> m_spCache;
+ char m_szFullUrl[ATL_URL_MAX_URL_LENGTH + 1];
+ FILETIME m_ftExpiration;
+ BOOL m_bIsCached;
+ CPageCachePeer::PeerInfo m_Headers;
+
+public:
+
+ BEGIN_COM_MAP(CCacheServerContext)
+ COM_INTERFACE_ENTRY(IHttpServerContext)
+ COM_INTERFACE_ENTRY(IPageCacheControl)
+ END_COM_MAP()
+
+ CCacheServerContext() throw()
+ {
+ }
+ virtual ~CCacheServerContext() throw()
+ {
+ }
+
+ BOOL Initialize(__in IHttpServerContext *pParent, __in IFileCache *pCache) throw()
+ {
+ ATLASSERT(pParent);
+ ATLASSERT(pCache);
+
+ if (pParent == NULL || pCache == NULL)
+ return FALSE;
+
+ m_spParent = pParent;
+ m_spCache = pCache;
+
+ if (FAILED(m_cacheFile.Create()))
+ return FALSE;
+
+ LPCSTR szPathInfo = pParent->GetPathInfo();
+ LPCSTR szQueryString = pParent->GetQueryString();
+ if ( szPathInfo == NULL || szQueryString == NULL)
+ return FALSE;
+
+ LPSTR szTo = m_szFullUrl;
+ int nSize = 0;
+ while (*szPathInfo && nSize < ATL_URL_MAX_URL_LENGTH)
+ {
+ *szTo++ = *szPathInfo++;
+ nSize++;
+ }
+ if (nSize >= ATL_URL_MAX_URL_LENGTH)
+ {
+ return FALSE;
+ }
+ *szTo++ = '?';
+ nSize++;
+ while (*szQueryString && nSize < ATL_URL_MAX_URL_LENGTH)
+ {
+ *szTo++ = *szQueryString++;
+ nSize++;
+ }
+ if (nSize >= ATL_URL_MAX_URL_LENGTH)
+ {
+ return FALSE;
+ }
+ *szTo = '\0';
+
+ memset(&m_ftExpiration, 0x00, sizeof(FILETIME));
+ m_bIsCached = TRUE;
+
+ return TRUE;
+ }
+
+ // IPageCacheControl methods
+ HRESULT GetExpiration(__out FILETIME *pftExpiration) throw()
+ {
+ ATLASSERT(pftExpiration);
+ if (!pftExpiration)
+ return E_INVALIDARG;
+
+ *pftExpiration = m_ftExpiration;
+
+ return S_OK;
+ }
+
+ HRESULT SetExpiration(__in FILETIME ftExpiration) throw()
+ {
+ m_ftExpiration = ftExpiration;
+
+ return S_OK;
+ }
+
+ __checkReturn BOOL IsCached() throw()
+ {
+ return m_bIsCached;
+ }
+
+ BOOL Cache(__in BOOL bCache) throw()
+ {
+ BOOL bRet = m_bIsCached;
+ m_bIsCached = bCache;
+ return bRet;
+ }
+
+ __checkReturn BOOL WriteClient(__in_bcount(*pdwBytes) void *pvBuffer, __inout DWORD *pdwBytes)
+ {
+ ATLENSURE(pvBuffer);
+ ATLENSURE(pdwBytes);
+
+ if (S_OK != m_cacheFile.Write(pvBuffer, *pdwBytes))
+ return FALSE;
+ ATLENSURE(m_spParent);
+ return m_spParent->WriteClient(pvBuffer, pdwBytes);
+ }
+
+ __checkReturn BOOL DoneWithSession(__in DWORD dwHttpStatusCode)
+ {
+ ATLENSURE(m_spParent);
+
+ _ATLTRY
+ {
+ if (m_bIsCached)
+ {
+ CT2CA strFileName(m_cacheFile.TempFileName());
+ m_cacheFile.HandsOff();
+ m_spCache->AddFile(m_szFullUrl, strFileName, &m_ftExpiration, &m_Headers, NULL);
+ }
+ else
+ m_cacheFile.Close();
+ }
+ _ATLCATCHALL()
+ {
+ m_cacheFile.Close();
+ }
+
+ return m_spParent->DoneWithSession(dwHttpStatusCode);
+ }
+
+ __checkReturn BOOL GetImpersonationToken(__out HANDLE * pToken)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Getting impersonation token for cached page")
+ _T(" -- Caching a page that requires special privileges to build is a possible security problem. ")
+ _T("Future hits may get a cached page without going through the security checks done during the page creation process"));
+ ATLENSURE(m_spParent);
+ ATLASSERT(pToken);
+ return m_spParent->GetImpersonationToken(pToken);
+ }
+
+ __checkReturn BOOL AppendToLog(__in LPCSTR szMessage, __in_opt DWORD* pdwLen)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Logging on cached page -- future hits will not log"));
+ ATLENSURE(m_spParent);
+ return m_spParent->AppendToLog(szMessage, pdwLen);
+ }
+
+ __checkReturn BOOL SendResponseHeader(
+ __in LPCSTR pszHeader = "Content-Type: text/html\r\n\r\n",
+ __in LPCSTR pszStatusCode = "200 OK",
+ __in BOOL fKeepConn=FALSE)
+ {
+ ATLENSURE(m_spParent);
+
+ m_Headers.strHeader = pszHeader;
+ m_Headers.strStatus = pszStatusCode;
+
+ return m_spParent->SendResponseHeader(pszHeader, pszStatusCode, fKeepConn);
+ }
+
+ // The methods below this point are actions that should not be performed on cached
+ // pages, as they will not behave correctly.
+ __checkReturn BOOL AsyncWriteClient(void * /*pvBuffer*/, DWORD * /*pdwBytes*/)
+ {
+ // Asynchronous calls will not work
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+ __checkReturn BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
+ {
+ // Nobody should be reading from this client if the page is being cached
+ // Also, only GET's are cached anyway
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+ __checkReturn BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
+ {
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+ __checkReturn BOOL SendRedirectResponse(LPCSTR /*pszRedirectUrl*/)
+ {
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+
+ __checkReturn BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/)
+ {
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+ __checkReturn BOOL TransmitFile(
+ HANDLE /*hFile*/,
+ PFN_HSE_IO_COMPLETION /*pfn*/,
+ void * /*pContext*/,
+ LPCSTR /*szStatusCode*/,
+ DWORD /*dwBytesToWrite*/,
+ DWORD /*dwOffset*/,
+ void * /*pvHead*/,
+ DWORD /*dwHeadLen*/,
+ void * /*pvTail*/,
+ DWORD /*dwTailLen*/,
+ DWORD /*dwFlags*/)
+ {
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+};
+
+
+// This class represents a collection of validation failures.
+// Use this class in combination with CValidateObject to validate
+// forms, cookies, or query strings and build up a collection of
+// failures. If appropriate, use the information in the collection
+// to return detailed responses to the client to help them correct the failures.
+
+
+class CValidateContext
+{
+public:
+ enum { ATL_EMPTY_PARAMS_ARE_FAILURES = 0x00000001 };
+
+ CValidateContext(__in DWORD dwFlags=0) throw()
+ {
+ m_bFailures = false;
+ m_dwFlags = dwFlags;
+ }
+
+ bool SetResultAt(__in LPCSTR szName, __in DWORD type)
+ {
+ _ATLTRY
+ {
+ if (!VALIDATION_SUCCEEDED(type) ||
+ (type == VALIDATION_S_EMPTY && (m_dwFlags & ATL_EMPTY_PARAMS_ARE_FAILURES)))
+ m_bFailures = true;
+
+ return TRUE == m_results.SetAt(szName,type);
+
+ }
+ _ATLCATCHALL()
+ {
+ }
+
+ return false;
+ }
+
+ // Call this function to add a validation result to the collection managed by this object.
+ // Each result is identified by a name and the type of result that occurred.
+ // The result codes are the VALIDATION_ codes defined at the top of this file.
+ // The bOnlyFailure parameter below is used to only allow failure results to
+ // be added to the list of failures. The reason you'd want to do this is that
+ // success codes should be the common case in validation routines so you can
+ // use bOnlyFailures to limit the number of allocations by this class's base
+ // map for mapping success results if you don't care about iterating successes.
+
+ bool AddResult(__in LPCSTR szName, __in DWORD type, __in bool bOnlyFailures = true) throw()
+ {
+ _ATLTRY
+ {
+ if (!VALIDATION_SUCCEEDED(type) ||
+ (type == VALIDATION_S_EMPTY && (m_dwFlags & ATL_EMPTY_PARAMS_ARE_FAILURES)))
+ m_bFailures = true;
+
+ if (!bOnlyFailures)
+ return TRUE == m_results.Add(szName, type); // add everything
+
+ else if (bOnlyFailures &&
+ (!VALIDATION_SUCCEEDED(type) ||
+ (type == VALIDATION_S_EMPTY && (m_dwFlags & ATL_EMPTY_PARAMS_ARE_FAILURES))))
+ return TRUE == m_results.Add(szName, type); // only add failures
+ }
+ _ATLCATCHALL()
+ {
+ }
+
+ return false;
+ }
+
+ // Returns true if there are no validation failures in the collection,
+ // returns false otherwise.
+ __checkReturn bool ParamsOK() throw()
+ {
+ return !m_bFailures;
+ }
+
+ // Returns the number of validation results in the collection.
+ __checkReturn int GetResultCount() throw()
+ {
+ return m_results.GetSize();
+ }
+
+ // Call this function to retrieve the name and type of a
+ // validation result based on its index in the collection.
+ // Returns true on success, false on failure.
+ //
+ // i The index of a result managed by this collection.
+ //
+ // strName On success, the name of the result with index i.
+ //
+ // type On success, the type of the result with index i.
+ __checkReturn bool GetResultAt(__in int i, __out CStringA& strName, __out DWORD& type) throw()
+ {
+ if ( i >= 0 && i < m_results.GetSize())
+ {
+ _ATLTRY
+ {
+ strName = m_results.GetKeyAt(i);
+ type = m_results.GetValueAt(i);
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ DWORD m_dwFlags;
+protected:
+ CSimpleMap<CStringA, DWORD> m_results;
+ bool m_bFailures;
+}; // CValidateContext
+
+
+
+class CAtlValidator
+{
+public:
+ template <class T, class TCompType>
+ static DWORD Validate(
+ __in T value,
+ __in TCompType nMinValue,
+ __in TCompType nMaxValue) throw()
+ {
+ DWORD dwRet = VALIDATION_S_OK;
+ if (value < static_cast<T>(nMinValue))
+ dwRet = VALIDATION_E_LENGTHMIN;
+ else if (value > static_cast<T>(nMaxValue))
+ dwRet = VALIDATION_E_LENGTHMAX;
+ return dwRet;
+ }
+
+ static DWORD Validate( __in LPCSTR pszValue, __in int nMinChars, __in int nMaxChars) throw()
+ {
+ DWORD dwRet = VALIDATION_S_OK;
+ if(!pszValue)
+ {
+ return VALIDATION_E_FAIL;
+ }
+ int nChars = (int) strlen(pszValue);
+ if (nChars < nMinChars)
+ dwRet = VALIDATION_E_LENGTHMIN;
+ else if (nChars > nMaxChars)
+ dwRet = VALIDATION_E_LENGTHMAX;
+ return dwRet;
+ }
+ static DWORD Validate( __in double dblValue, __in double dblMinValue, __in double dblMaxValue) throw()
+ {
+ DWORD dwRet = VALIDATION_S_OK;
+ if ( dblValue < (dblMinValue - ATL_EPSILON) )
+ dwRet = VALIDATION_E_LENGTHMIN;
+ else if ( dblValue > (dblMaxValue + ATL_EPSILON) )
+ dwRet = VALIDATION_E_LENGTHMAX;
+ return dwRet;
+ }
+};
+
+// This class provides functions for retrieving and validating named values.
+//
+// The named values are expected to be provided in string form by the class used as
+// the template parameter. CValidateObject provides the means of
+// retrieving these values converted to data types chosen by you. You can validate the values
+// by specifying a range for numeric values or by specifying a minimum and maximum length
+// for string values.
+//
+// Call one of the Exchange overloads to retrieve a named value converted to your chosen data type.
+// Call one of the Validate overloads to retrieve a named value converted to your chosen data type
+// and validated against a minimum and maximum value or length supplied by you.
+//
+// To add validation functionality to the class TLookupClass, derive that class from CValidateObject<TLookupClass>
+// and provide a Lookup function that takes a name as a string and returns the corresponding value
+// also as a string:
+// LPCSTR Lookup(LPCSTR szName);
+template <class TLookupClass, class TValidator = CAtlValidator>
+class CValidateObject
+{
+public:
+ // Exchange Routines
+
+ // Call this function to retrieve a named value converted to your chosen data type.
+ // Returns one of the following validation status codes:
+ // VALIDATION_S_OK The named value was found and could be converted successfully
+ // VALIDATION_S_EMPTY The name was present, but the value was empty
+ // VALIDATION_E_PARAMNOTFOUND The named value was not found
+ // VALIDATION_E_INVALIDPARAM The name was present, but the value could not be converted to the requested data type
+ // VALIDATION_E_FAIL An unspecified error occurred
+ // Pass a pointer to a validation context object if you want to add
+ // failures to the collection managed by that object.
+ template <class T>
+ ATL_NOINLINE __checkReturn DWORD Exchange(
+ __in LPCSTR szParam,
+ __out T* pValue,
+ __inout_opt CValidateContext *pContext = NULL) const throw()
+ {
+ DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
+ if (pValue)
+ {
+ _ATLTRY
+ {
+ const TLookupClass *pT = static_cast<const TLookupClass*>(this);
+ LPCSTR szValue = pT->Lookup(szParam);
+ if (szValue)
+ {
+ if (*szValue=='\0')
+ dwRet = VALIDATION_S_EMPTY;
+ else
+ {
+ dwRet = ConvertNumber(szValue, pValue);
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return VALIDATION_E_FAIL;
+ }
+ }
+ else
+ dwRet = VALIDATION_E_FAIL; // invalid input
+
+ if (pContext)
+ pContext->AddResult(szParam, dwRet);
+ return dwRet;
+ }
+
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Exchange(
+ __in LPCSTR szParam,
+ __out_opt CString* pstrValue,
+ __in_opt CValidateContext *pContext) const throw()
+ {
+ _ATLTRY
+ {
+ LPCSTR pszValue = NULL;
+ DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
+ if (pstrValue)
+ {
+ dwRet = Exchange(szParam, &pszValue, pContext);
+ if (VALIDATION_SUCCEEDED(dwRet) && pstrValue != NULL)
+ *pstrValue = CA2T(pszValue);
+ }
+ else
+ {
+ dwRet = VALIDATION_E_FAIL; // invalid input
+ if (pContext)
+ pContext->AddResult(szParam, dwRet);
+ }
+
+ return dwRet;
+ }
+ _ATLCATCHALL()
+ {
+ return VALIDATION_E_FAIL;
+ }
+ }
+
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Exchange(
+ __in LPCSTR szParam,
+ __deref_out_opt LPCSTR* ppszValue,
+ __inout_opt CValidateContext *pContext) const throw()
+ {
+ DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
+ if (ppszValue)
+ {
+ _ATLTRY
+ {
+ *ppszValue = NULL;
+ const TLookupClass *pT = static_cast<const TLookupClass*>(this);
+ LPCSTR szValue = pT->Lookup(szParam);
+ if (szValue)
+ {
+ if (*szValue=='\0')
+ dwRet = VALIDATION_S_EMPTY;
+ else
+ {
+ *ppszValue = szValue;
+ dwRet = VALIDATION_S_OK;
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return VALIDATION_E_FAIL;
+ }
+ }
+ else
+ dwRet = VALIDATION_E_FAIL; // invalid input
+
+ if (pContext)
+ pContext->AddResult(szParam, dwRet);
+ return dwRet;
+ }
+
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Exchange(
+ __in LPCSTR szParam,
+ __out GUID* pValue,
+ __inout_opt CValidateContext *pContext) const throw()
+ {
+ DWORD dwRet = VALIDATION_E_PARAMNOTFOUND;
+ if (pValue)
+ {
+ _ATLTRY
+ {
+ const TLookupClass *pT = static_cast<const TLookupClass*>(this);
+ LPCSTR szValue = pT->Lookup(szParam);
+ if (szValue)
+ {
+ if (*szValue=='\0')
+ dwRet = VALIDATION_S_EMPTY;
+ else
+ {
+ if (S_OK != CLSIDFromString(CA2W(szValue), pValue))
+ {
+ dwRet = VALIDATION_E_INVALIDPARAM;
+ }
+ else
+ dwRet = VALIDATION_S_OK;
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return VALIDATION_E_FAIL;
+ }
+ }
+ else
+ dwRet = VALIDATION_E_FAIL; // invalid input
+
+ if (pContext)
+ pContext->AddResult(szParam, dwRet);
+ return dwRet;
+ }
+
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Exchange(
+ __in LPCSTR szParam,
+ __out bool* pbValue,
+ __inout_opt CValidateContext *pContext) const throw()
+ {
+ DWORD dwRet = VALIDATION_S_OK;
+ if (pbValue)
+ {
+ _ATLTRY
+ {
+ const TLookupClass *pT = static_cast<const TLookupClass*>(this);
+ LPCSTR szValue = pT->Lookup(szParam);
+ *pbValue = false;
+ if (szValue)
+ {
+ if (*szValue != '\0')
+ *pbValue = true;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return VALIDATION_E_FAIL;
+ }
+ }
+ else
+ dwRet = VALIDATION_E_FAIL; // invalid input
+
+ if (pContext)
+ pContext->AddResult(szParam, dwRet);
+
+ return dwRet;
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out ULONGLONG *pnVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pnVal);
+ if (!pnVal)
+ return VALIDATION_E_FAIL;
+ char *pEnd = NULL;
+ ULONGLONG n = 0;
+ errno_t errnoValue = AtlStrToNum(&n, szVal, &pEnd, 10);
+ if (pEnd == szVal || errnoValue == ERANGE)
+ {
+ return VALIDATION_E_INVALIDPARAM;
+ }
+ *pnVal = n;
+ return VALIDATION_S_OK;
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out LONGLONG *pnVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pnVal);
+ if (!pnVal)
+ return VALIDATION_E_FAIL;
+ char *pEnd = NULL;
+ LONGLONG n = 0;
+ errno_t errnoValue = AtlStrToNum(&n, szVal, &pEnd, 10);
+ if (pEnd == szVal || errnoValue == ERANGE)
+ {
+ return VALIDATION_E_INVALIDPARAM;
+ }
+ *pnVal = n;
+ return VALIDATION_S_OK;
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out double *pdblVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pdblVal);
+ if (!pdblVal)
+ return VALIDATION_E_FAIL;
+ char *pEnd = NULL;
+ double d = 0.0;
+ errno_t errnoValue = AtlStrToNum(&d, szVal, &pEnd);
+ if (pEnd == szVal || errnoValue == ERANGE)
+ {
+ return VALIDATION_E_INVALIDPARAM;
+ }
+ *pdblVal = d;
+ return VALIDATION_S_OK;
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out int *pnVal) const throw()
+ {
+ return ConvertNumber(szVal, (long*)pnVal);
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out unsigned int *pnVal) const throw()
+ {
+ return ConvertNumber(szVal, (unsigned long*)pnVal);
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out long *pnVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pnVal);
+ if (!pnVal)
+ return VALIDATION_E_FAIL;
+ char *pEnd = NULL;
+ long n = 0;
+ errno_t errnoValue = AtlStrToNum(&n, szVal, &pEnd, 10);
+ if (pEnd == szVal || errnoValue == ERANGE)
+ {
+ return VALIDATION_E_INVALIDPARAM;
+ }
+ *pnVal = n;
+ return VALIDATION_S_OK;
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out unsigned long *pnVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pnVal);
+ if (!pnVal)
+ return VALIDATION_E_FAIL;
+ char *pEnd = NULL;
+ unsigned long n = 0;
+ errno_t errnoValue = AtlStrToNum(&n, szVal, &pEnd, 10);
+ if (pEnd == szVal || errnoValue == ERANGE)
+ {
+ return VALIDATION_E_INVALIDPARAM;
+ }
+ *pnVal = n;
+ return VALIDATION_S_OK;
+ }
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out short *pnVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pnVal);
+ if (!pnVal)
+ return VALIDATION_E_FAIL;
+ long nVal = 0;
+ DWORD dwRet = ConvertNumber(szVal, &nVal);
+ if (dwRet == VALIDATION_S_OK)
+ {
+ // clamp to the size of a short
+ if(nVal <= SHRT_MAX &&
+ nVal >= SHRT_MIN)
+ {
+ *pnVal = (short)nVal;
+ }
+ else
+ {
+ dwRet = VALIDATION_E_INVALIDPARAM;
+ }
+ }
+ return dwRet;
+ };
+
+ __checkReturn DWORD ConvertNumber(__in LPCSTR szVal, __out unsigned short *pnVal) const throw()
+ {
+ if (!szVal)
+ return VALIDATION_E_FAIL;
+
+ ATLASSERT(pnVal);
+ if (!pnVal)
+ return VALIDATION_E_FAIL;
+ unsigned long nVal = 0;
+ DWORD dwRet = ConvertNumber(szVal, &nVal);
+ if (dwRet == VALIDATION_S_OK)
+ {
+ // clamp to the size of a short
+ if(nVal <= USHRT_MAX &&
+ nVal >= 0)
+ {
+ *pnVal = (unsigned short)nVal;
+ }
+ else
+ {
+ dwRet = VALIDATION_E_INVALIDPARAM;
+ }
+ }
+ return dwRet;
+ };
+
+ // Call this function to retrieve a named value converted to your chosen data type
+ // and validated against a minimum and maximum value or length supplied by you.
+ //
+ // Returns one of the following validation status codes:
+ // VALIDATION_S_OK The named value was found and could be converted successfully
+ // VALIDATION_S_EMPTY The name was present, but the value was empty
+ // VALIDATION_E_PARAMNOTFOUND The named value was not found
+ // VALIDATION_E_INVALIDPARAM The name was present, but the value could not be converted to the requested data type
+ // VALIDATION_E_LENGTHMIN The name was present and could be converted to the requested data type, but the value was too small
+ // VALIDATION_E_LENGTHMAX The name was present and could be converted to the requested data type, but the value was too large
+ // VALIDATION_E_FAIL An unspecified error occurred
+ //
+ // Validate can be used to convert and validate name-value pairs
+ // such as those associated with HTTP requests (query string, form fields, or cookie values).
+ // The numeric specializations validate the minimum and maximum value.
+ // The string specializations validate the minimum and maximum length.
+ //
+ // Pass a pointer to a validation context object if you want to add
+ // failures to the collection managed by that object.
+ //
+ // Note that you can validate the value of a parameter without
+ // storing its value by passing NULL for the second parameter. However
+ // if you pass NULL for the second parameter, make sure you cast the NULL to a
+ // type so that the compiler will call the correct specialization of Validate.
+ template <class T, class TCompType>
+ ATL_NOINLINE __checkReturn DWORD Validate(
+ __in LPCSTR Param,
+ __out_opt T *pValue,
+ __in TCompType nMinValue,
+ __in TCompType nMaxValue,
+ __inout_opt CValidateContext *pContext = NULL) const throw()
+ {
+ T value;
+ DWORD dwRet = Exchange(Param, &value, pContext);
+ if ( dwRet == VALIDATION_S_OK )
+ {
+ if (pValue)
+ *pValue = value;
+ dwRet = TValidator::Validate(value, nMinValue, nMaxValue);
+ if (pContext && dwRet != VALIDATION_S_OK)
+ pContext->AddResult(Param, dwRet);
+ }
+ else if (dwRet == VALIDATION_S_EMPTY &&
+ !IsNullByType(nMinValue))
+ {
+ dwRet = VALIDATION_E_LENGTHMIN;
+ if (pContext)
+ {
+ pContext->SetResultAt(Param, VALIDATION_E_LENGTHMIN);
+ }
+ }
+
+ return dwRet;
+ }
+
+ // Specialization for strings. Comparison is for number of characters.
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Validate(
+ __in LPCSTR Param,
+ __deref_opt_out LPCSTR* ppszValue,
+ __in int nMinChars,
+ __in int nMaxChars,
+ __inout_opt CValidateContext *pContext) const throw()
+ {
+ LPCSTR pszValue = NULL;
+ DWORD dwRet = Exchange(Param, &pszValue, pContext);
+ if (dwRet == VALIDATION_S_OK )
+ {
+ if (ppszValue)
+ *ppszValue = pszValue;
+ dwRet = TValidator::Validate(pszValue, nMinChars, nMaxChars);
+ if (pContext && dwRet != VALIDATION_S_OK)
+ pContext->AddResult(Param, dwRet);
+ }
+ else if (dwRet == VALIDATION_S_EMPTY &&
+ nMinChars > 0)
+ {
+ dwRet = VALIDATION_E_LENGTHMIN;
+ if (pContext)
+ {
+ pContext->SetResultAt(Param, VALIDATION_E_LENGTHMIN);
+ }
+ }
+
+
+ return dwRet;
+ }
+
+ // Specialization for CString so caller doesn't have to cast CString
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Validate(
+ __in LPCSTR Param,
+ __out_opt CString* pstrValue,
+ __in int nMinChars,
+ __in int nMaxChars,
+ __inout_opt CValidateContext *pContext) const throw()
+ {
+ _ATLTRY
+ {
+ LPCSTR szValue;
+ DWORD dwRet = Validate(Param, &szValue, nMinChars, nMaxChars, pContext);
+ if (pstrValue && dwRet == VALIDATION_S_OK )
+ *pstrValue = szValue;
+ return dwRet;
+ }
+ _ATLCATCHALL()
+ {
+ return VALIDATION_E_FAIL;
+ }
+ }
+
+ // Specialization for doubles, uses a different comparison.
+ template<>
+ ATL_NOINLINE __checkReturn DWORD Validate(
+ __in LPCSTR Param,
+ __out_opt double* pdblValue,
+ __in double dblMinValue,
+ __in double dblMaxValue,
+ __inout_opt CValidateContext *pContext) const throw()
+ {
+ double dblValue;
+ DWORD dwRet = Exchange(Param, &dblValue, pContext);
+ if (dwRet == VALIDATION_S_OK)
+ {
+ if (pdblValue)
+ *pdblValue = dblValue;
+ dwRet = TValidator::Validate(dblValue, dblMinValue, dblMaxValue);
+ if (pContext && dwRet != VALIDATION_S_OK)
+ pContext->AddResult(Param, dwRet);
+ }
+ else if (dwRet == VALIDATION_S_EMPTY &&
+ (dblMinValue < -ATL_EPSILON ||
+ dblMinValue > ATL_EPSILON))
+ {
+ dwRet = VALIDATION_E_LENGTHMIN;
+ if (pContext)
+ {
+ pContext->SetResultAt(Param, VALIDATION_E_LENGTHMIN);
+ }
+ }
+ return dwRet;
+ }
+};
+
+// Cookies provide a way for a server to store a small amount of data on a client
+// and have that data returned to it on each request the client makes.
+// Use this class to represent a cookie to be sent from the server to a client
+// or to represent a cookie that has been returned by a client to the originating server.
+//
+// At the HTTP level, a cookie is an application-defined name-value pair
+// plus some standard attribute-value pairs that describe the way in which the user agent (web browser)
+// should interact with the cookie. The HTTP format of a cookie is described in RFC 2109.
+//
+// The CCookie class provides methods to set and get the application-defined name and value
+// as well as methods for the standard attributes. In addition, CCookie provides an abstraction
+// on top of the application-defined value that allows it to be treated as a collection of name-value
+// pairs if that model makes sense to you. Cookies with a single value are known as single-valued cookies.
+// Cookies whose value consists of name-value pairs are known as multi-valued cookies or dictionary cookies.
+//
+// You can set the name of a cookie by calling SetName or using the appropriate constructor.
+// The name of a cookie can be 0 or more characters.
+//
+// You can set the value of a cookie by calling SetValue or using the appropriate constructor.
+// If the cookie has a value set, it is a single-valued cookie and attempts to add a name-value pair will fail.
+// You can remove the value of a cookie by calling SetValue(NULL).
+//
+// You can add a name-value pair to a cookie by calling AddValue.
+// If the cookie has any name-value pairs, it is a multi-valued cookie and attempts to set the primary value will fail.
+// You can remove all the name-value pairs of a cookie by calling RemoveAllValues.
+//
+// Class CCookie follows the same rules for creating cookies as ASP does.
+class CCookie :
+ public CValidateObject<CCookie>
+{
+ typedef CAtlMap<CStringA, CStringA, CStringElementTraits<CStringA>,
+ CStringElementTraits<CStringA> > mapType;
+
+ const static DWORD ATLS_MAX_HTTP_DATE = 64;
+
+public:
+ // Constructs a named cookie.
+ CCookie(__in LPCSTR szName) throw(...)
+ {
+ ATLENSURE(SetName(szName));
+ }
+
+ // Constructs a single-valued cookie.
+ CCookie(__in LPCSTR szName, __in_opt LPCSTR szValue) throw(...)
+ {
+ ATLENSURE(SetName(szName));
+ ATLENSURE(SetValue(szValue));
+ }
+
+ CCookie(__in const CCookie& thatCookie) throw(...)
+ {
+ Copy(thatCookie);
+ }
+
+ CCookie& operator=(__in const CCookie& thatCookie) throw(...)
+ {
+ if(this!=&thatCookie)
+ {
+ return Copy(thatCookie);
+ }
+ return *this;
+ }
+
+ CCookie() throw()
+ {
+
+ }
+
+ __checkReturn BOOL IsEmpty() const throw()
+ {
+ return m_strName.IsEmpty();
+ }
+
+ // Call this function to set the name of this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The name of a cookie cannot contain whitespace, semicolons or commas.
+ // The name should not begin with a dollar sign ($) since such names are reserved for future use.
+ __checkReturn BOOL SetName(__in LPCSTR szName) throw()
+ {
+ _ATLTRY
+ {
+ if (szName && *szName)
+ {
+ m_strName = szName;
+ return TRUE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to retrieve the name of this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL GetName(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) const throw()
+ {
+ return CopyCString(m_strName, szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve the name of this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL GetName(__out CStringA &strName) const throw()
+ {
+ _ATLTRY
+ {
+ strName = m_strName;
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to set the value of this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // Will fail if the cookie is multi-valued.
+ // Pass NULL to remove the cookie's value.
+ __checkReturn BOOL SetValue(__in_opt LPCSTR szValue) throw()
+ {
+ _ATLTRY
+ {
+ if (m_Values.GetCount())
+ return FALSE; //already dictionary values in the cookie
+
+ if (!szValue)
+ m_strValue.Empty();
+ else
+ m_strValue = szValue;
+
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to retrieve the value of this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // Returns TRUE if there is no value or the value is of zero length.
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetValue(__out_ecount(*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) const throw()
+ {
+ return CopyCString(m_strValue, szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve the value of this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL GetValue(__out CStringA &strValue) const throw()
+ {
+ _ATLTRY
+ {
+ strValue = m_strValue;
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to add a name-value pair to the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // Will fail if the cookie is single-valued.
+ // If the named value is already present in the cookie, calling this function
+ // will modify the current value, otherwise a new name-value pair is added to the cookie.
+ // Call RemoveValue or RemoveAllValues to remove the name-value pairs
+ // added by this function.
+ __checkReturn BOOL AddValue(__in LPCSTR szName, __in_opt LPCSTR szValue) throw()
+ {
+ if (m_strValue.GetLength())
+ return FALSE;
+ _ATLTRY
+ {
+ return m_Values.SetAt(szName, szValue) != NULL;
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to modify a name-value pair associated with the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // Will fail if the cookie is single-valued.
+ // This function just calls AddValue so the name-value pair will be added if not already present.
+ // Use this function instead of AddValue to document the intentions of your call.
+ __checkReturn BOOL ModifyValue(__in LPCSTR szName, __in LPCSTR szValue) throw()
+ {
+ return AddValue(szName, szValue);
+ }
+
+ // Call this function to remove a name-value pair from the collection managed by this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL RemoveValue(__in LPCSTR szName) throw()
+ {
+ return m_Values.RemoveKey(szName);
+ }
+
+ // Call this function to remove all the name-value pairs from the collection managed by this cookie.
+ void RemoveAllValues() throw()
+ {
+ m_Values.RemoveAll();
+ }
+
+ // Call this function to add an attribute-value pair to the collection of attributes for this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // This function is equivalent to calling ModifyAttribute.
+ // Both functions will add the attribute if not already present or
+ // change its value if it has already been applied to the cookie.
+ __checkReturn BOOL AddAttribute(__in LPCSTR szName, __in LPCSTR szValue) throw()
+ {
+ if (!szName || !*szName || !szValue)
+ return FALSE;
+
+ _ATLTRY
+ {
+ return (m_Attributes.SetAt(szName, szValue) != NULL);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+
+ }
+
+ // Call this function to modify an attribute-value pair associated with the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // This function is equivalent to calling AddAttribute.
+ // Both functions will add the attribute if not already present or
+ // change its value if it has already been applied to the cookie.
+ __checkReturn BOOL ModifyAttribute(__in LPCSTR szName, __in LPCSTR szValue) throw()
+ {
+ return AddAttribute(szName, szValue);
+ }
+
+ // Call this function to remove an attribute-value pair from the collection of attributes managed by this cookie.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL RemoveAttribute(__in LPCSTR szName) throw()
+ {
+ return m_Attributes.RemoveKey(szName);
+ }
+
+ // Call this function to remove all the attribute-value pairs from the collection of attributes managed by this cookie.
+ void RemoveAllAttributes() throw()
+ {
+ m_Attributes.RemoveAll();
+ }
+
+
+ // Call this function to set the Comment attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Comment attribute allows a web server to document its
+ // intended use of a cookie. This information may be displayed
+ // by supporting browsers so that the user of the web site can
+ // decide whether to initiate or continue a session with this cookie.
+ // This attribute is optional.
+ // Version 1 attribute.
+ __checkReturn BOOL SetComment(__in LPCSTR szComment) throw()
+ {
+ BOOL bRet = SetVersion(1);
+ if (bRet)
+ bRet = AddAttribute("comment", szComment);
+ return bRet;
+ }
+
+ // Call this function to set the CommentUrl attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The CommentUrl attribute allows a web server to document its intended
+ // use of a cookie via a URL that the user of the web site can navigate to.
+ // The URL specified here should not send further cookies to the client to
+ // avoid frustrating the user.
+ // This attribute is optional.
+ // Version 1 attribute.
+ __checkReturn BOOL SetCommentUrl(__in LPCSTR szUrl) throw()
+ {
+ BOOL bRet = SetVersion(1);
+ if (bRet)
+ bRet = AddAttribute("commenturl", szUrl);
+ return bRet;
+ }
+
+ // Call this function to add or remove the Discard attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Discard attribute does not have a value.
+ // Call SetDiscard(TRUE) to add the Discard attribute
+ // or SetDiscard(FALSE) to remove the Discard attribute.
+ // Setting the Discard attribute tells a web browser that it should
+ // discard this cookie when the browser exits regardless of the
+ // value of the Max-Age attribute.
+ // This attribute is optional.
+ // When omitted, the default behavior is that the Max-Age attribute
+ // controls the lifetime of the cookie.
+ // Version 1 attribute.
+ __checkReturn BOOL SetDiscard(__in BOOL bDiscard) throw()
+ {
+ BOOL bRet = FALSE;
+ LPCSTR szKey = "Discard";
+ bRet = SetVersion(1);
+ if (bRet)
+ {
+ if (bDiscard == 0)
+ {
+ bRet = m_Attributes.RemoveKey(szKey);
+ }
+ else
+ {
+ _ATLTRY
+ {
+ bRet = m_Attributes.SetAt(szKey, " ") != 0;
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+ }
+ return bRet;
+ }
+
+ // Call this function to set the Domain attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Domain attribute is used to indicate the domain to which the current
+ // cookie applies. Browsers should only send cookies back to the relevant domains.
+ // This attribute is optional.
+ // When omitted, the default behavior is for
+ // browsers to use the full domain of the server originating the cookie. You can
+ // set this attribute value explicitly if you want to share cookies among several servers.
+ // Version 0 & Version 1 attribute.
+ __checkReturn BOOL SetDomain(__in LPCSTR szDomain) throw()
+ {
+ BOOL bRet = SetVersion(1);
+ if (bRet)
+ bRet = AddAttribute("domain", szDomain);
+ return bRet;
+ }
+
+ // Call this function to set the Max-Age attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The value of the Max-Age attribute is a lifetime in seconds for the cookie.
+ // When the time has expired, compliant browsers will discard this cookie
+ // (if they haven't already done so as a result of the Discard attribute).
+ // If Max-Age is set to zero, the browser discards the cookie immediately.
+ // This attribute is the Version 1 replacement for the Expires attribute.
+ // This attribute is optional.
+ // When omitted, the default behavior is for browsers to discard cookies
+ // when the user closes the browser.
+ // Version 1 attribute.
+ __checkReturn BOOL SetMaxAge(__in UINT nMaxAge) throw()
+ {
+ BOOL bRet = FALSE;
+ bRet = SetVersion(1);
+ if (bRet)
+ {
+ CHAR buff[20];
+ if (0 == _itoa_s(nMaxAge, buff, _countof(buff), 10))
+ {
+ bRet = AddAttribute("max-age", buff);
+ }
+ }
+ return bRet;
+ }
+
+ // Call this function to set the Path attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Path attribute specifies the subset of URLs to which this cookie applies.
+ // Only URLs that contain that path are allowed to read or modify the cookie.
+ // This attribute is optional.
+ // When omitted the default behavior is for browsers to treat the path of a cookie
+ // as the path of the request URL that generated the Set-Cookie response, up to,
+ // but not including, the right-most /.
+ // Version 0 & Version 1 attribute.
+ __checkReturn BOOL SetPath(__in LPCSTR szPath) throw()
+ {
+ BOOL bRet = SetVersion(1);
+ if (bRet)
+ bRet = AddAttribute("path", szPath);
+ return bRet;
+ }
+
+ // Call this function to set the Port attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Port attribute specifies the port to which this cookie applies.
+ // Only URLs accessed via that port are allowed to read or modify the cookie.
+ // This attribute is optional.
+ // When omitted the default behavior is for browsers to return the cookie via any port.
+ // Version 1 attribute.
+ __checkReturn BOOL SetPort(__in LPCSTR szPort) throw()
+ {
+ BOOL bRet = SetVersion(1);
+ if (bRet)
+ bRet = AddAttribute("port", szPort);
+ return bRet;
+ }
+
+ // Call this function to add or remove the Secure attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Secure attribute does not have a value.
+ // Call SetSecure(TRUE) to add the Secure attribute
+ // or SetSecure(FALSE) to remove the Secure attribute.
+ // Setting the Secure attribute tells a browser that it should
+ // transmit the cookie to the web server only via secure means such as HTTPS.
+ // This attribute is optional.
+ // When omitted, the default behavior is that the cookie
+ // will be sent via unsecured protocols.
+ // Version 0 & Version 1 attribute.
+ __checkReturn BOOL SetSecure(__in BOOL bSecure) throw()
+ {
+ BOOL bRet = FALSE;
+ LPCSTR szKey = "secure";
+ bRet = SetVersion(1);
+ if (bRet)
+ {
+ if (bSecure == 0)
+ {
+ bRet = m_Attributes.RemoveKey(szKey);
+ }
+ else
+ {
+ _ATLTRY
+ {
+ bRet = m_Attributes.SetAt(szKey, " ") != 0;
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+ }
+ return bRet;
+ }
+
+ // Call this function to set the Version attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // This attribute is required for Version 1 cookies by RFC 2109 and must have a value of 1.
+ // However, you do not need to call SetVersion explicitly from your own code unless you need to
+ // force RFC 2109 compliance. CCookie will automatically set this attribute whenever
+ // you use a Version 1 attribute in your cookie.
+ // Version 1 attribute.
+ __checkReturn BOOL SetVersion(__in UINT nVersion) throw()
+ {
+ BOOL bRet = FALSE;
+ CHAR buff[20];
+ if (0 == _itoa_s(nVersion, buff, _countof(buff), 10))
+ {
+ bRet = AddAttribute("version", buff);
+ }
+ return bRet;
+ }
+
+ // Call this function to set the Expires attribute of the cookie.
+ // Returns TRUE on success, FALSE on failure.
+ // The Expires attribute specifies an absolute date and time at which this cookie
+ // should be discarded by web browsers. Pass a SYSTEMTIME holding a Greenwich Mean Time (GMT)
+ // value or a string in the following format:
+ // Wdy, DD-Mon-YY HH:MM:SS GMT
+ // This attribute is optional.
+ // When omitted, the default behavior is for browsers to discard cookies
+ // when the user closes the browser.
+ // This attribute has been superceded in Version 1 by the Max-Age attribute,
+ // but you should continue to use this attribute for Version 0 clients.
+ // Version 0 attribute.
+ __checkReturn BOOL SetExpires(__in LPCSTR szExpires) throw()
+ {
+ return AddAttribute("expires", szExpires);
+ }
+
+ __checkReturn BOOL SetExpires(__in const SYSTEMTIME &st) throw()
+ {
+ _ATLTRY
+ {
+ CFixedStringT<CStringA, ATLS_MAX_HTTP_DATE> strTime;
+ SystemTimeToHttpDate(st, strTime);
+ return SetExpires(strTime);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to look up the value of a name-value pair applied to this cookie.
+ // Returns the requested value if present or NULL if the name was not found.
+ __checkReturn LPCSTR Lookup(__in_opt LPCSTR szName=NULL) const throw()
+ {
+ if (IsEmpty())
+ return NULL;
+
+ if (m_strValue.GetLength())
+ {
+ ATLASSERT(szName == NULL);
+ return m_strValue;
+ }
+
+ if (m_Values.GetCount())
+ {
+ ATLENSURE_RETURN_VAL(szName, NULL);
+ const mapType::CPair *pPair = NULL;
+ ATLTRY(pPair = m_Values.Lookup(szName));
+ if (pPair)
+ return (LPCSTR)pPair->m_value;
+ }
+
+ return NULL;
+ }
+
+ // Call this function to clear the cookie of all content
+ // including name, value, name-value pairs, and attributes.
+ void Empty() throw()
+ {
+ m_strName.Empty();
+ m_strValue.Empty();
+ m_Attributes.RemoveAll();
+ m_Values.RemoveAll();
+ }
+
+ // Call this function to create a CCookie from a buffer.
+ // The passed in buffer contains a cookie header retrieved
+ // from the HTTP_COOKIE request variable
+ bool ParseValue(__in const char *pstart)
+ {
+ ATLASSERT(pstart);
+ if (!pstart || *pstart == '\0')
+ return false;
+
+ // could be just a value or could be an array of name=value pairs
+ LPCSTR pEnd = pstart;
+ LPCSTR pStart = pstart;
+ CStringA name, value;
+
+ while (*pEnd != '\0')
+ {
+ while (*pEnd && *pEnd != '=' && *pEnd != '&')
+ pEnd++;
+
+ if (*pEnd == '\0' || *pEnd == '&')
+ {
+ if (pEnd > pStart)
+ CopyToCString(value, pStart, pEnd);
+ SetValue(value);
+ if (*pEnd == '&')
+ {
+ pEnd++;
+ pStart = pEnd;
+ continue;
+ }
+ return true; // we're done;
+ }
+ else if (*pEnd == '=' )
+ {
+ // we have name=value
+ if (pEnd > pStart)
+ {
+ CopyToCString(name, pStart, pEnd);
+ }
+ else
+ {
+ pEnd++;
+ pStart = pEnd;
+ break;
+ }
+
+ // skip '=' and go for value
+ pEnd++;
+ pStart = pEnd;
+ while (*pEnd && *pEnd != '&' && *pEnd != '=')
+ pEnd++;
+ if (pEnd > pStart)
+ CopyToCString(value, pStart, pEnd);
+
+ ATLENSURE(AddValue(name, value));
+
+ if (*pEnd != '\0')
+ pEnd++;
+ pStart = pEnd;
+
+ }
+ }
+
+ return true;
+ }
+
+ // Call this function to render this cookie
+ // into a buffer. Returns TRUE on success, FALSE on failure.
+ // On entry, pdwLen should point to a DWORD that indicates
+ // the size of the buffer in bytes. On exit, the DWORD contains
+ // the number of bytes transferred or available to be transferred
+ // into the buffer (including the nul-terminating byte). On
+ // success, the buffer will contain the correct HTTP
+ // representation of the current cookie suitable for sending to
+ // a client as the body of a Set-Cookie header.
+ __success(return==true) ATL_NOINLINE __checkReturn BOOL Render(__out_ecount_part_opt(*pdwLen,*pdwLen) LPSTR szCookieBuffer, __inout DWORD *pdwLen) const throw()
+ {
+ if (!pdwLen)
+ return FALSE;
+ CStringA strCookie;
+ CStringA name, value;
+ DWORD dwLenBuff = *pdwLen;
+ *pdwLen = 0;
+
+ // A cookie must have a name!
+ if (!m_strName.GetLength())
+ {
+ *pdwLen = 0;
+ return FALSE;
+ }
+ _ATLTRY
+ {
+ strCookie = m_strName;
+ int nValSize = (int) m_Values.GetCount();
+ if (nValSize)
+ {
+ strCookie += '=';
+ POSITION pos = m_Values.GetStartPosition();
+ for (int i=0; pos; i++)
+ {
+ m_Values.GetNextAssoc(pos, name, value);
+ strCookie += name;
+ if (value.GetLength())
+ {
+ strCookie += '=';
+ strCookie += value;
+ }
+ if (i <= nValSize-2)
+ strCookie += '&';
+ }
+ }
+ else
+ {
+ strCookie += '=';
+ if (m_strValue.GetLength())
+ strCookie += m_strValue;
+ }
+
+ CStringA strAttributes;
+ if (!RenderAttributes(strAttributes))
+ return FALSE;
+ if (strAttributes.GetLength() > 0)
+ {
+ strCookie += "; ";
+ strCookie += strAttributes;
+ }
+
+ DWORD dwLenCookie = strCookie.GetLength() + 1;
+ *pdwLen = dwLenCookie;
+ if (!szCookieBuffer)
+ return TRUE; // caller just wanted the length
+
+ // see if buffer is big enough
+ if (dwLenCookie > dwLenBuff)
+ return FALSE; //buffer wasn't big enough
+
+ // copy the buffer
+ Checked::strcpy_s(szCookieBuffer, *pdwLen, strCookie);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ POSITION GetFirstAttributePos() const throw()
+ {
+ return m_Attributes.GetStartPosition();
+ }
+
+ const CStringA& GetNextAttributeName(__inout POSITION& pos) const throw()
+ {
+ return m_Attributes.GetNextKey(pos);
+ }
+
+ const CStringA& GetAttributeValueAt(__in POSITION pos) const throw()
+ {
+ return m_Attributes.GetValueAt(pos);
+ }
+
+ BOOL GetNextAttrAssoc(__inout POSITION& pos, __out CStringA& key,
+ __out CStringA& val) const throw()
+ {
+ _ATLTRY
+ {
+ m_Attributes.GetNextAssoc(pos, key, val);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ POSITION GetFirstValuePos() const throw()
+ {
+ return m_Values.GetStartPosition();
+ }
+
+ const CStringA& GetNextValueName(__inout POSITION& pos) const throw()
+ {
+ return m_Values.GetNextKey(pos);
+ }
+
+ const CStringA& GetValueAt(__in POSITION pos) const throw()
+ {
+ return m_Values.GetValueAt(pos);
+ }
+
+ BOOL GetNextValueAssoc(__inout POSITION& pos, __out CStringA& key,
+ __out CStringA& val) const throw()
+ {
+ _ATLTRY
+ {
+ m_Values.GetNextAssoc(pos, key, val);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+protected:
+// Implementation
+ BOOL RenderAttributes(__out CStringA& strAttributes) const throw()
+ {
+ _ATLTRY
+ {
+ strAttributes.Empty();
+
+ POSITION pos = m_Attributes.GetStartPosition();
+ CStringA key, val;
+ for (int i=0; pos; i++)
+ {
+ if (i)
+ strAttributes += ";";
+ m_Attributes.GetNextAssoc(pos, key, val);
+ strAttributes += key;
+ strAttributes += '=';
+ strAttributes += val;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ return TRUE;
+ }
+private:
+ CCookie& Copy(__in const CCookie& thatCookie) throw(...)
+ {
+ m_strName = thatCookie.m_strName;
+ m_strValue = thatCookie.m_strValue;
+ POSITION pos = NULL;
+ CStringA strName, strValue;
+ if (!thatCookie.m_Attributes.IsEmpty())
+ {
+ pos = thatCookie.m_Attributes.GetStartPosition();
+ while (pos)
+ {
+ thatCookie.m_Attributes.GetNextAssoc(pos, strName, strValue);
+ m_Attributes.SetAt(strName, strValue);
+ }
+ }
+ if (!thatCookie.m_Values.IsEmpty())
+ {
+ strName.Empty();
+ strValue.Empty();
+ pos = thatCookie.m_Values.GetStartPosition();
+ while (pos)
+ {
+ thatCookie.m_Values.GetNextAssoc(pos, strName, strValue);
+ m_Values.SetAt(strName, strValue);
+ }
+ }
+ return *this;
+ }
+
+ // Call this function to copy a substring to a CString reference and ensure nul-termination.
+ void CopyToCString(__out CStringA& string, __in_ecount(pEnd-pStart) LPCSTR pStart, __in LPCSTR pEnd) throw( ... )
+ {
+ ATLENSURE( pStart != NULL );
+ ATLENSURE( pEnd != NULL );
+
+ string.SetString(pStart, (int)(pEnd-pStart));
+ string.Trim();
+ }
+
+
+public:
+ // These are implementation only, use at your own risk!
+ // Map of attribute-value pairs applied to this cookie.
+ mapType m_Attributes;
+
+ // Map of name-value pairs applied to this cookie.
+ mapType m_Values;
+
+ // The name of this cookie.
+ CStringA m_strName;
+
+ // The value of this cookie.
+ CStringA m_strValue;
+
+}; // class CCookie
+
+class CSessionCookie : public CCookie
+{
+public:
+ CSessionCookie() throw(...)
+ {
+ if (!SetName(SESSION_COOKIE_NAME) ||
+ !SetPath("/"))
+ AtlThrow(E_OUTOFMEMORY);
+ }
+
+ CSessionCookie(LPCSTR szSessionID) throw(...)
+ {
+ if (!SetName(SESSION_COOKIE_NAME) ||
+ !SetPath("/") ||
+ !SetSessionID(szSessionID) )
+ AtlThrow(E_OUTOFMEMORY);
+ }
+
+ BOOL SetSessionID(LPCSTR szSessionID) throw()
+ {
+ ATLASSERT(szSessionID && szSessionID[0]);
+ return SetValue(szSessionID);
+ }
+}; // class CSessionCookie
+
+template<>
+class CElementTraits< CCookie > :
+ public CElementTraitsBase< CCookie >
+{
+public:
+ typedef const CCookie& INARGTYPE;
+ typedef CCookie& OUTARGTYPE;
+
+ static ULONG Hash( __in INARGTYPE cookie )
+ {
+ return CStringElementTraits<CStringA>::Hash( cookie.m_strName );
+ }
+
+ static bool CompareElements( __in INARGTYPE cookie1, __in INARGTYPE cookie2 )
+ {
+ return( cookie1.m_strName == cookie2.m_strName );
+ }
+
+ static int CompareElementsOrdered( __in INARGTYPE cookie1, __in INARGTYPE cookie2 )
+ {
+ return( cookie1.m_strName.Compare( cookie2.m_strName ) );
+ }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Request and response classes and support functions
+
+
+// This class is a wrapper for CAtlMap that allows maps to be chained.
+// It simply adds a bool that tells whether or not a map shares values
+template <typename K, typename V, typename KTraits=CElementTraits<K>, typename VTraits=CElementTraits<V> >
+class CHttpMap
+{
+private:
+
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ typedef CRBMultiMap<K, V, KTraits, VTraits> MAPTYPE;
+#else
+ typedef CAtlMap<K, V, KTraits, VTraits> MAPTYPE;
+#endif // ATL_HTTP_PARAM_MULTIMAP
+
+public:
+
+ typedef typename KTraits::INARGTYPE KINARGTYPE;
+ typedef typename KTraits::OUTARGTYPE KOUTARGTYPE;
+ typedef typename VTraits::INARGTYPE VINARGTYPE;
+ typedef typename VTraits::OUTARGTYPE VOUTARGTYPE;
+
+ typedef typename MAPTYPE::CPair CPair;
+
+private:
+
+ bool m_bShared;
+
+ MAPTYPE m_map;
+
+public:
+
+ CHttpMap() throw()
+ : m_bShared(false)
+ {
+ }
+
+ virtual ~CHttpMap()
+ {
+ }
+
+ inline bool IsShared() const throw()
+ {
+ return m_bShared;
+ }
+
+ inline void SetShared(__in bool bShared) throw()
+ {
+ m_bShared = bShared;
+ }
+
+ //
+ // exposed lookup and iteration functionality
+ //
+
+ inline size_t GetCount() const throw()
+ {
+ return m_map.GetCount();
+ }
+
+ inline bool IsEmpty() const throw()
+ {
+ return m_map.IsEmpty();
+ }
+
+ inline POSITION GetStartPosition() const throw()
+ {
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ return m_map.GetHeadPosition();
+#else
+ return m_map.GetStartPosition();
+#endif // ATL_HTTP_PARAM_MULTIMAP
+ }
+
+ // Lookup wrappers
+ bool Lookup( __in KINARGTYPE key, __out VOUTARGTYPE value ) const throw()
+ {
+ _ATLTRY
+ {
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ CPair *p = Lookup(key);
+ if (p != NULL)
+ {
+ value = p->m_value;
+ return true;
+ }
+ return false;
+#else
+ return m_map.Lookup(key, value);
+#endif // ATL_HTTP_PARAM_MULTIMAP
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+ }
+
+ const CPair* Lookup( __in KINARGTYPE key ) const throw()
+ {
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ POSITION pos = m_map.FindFirstWithKey(key);
+ if (pos != NULL)
+ {
+ return m_map.GetAt(pos);
+ }
+ return NULL;
+#else
+ return m_map.Lookup(key);
+#endif // ATL_HTTP_PARAM_MULTIMAP
+ }
+
+ CPair* Lookup( __in KINARGTYPE key ) throw()
+ {
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ POSITION pos = m_map.FindFirstWithKey(key);
+ if (pos != NULL)
+ {
+ return m_map.GetAt(pos);
+ }
+ return NULL;
+#else
+ return m_map.Lookup(key);
+#endif // ATL_HTTP_PARAM_MULTIMAP
+ }
+
+ // iteration wrappers
+ void GetNextAssoc( __inout POSITION& pos, __out KOUTARGTYPE key, __out VOUTARGTYPE value ) const throw(...)
+ {
+ m_map.GetNextAssoc(pos, key, value);
+ }
+
+ const CPair* GetNext( __inout POSITION& pos ) const throw()
+ {
+ return m_map.GetNext(pos);
+ }
+
+ CPair* GetNext( __inout POSITION& pos ) throw()
+ {
+ return m_map.GetNext(pos);
+ }
+
+ const K& GetNextKey( __inout POSITION& pos ) const throw()
+ {
+ return m_map.GetNextKey(pos);
+ }
+
+ const V& GetNextValue( __inout POSITION& pos ) const throw()
+ {
+ return m_map.GetNextValue(pos);
+ }
+
+ V& GetNextValue( __inout POSITION& pos ) throw()
+ {
+ return m_map.GetNextValue(pos);
+ }
+
+ void GetAt( __in POSITION pos, __out KOUTARGTYPE key, __out VOUTARGTYPE value ) const throw(...)
+ {
+ return m_map.GetAt(pos, key, value);
+ }
+
+ CPair* GetAt( __in POSITION pos ) throw()
+ {
+ return m_map.GetAt(pos);
+ }
+
+ const CPair* GetAt( __in POSITION pos ) const throw()
+ {
+ return m_map.GetAt(pos);
+ }
+
+ const K& GetKeyAt( __in POSITION pos ) const throw()
+ {
+ return m_map.GetKeyAt(pos);
+ }
+
+ const V& GetValueAt( __in POSITION pos ) const throw()
+ {
+ return m_map.GetValueAt(pos);
+ }
+
+ V& GetValueAt( __in POSITION pos ) throw()
+ {
+ return m_map.GetValueAt(pos);
+ }
+
+ // modification wrappers
+ POSITION SetAt( __in KINARGTYPE key, __in_opt VINARGTYPE value ) throw(...)
+ {
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ return m_map.Insert(key, value);
+#else
+ return m_map.SetAt(key, value);
+#endif // ATL_HTTP_PARAM_MULTIMAP
+ }
+
+ bool RemoveKey( __in KINARGTYPE key ) throw()
+ {
+#ifdef ATL_HTTP_PARAM_MULTIMAP
+ return (m_map.RemoveKey(key) != 0);
+#else
+ return m_map.RemoveKey(key);
+#endif // ATL_HTTP_PARAM_MULTIMAP
+ }
+
+ virtual void RemoveAll()
+ {
+ m_map.RemoveAll();
+ }
+};
+
+// This class is a wrapper for CHttpMap that assumes it's values are pointers that
+// should be deleted on RemoveAll
+template <typename K, typename V, typename KTraits=CElementTraits<K>, typename VTraits=CElementTraits<V> >
+class CHttpPtrMap : public CHttpMap<K, V, KTraits, VTraits>
+{
+public:
+ typedef CHttpMap<K, V, KTraits, VTraits> Base;
+
+ void RemoveAll()
+ {
+ if (!IsShared())
+ {
+ POSITION pos = GetStartPosition();
+ while (pos)
+ {
+ GetNextValue(pos)->Free();
+ }
+ }
+ Base::RemoveAll();
+ }
+
+ ~CHttpPtrMap()
+ {
+ RemoveAll();
+ }
+};
+
+// This class represents a collection of request parameters - the name-value pairs
+// found, for example, in a query string or in the data provided when a form is submitted to the server.
+// Call Parse to build the collection from a string of URL-encoded data.
+// Use the standard collection methods of the CHttpMap base class to retrieve the
+// decoded names and values.
+// Use the methods of the CValidateObject base class to validate the parameters.
+class CHttpRequestParams :
+#if (defined(ATL_HTTP_PARAM_MAP_CASEINSENSITIVE))
+ public CHttpMap<CStringA, CStringA, CStringElementTraitsI<CStringA>, CStringElementTraitsI<CStringA> >,
+#else
+ public CHttpMap<CStringA, CStringA, CStringElementTraits<CStringA>, CStringElementTraits<CStringA> >,
+#endif
+ public CValidateObject<CHttpRequestParams>
+{
+public:
+#if (defined(ATL_HTTP_PARAM_MAP_CASEINSENSITIVE))
+ typedef CHttpMap<CStringA, CStringA, CStringElementTraitsI<CStringA>, CStringElementTraitsI<CStringA> > BaseMap;
+#else
+ typedef CHttpMap<CStringA, CStringA, CStringElementTraits<CStringA>, CStringElementTraits<CStringA> > BaseMap;
+#endif
+
+ LPCSTR Lookup(__in LPCSTR szName) const throw()
+ {
+ _ATLTRY
+ {
+ if (!szName)
+ return NULL;
+
+ const CPair *p = BaseMap::Lookup(szName);
+ if (p)
+ {
+ return p->m_value;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return NULL;
+ }
+
+ // Call this function to build a collection of name-value pairs from a string of URL-encoded data.
+ // Returns TRUE on success, FALSE on failure.
+ // URL-encoded data:
+ // Each name-value pair is separated from the next by an ampersand (&)
+ // Each name is separated from its corresponding value by an equals signs (=)
+ // The end of the data to be parsed is indicated by a nul character (\0) or a pound symbol (#)
+ // A plus sign (+) in the input will be decoded as a space
+ // A percent sign (%) in the input signifies the start of an escaped octet.
+ // The next two digits represent the hexadecimal code of the character.
+ // For example, %21 is the escaped encoding for the US-ASCII exclamation mark and will be decoded as !.
+ // Common sources of URL-encoded data are query strings and the bodies of POST requests with content type of application/x-www-form-urlencoded.
+ //
+ // Parse and Render are complementary operations.
+ // Parse creates a collection from a string.
+ // Render creates a string from a collection.
+ ATL_NOINLINE __checkReturn BOOL Parse(__inout LPSTR szQueryString) throw()
+ {
+ while (szQueryString && *szQueryString)
+ {
+ LPSTR szUrlCurrent = szQueryString;
+ LPSTR szName = szUrlCurrent;
+ LPSTR szPropValue;
+
+ while (*szQueryString)
+ {
+ if (*szQueryString == '=')
+ {
+ szQueryString++;
+ break;
+ }
+ if (*szQueryString == '&')
+ {
+ break;
+ }
+ if (*szQueryString == '+')
+ *szUrlCurrent = ' ';
+ else if (*szQueryString == '%')
+ {
+ // if there is a % without two characters
+ // at the end of the url we skip it
+ if (*(szQueryString+1) && *(szQueryString+2))
+ {
+ short nFirstDigit = AtlHexValue(szQueryString[1]);
+ short nSecondDigit = AtlHexValue(szQueryString[2]);
+
+ if( nFirstDigit < 0 || nSecondDigit < 0 )
+ {
+ break;
+ }
+ *szUrlCurrent = static_cast<CHAR>(16*nFirstDigit+nSecondDigit);
+ szQueryString += 2;
+ }
+ else
+ *szUrlCurrent = '\0';
+ }
+ else
+ *szUrlCurrent = *szQueryString;
+
+ szQueryString++;
+ szUrlCurrent++;
+ }
+
+ if (*szUrlCurrent == '&')
+ {
+ *szUrlCurrent++ = '\0';
+ szQueryString++;
+ szPropValue = "";
+ }
+ else
+ {
+ if (*szUrlCurrent)
+ *szUrlCurrent++ = '\0';
+
+ // we have the property name
+ szPropValue = szUrlCurrent;
+ while (*szQueryString && *szQueryString != '#')
+ {
+ if (*szQueryString == '&')
+ {
+ szQueryString++;
+ break;
+ }
+ if (*szQueryString == '+')
+ *szUrlCurrent = ' ';
+ else if (*szQueryString == '%')
+ {
+ // if there is a % without two characters
+ // at the end of the url we skip it
+ if (*(szQueryString+1) && *(szQueryString+2))
+ {
+ short nFirstDigit = AtlHexValue(szQueryString[1]);
+ short nSecondDigit = AtlHexValue(szQueryString[2]);
+
+ if( nFirstDigit < 0 || nSecondDigit < 0 )
+ {
+ break;
+ }
+ *szUrlCurrent = static_cast<CHAR>(16*nFirstDigit+nSecondDigit);
+ szQueryString += 2;
+ }
+ else
+ *szUrlCurrent = '\0';
+ }
+ else
+ *szUrlCurrent = *szQueryString;
+ szQueryString++;
+ szUrlCurrent++;
+ }
+ // we have the value
+ *szUrlCurrent = '\0';
+ szUrlCurrent++;
+ }
+
+ _ATLTRY
+ {
+ SetAt(szName, szPropValue);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+
+ // Call this function to render the map of names and values into a buffer as a URL-encoded string.
+ // Returns TRUE on success, FALSE on failure.
+ // On entry, pdwLen should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ // On success, the buffer will contain the correct URL-encoded representation of the current object
+ // suitable for sending to a server as a query string or in the body of a form.
+ // URL-encoding:
+ // Each name-value pair is separated from the next by an ampersand (&)
+ // Each name is separated from its corresponding value by an equals signs (=)
+ // A space is encoded as a plus sign (+).
+ // Other unsafe characters (as determined by AtlIsUnsafeUrlChar) are encoded as escaped octets.
+ // An escaped octet is a percent sign (%) followed by two digits representing the hexadecimal code of the character.
+ //
+ // Parse and Render are complementary operations.
+ // Parse creates a collection from a string.
+ // Render creates a string from a collection.
+ ATL_NOINLINE __checkReturn BOOL Render(__out_ecount(pdwLen) LPSTR szParameters, __inout LPDWORD pdwLen)
+ {
+ ATLENSURE(szParameters);
+ ATLENSURE(pdwLen);
+ _ATLTRY
+ {
+ if (GetCount() == 0)
+ {
+ *szParameters = '\0';
+ *pdwLen = 0;
+ return TRUE;
+ }
+
+ CStringA strParams;
+ POSITION pos = GetStartPosition();
+ while (pos != NULL)
+ {
+ LPCSTR szBuf = GetKeyAt(pos);
+ EscapeToCString(strParams, szBuf);
+ szBuf = GetValueAt(pos);
+ if (*szBuf)
+ {
+ strParams+= '=';
+ EscapeToCString(strParams, szBuf);
+ }
+ strParams+= '&';
+ GetNext(pos);
+ }
+
+ DWORD dwLen = strParams.GetLength();
+ strParams.Delete(dwLen-1);
+ BOOL bRet = TRUE;
+ if (dwLen > *pdwLen)
+ {
+ bRet = FALSE;
+ }
+ else
+ {
+ dwLen--;
+ Checked::memcpy_s(szParameters, *pdwLen, static_cast<LPCSTR>(strParams), dwLen);
+ szParameters[dwLen] = '\0';
+ }
+
+ *pdwLen = dwLen;
+ return bRet;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+}; // class CHttpRequestParams
+
+#ifndef MAX_TOKEN_LENGTH
+#define MAX_TOKEN_LENGTH (MAX_PATH)
+#endif
+
+// This class represents the information about a file that has been uploaded to the web server.
+class CHttpRequestFile : public IHttpFile
+{
+private:
+
+ // The name of the form field used to upload the file.
+ CHAR m_szParamName[MAX_TOKEN_LENGTH];
+
+ // The original file name of the uploaded file as set by the client.
+ CHAR m_szFileName[MAX_PATH];
+
+ // The original path and file name of the uploaded file as set by the client.
+ CHAR m_szFullFileName[MAX_PATH];
+
+ // The MIME type of the uploaded file.
+ CHAR m_szContentType[MAX_TOKEN_LENGTH];
+
+ // The name of the uploaded file on the server.
+ CHAR m_szTempFileName[MAX_PATH];
+
+ // The size of the file in bytes.
+ ULONGLONG m_nFileSize;
+
+public:
+
+ CHttpRequestFile() throw()
+ {
+ *m_szParamName = '\0';
+ *m_szFileName = '\0';
+ *m_szFullFileName = '\0';
+ *m_szContentType = '\0';
+ m_nFileSize = 0;
+ }
+
+ __checkReturn BOOL Initialize(
+ __in_opt LPCSTR pParamName,
+ __in LPCSTR pFileName,
+ __in_opt LPCSTR pTempFileName,
+ __in_opt LPCSTR pContentType,
+ __in const ULONGLONG &nFileSize)
+ {
+ ATLENSURE( pFileName != NULL );
+
+ if (!SafeStringCopy(m_szFullFileName, pFileName))
+ {
+ // path too long
+ return FALSE;
+ }
+
+ if (pParamName && *pParamName)
+ {
+ if (!SafeStringCopy(m_szParamName, pParamName))
+ {
+ // string too long
+ return FALSE;
+ }
+ }
+
+ if (pTempFileName && *pTempFileName)
+ {
+ if (!SafeStringCopy(m_szTempFileName, pTempFileName))
+ {
+ // path too long
+ return FALSE;
+ }
+ }
+
+ if (pContentType && *pContentType)
+ {
+ if (!SafeStringCopy(m_szContentType, pContentType))
+ {
+ // string too long
+ return FALSE;
+ }
+ }
+
+ // Set m_szFileName to be the file name without the path.
+ // This is likely to be the most meaningful part of the
+ // original file name once the file reaches the server.
+
+ LPSTR szTmp = m_szFullFileName;
+ LPSTR szFile = m_szFileName;
+ LPSTR pszLastCharFileBuf=m_szFileName + _countof(m_szFileName) - 1;
+ while (*szTmp)
+ {
+ if (*szTmp == '\\')
+ {
+ szFile = m_szFileName;
+ }
+ else
+ {
+ if (szFile > pszLastCharFileBuf)
+ {
+ return FALSE;
+ }
+ *szFile++ = *szTmp;
+ }
+ szTmp++;
+ }
+ if (szFile > pszLastCharFileBuf)
+ {
+ return FALSE;
+ }
+ *szFile = 0;
+
+ m_nFileSize = nFileSize;
+ return TRUE;
+ }
+
+
+ //=======================================
+ // IHttpFile interface
+ //=======================================
+ __checkReturn LPCSTR GetParamName()
+ {
+ return m_szParamName;
+ }
+
+ __checkReturn LPCSTR GetFileName()
+ {
+ return m_szFileName;
+ }
+
+ __checkReturn LPCSTR GetFullFileName()
+ {
+ return m_szFullFileName;
+ }
+
+ __checkReturn LPCSTR GetContentType()
+ {
+ return m_szContentType;
+ }
+
+ __checkReturn LPCSTR GetTempFileName()
+ {
+ return m_szTempFileName;
+ }
+
+ __checkReturn ULONGLONG GetFileSize()
+ {
+ return m_nFileSize;
+ }
+
+ void Free()
+ {
+ delete this;
+ }
+
+}; // class CHttpRequestFile
+
+
+// utility function to ReadData from a ServerContext
+ATL_NOINLINE inline
+__checkReturn BOOL ReadClientData(__inout IHttpServerContext *pServerContext, __out_ecount_part(*pdwLen,*pdwLen) LPSTR pbDest, __inout LPDWORD pdwLen, __in DWORD dwBytesRead)
+{
+ ATLENSURE(pServerContext != NULL);
+ ATLENSURE(pdwLen != NULL);
+ ATLENSURE(pbDest != NULL);
+
+ _ATLTRY
+ {
+ DWORD dwToRead = *pdwLen;
+ DWORD dwAvailableBytes = pServerContext->GetAvailableBytes();
+ DWORD dwRead(0);
+
+ // Read from available data first
+ if (dwBytesRead < dwAvailableBytes)
+ {
+ LPBYTE pbAvailableData = pServerContext->GetAvailableData();
+ pbAvailableData+= dwBytesRead;
+ DWORD dwAvailableToRead = __min(dwToRead, dwAvailableBytes-dwBytesRead);
+ Checked::memcpy_s(pbDest, *pdwLen, pbAvailableData, dwAvailableToRead);
+ dwBytesRead+= dwAvailableToRead;
+ dwToRead-= dwAvailableToRead;
+ pbDest+= dwAvailableToRead;
+ dwRead+= dwAvailableToRead;
+ }
+
+ DWORD dwTotalBytes = pServerContext->GetTotalBytes();
+
+ // If there is still more to read after the available data is exhausted
+ if (dwToRead && dwBytesRead < dwTotalBytes)
+ {
+ DWORD dwClientBytesToRead = __min(pServerContext->GetTotalBytes()-dwBytesRead, dwToRead);
+ DWORD dwClientBytesRead = 0;
+
+ // keep on reading until we've read the amount requested
+ do
+ {
+ dwClientBytesRead = dwClientBytesToRead;
+ if (!pServerContext->ReadClient(pbDest, &dwClientBytesRead))
+ {
+ return FALSE;
+ }
+ dwClientBytesToRead-= dwClientBytesRead;
+ dwRead+= dwClientBytesRead;
+ pbDest+= dwClientBytesRead;
+
+ } while (dwClientBytesToRead != 0 && dwClientBytesRead != 0);
+
+
+ }
+
+ *pdwLen = dwRead;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#ifndef MAX_MIME_BOUNDARY_LEN
+ #define MAX_MIME_BOUNDARY_LEN 128
+#endif
+
+enum ATL_FORM_FLAGS
+{
+ ATL_FORM_FLAG_NONE = 0,
+ ATL_FORM_FLAG_IGNORE_FILES = 1,
+ ATL_FORM_FLAG_REFUSE_FILES = 2,
+ ATL_FORM_FLAG_IGNORE_EMPTY_FILES = 4,
+ ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS = 8,
+};
+
+// Use this class to read multipart/form-data from the associated server context
+// and generate files as necessary from the data in the body of the request.
+class CMultiPartFormParser
+{
+protected:
+
+ LPSTR m_pCurrent;
+ LPSTR m_pEnd;
+ LPSTR m_pStart;
+ CHAR m_szBoundary[MAX_MIME_BOUNDARY_LEN+2];
+ DWORD m_dwBoundaryLen;
+ BOOL m_bFinished;
+ CComPtr<IHttpServerContext> m_spServerContext;
+
+public:
+
+ typedef CHttpMap<CStringA, IHttpFile*, CStringElementTraits<CStringA> > FILEMAPTYPE;
+
+ CMultiPartFormParser(__in IHttpServerContext* pServerContext) throw() :
+ m_pCurrent(NULL),
+ m_pEnd(NULL),
+ m_pStart(NULL),
+ m_dwBoundaryLen(0),
+ m_bFinished(FALSE),
+ m_spServerContext(pServerContext)
+ {
+ *m_szBoundary = '\0';
+ }
+
+ ~CMultiPartFormParser() throw()
+ {
+ _ATLTRY
+ {
+ // free memory if necessary
+ if (m_spServerContext->GetTotalBytes() > m_spServerContext->GetAvailableBytes())
+ {
+ free(m_pStart);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+
+ // Call this function to read multipart/form-data from the current HTTP request,
+ // allowing files to be uploaded to the web server.
+ //
+ // Returns TRUE on success, FALSE on failure.
+ //
+ // Forms can be sent to a web server using one of two encodings: application/x-www-form-urlencoded or multipart/form-data.
+ // In addition to the simple name-value pairs typically associated with
+ // application/x-www-form-urlencoded form data, multipart/form-data (as
+ // described in RFC 2388) can also contain files to be uploaded
+ // to the web server.
+ //
+ // This function will generate a physical file for each file contained in the multipart/form-data request body.
+ // The generated files are stored in the server's temporary directory as returned by the
+ // GetTempPath API and are named using the GetTempFileName API.
+ // The information about each file can be obtained from the elements of the Files array.
+ // You can retrieve the original name of the file on the client, the name of the generated file on the server,
+ // the MIME content type of the uploaded file, the name of the form field associated with that file, and the size in
+ // bytes of the file. All this information is exposed by the CHttpRequestFile objects in the array.
+ //
+ // In addition to generating files and populating the Files array with information about them,
+ // this function also populates the pQueryParams array with the names and values of the other form fields
+ // contained in the current request. The file fields are also added to this array. The value of these fields
+ // is the full name of the generated file on the server.
+ //
+ // Note that files can be generated even if this function returns FALSE unless you specify either the
+ // ATL_FORM_FLAG_IGNORE_FILES or the ATL_FORM_FLAG_REFUSE_FILES flag. If you don't specify one of these
+ // flags, you should always check the Files array for generated files and delete any that are no longer
+ // needed to prevent your web server from running out of disk space.
+ //
+ // dwFlags can be a combination of one or more of the following values:
+ // ATL_FORM_FLAG_NONE Default behavior.
+ // ATL_FORM_FLAG_IGNORE_FILES Any attempt to upload files is ignored.
+ // ATL_FORM_FLAG_REFUSE_FILES Any attempt to upload files is treated as a failure. The function will return FALSE.
+ // ATL_FORM_FLAG_IGNORE_EMPTY_FILES Files with a size of zero bytes are ignored.
+ // ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS Fields with no content are ignored.
+ ATL_NOINLINE BOOL GetMultiPartData(
+ __inout FILEMAPTYPE& Files,
+ __inout CHttpRequestParams* pQueryParams,
+ __in DWORD dwFlags=ATL_FORM_FLAG_NONE) throw()
+ {
+ _ATLTRY
+ {
+ if (!InitializeParser())
+ {
+ return FALSE;
+ }
+
+ //Get to the first boundary
+ if (!ReadUntilBoundary())
+ {
+ return FALSE;
+ }
+
+ CStringA strParamName;
+ CStringA strFileName;
+ CStringA strContentType;
+ CStringA strData;
+ BOOL bFound;
+
+ while (!m_bFinished)
+ {
+ // look for "name" field
+ if (!GetMimeData(strParamName, "name=", sizeof("name=")-1, &bFound, TRUE) || !bFound)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
+ return FALSE;
+ }
+
+ // see if it's a file
+ if (!GetMimeData(strFileName, "filename=", sizeof("filename=")-1, &bFound, TRUE))
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
+ return FALSE;
+ }
+
+ if (bFound)
+ {
+ if (dwFlags & ATL_FORM_FLAG_REFUSE_FILES)
+ {
+ return FALSE;
+ }
+
+ if (!strFileName.GetLength())
+ {
+ if(!ReadUntilBoundary())
+ {
+ return FALSE;
+ }
+ continue;
+ }
+
+ if (!GetMimeData(strContentType, "Content-Type:", sizeof("Content-Type:")-1, &bFound, TRUE))
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
+ return FALSE;
+ }
+
+ // move to the actual uploaded data
+ if (!MoveToData())
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
+ return FALSE;
+ }
+
+ // if the user doesn't want files, don't save the file
+ if (dwFlags & ATL_FORM_FLAG_IGNORE_FILES)
+ {
+ if (!ReadUntilBoundary(NULL, NULL))
+ {
+ return FALSE;
+ }
+ continue;
+ }
+
+ CAtlTemporaryFile ctf;
+ HRESULT hr = ctf.Create();
+ if (hr != S_OK)
+ return FALSE;
+
+ if (!ReadUntilBoundary(NULL, &ctf))
+ {
+ ctf.Close();
+ return FALSE;
+ }
+ ULONGLONG nFileSize = 0;
+ if (ctf.GetSize(nFileSize) != S_OK)
+ return FALSE;
+
+ if ((dwFlags & ATL_FORM_FLAG_IGNORE_EMPTY_FILES) && nFileSize == 0)
+ {
+ ctf.Close();
+ continue;
+ }
+
+ CAutoPtr<CHttpRequestFile> spFile;
+
+ CT2AEX<MAX_PATH+1> szTempFileNameA(ctf.TempFileName());
+
+ ATLTRY(spFile.Attach(new CHttpRequestFile()));
+ if (!spFile)
+ {
+ return FALSE;
+ }
+
+ if (!spFile->Initialize(strParamName, strFileName, szTempFileNameA, strContentType, nFileSize))
+ {
+ // one of the strings was too long
+ return FALSE;
+ }
+
+ if (!Files.SetAt(szTempFileNameA, spFile))
+ {
+ return FALSE;
+ }
+
+ spFile.Detach();
+
+ if (!pQueryParams || !pQueryParams->SetAt(strParamName, szTempFileNameA))
+ {
+ return FALSE;
+ }
+
+ ctf.HandsOff();
+
+ continue;
+ }
+
+ // move to the actual uploaded data
+ if (!MoveToData())
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
+ return FALSE;
+ }
+
+ if (!ReadUntilBoundary(&strData))
+ {
+ return FALSE;
+ }
+
+ if ((dwFlags & ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS) && strData.GetLength() == 0)
+ continue;
+
+ if (!pQueryParams || !pQueryParams->SetAt(strParamName, strData))
+ {
+ return FALSE;
+ }
+
+ }
+
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+private:
+
+ // implementation
+
+ // case insensitive substring search -- does not handle multibyte characters (unlike tolower)
+ // allows searching up to a maximum point in a string
+ inline char AtlCharLower(__in char ch) throw()
+ {
+ if (ch > 64 && ch < 91)
+ {
+ return ch+32;
+ }
+
+ return ch;
+ }
+
+ inline __checkReturn char * _stristr (__in const char * str1, __in const char * str2)
+ {
+ ATLENSURE(str1!=NULL);
+ ATLENSURE(str2!=NULL);
+ char *cp = (char *) str1;
+ char *s1, *s2;
+
+ if ( !*str2 )
+ return((char *)str1);
+
+ while (*cp)
+ {
+ s1 = cp;
+ s2 = (char *) str2;
+
+ while ( *s1 && *s2 && !(AtlCharLower(*s1)-AtlCharLower(*s2)) )
+ {
+ s1++, s2++;
+ }
+
+ if (!*s2)
+ {
+ return(cp);
+ }
+
+ cp++;
+ }
+
+ return(NULL);
+ }
+
+
+ inline __checkReturn char * _stristrex (__in const char * str1, __in const char * str2, __in const char * str1End)
+ {
+ ATLENSURE(str1!=NULL);
+ ATLENSURE(str2!=NULL);
+ char *cp = (char *) str1;
+ char *s1, *s2;
+
+ if ( !*str2 )
+ return((char *)str1);
+
+ while (cp != str1End)
+ {
+ s1 = cp;
+ s2 = (char *) str2;
+
+ while ( s1 != str1End && *s2 && !(AtlCharLower(*s1)-AtlCharLower(*s2)) )
+ {
+ s1++, s2++;
+ }
+
+ if (!*s2)
+ {
+ return (cp);
+ }
+
+ if (s1 == str1End)
+ {
+ return (NULL);
+ }
+
+ cp++;
+ }
+
+ return(NULL);
+ }
+
+ inline __checkReturn char * _strstrex (__in const char * str1, __in const char * str2, __in const char * str1End)
+ {
+ ATLENSURE(str1!=NULL);
+ ATLENSURE(str2!=NULL);
+ char *cp = (char *) str1;
+ char *s1, *s2;
+
+ if ( !*str2 )
+ return((char *)str1);
+
+ while (cp != str1End)
+ {
+ s1 = cp;
+ s2 = (char *) str2;
+
+ while ( s1 != str1End && *s2 && !((*s1)-(*s2)) )
+ {
+ s1++, s2++;
+ }
+
+ if (!*s2)
+ {
+ return (cp);
+ }
+
+ if (s1 == str1End)
+ {
+ return (NULL);
+ }
+
+ cp++;
+ }
+
+ return(NULL);
+ }
+
+ ATL_NOINLINE __checkReturn BOOL InitializeParser() throw()
+ {
+ ATLASSUME( m_spServerContext != NULL );
+
+ _ATLTRY
+ {
+ DWORD dwBytesTotal = m_spServerContext->GetTotalBytes();
+
+ // if greater than bytes available, allocate necessary space
+ if (dwBytesTotal > m_spServerContext->GetAvailableBytes())
+ {
+ m_pStart = (LPSTR) malloc(dwBytesTotal);
+ if (!m_pStart)
+ {
+ return FALSE;
+ }
+ m_pCurrent = m_pStart;
+ DWORD dwLen = dwBytesTotal;
+ if (!ReadClientData(m_spServerContext, m_pStart, &dwLen, 0) || dwLen != dwBytesTotal)
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ m_pStart = (LPSTR) m_spServerContext->GetAvailableData();
+ }
+
+ m_pCurrent = m_pStart;
+ m_pEnd = m_pCurrent + dwBytesTotal;
+
+ //get the boundary
+ LPCSTR pszContentType = m_spServerContext->GetContentType();
+ ATLASSERT(pszContentType != NULL);
+
+ LPCSTR pszTmp = _stristr(pszContentType, "boundary=");
+ if (!pszTmp)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed Form-Data"));
+ return FALSE;
+ }
+
+ pszTmp += sizeof("boundary=")-1;
+ BOOL bInQuote = FALSE;
+ if (*pszTmp == '\"')
+ {
+ bInQuote = TRUE;
+ pszTmp++;
+ }
+
+ LPSTR pszMimeBoundary = m_szBoundary;
+ *pszMimeBoundary++ = '-';
+ *pszMimeBoundary++ = '-';
+ m_dwBoundaryLen = 2;
+ while (*pszTmp && (bInQuote || IsStandardBoundaryChar(*pszTmp)))
+ {
+ if (m_dwBoundaryLen >= MAX_MIME_BOUNDARY_LEN)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Malformed MIME boundary"));
+ return FALSE;
+ }
+
+ if (*pszTmp == '\r' || *pszTmp == '\n')
+ {
+ if (bInQuote)
+ {
+ pszTmp++;
+ continue;
+ }
+ break;
+ }
+ if (bInQuote && *pszTmp == '"')
+ {
+ break;
+ }
+
+ *pszMimeBoundary++ = *pszTmp++;
+ m_dwBoundaryLen++;
+ }
+
+ *pszMimeBoundary = '\0';
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ inline __checkReturn BOOL MoveToData() throw()
+ {
+ LPSTR szEnd = _strstrex(m_pCurrent, "\r\n\r\n", m_pEnd);
+ if (!szEnd)
+ {
+ return FALSE;
+ }
+
+ m_pCurrent = szEnd+4;
+ if (m_pCurrent >= m_pEnd)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ inline __checkReturn BOOL GetMimeData(__inout CStringA &str, __in_ecount(dwFieldLen) LPCSTR szField, __in DWORD dwFieldLen, __out LPBOOL pbFound, __in BOOL bIgnoreCase = FALSE) throw()
+ {
+ _ATLTRY
+ {
+ ATLASSERT( szField != NULL );
+ ATLENSURE( pbFound != NULL );
+
+ *pbFound = FALSE;
+
+ LPSTR szEnd = _strstrex(m_pCurrent, "\r\n\r\n", m_pEnd);
+ if (!szEnd)
+ {
+ return FALSE;
+ }
+
+ LPSTR szDataStart = NULL;
+
+ if (!bIgnoreCase)
+ {
+ szDataStart = _strstrex(m_pCurrent, szField, szEnd);
+ }
+ else
+ {
+ szDataStart = _stristrex(m_pCurrent, szField, szEnd);
+ }
+
+ if (szDataStart)
+ {
+ szDataStart+= dwFieldLen;
+ if (szDataStart >= m_pEnd)
+ {
+ return FALSE;
+ }
+
+ BOOL bInQuote = FALSE;
+ if (*szDataStart == '\"')
+ {
+ bInQuote = TRUE;
+ szDataStart++;
+ }
+
+ LPSTR szDataEnd = szDataStart;
+ while (!bInQuote && (szDataEnd < m_pEnd) && (*szDataEnd == ' ' || *szDataEnd == '\t'))
+ {
+ szDataEnd++;
+ }
+
+ if (szDataEnd >= m_pEnd)
+ {
+ return FALSE;
+ }
+
+ while (szDataEnd < m_pEnd)
+ {
+ if (!IsValidTokenChar(*szDataEnd))
+ {
+ if (*szDataEnd == '\"' || !bInQuote)
+ {
+ break;
+ }
+ }
+ szDataEnd++;
+ }
+
+ if (szDataEnd >= m_pEnd)
+ {
+ return FALSE;
+ }
+
+ str.SetString(szDataStart, (int)(szDataEnd-szDataStart));
+ *pbFound = TRUE;
+ }
+
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ ATL_NOINLINE __checkReturn BOOL ReadUntilBoundary(__inout_opt CStringA* pStrData=NULL, __inout_opt CAtlTemporaryFile* pCtf=NULL) throw()
+ {
+ _ATLTRY
+ {
+ LPSTR szBoundaryStart = m_pCurrent;
+ LPSTR szBoundaryEnd = NULL;
+
+ do
+ {
+ szBoundaryStart = _strstrex(szBoundaryStart, m_szBoundary, m_pEnd);
+ if (szBoundaryStart)
+ {
+ if ((szBoundaryStart-m_pStart) >= 2)
+ {
+ if (*(szBoundaryStart-1) != 0x0a || *(szBoundaryStart-2) != 0x0d)
+ {
+ szBoundaryStart++;
+ continue;
+ }
+ }
+ szBoundaryEnd = szBoundaryStart+m_dwBoundaryLen;
+ if (szBoundaryEnd+2 >= m_pEnd)
+ {
+ return FALSE;
+ }
+ if (szBoundaryEnd[0] == '\r' && szBoundaryEnd[1] == '\n')
+ {
+ break;
+ }
+ if (szBoundaryEnd[0] == '-' && szBoundaryEnd[1] == '-')
+ {
+ m_bFinished = TRUE;
+ break;
+ }
+ szBoundaryStart++;
+ }
+ } while (szBoundaryStart && szBoundaryStart < m_pEnd);
+
+ if (!szBoundaryStart || szBoundaryStart >= m_pEnd)
+ {
+ return FALSE;
+ }
+
+ if ((szBoundaryStart-m_pStart) >= 2)
+ {
+ szBoundaryStart-= 2;
+ }
+ if (pStrData)
+ {
+ pStrData->SetString(m_pCurrent, (int)(szBoundaryStart-m_pCurrent));
+ }
+ if (pCtf)
+ {
+ if (FAILED(pCtf->Write(m_pCurrent, (DWORD)(szBoundaryStart-m_pCurrent))))
+ {
+ return FALSE;
+ }
+ }
+
+ if (!m_bFinished)
+ {
+ m_pCurrent = szBoundaryEnd+2;
+ if (m_pCurrent >= m_pEnd)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ static inline __checkReturn BOOL IsStandardBoundaryChar(__in CHAR ch) throw()
+ {
+ if ( (ch >= 'A' && ch <= 'Z') ||
+ (ch >= 'a' && ch <= 'z') ||
+ (ch >= '0' && ch <= '9') ||
+ (ch == '\'') ||
+ (ch == '+') ||
+ (ch == '_') ||
+ (ch == '-') ||
+ (ch == '=') ||
+ (ch == '?') )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ inline __checkReturn BOOL IsValidTokenChar(__in CHAR ch) throw()
+ {
+ return ( (ch != 0) && (ch != 0xd) && (ch != 0xa) && (ch != ' ') && (ch != '\"') );
+ }
+
+private:
+ // Prevents copying.
+ CMultiPartFormParser(__in const CMultiPartFormParser& /*that*/) throw()
+ {
+ ATLASSERT(FALSE);
+ }
+
+ const CMultiPartFormParser& operator=(__in const CMultiPartFormParser& /*that*/) throw()
+ {
+ ATLASSERT(FALSE);
+ return (*this);
+ }
+}; // class CMultiPartFormParser
+
+
+// 48K max form size
+#ifndef DEFAULT_MAX_FORM_SIZE
+#define DEFAULT_MAX_FORM_SIZE 49152
+#endif
+
+// This class provides access to the information contained in an HTTP request submitted to a web server.
+//
+// CHttpRequest provides access to the query string parameters, form fields, cookies, and files
+// that make up an HTTP request, as well as many other important properties of the request.
+class CHttpRequest : public IHttpRequestLookup
+{
+protected:
+ // Implementation: Array used to map an HTTP request method (for example, "GET" or "POST")
+ // from a string to a numeric constant from the HTTP_METHOD enum (HTTP_METHOD_GET or HTTP_METHOD_HEAD).
+ static const char* const m_szMethodStrings[];
+
+ // Implementation: The server context.
+ CComPtr<IHttpServerContext> m_spServerContext;
+
+ // Implementation: The number of bytes read from the body of the request.
+ DWORD m_dwBytesRead;
+
+ // Implementation: TRUE if the request method was POST and the encoding was
+ // multipart/form-data, FALSE otherwise.
+ BOOL m_bMultiPart;
+
+ CCookie m_EmptyCookie;
+
+ // Implementation: Constructor function used to reinitialize all data members.
+ void Construct() throw()
+ {
+ m_spServerContext.Release();
+ m_bMultiPart = FALSE;
+ m_dwBytesRead = 0;
+ if (m_pFormVars != &m_QueryParams)
+ delete m_pFormVars;
+
+ m_pFormVars = NULL;
+ m_pFormVars = &m_QueryParams;
+ m_QueryParams.RemoveAll();
+ m_QueryParams.SetShared(false);
+
+ ClearFilesAndCookies();
+ }
+
+ void ClearFilesAndCookies() throw()
+ {
+ m_Files.RemoveAll();
+ m_Files.SetShared(false);
+ m_requestCookies.RemoveAll();
+ m_requestCookies.SetShared(false);
+ }
+
+public:
+
+ // Implementation: The collection of query parameters (name-value pairs) obtained from the query string.
+ CHttpRequestParams m_QueryParams;
+
+ // Implementation: The collection of form fields (name-value pairs).
+ // The elements of this collection are obtained from the query string for a GET request,
+ // or from the body of the request for a POST request.
+ CHttpRequestParams *m_pFormVars;
+
+ // The array of CHttpRequestFiles obtained from the current request.
+ // See CHttpRequest::Initialize and CMultiPartFormParser::GetMultiPartData for more information.
+ typedef CHttpPtrMap<CStringA, IHttpFile*, CStringElementTraits<CStringA> > FileMap;
+ FileMap m_Files;
+
+ // Implementation: The array of cookies obtained from the current request.
+ typedef CHttpMap<CStringA, CCookie, CStringElementTraits<CStringA> > CookieMap;
+ CookieMap m_requestCookies;
+
+ // Numeric constants for the HTTP request methods (such as GET and POST).
+ enum HTTP_METHOD
+ {
+ HTTP_METHOD_UNKNOWN=-1,
+ HTTP_METHOD_GET,
+ HTTP_METHOD_POST,
+ HTTP_METHOD_HEAD,
+ HTTP_METHOD_DELETE,
+ HTTP_METHOD_LINK,
+ HTTP_METHOD_UNLINK,
+ HTTP_METHOD_DEBUG // Debugging support for VS7
+ };
+
+ // The collection of query parameters (name-value pairs) obtained from the query string.
+ // A read-only property.
+ __declspec(property(get=GetQueryParams)) const CHttpRequestParams& QueryParams;
+
+ // Returns a reference to the collection of query parameters(name-value pairs)
+ // obtained from the query string.
+ const CHttpRequestParams& GetQueryParams() const throw()
+ {
+ return m_QueryParams;
+ }
+
+ // The collection of form fields (name-value pairs).
+ // The elements of this collection are obtained from the query string for a GET request,
+ // or from the body of the request for a POST request.
+ // A read-only property.
+ __declspec(property(get=GetFormVars)) const CHttpRequestParams& FormVars;
+
+ // Returns a reference to the collection of form fields (name-value pairs)
+ // obtained from the query string for a GET request,
+ // or from the body of the request for a POST request.
+ const CHttpRequestParams& GetFormVars() const throw()
+ {
+ return *m_pFormVars;
+ }
+
+ CHttpRequest() throw()
+ {
+ m_bMultiPart = FALSE;
+ m_dwBytesRead = 0;
+ m_pFormVars = &m_QueryParams;
+ }
+
+ virtual ~CHttpRequest() throw()
+ {
+ DeleteFiles();
+
+ if (m_pFormVars != &m_QueryParams)
+ {
+ delete m_pFormVars;
+ m_pFormVars = NULL;
+ }
+ }
+
+ // Constructs and initializes the object.
+ CHttpRequest(
+ __in IHttpServerContext *pServerContext,
+ __in DWORD dwMaxFormSize=DEFAULT_MAX_FORM_SIZE,
+ __in DWORD dwFlags=ATL_FORM_FLAG_NONE) throw(...)
+ :m_pFormVars(NULL)
+ {
+ Construct();
+ if (!Initialize(pServerContext, dwMaxFormSize, dwFlags))
+ AtlThrow(E_FAIL);
+ }
+
+ CHttpRequest(__in IHttpRequestLookup *pRequestLookup) throw(...)
+ :m_pFormVars(NULL)
+ {
+ if (!Initialize(pRequestLookup)) // Calls Construct for you
+ AtlThrow(E_FAIL);
+ }
+
+ //=========================================================================================
+ // BEGIN IHttpRequestLoookup interface
+ //=========================================================================================
+ __success(return != NULL) __checkReturn POSITION GetFirstQueryParam(__deref_out LPCSTR *ppszName, __deref_out LPCSTR *ppszValue)
+ {
+ POSITION pos = m_QueryParams.GetStartPosition();
+ if (pos != NULL)
+ {
+ ATLENSURE(ppszName != NULL);
+ ATLENSURE(ppszValue != NULL);
+ *ppszName = m_QueryParams.GetKeyAt(pos);
+ *ppszValue = m_QueryParams.GetValueAt(pos);
+ }
+
+ return pos;
+ }
+
+ __success(return != NULL) __checkReturn POSITION GetNextQueryParam(__in POSITION pos, __deref_out LPCSTR *ppszName, __deref_out LPCSTR *ppszValue)
+ {
+ ATLENSURE(pos != NULL);
+
+
+ POSITION posNext(pos);
+ m_QueryParams.GetNext(posNext);
+ if (posNext != NULL)
+ {
+ ATLENSURE(ppszName != NULL);
+ ATLENSURE(ppszValue != NULL);
+ *ppszName = m_QueryParams.GetKeyAt(posNext);
+ *ppszValue = m_QueryParams.GetValueAt(posNext);
+ }
+
+ return posNext;
+ }
+
+ __success(return != NULL) __checkReturn POSITION GetFirstFormVar(__deref_out LPCSTR *ppszName, __deref_out LPCSTR *ppszValue)
+ {
+ // if no form vars and just pointing to the query params,
+ // then return NULL
+ if (m_pFormVars == &m_QueryParams)
+ return NULL;
+
+ POSITION pos = m_pFormVars->GetStartPosition();
+ if (pos != NULL)
+ {
+ ATLENSURE(ppszName != NULL);
+ ATLENSURE(ppszValue != NULL);
+ *ppszName = m_pFormVars->GetKeyAt(pos);
+ *ppszValue = m_pFormVars->GetValueAt(pos);
+ }
+
+ return pos;
+ }
+
+ __success(return != NULL) __checkReturn POSITION GetNextFormVar(__in POSITION pos, __deref_out LPCSTR *ppszName, __deref_out LPCSTR *ppszValue)
+ {
+ ATLASSERT(pos != NULL);
+ ATLASSERT(ppszName != NULL);
+ ATLASSERT(ppszValue != NULL);
+
+ POSITION posNext(pos);
+ m_pFormVars->GetNext(posNext);
+ if (posNext != NULL)
+ {
+ *ppszName = m_pFormVars->GetKeyAt(posNext);
+ *ppszValue = m_pFormVars->GetValueAt(posNext);
+ }
+
+ return posNext;
+ }
+
+ POSITION GetFirstFile(__in LPCSTR *ppszName, __deref_out IHttpFile **ppFile)
+ {
+ ATLASSERT(ppszName != NULL);
+ ATLASSERT(ppFile != NULL);
+
+ POSITION pos = m_Files.GetStartPosition();
+ if (pos != NULL)
+ {
+ *ppszName = m_Files.GetKeyAt(pos);
+ *ppFile = m_Files.GetValueAt(pos);
+ }
+
+ return pos;
+ }
+
+ __success(return != NULL) __checkReturn POSITION GetNextFile(__in POSITION pos, __deref_out LPCSTR *ppszName, __deref_out IHttpFile **ppFile)
+ {
+ ATLASSERT(pos != NULL);
+ ATLASSERT(ppszName != NULL);
+ ATLASSERT(ppFile != NULL);
+
+ m_Files.GetNext(pos);
+ if (pos != NULL)
+ {
+ *ppszName = m_Files.GetKeyAt(pos);
+ *ppFile = m_Files.GetValueAt(pos);
+ }
+
+ return pos;
+ }
+
+ // Returns a pointer to the IHttpServerContext interface for the current request.
+ HRESULT GetServerContext(__deref_out_opt IHttpServerContext ** ppOut)
+ {
+ return m_spServerContext.CopyTo(ppOut);
+ }
+ //=========================================================================================
+ // END IHttpRequestLookup interface
+ //=========================================================================================
+
+ __success(return != NULL) __checkReturn POSITION GetFirstCookie(__deref_out LPCSTR *ppszName, __deref_out const CCookie **ppCookie) throw()
+ {
+ ATLASSERT(ppszName != NULL);
+ ATLASSERT(ppCookie != NULL);
+ POSITION pos = NULL;
+ if (GetRequestCookies())
+ {
+ pos = m_requestCookies.GetStartPosition();
+ if (pos != NULL)
+ {
+ *ppszName = m_requestCookies.GetKeyAt(pos);
+ *ppCookie = &(m_requestCookies.GetValueAt(pos));
+ }
+ }
+ return pos;
+ }
+
+ __success(return != NULL) __checkReturn POSITION GetNextCookie(__in POSITION pos, __deref_out LPCSTR *ppszName, __deref_out const CCookie **ppCookie) throw()
+ {
+ ATLASSERT(pos != NULL);
+ ATLASSERT(ppszName != NULL);
+ ATLASSERT(ppCookie != NULL);
+
+ POSITION posNext(pos);
+ m_requestCookies.GetNext(posNext);
+ if (posNext != NULL)
+ {
+ *ppszName = m_requestCookies.GetKeyAt(posNext);
+ *ppCookie = &(m_requestCookies.GetValueAt(posNext));
+ }
+ return posNext;
+ }
+
+ void SetServerContext(__in IHttpServerContext *pServerContext) throw()
+ {
+ m_spServerContext = pServerContext;
+ }
+
+ BOOL Initialize(__in IHttpRequestLookup *pRequestLookup) throw()
+ {
+ _ATLTRY
+ {
+ ATLASSERT(pRequestLookup != NULL);
+ // if there's no pRequestLookup, just return
+ if (!pRequestLookup)
+ return TRUE;
+
+ Construct();
+ HRESULT hr = pRequestLookup->GetServerContext(&m_spServerContext);
+ if (FAILED(hr))
+ return FALSE;
+
+ LPCSTR szName(NULL);
+ LPCSTR szValue(NULL);
+
+ // Initialize query params from the IHttpRequestLookup*
+ POSITION pos(pRequestLookup->GetFirstQueryParam(&szName, &szValue));
+ while (pos != NULL)
+ {
+ m_QueryParams.SetAt(szName, szValue);
+ pos = pRequestLookup->GetNextQueryParam(pos, &szName, &szValue);
+ }
+ m_QueryParams.SetShared(true);
+
+ // Initialize the form vars from the IHttpRequestLookup*
+ pos = pRequestLookup->GetFirstFormVar(&szName, &szValue);
+ if (pos)
+ {
+ m_pFormVars = NULL;
+ ATLTRY(m_pFormVars = new CHttpRequestParams);
+ if (!m_pFormVars)
+ return FALSE;
+
+ while (pos != NULL)
+ {
+ m_pFormVars->SetAt(szName, szValue);
+ pos = pRequestLookup->GetNextFormVar(pos, &szName, &szValue);
+ }
+ m_pFormVars->SetShared(true);
+ }
+ else
+ {
+ m_pFormVars = &m_QueryParams;
+ }
+
+ // Initialize the files from the IHttpRequestLookup*
+ IHttpFile *pFile(NULL);
+ pos = pRequestLookup->GetFirstFile(&szName, &pFile);
+ while (pos != NULL)
+ {
+ m_Files.SetAt(szName, pFile);
+ pos = pRequestLookup->GetNextFile(pos, &szName, &pFile);
+ }
+ m_Files.SetShared(true);
+
+ // Initialzie the cookies form the IHttpRequestLookup*
+ BOOL bRet = FALSE;
+ CStringA strCookies;
+ bRet = GetCookies(strCookies);
+ if (bRet)
+ {
+ bRet = Parse(strCookies);
+ }
+ m_requestCookies.SetShared(false);
+ return bRet;
+ } // _ATLTRY
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to initialize the object with information about the current request.
+ //
+ // Returns TRUE on success, FALSE on failure.
+ //
+ // Call Initialize directly or via the appropriate constructor before using the methods and
+ // properties of the request object.
+ //
+ // Initialize does the following:
+ //
+ // Parses and decodes the query string into a collection of name-value pairs.
+ // This collection is accessible via the GetQueryParams method or the QueryParams property.
+ //
+ // Sets m_bMultiPart to TRUE if the request is a POST request with multipart/form-data encoding.
+ //
+ // Parses the body of a POST request if the size of the request data is less than or equal to dwMaxFormSize.
+ // The body of the request will consist of simple form fields and may also contain files if the request is encoded as multipart/form-data.
+ // In that case, the dwFlags parameter is passed to CMultiPartFormParser::GetMultiPartData to control the creation of the files.
+ // The collection of form fields is accessible via the GetFormVars method or the FormVars property.
+ // The collection of files is accessible via the m_Files member.
+ //
+ // Note that Initialize does not parse the cookies associated with a request.
+ // Cookies are not processed until an attempt is made to access a cookie in the collection.
+ BOOL Initialize(
+ __in IHttpServerContext *pServerContext,
+ __in DWORD dwMaxFormSize=DEFAULT_MAX_FORM_SIZE,
+ __in DWORD dwFlags=ATL_FORM_FLAG_NONE) throw()
+ {
+ _ATLTRY
+ {
+ ATLASSERT(pServerContext != NULL);
+ if (!pServerContext)
+ {
+ return FALSE;
+ }
+
+ m_spServerContext = pServerContext;
+
+ HTTP_METHOD httpMethod = GetMethod();
+ LPCSTR pszQueryString = GetQueryString();
+ if (pszQueryString && *pszQueryString)
+ {
+ // Parse the query string.
+ CHAR szQueryString[ATL_URL_MAX_URL_LENGTH];
+ if (!SafeStringCopy(szQueryString, pszQueryString))
+ {
+ return FALSE;
+ }
+
+ if (!m_QueryParams.Parse(szQueryString))
+ {
+ return FALSE;
+ }
+ }
+
+ if (m_QueryParams.IsShared())
+ {
+ return TRUE;
+ }
+
+ // If this is a GET request, the collection of form fields
+ // is the same as the collection of query parameters.
+ if (httpMethod == HTTP_METHOD_GET)
+ {
+ m_pFormVars = &m_QueryParams;
+ return TRUE;
+ }
+ else if (httpMethod == HTTP_METHOD_POST)
+ {
+ LPCSTR szContentType = GetContentType();
+ if (!szContentType)
+ return FALSE;
+
+ // Don't parse the form data if the size is bigger than the maximum specified.
+ if (m_spServerContext->GetTotalBytes() > dwMaxFormSize)
+ {
+ if (strncmp(szContentType, "multipart/form-data", 19) == 0)
+ m_bMultiPart = TRUE;
+
+ m_dwBytesRead = 0;
+
+ return TRUE;
+ }
+
+ // If POSTed data is urlencoded, call InitFromPost.
+ if (strncmp(szContentType, "application/x-www-form-urlencoded", 33) == 0 && !m_pFormVars->IsShared())
+ return InitFromPost();
+
+ // If POSTed data is encoded as multipart/form-data, use CMultiPartFormParser.
+ if (strncmp(szContentType, "multipart/form-data", 19) == 0 && !m_pFormVars->IsShared())
+ {
+ if (m_pFormVars != &m_QueryParams)
+ delete m_pFormVars;
+ m_pFormVars = NULL;
+
+ CMultiPartFormParser FormParser(m_spServerContext);
+ ATLTRY(m_pFormVars = new CHttpRequestParams);
+ if (!m_pFormVars)
+ return FALSE;
+
+ BOOL bRet = FormParser.GetMultiPartData(m_Files, m_pFormVars, dwFlags);
+ return bRet;
+ }
+
+ // else initialize m_dwBytesRead for ReadData
+ m_dwBytesRead = 0;
+ }
+
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+
+ }
+
+ // Implementation: Call this function to initialize the collection of form fields
+ // from the body of an application/x-www-form-urlencoded POST request.
+ ATL_NOINLINE BOOL InitFromPost() throw()
+ {
+ _ATLTRY
+ {
+ ATLASSUME(m_spServerContext != NULL);
+
+ // create our m_pFormVars
+ if (m_pFormVars == NULL || m_pFormVars == &m_QueryParams)
+ {
+ ATLTRY(m_pFormVars = new CHttpRequestParams);
+ if (m_pFormVars == NULL)
+ {
+ return FALSE;
+ }
+ }
+
+ // read the form data into a buffer
+ DWORD dwBytesTotal = m_spServerContext->GetTotalBytes();
+ CAutoVectorPtr<CHAR> szBuff;
+ if ((dwBytesTotal+1 < dwBytesTotal) || (dwBytesTotal < 0))
+ {
+ return FALSE;
+ }
+ if (!szBuff.Allocate(dwBytesTotal+1))
+ {
+ return FALSE;
+ }
+ // first copy the available
+ BOOL bRet = ReadClientData(m_spServerContext, szBuff, &dwBytesTotal, 0);
+ if (bRet)
+ {
+ szBuff[dwBytesTotal] = '\0';
+ bRet = m_pFormVars->Parse(szBuff);
+ }
+
+ return bRet;
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to remove the files listed in m_Files from the web server's hard disk.
+ // Returns the number of files deleted.
+ int DeleteFiles() throw()
+ {
+ int nDeleted = 0;
+ POSITION pos = m_Files.GetStartPosition();
+ while (pos != NULL)
+ {
+ LPCSTR szTempFile = m_Files.GetKeyAt(pos);
+ if (szTempFile && DeleteFileA(szTempFile))
+ {
+ nDeleted++;
+ }
+ m_Files.GetNext(pos);
+ }
+
+ return nDeleted;
+ }
+
+ // Read a specified amount of data into pbDest and return the bytes read in pdwLen.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL ReadData(__out_ecount_part(*pdwLen,*pdwLen) LPSTR pDest, __inout LPDWORD pdwLen)
+ {
+ ATLENSURE(pDest);
+ ATLENSURE(pdwLen);
+
+ BOOL bRet = ReadClientData(m_spServerContext, pDest, pdwLen, m_dwBytesRead);
+ if (bRet)
+ m_dwBytesRead+= *pdwLen;
+ return bRet;
+ }
+
+ // Returns the number of bytes available in the request buffer accessible via GetAvailableData.
+ // If GetAvailableBytes returns the same value as GetTotalBytes, the request buffer contains the whole request.
+ // Otherwise, the remaining data should be read from the client using ReadData.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::cbAvailable.
+ __checkReturn DWORD GetAvailableBytes() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetAvailableBytes();
+ }
+
+ // Returns the total number of bytes to be received from the client.
+ // If this value is 0xffffffff, then there are four gigabytes or more of available data.
+ // In this case, ReadData should be called until no more data is returned.
+ // Equivalent to the CONTENT_LENGTH server variable or EXTENSION_CONTROL_BLOCK::cbTotalBytes.
+ __checkReturn DWORD GetTotalBytes() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetTotalBytes();
+ }
+
+ // Returns a pointer to the request buffer containing the data sent by the client.
+ // The size of the buffer can be determined by calling GetAvailableBytes.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::lpbData
+ __checkReturn LPBYTE GetAvailableData() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetAvailableData();
+ }
+
+
+ // Returns a nul-terminated string that contains the query information.
+ // This is the part of the URL that appears after the question mark (?).
+ // Equivalent to the QUERY_STRING server variable or EXTENSION_CONTROL_BLOCK::lpszQueryString.
+ __checkReturn LPCSTR GetQueryString() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetQueryString();
+ }
+
+ // Returns a nul-terminated string that contains the HTTP method of the current request.
+ // Examples of common HTTP methods include "GET" and "POST".
+ // Equivalent to the REQUEST_METHOD server variable or EXTENSION_CONTROL_BLOCK::lpszMethod.
+ __checkReturn LPCSTR GetMethodString() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetRequestMethod();
+ }
+
+ // Returns an HTTP_METHOD enum value corresponding to the HTTP method of the current request.
+ // Returns HTTP_METHOD_UNKNOWN if the request method is not one of the following methods:
+ // GET
+ // POST
+ // HEAD
+ // DELETE
+ // LINK
+ // UNLINK
+ __checkReturn HTTP_METHOD GetMethod() throw(...)
+ {
+ LPCSTR szMethod = GetMethodString();
+ if (!szMethod)
+ return HTTP_METHOD_UNKNOWN;
+ for (int i=0; m_szMethodStrings[i]; i++)
+ {
+ if (strcmp(szMethod, m_szMethodStrings[i]) == 0)
+ return (HTTP_METHOD) i;
+ }
+ return HTTP_METHOD_UNKNOWN;
+ }
+
+ // Returns a nul-terminated string that contains the content type of the data sent by the client.
+ // Equivalent to the CONTENT_TYPE server variable or EXTENSION_CONTROL_BLOCK::lpszContentType.
+ __checkReturn LPCSTR GetContentType() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetContentType();
+ }
+
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_USER" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetAuthUserName(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("AUTH_USER", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "APPL_PHYSICAL_PATH" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetPhysicalPath(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("APPL_PHYSICAL_PATH", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_PASSWORD" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetAuthUserPassword(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("AUTH_PASSWORD", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "URL" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUrl(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("URL", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_HOST" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUserHostName(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("REMOTE_HOST", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_ADDR" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUserHostAddress(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("REMOTE_ADDR", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the physical path of the script.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ // The script path is the same as GetPathTranslated up to the first .srf or .dll.
+ // For example, if GetPathTranslated returns "c:\inetpub\vcisapi\hello.srf\goodmorning",
+ // then this function returns "c:\inetpub\vcisapi\hello.srf".
+ __checkReturn LPCSTR GetScriptPathTranslated() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetScriptPathTranslated();
+ }
+
+ // Returns a nul-terminated string that contains the physical path of the requested resource on the local server.
+ // Equivalent to the PATH_TRANSLATED server variable or EXTENSION_CONTROL_BLOCK::lpszPathTranslated.
+ __checkReturn LPCSTR GetPathTranslated() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetPathTranslated();
+ }
+
+ // Returns a nul-terminated string that contains the path of the current request.
+ // This is the part of the URL that appears after the server name, but before the query string.
+ // Equivalent to the PATH_INFO server variable or EXTENSION_CONTROL_BLOCK::lpszPathInfo.
+ __checkReturn LPCSTR GetPathInfo() throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetPathInfo();
+ }
+
+ // Call this function to determine whether the current request was authenticated.
+ __checkReturn BOOL GetAuthenticated() throw(...)
+ {
+ // We assume that if we get an authentication type from IIS,
+ // then the user has been authenticated.
+ CStringA strAuthType;
+ if (GetAuthenticationType(strAuthType) &&
+ strAuthType.GetLength() > 0)
+ return TRUE;
+
+ return FALSE;
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "AUTH_TYPE" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetAuthenticationType(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("AUTH_TYPE", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "REMOTE_USER" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUserName(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, DWORD __inout *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("REMOTE_USER", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_USER_AGENT" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUserAgent(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("HTTP_USER_AGENT", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT_LANGUAGE" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUserLanguages(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("HTTP_ACCEPT_LANGUAGE", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetAcceptTypes(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("HTTP_ACCEPT", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_ACCEPT_ENCODING" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetAcceptEncodings(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("HTTP_ACCEPT_ENCODING", szBuff, pdwSize);
+ }
+
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "HTTP_REFERER" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetUrlReferer(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("HTTP_REFERER", szBuff, pdwSize);
+ }
+
+ // Call this function to retrieve a nul-terminated string containing the value of the "SCRIPT_NAME" server variable.
+ //
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ //
+ // On entry, pdwSize should point to a DWORD that indicates the size of the buffer in bytes.
+ // On exit, the DWORD contains the number of bytes transferred or available to be transferred into the buffer (including the nul-terminating byte).
+ __checkReturn BOOL GetScriptName(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuff, __inout DWORD *pdwSize) throw(...)
+ {
+ ATLENSURE(m_spServerContext);
+ return m_spServerContext->GetServerVariable("SCRIPT_NAME", szBuff, pdwSize);
+ }
+
+ // Fills a buffer with the contents of the HTTP_COOKIE headers sent
+ // from the browser.
+ __checkReturn BOOL GetCookies(__out_ecount_part(*pdwSize,*pdwSize) LPSTR szBuf, __inout LPDWORD pdwSize) const throw(...)
+ {
+ ATLASSERT(szBuf != NULL);
+
+ CStringA strCookie;
+ if (GetCookies(strCookie))
+ {
+ ATLENSURE(pdwSize != NULL);
+ if (pdwSize && *pdwSize > (DWORD)strCookie.GetLength())
+ {
+ Checked::strcpy_s(szBuf, *pdwSize, strCookie);
+ *pdwSize = strCookie.GetLength();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Fills a CStringA with the contents of the HTTP_COOKIE headers sent
+ // from the browser.
+ __checkReturn BOOL GetCookies(__inout CStringA& strBuff) const throw()
+ {
+ return GetServerVariable("HTTP_COOKIE", strBuff);
+ }
+
+ // Call this function to retrieve a reference to the specified cookie.
+ // Returns a CCookie reference to the specified cookie or a
+ // reference to an empty cookie if the name can not be found.
+ ATL_NOINLINE const CCookie& Cookies(__in LPCSTR szName) throw(...)
+ {
+ if (GetRequestCookies())
+ {
+ // p->m_value is a const CCookie&
+ CookieMap::CPair *p = NULL;
+ ATLTRY(p = m_requestCookies.Lookup(szName));
+ if (p)
+ {
+ return p->m_value;
+ }
+ }
+ m_EmptyCookie.Empty(); // make sure it is cleared.
+ return m_EmptyCookie;
+ }
+
+
+ // Call this function to retrieve the session cookie.
+ const CCookie& GetSessionCookie() throw(...)
+ {
+ return Cookies(SESSION_COOKIE_NAME);
+ }
+
+ // Call this function to retrieve the value of the requested server variable in a CStringA object.
+ // Returns TRUE on success, and FALSE on failure. Call GetLastError to get extended error information.
+ // Equivalent to EXTENSION_CONTROL_BLOCK::GetServerVariable.
+ BOOL GetServerVariable(__in LPCSTR szVariable, __out CStringA &str) const
+ {
+ ATLENSURE(m_spServerContext);
+
+ DWORD dwSize = 0;
+ BOOL bRet = FALSE;
+ _ATLTRY
+ {
+ m_spServerContext->GetServerVariable(szVariable, NULL, &dwSize);
+ bRet = m_spServerContext->GetServerVariable(szVariable, str.GetBuffer(dwSize), &dwSize);
+ if (dwSize > 0)
+ dwSize--;
+ str.ReleaseBuffer(dwSize);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ return bRet;
+ }
+
+ // Call this function to retrieve the value of the "APPL_PHYSICAL_PATH" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetPhysicalPath(__out CStringA &str) throw()
+ {
+ return GetServerVariable("APPL_PHYSICAL_PATH", str);
+ }
+
+ // Call this function to retrieve the value of the "REMOTE_HOST" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUserHostName(__out CStringA &str) throw()
+ {
+ return GetServerVariable("REMOTE_HOST", str);
+ }
+
+ // Call this function to retrieve the value of the "REMOTE_ADDR" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUserHostAddress(__out CStringA &str) throw()
+ {
+ return GetServerVariable("REMOTE_ADDR", str);
+ }
+
+ // Call this function to retrieve the value of the "AUTH_TYPE" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetAuthenticationType(__out CStringA &str) throw()
+ {
+ return GetServerVariable("AUTH_TYPE", str);
+ }
+
+ // Call this function to retrieve the value of the "REMOTE_USER" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUserName(__out CStringA &str) throw()
+ {
+ return GetServerVariable("REMOTE_USER", str);
+ }
+
+ // Call this function to retrieve the value of the "HTTP_USER_AGENT" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUserAgent(__out CStringA &str) throw()
+ {
+ return GetServerVariable("HTTP_USER_AGENT", str);
+ }
+
+ // Call this function to retrieve the value of the "HTTP_ACCEPT_LANGUAGE" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUserLanguages(__out CStringA &str) throw()
+ {
+ return GetServerVariable("HTTP_ACCEPT_LANGUAGE", str);
+ }
+
+ // Call this function to retrieve the value of the "AUTH_USER" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetAuthUserName(__out CStringA &str) throw()
+ {
+ return GetServerVariable("AUTH_USER", str);
+ }
+
+ // Call this function to retrieve the value of the "AUTH_PASSWORD" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetAuthUserPassword(__out CStringA &str) throw()
+ {
+ return GetServerVariable("AUTH_PASSWORD", str);
+ }
+
+ // Call this function to retrieve the value of the "URL" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUrl(__out CStringA &str) throw()
+ {
+ return GetServerVariable("URL", str);
+ }
+
+ // Call this function to retrieve the value of the "HTTP_ACCEPT" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetAcceptTypes(__out CStringA &str) throw()
+ {
+ return GetServerVariable("HTTP_ACCEPT", str);
+ }
+
+ // Call this function to retrieve the value of the "HTTP_ACCEPT_ENCODING" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetAcceptEncodings(__out CStringA& str) throw()
+ {
+ return GetServerVariable("HTTP_ACCEPT_ENCODING", str);
+ }
+
+ // Call this function to retrieve the value of the "HTTP_REFERER" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetUrlReferer(__out CStringA &str) throw()
+ {
+ return GetServerVariable("HTTP_REFERER", str);
+ }
+
+ // Call this function to retrieve the value of the "SCRIPT_NAME" server variable.
+ // Returns TRUE on success, FALSE on failure. Call GetLastError to get extended error information.
+ __checkReturn BOOL GetScriptName(__out CStringA &str) throw()
+ {
+ return GetServerVariable("SCRIPT_NAME", str);
+ }
+
+ // Implementation: Call this function to populate the collection
+ // of CCookie objects with the cookies in the current request.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL GetRequestCookies() throw()
+ {
+ BOOL bRet = FALSE;
+
+ if (m_requestCookies.GetCount())
+ return TRUE; // we already got the cookies!
+
+ CStringA strCookies;
+ if (GetCookies(strCookies))
+ {
+ bRet = Parse(strCookies);
+ }
+ return bRet;
+ }
+
+private:
+ struct Pair
+ {
+ Pair(__in const CStringA &n, __in const CStringA& v)
+ {
+ name = n;
+ value = v;
+ }
+
+ Pair(__in const Pair& rhs)
+ {
+ name = rhs.name;
+ value = rhs.value;
+ }
+
+ CStringA name;
+ CStringA value;
+ };
+ static const int INVALID_COOKIE_VERSION = -1;
+public:
+
+ // Implementation: Call this function to populate m_requestCookies
+ // with a collection of CCookie objects which represents the
+ // cookies contained in szCookie header sent from the browser.
+ BOOL Parse(__in LPCSTR szCookieIn)
+ {
+ ATLENSURE(szCookieIn!=NULL);
+ UINT acp = GetACP();
+ LPCSTR szStart;
+ LPCSTR szEnd;
+ CStringA name, value;
+ CAtlList<Pair> pair_list;
+
+ // create a list of name=value pairs
+ // these are separated by or ;
+ szStart = szCookieIn;
+ szEnd = szStart;
+ while (*szStart)
+ {
+ name.Empty();
+ value.Empty();
+ szStart = SkipSpace(szStart, (WORD)acp);
+ if (*szStart == '\0')
+ break;
+
+ szEnd = szStart;
+ while (*szEnd && *szEnd != '=' && *szEnd != ';' )
+ szEnd++;
+
+ if (szEnd <= szStart)
+ {
+ if (*szEnd ==';')
+ {
+ szEnd++;
+ szStart = szEnd;
+ continue;
+ }
+ szStart = szEnd;
+ break; // no name or error
+ }
+
+ CopyToCString(name, szStart, szEnd);
+ if (*szEnd == '\0' || *szEnd == ';' )
+ {
+ // no value expected;
+ pair_list.AddTail(Pair(name, value));
+ szStart = szEnd;
+ }
+ else if ( *szEnd == '=')
+ {
+ szEnd++; // skip '='
+ szStart = szEnd;
+ while (*szEnd && *szEnd != ';')
+ szEnd++;
+
+ if (szEnd <= szStart)
+ {
+ if (*szEnd == ';')
+ {
+ szEnd++;
+ szStart = szEnd;
+ continue;
+ }
+ szStart = szEnd;
+ pair_list.AddTail(Pair(name,value));
+ break; // no value
+ }
+
+ CopyToCString(value, szStart, szEnd);
+ pair_list.AddTail(Pair(name,value));
+ szStart = szEnd;
+ }
+ }
+
+ // now make cookies out of the list
+ // The first item could be $Version, which would
+ // be the version for all cookies in the list.
+ // any other $Version's found in the list will be ignored.
+ if (pair_list.GetCount() <= 0)
+ return TRUE; // nothing in the list
+
+ // check for $Version
+ static const char szVersion[] = "$Version";
+ int nVersion = INVALID_COOKIE_VERSION;
+ if (!strncmp(pair_list.GetHead().name, szVersion, sizeof(szVersion)+1))
+ {
+ char *pStop = NULL;
+ errno_t errnoValue = AtlStrToNum<int, char>(&nVersion, pair_list.GetHead().value, &pStop, 10);
+ if (errnoValue == ERANGE)
+ nVersion = INVALID_COOKIE_VERSION; // $Version contained garbage
+ pair_list.RemoveHead(); // Remove $Version
+ }
+
+ POSITION pos = pair_list.GetHeadPosition();
+ bool bInCookie = false;
+ CCookie cookie;
+
+ while (pos)
+ {
+ const Pair &p = pair_list.GetNext(pos);
+ if (p.name[0] == '$')
+ {
+ if (!bInCookie)
+ return FALSE; // invalid cookie string
+ else
+ {
+ //add attribute to current cookie
+ if(!cookie.AddAttribute(p.name.Right(p.name.GetLength()-1),p.value))
+ {
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ if (!bInCookie)
+ {
+ bInCookie = true;
+ cookie.SetName(p.name);
+ cookie.ParseValue(p.value);
+ if (nVersion != INVALID_COOKIE_VERSION)
+ cookie.SetVersion(nVersion);
+ }
+ else
+ {
+ // add previous cookie
+ CStringA strPrevName;
+ if(!cookie.GetName(strPrevName))
+ {
+ return FALSE;
+ }
+ m_requestCookies.SetAt(strPrevName, cookie);
+
+ // empty current cookie and start over
+ cookie.Empty();
+ cookie.SetName(p.name);
+ cookie.ParseValue(p.value);
+ if (nVersion != INVALID_COOKIE_VERSION)
+ cookie.SetVersion(nVersion);
+
+ }
+ }
+ }
+
+ if (!cookie.IsEmpty())
+ {
+ CStringA strName;
+ if(!cookie.GetName(strName))
+ {
+ return FALSE;
+ }
+ m_requestCookies.SetAt(strName, cookie);
+ }
+ return TRUE;
+
+ }
+
+private:
+ // Call this function to copy a substring to a CString reference and ensure nul-termination.
+ inline void CopyToCString(__out CStringA& string, __in_ecount(pEnd-pStart) LPCSTR pStart, __in LPCSTR pEnd) throw( ... )
+ {
+ ATLENSURE( pStart != NULL );
+ ATLENSURE( pEnd != NULL );
+ ATLENSURE( pEnd>=pStart );
+
+ string.SetString(pStart, (int)(pEnd-pStart));
+ string.Trim();
+ }
+
+ inline LPCSTR SkipSpace(__in LPCSTR sz, __in WORD nCodePage) throw()
+ {
+ if (sz == NULL)
+ return NULL;
+
+ while (isspace(static_cast<unsigned char>(*sz)))
+ sz = CharNextExA(nCodePage, sz, 0);
+ return sz;
+ }
+public:
+
+
+ __success(SUCCEEDED(return)) __checkReturn HRESULT STDMETHODCALLTYPE QueryInterface(__in REFIID riid, __deref_out void **ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+ if (InlineIsEqualGUID(riid, __uuidof(IHttpRequestLookup)))
+ {
+ *ppv = static_cast<IUnknown*>(static_cast<IHttpRequestLookup*>(this));
+ AddRef();
+ return S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IUnknown)))
+ {
+ *ppv = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+}; // class CHttpRequest
+
+__declspec(selectany) const char* const CHttpRequest::m_szMethodStrings[] = {
+ "GET",
+ "POST",
+ "HEAD",
+ "DELETE",
+ "LINK",
+ "UNLINK",
+ "DEBUG", // Debugging support for VS7
+ NULL
+};
+
+// This class provides type conversions via the Write method
+// and overloaded left shift << operator for writing
+// data to the IWriteStream interface. The IWriteStream interface
+// only accepts strings, but this helper class allows you to transparently
+// pass many different types by providing automatic type conversions.
+//
+// Notes on Type Conversions:
+// Numeric types are converted to their decimal representations.
+// Floating point values are output with a precision of 6 decimal places.
+// Currency values are converted according to the locale settings of the current thread.
+class CWriteStreamHelper
+{
+protected:
+ // Implementation: The IWriteStream interface.
+ IWriteStream *m_pStream;
+ UINT m_ulACP;
+
+public:
+ CWriteStreamHelper() throw()
+ :m_pStream(NULL)
+ {
+ m_ulACP = _AtlGetConversionACP();
+ }
+
+ CWriteStreamHelper(__in_opt IWriteStream *pStream) throw()
+ {
+ m_pStream = pStream;
+ m_ulACP = _AtlGetConversionACP();
+ }
+
+ // Attach a IWriteStream
+ IWriteStream *Attach(__in IWriteStream *pStream) throw()
+ {
+ IWriteStream *pRetStream = m_pStream;
+ m_pStream = pStream;
+ return pRetStream;
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in_z LPCSTR szOut) throw()
+ {
+ ATLASSUME(m_pStream != NULL);
+
+ if (!szOut)
+ return FALSE;
+
+ DWORD dwWritten;
+ return SUCCEEDED(m_pStream->WriteStream(szOut, (int) strlen(szOut), &dwWritten));
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in int n) throw()
+ {
+ CHAR szTmp[21];
+ Checked::itoa_s(n, szTmp, _countof(szTmp), 10);
+ return Write(szTmp);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in unsigned int u) throw()
+ {
+ CHAR szTmp[21];
+ Checked::ultoa_s(u, szTmp, _countof(szTmp), 10);
+ return Write(szTmp);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in short int w) throw()
+ {
+ return Write((int) w);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in unsigned short int w) throw()
+ {
+ return Write((unsigned int) w);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in long int dw) throw()
+ {
+ CHAR szTmp[21];
+ Checked::ltoa_s(dw, szTmp, _countof(szTmp), 10);
+ return Write(szTmp);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in unsigned long int dw) throw()
+ {
+ CHAR szTmp[21];
+ Checked::ultoa_s(dw, szTmp, _countof(szTmp), 10);
+ return Write(szTmp);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in double d, __in int nDigitCount=ATL_DEFAULT_PRECISION) throw()
+ {
+ ATLASSUME(m_pStream != NULL);
+
+ CHAR szTmp[512];
+ int nDec = 0;
+ int nSign = 0;
+ bool fWriteDec=true;
+ if (0 != _fcvt_s(szTmp, _countof(szTmp), d, nDigitCount, &nDec, &nSign))
+ {
+ // too large
+ return FALSE;
+ }
+
+ if (nSign != 0)
+ m_pStream->WriteStream("-", 1, NULL);
+ if (nDec < 0)
+ {
+ nDec *= -1;
+ m_pStream->WriteStream("0.", 2, NULL);
+ for (int i=0;i<nDec;i++)
+ {
+ m_pStream->WriteStream("0", 1, NULL);
+ }
+ nDec = 0;
+ fWriteDec=false;
+ }
+
+ char *p = szTmp;
+ while (*p)
+ {
+ // if the decimal lies at the end of the number
+ // (no digits to the right of the decimal, we don't
+ // print it.
+ if (nDec == 0 && fWriteDec)
+ m_pStream->WriteStream(".", 1, NULL);
+ m_pStream->WriteStream(p, 1, NULL);
+ nDec--;
+ p++;
+ }
+ return TRUE;
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in __int64 i) throw()
+ {
+ CHAR szTmp[21];
+ Checked::i64toa_s(i, szTmp, _countof(szTmp), 10);
+ return Write(szTmp);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in unsigned __int64 i) throw()
+ {
+ CHAR szTmp[21];
+ Checked::ui64toa_s(i, szTmp, _countof(szTmp), 10);
+ return Write(szTmp);
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in CURRENCY c) throw()
+ {
+ CHAR szDest[256];
+ CHAR szNumber[32];
+
+ Checked::i64toa_s(c.int64, szNumber, _countof(szNumber), 10);
+ int nLen = (int) strlen(szNumber);
+ if (nLen < 5)
+ {
+ // prepend ascii zeros
+ Checked::memmove_s(szNumber+5-nLen, 32-(5-nLen), szNumber, nLen+1);
+ memset(szNumber, '0', 5-nLen);
+ nLen = 5;
+ }
+
+ Checked::memmove_s(szNumber+nLen-3, 32-(nLen-3), szNumber+nLen-4, 5);
+ szNumber[nLen-4] = '.';
+
+ int nRet = GetCurrencyFormatA(GetThreadLocale(), 0, szNumber, NULL, szDest, sizeof(szDest));
+ if (nRet > 0)
+ return Write(szDest);
+
+ ATLASSERT(GetLastError()==ERROR_INSUFFICIENT_BUFFER);
+
+ nRet = GetCurrencyFormatA(GetThreadLocale(), 0, szNumber, NULL, NULL, 0);
+ ATLASSERT(nRet > 0);
+
+ if (nRet <= 0)
+ return FALSE;
+
+ CAutoVectorPtr<CHAR> szBuffer;
+ if (!szBuffer.Allocate(nRet))
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ nRet = GetCurrencyFormatA(GetThreadLocale(), 0, szNumber, NULL, szBuffer, nRet);
+
+ ATLASSERT(nRet > 0);
+ BOOL bRet = FALSE;
+ if (nRet > 0)
+ bRet = Write(szBuffer);
+
+ return bRet;
+ }
+
+ // Call this function to write data to the IWriteStream interface managed by this object.
+ // Returns TRUE on success, FALSE on failure.
+ BOOL Write(__in LPCWSTR wsz) throw()
+ {
+ ATLASSUME(m_pStream != NULL);
+
+ BOOL bRet;
+
+ _ATLTRY
+ {
+ CW2A sz(wsz, m_ulACP);
+
+ if (!sz)
+ {
+ bRet = FALSE;
+ }
+
+ DWORD dwWritten;
+ bRet = SUCCEEDED(m_pStream->WriteStream(sz, (int) strlen(sz), &dwWritten));
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+
+ return bRet;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in LPCSTR szStr) throw(...)
+ {
+ if (!Write(szStr))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in LPCWSTR wszStr) throw(...)
+ {
+ if (!Write(wszStr))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in int n) throw(...)
+ {
+ if (!Write(n))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in short int w) throw(...)
+ {
+ if (!Write(w))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in unsigned int u) throw(...)
+ {
+ if (!Write(u))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in long int dw) throw(...)
+ {
+ if (!Write(dw))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in unsigned long int dw) throw(...)
+ {
+ if (!Write(dw))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in double d) throw(...)
+ {
+ if (!Write(d))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in __int64 i) throw(...)
+ {
+ if (!Write(i))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in unsigned __int64 i) throw(...)
+ {
+ if (!Write(i))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ // Use this operator to write data to the IWriteStream interface managed by this object.
+ CWriteStreamHelper& operator<<(__in CURRENCY c) throw(...)
+ {
+ if (!Write(c))
+ AtlThrow(E_FAIL);
+ return *this;
+ }
+
+ UINT SetConversionCodePage(__in UINT nNewCP)
+ {
+ UINT nOldCP = m_ulACP;
+ m_ulACP = nNewCP;
+ return nOldCP;
+ }
+};
+
+
+template <DWORD dwSizeT=ATL_ISAPI_BUFFER_SIZE>
+class CAtlIsapiBuffer
+{
+protected:
+ char m_szBuffer[dwSizeT];
+ LPSTR m_pBuffer;
+ DWORD m_dwLen;
+ DWORD m_dwAlloc;
+ HANDLE m_hProcHeap;
+
+public:
+ CAtlIsapiBuffer() throw()
+ {
+ if (dwSizeT > 0)
+ m_szBuffer[0] = 0;
+
+ m_pBuffer = m_szBuffer;
+ m_dwLen = 0;
+ m_dwAlloc = dwSizeT;
+ m_hProcHeap = GetProcessHeap();
+ }
+
+ CAtlIsapiBuffer(__in LPCSTR sz)
+ {
+ m_pBuffer = m_szBuffer;
+ m_dwLen = 0;
+ m_dwAlloc = dwSizeT;
+ m_hProcHeap = GetProcessHeap();
+
+ if (!Append(sz))
+ AtlThrow(E_OUTOFMEMORY);
+ }
+
+ ~CAtlIsapiBuffer() throw()
+ {
+ Free();
+ }
+
+ BOOL Alloc(__in DWORD dwSize) throw()
+ {
+ if (m_dwAlloc >= dwSize)
+ {
+ return TRUE;
+ }
+ if (m_pBuffer != m_szBuffer)
+ {
+ HeapFree(m_hProcHeap, 0, m_pBuffer);
+ m_dwLen = 0;
+ m_dwAlloc = 0;
+ }
+ m_pBuffer = (LPSTR)HeapAlloc(m_hProcHeap, 0, dwSize);
+ if (m_pBuffer)
+ {
+ m_dwAlloc = dwSize;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ BOOL ReAlloc(__in DWORD dwNewSize) throw()
+ {
+ if (dwNewSize <= m_dwAlloc)
+ return TRUE;
+
+ if (m_pBuffer == m_szBuffer)
+ {
+ BOOL bRet = Alloc(dwNewSize);
+ if (bRet)
+ {
+ Checked::memcpy_s(m_pBuffer, dwNewSize, m_szBuffer, m_dwLen);
+ }
+ return bRet;
+ }
+
+ LPSTR pvNew = (LPSTR )HeapReAlloc(m_hProcHeap, 0, m_pBuffer, dwNewSize);
+ if (pvNew)
+ {
+ m_pBuffer = pvNew;
+ m_dwAlloc = dwNewSize;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ void Free() throw()
+ {
+ if (m_pBuffer != m_szBuffer)
+ {
+ HeapFree(m_hProcHeap,0 , m_pBuffer);
+ m_dwAlloc = dwSizeT;
+ m_pBuffer = m_szBuffer;
+ }
+ Empty();
+ }
+
+ void Empty() throw()
+ {
+ if (m_pBuffer)
+ {
+ m_pBuffer[0]=0;
+ m_dwLen = 0;
+ }
+ }
+
+ DWORD GetLength() throw()
+ {
+ return m_dwLen;
+ }
+
+ BOOL Append(__in LPCSTR sz, __in int nLen = -1) throw()
+ {
+ if (!sz)
+ return FALSE;
+
+ if (nLen == -1)
+ nLen = (int) strlen(sz);
+
+ DWORD newLen = m_dwLen + nLen + 1;
+ if (newLen <= m_dwLen || newLen <= (DWORD)nLen)
+ {
+ return FALSE;
+ }
+ if (newLen > m_dwAlloc)
+ {
+ if (!ReAlloc(m_dwAlloc + (nLen+1 > ATL_ISAPI_BUFFER_SIZE ? nLen+1 : ATL_ISAPI_BUFFER_SIZE)))
+ return FALSE;
+ }
+ Checked::memcpy_s(m_pBuffer + m_dwLen, m_dwAlloc-m_dwLen, sz, nLen);
+ m_dwLen += nLen;
+ m_pBuffer[m_dwLen]=0;
+ return TRUE;
+ }
+
+ operator LPCSTR() throw()
+ {
+ return m_pBuffer;
+ }
+
+ CAtlIsapiBuffer& operator+=(__in LPCSTR sz)
+ {
+ if (!Append(sz))
+ AtlThrow(E_OUTOFMEMORY);
+ return *this;
+ }
+}; // class CAtlIsapiBuffer
+
+// This class represents the response that the web server will send back to the client.
+//
+// CHttpResponse provides friendly functions for building up the headers, cookies, and body of an HTTP response.
+// The class derives from IWriteStream and CWriteStreamHelper, allowing you to call those classes' methods
+// to build up the body of the response. By default, the class improves performance by buffering the response until it is complete before sending it back to the client.
+class CHttpResponse : public IWriteStream, public CWriteStreamHelper
+{
+private:
+
+ // Implementation: A map of HTTP response headers.
+ // The key is the name of the response header.
+ // The value is the data for the response header.
+ CSimpleMap<CStringA, CStringA> m_headers;
+
+ // Implementation: Determines whether the response is currently being buffered.
+ BOOL m_bBufferOutput;
+
+ // Implementation: Determines whether any output should be sent to the client.
+ // Intended mainly for HEAD requests, where the client should get the same headers
+ // (i.e. Content-Length) as for a GET request
+ BOOL m_bSendOutput;
+
+ // Implementation: The limit in bytes of the response buffer.
+ // When the limit is reached, the buffer is automatically flushed
+ // and data is sent to the client. You can set this to ULONG_MAX
+ // to enable full buffering (this is the default, and is required
+ // for enabling keep alive connections).
+ DWORD m_dwBufferLimit;
+
+ // Implementation: The server context.
+ CComPtr<IHttpServerContext> m_spServerContext;
+
+ // Implementation: The HTTP status code for the response.
+ int m_nStatusCode;
+
+ // Implementation: Determines whether the response headers have already been sent to the client.
+ BOOL m_bHeadersSent;
+
+ // Implementation: Handle of the file being transmitted so it can be closed
+ // when the async I/O completes
+ HANDLE m_hFile;
+
+public:
+ // Implementation: The buffer used to store the response before
+ // the data is sent to the client.
+ CAtlIsapiBuffer<> m_strContent;
+
+ // Numeric constants for the HTTP status codes used for redirecting client requests.
+ enum HTTP_REDIRECT
+ {
+ HTTP_REDIRECT_MULTIPLE=300,
+ HTTP_REDIRECT_MOVED=301,
+ HTTP_REDIRECT_FOUND=302,
+ HTTP_REDIRECT_SEE_OTHER=303,
+ HTTP_REDIRECT_NOT_MODIFIED=304,
+ HTTP_REDIRECT_USE_PROXY=305,
+ HTTP_REDIRECT_TEMPORARY_REDIRECT=307
+ };
+
+ CHttpResponse() throw()
+ {
+ m_bBufferOutput = TRUE;
+ m_dwBufferLimit = ULONG_MAX;
+ m_nStatusCode = 200;
+ m_pStream = this;
+ m_bHeadersSent = FALSE;
+ m_bSendOutput = TRUE;
+ m_hFile = INVALID_HANDLE_VALUE;
+ }
+
+ CHttpResponse(__in IHttpServerContext *pServerContext)
+ {
+ m_bBufferOutput = TRUE;
+ m_dwBufferLimit = ULONG_MAX;
+ m_nStatusCode = 200;
+ m_pStream = this;
+ m_bHeadersSent = FALSE;
+ ATLENSURE(Initialize(pServerContext));
+ m_bSendOutput = TRUE;
+ m_hFile = INVALID_HANDLE_VALUE;
+ }
+
+ // The destructor flushes the buffer if there is content that
+ // hasn't yet been sent to the client.
+ virtual ~CHttpResponse() throw()
+ {
+ Flush(TRUE);
+ if (m_hFile && m_hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(m_hFile);
+ }
+
+ // Call this function to initialize the response object with a pointer to the server context.
+ // Returns TRUE on success, FALSE on failure.
+ __checkReturn BOOL Initialize(__in IHttpServerContext *pServerContext) throw()
+ {
+ ATLASSERT(pServerContext != NULL);
+ if (!pServerContext)
+ return FALSE;
+
+ m_spServerContext = pServerContext;
+
+ return TRUE;
+ }
+
+ // This is called to initialize the CHttpResponse for a child handler. By default, it
+ // assumes the parent will be responsible for sending the headers.
+ __checkReturn BOOL Initialize(__in IHttpRequestLookup *pLookup) throw(...)
+ {
+ ATLASSERT(pLookup);
+ if (!pLookup)
+ return FALSE;
+
+ CComPtr<IHttpServerContext> spContext;
+ HRESULT hr = pLookup->GetServerContext(&spContext);
+ if (FAILED(hr))
+ return FALSE;
+
+ if (!Initialize(spContext))
+ return FALSE;
+
+ m_bHeadersSent = TRUE;
+
+ return TRUE;
+ }
+
+ // Returns a pointer to the IHttpServerContext interface for the current request.
+ __checkReturn HRESULT GetServerContext(__deref_out_opt IHttpServerContext ** ppOut) throw()
+ {
+ return m_spServerContext.CopyTo(ppOut);
+ }
+
+ void Detach()
+ {
+ m_spServerContext.Release();
+ HaveSentHeaders(TRUE);
+ }
+ // Call this function to set buffering options for the response.
+ //
+ // This function allows you to turn buffering on or off, and to set a size limit
+ // on the amount of data that will be buffered before being sent to the client.
+ //
+ // When you turn off buffering, the current contents of the buffer will be sent to the client.
+ // If you need to clear the buffer without sending the contents to the client, call ClearContent instead.
+ //
+ // When the size of the buffer is reduced below the current size of the buffered content,
+ // the entire buffer is flushed.
+ void SetBufferOutput(__in BOOL bBufferOutput, __in DWORD dwBufferLimit=ATL_ISAPI_BUFFER_SIZE) throw()
+ {
+ if (m_bBufferOutput && !bBufferOutput)
+ {
+ // before turning off buffering, flush
+ // the current contents
+ Flush();
+ }
+ SetBufferLimit(dwBufferLimit);
+
+ m_bBufferOutput = bBufferOutput;
+ }
+
+ // Call this function to determine whether data written to the response object is being buffered or not.
+ // Returns TRUE if output is being buffered, FALSE otherwise.
+ __checkReturn BOOL GetBufferOutput() throw()
+ {
+ return m_bBufferOutput;
+ }
+
+ // Call this function to determine whether the response headers have been sent
+ // Returns TRUE if headers have been sent, FALSE otherwise.
+ __checkReturn BOOL HaveSentHeaders() throw()
+ {
+ return m_bHeadersSent;
+ }
+
+ // Call this function to override the m_bHeadersSent state. This is useful
+ // when you want child handlers (e.g. from an include or subhandler) to send the headers
+ void HaveSentHeaders(__in BOOL bSent) throw()
+ {
+ m_bHeadersSent = bSent;
+ }
+
+ // Call this function to set a size limit on the amount of data buffered by the reponse object.
+ // When the size of the buffer is reduced below the current size of the buffered content,
+ // the entire buffer is flushed.
+ // See GetBufferLimit.
+ void SetBufferLimit(__in DWORD dwBufferLimit) throw()
+ {
+ if (m_bBufferOutput)
+ {
+ if (m_strContent.GetLength() >= dwBufferLimit)
+ {
+ // new buffer limit is less than the
+ // size currently buffered. So flush
+ // the current buffer
+ Flush();
+ }
+ }
+ m_dwBufferLimit = dwBufferLimit;
+ }
+
+ // Returns the current size limit of the buffer in bytes.
+ // See SetBufferLimit.
+ DWORD GetBufferLimit() throw()
+ {
+ return m_dwBufferLimit;
+ }
+
+ // Returns the current value of the Content-Type header if present, otherwise returns NULL.
+ LPCSTR GetContentType() throw()
+ {
+ // return the content type from the
+ // header collection if any
+ _ATLTRY
+ {
+ CStringA strKey("Content-Type");
+
+ int nIndex = m_headers.FindKey(strKey);
+ if (nIndex >= 0)
+ return m_headers.GetValueAt(nIndex);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return NULL;
+ }
+
+ // Call this function to set the Content-Type of the HTTP response.
+ // Examples of common MIME content types include text/html and text/plain.
+ BOOL SetContentType(__in_opt LPCSTR szContentType) throw()
+ {
+ _ATLTRY
+ {
+ if (!m_headers.SetAt("Content-Type", szContentType))
+ return m_headers.Add("Content-Type", szContentType);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to set the HTTP status code of the response.
+ // If not set explicitly, the default status code is 200 (OK).
+ // See GetStatusCode.
+ void SetStatusCode(__in int nCode) throw()
+ {
+ m_nStatusCode = nCode;
+ }
+
+ // Returns the current HTTP status code of the response.
+ // See SetStatusCode.
+ int GetStatusCode() throw()
+ {
+ return m_nStatusCode;
+ }
+
+ // Call this function to set the Cache-Control http header of the response.
+ // Examples of common Cache-Control header values: public, private, max-age=delta-seconds
+ BOOL SetCacheControl(__in_opt LPCSTR szCacheControl) throw()
+ {
+ _ATLTRY
+ {
+ if (!m_headers.SetAt("Cache-Control", szCacheControl))
+ return m_headers.Add("Cache-Control", szCacheControl);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to set the Expires HTTP header to the absolute date/time
+ // specified in the stExpires parameter
+ BOOL SetExpiresAbsolute(__in const SYSTEMTIME& stExpires) throw()
+ {
+ _ATLTRY
+ {
+ CStringA strExpires;
+ SystemTimeToHttpDate(stExpires, strExpires);
+
+ if (!m_headers.SetAt("Expires", strExpires))
+ return m_headers.Add("Expires", strExpires);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ return FALSE;
+ }
+
+ // Call this function to set the Expires HTTP header to a relative date/time
+ // value specified in minutes;
+ BOOL SetExpires(__in long lMinutes) throw()
+ {
+ CFileTime ft;
+ GetSystemTimeAsFileTime(&ft);
+
+ // add the specified number of minutes
+ ft += CFileTimeSpan(((ULONGLONG)lMinutes)*60*10000000);
+
+ SYSTEMTIME st;
+ FileTimeToSystemTime(&ft, &st);
+ return SetExpiresAbsolute(st);
+ }
+
+ // Call this function to set whether or not to output to client.
+ // Intended primarily for HEAD requests
+ BOOL SetWriteToClient(__in BOOL bSendOutput) throw()
+ {
+ m_bSendOutput = bSendOutput;
+ return TRUE;
+ }
+
+ // Call this function to determine whether or not the data is
+ // sent to the client. Intended primarily for HEAD requests
+ BOOL GetWriteToClient() throw()
+ {
+ return m_bSendOutput;
+ }
+
+ // Call this function to write data to the response object.
+ //
+ // Returns S_OK on success, E_INVALIDARG or E_FAIL on failure.
+ //
+ // See WriteClient for comments on buffering.
+ //
+ // szOut A pointer to the first byte of the data to be written.
+ //
+ // nLen The number of bytes to write. If this parameter is -1,
+ // szOut is assumed to be a nul-terminated string and the
+ // whole string will be written.
+ //
+ // pdwWritten A DWORD pointer that can be used to get the number of bytes written.
+ // This parameter can be NULL.
+ __checkReturn HRESULT WriteStream(__in_z LPCSTR szOut, __in int nLen, __out_opt DWORD *pdwWritten)
+ {
+ ATLASSUME(m_spServerContext != NULL);
+
+ if (pdwWritten)
+ *pdwWritten = 0;
+ if (nLen == -1)
+ {
+ if (!szOut)
+ return E_INVALIDARG;
+ nLen = (int) strlen(szOut);
+ }
+ BOOL bRet = WriteLen(szOut, nLen);
+ if (!bRet)
+ {
+ return AtlHresultFromLastError();
+ }
+ if (pdwWritten)
+ *pdwWritten = nLen;
+ return S_OK;
+ }
+
+ // Call this function to write data to the response object.
+ //
+ // Returns TRUE on success. FALSE on failure.
+ //
+ // If buffering is disabled, data is written directly to the client.
+ // If buffering is enabled, this function attempts to write to the buffer.
+ // If the buffer is too small to contain its existing data and the new data,
+ // the current contents of the buffer are flushed.
+ // If the buffer is still too small to contain the new data, that data is written
+ // directly to the client. Otherwise the new data is written to the buffer.
+ //
+ // Any headers that have been set in the response will be sent just before the
+ // data is written to the client if no headers have been sent up to that point.
+ //
+ // szOut A pointer to the first byte of the data to be written.
+ //
+ // nLen The number of bytes to write.
+ __checkReturn BOOL WriteLen(__in_ecount(dwLen) LPCSTR szOut, __in DWORD dwLen) throw()
+ {
+ ATLASSUME(m_spServerContext != NULL);
+ if (!szOut)
+ return FALSE;
+
+ if (m_bBufferOutput)
+ {
+ if (m_strContent.GetLength()+dwLen >= m_dwBufferLimit)
+ {
+ if (!Flush())
+ return FALSE;
+ }
+ if (dwLen <= m_dwBufferLimit)
+ return m_strContent.Append(szOut, dwLen);
+ }
+ BOOL bRet = SendHeadersInternal();
+
+ if (bRet && m_bSendOutput)
+ bRet = m_spServerContext->WriteClient((void *) szOut, &dwLen);
+
+ return bRet;
+ }
+
+ // Call this function to redirect the client to a different resource.
+ //
+ // Returns TRUE on success, FALSE on failure.
+ //
+ // szURL A nul-terminated string specifying the resource the client should navigate to.
+ //
+ // statusCode An HTTP status code from the HTTP_REDIRECT enumeration describing the reason
+ // for the redirection.
+ //
+ // bSendBody Specifies whether to generate and send a response body with the headers.
+ //
+ // This function allows (and RFC 2616 encourages) a response body to be sent
+ // with the following redirect types:
+ // HTTP_REDIRECT_MOVED
+ // HTTP_REDIRECT_FOUND
+ // HTTP_REDIRECT_SEE_OTHER
+ // HTTP_REDIRECT_TEMPORARY_REDIRECT
+ // No body will be sent with other redirect types.
+ //
+ // The response body contains a short hypertext note with a hyperlink to the new resource.
+ // A meta refresh tag is also included to allow browsers to automatically redirect
+ // the user to the resource even if they don't understand the redirect header.
+ //
+ // See RFC 2616 section 10.3 for more information on redirection.
+ BOOL Redirect(__in LPCSTR szUrl, __in HTTP_REDIRECT statusCode=HTTP_REDIRECT_MOVED, __in BOOL bSendBody=TRUE) throw(...)
+ {
+ CStringA strBody;
+ LPCSTR szBody = NULL;
+ if (bSendBody &&
+ (HTTP_REDIRECT_MOVED == statusCode || HTTP_REDIRECT_FOUND == statusCode ||
+ HTTP_REDIRECT_SEE_OTHER == statusCode || HTTP_REDIRECT_TEMPORARY_REDIRECT == statusCode))
+ {
+ _ATLTRY
+ {
+ ATLENSURE(szUrl!=NULL);
+ strBody.Format(
+ "<html>\r\n"
+ "<head>\r\n"
+ "<meta http-equiv=\"refresh\" content=\"0; url=%s\">\r\n"
+ "</head>\r\n"
+ "<body>Please use the following link to access this resource:"
+ " <a href=\"%s\">%s</a>\r\n"
+ "</body>\r\n"
+ "</html>\r\n",
+ szUrl, szUrl, szUrl);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ szBody = (LPCSTR) strBody;
+ }
+ return Redirect(szUrl, szBody, statusCode);
+ }
+
+ // Call this function to redirect the client to a different resource.
+ //
+ // Returns TRUE on success, FALSE on failure.
+ //
+ // szURL A nul-terminated string specifying the resource the client should navigate to.
+ //
+ // szBody A nul-terminated string containing the body of the response to be sent to the client.
+ //
+ // statusCode An HTTP status code from the HTTP_REDIRECT enumeration describing the reason
+ // for the redirection.
+ //
+ // This function allows (and RFC 2616 encourages) a response body to be sent
+ // with the following redirect types:
+ // HTTP_REDIRECT_MOVED
+ // HTTP_REDIRECT_FOUND
+ // HTTP_REDIRECT_SEE_OTHER
+ // HTTP_REDIRECT_TEMPORARY_REDIRECT
+ // No body will be sent with other redirect types.
+ //
+ // The response body should contain a short hypertext note with a hyperlink to the new resource.
+ // You can include a meta refresh tag to allow browsers to automatically redirect
+ // the user to the resource even if they don't understand the redirect header.
+ //
+ // See RFC 2616 section 10.3 for more information on redirection.
+ BOOL Redirect(__in LPCSTR szUrl, __in LPCSTR szBody, __in HTTP_REDIRECT statusCode=HTTP_REDIRECT_MOVED) throw()
+ {
+ SetStatusCode(statusCode);
+ AppendHeader("Location", szUrl);
+
+ _ATLTRY
+ {
+ if (!SendHeadersInternal())
+ return FALSE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ if (szBody &&
+ (HTTP_REDIRECT_MOVED == statusCode || HTTP_REDIRECT_FOUND == statusCode ||
+ HTTP_REDIRECT_SEE_OTHER == statusCode || HTTP_REDIRECT_TEMPORARY_REDIRECT == statusCode))
+ {
+ Write(szBody);
+ return Flush();
+ }
+ return TRUE;
+ }
+
+ // Call this function to append a header to the collection of HTTP headers managed by this object.
+ //
+ // szName A nul-teminated string containing the name of the HTTP header.
+ //
+ // szValue A nul-teminated string containing the value of the HTTP header.
+ BOOL AppendHeader(__in LPCSTR szName, __in_opt LPCSTR szValue) throw()
+ {
+ ATLASSERT(szName);
+ BOOL bRet = FALSE;
+ _ATLTRY
+ {
+ bRet = m_headers.Add(szName, szValue);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ return bRet;
+ }
+
+ // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
+ //
+ // pCookie A pointer to a CCookie object describing the cookie to be sent to the client.
+ BOOL AppendCookie(__in const CCookie *pCookie)
+ {
+ ATLENSURE(pCookie);
+ return AppendCookie((const CCookie&)*pCookie);
+ }
+
+ // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
+ //
+ // cookie A reference to a CCookie object describing the cookie to be sent to the client.
+ BOOL AppendCookie(__in const CCookie& cookie) throw()
+ {
+ CHAR szCookie[ATL_MAX_COOKIE_LEN];
+ DWORD dwBuffSize = ATL_MAX_COOKIE_LEN;
+ BOOL bRet = FALSE;
+ bRet = cookie.Render(szCookie, &dwBuffSize);
+ if (bRet)
+ {
+ _ATLTRY
+ {
+ bRet = m_headers.Add("Set-Cookie", szCookie);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ dwBuffSize = 0;
+ }
+ }
+
+ if (!bRet && dwBuffSize > 0 && dwBuffSize+1 > dwBuffSize) //static buffer wasn't big enough.
+ {
+ //We'll have to try dynamically allocating it
+ //allocate a buffer
+ CAutoVectorPtr<CHAR> sz;
+ if (sz.Allocate(dwBuffSize+1))
+ {
+ DWORD dwSizeNew = dwBuffSize + 1;
+ if (cookie.Render(sz, &dwSizeNew))
+ {
+ _ATLTRY
+ {
+ bRet = m_headers.Add("Set-Cookie", (const char *) sz);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+ }
+ }
+ return bRet;
+ }
+
+ // Call this function to add a Set-Cookie header to the collection of HTTP headers managed by this object.
+ //
+ // szName A nul-terminated string containing the name of the cookie to be sent to the client.
+ //
+ // szValue A nul-terminated string containing the value of the cookie to be sent to the client.
+ BOOL AppendCookie(__in LPCSTR szName, __in_opt LPCSTR szValue) throw()
+ {
+ ATLASSERT(szName);
+ BOOL bRet = FALSE;
+ _ATLTRY
+ {
+ CCookie c(szName, szValue);
+ bRet = AppendCookie(c);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ return bRet;
+ }
+
+ // Call this function to add a Set-Cookie header that removes a cookie value
+ // to the collection of HTTP headers managed by this object.
+ //
+ // szName A nul-terminated string containing the name of the cookie to be deleted
+ BOOL DeleteCookie(__in LPCSTR szName) throw()
+ {
+ ATLASSERT(szName);
+ BOOL bRet = FALSE;
+ _ATLTRY
+ {
+ CCookie cookie(szName, "");
+ bRet=cookie.SetMaxAge(0);
+ if(bRet)
+ {
+ bRet = AppendCookie(cookie);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ return bRet;
+
+ }
+
+ // Call this function to clear the collection of HTTP response headers maintained by this object.
+ //
+ // Note that clearing the headers includes removing all cookies associated with the response
+ // object. Cookies are sent to the client as Set-Cookie HTTP headers.
+ void ClearHeaders() throw()
+ {
+ m_headers.RemoveAll();
+ }
+
+ // Call this function to clear theresponse buffer without sending the contents to the client.
+ // If you need to empty the buffer but you do want the current contents sent to the client, call Flush instead.
+ void ClearContent() throw()
+ {
+ m_strContent.Empty();
+ }
+
+ // Call this function to send the current headers associated with this object to the client.
+ //
+ // Returns TRUE on success, FALSE on failure.
+ //
+ // The response headers are sent to the client using the current status code for the
+ // response object. See SetStatusCode and GetStatusCode.
+ BOOL SendHeadersInternal(__in BOOL fKeepConn=FALSE)
+ {
+ if (m_bHeadersSent)
+ return TRUE;
+
+ ATLENSURE(m_spServerContext != NULL);
+
+ CStringA strHeaders;
+ RenderHeaders(strHeaders);
+
+ BOOL bRet = FALSE;
+ _ATLTRY
+ {
+ if (m_nStatusCode == 200)
+ {
+ bRet = m_spServerContext->SendResponseHeader(strHeaders, "200 OK", fKeepConn);
+ if (bRet)
+ {
+ m_bHeadersSent = TRUE;
+ }
+ return bRet;
+ }
+
+ CFixedStringT<CStringA, 256> strStatus;
+ CDefaultErrorProvider prov;
+ GetStatusHeader(strStatus, m_nStatusCode, SUBERR_NONE, &prov);
+ bRet = m_spServerContext->SendResponseHeader(strHeaders, strStatus, fKeepConn);
+ if (bRet)
+ {
+ m_bHeadersSent = TRUE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ return bRet;
+ }
+
+ // Call this function to get a string containing all the HTTP headers associated with
+ // this object in a format suitable for sending to a client.
+ //
+ // strHeaders A CStringA reference to which will be appended the HTTP headers.
+ void RenderHeaders(CStringA& strHeaders) throw()
+ {
+ _ATLTRY
+ {
+ strHeaders.Preallocate(::ATL::AtlMultiplyThrow(m_headers.GetSize(),64));
+ for (int i=0; i<m_headers.GetSize(); i++)
+ {
+ strHeaders += m_headers.GetKeyAt(i);
+ strHeaders.Append(": ", sizeof(": ")-1);
+ strHeaders += m_headers.GetValueAt(i);
+ strHeaders.Append("\r\n", sizeof("\r\n")-1);
+ }
+ strHeaders.Append("\r\n", sizeof("\r\n")-1);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+
+ // Call this function to empty the response buffer and send its current
+ // contents to the client.
+ //
+ // Returns S_OK on success, or an error HRESULT on failure.
+ HRESULT FlushStream()
+ {
+ if (!Flush())
+ return AtlHresultFromLastError();
+ return S_OK;
+ }
+
+ // Call this function to empty the response buffer and send its current
+ // contents to the client.
+ //
+ // Returns TRUE on success, or FALSE on failure.
+ //
+ // Any headers that have been set in the response will be sent just before the
+ // data is written to the client if no headers have been sent up to that point.
+ BOOL Flush(BOOL bFinal=FALSE) throw()
+ {
+ if (!m_spServerContext)
+ return FALSE;
+
+ BOOL bRet = TRUE;
+
+ _ATLTRY
+ {
+ // if the headers haven't been sent,
+ // send them now
+
+ if (!m_bHeadersSent)
+ {
+ char szProtocol[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwProtocolLen = sizeof(szProtocol);
+
+ if (bFinal && m_bBufferOutput && m_dwBufferLimit==ULONG_MAX)
+ {
+ if (m_spServerContext->GetServerVariable("SERVER_PROTOCOL", szProtocol, &dwProtocolLen) &&
+ !strcmp(szProtocol, "HTTP/1.0"))
+ AppendHeader("Connection", "Keep-Alive");
+ Checked::itoa_s(m_strContent.GetLength(), szProtocol, _countof(szProtocol), 10);
+ AppendHeader("Content-Length", szProtocol);
+ bRet = SendHeadersInternal(TRUE);
+ }
+ else
+ bRet = SendHeadersInternal();
+ }
+ if (m_bBufferOutput)
+ {
+ DWORD dwLen = 0;
+
+ dwLen = m_strContent.GetLength();
+ if (dwLen)
+ {
+ if (m_bSendOutput &&
+ m_spServerContext->WriteClient((void *) (LPCSTR) m_strContent, &dwLen) != TRUE)
+ {
+ m_strContent.Empty();
+ return FALSE;
+ }
+ m_strContent.Empty();
+ }
+ }
+ } // _ATLTRY
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ return bRet;
+ }
+
+ // Call this function to clear the response object of any headers
+ // and the contents of the buffer.
+ void ClearResponse() throw()
+ {
+ m_strContent.Empty();
+ m_headers.RemoveAll();
+ }
+
+ BOOL AsyncPrep(__in BOOL fKeepConn=FALSE) throw()
+ {
+ ATLASSUME(m_spServerContext != NULL);
+
+ return SendHeadersInternal(fKeepConn);
+ }
+
+ BOOL AsyncFlush() throw()
+ {
+ ATLASSUME(m_spServerContext != NULL);
+
+ BOOL bRet = SendHeadersInternal();
+
+ if (bRet && m_bBufferOutput)
+ {
+ DWORD dwLen = 0;
+
+ dwLen = m_strContent.GetLength();
+ if (dwLen)
+ {
+ _ATLTRY
+ {
+ if (m_spServerContext->AsyncWriteClient((void *) (LPCSTR) m_strContent, &dwLen) != TRUE)
+ {
+ bRet = FALSE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+ }
+
+ return bRet;
+ }
+
+ BOOL TransmitFile(__in HANDLE hFile, __in LPCSTR szContentType="text/plain") throw()
+ {
+ ATLASSUME(m_spServerContext != NULL);
+ ATLASSERT(hFile != NULL && hFile != INVALID_HANDLE_VALUE);
+
+ SetContentType(szContentType);
+
+ if (m_strContent.GetLength())
+ if (!Flush())
+ return FALSE;
+
+ BOOL bRet = SendHeadersInternal();
+ if (bRet)
+ {
+ _ATLTRY
+ {
+ bRet = m_spServerContext->TransmitFile(hFile, NULL, NULL, NULL,
+ 0, 0, NULL, 0, NULL, 0, HSE_IO_ASYNC | HSE_IO_NODELAY);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+
+ return bRet;
+ }
+}; // class CHttpResponse
+
+
+
+#define ATLS_FLAG_NONE 0
+#define ATLS_FLAG_ASYNC 1 // handler might do some async handling
+
+// push_macro/pop_macro doesn't work in a template definition.
+#pragma push_macro("new")
+#undef new
+template <class T>
+class PerThreadWrapper : public CComObjectNoLock<T>
+{
+public:
+ void *operator new(size_t /*size*/, void *p) throw()
+ {
+ return p;
+ }
+
+ void operator delete(void * /*p*/) throw()
+ {
+ }
+
+ STDMETHOD_(ULONG, Release)() throw()
+ {
+ ULONG l = InternalRelease();
+ if (l == 0)
+ {
+ T *pT = static_cast<T*>(this);
+ ATLASSERT(pT->m_spExtension != NULL);
+ CIsapiWorker *pWorker = pT->m_spExtension->GetThreadWorker();
+ ATLASSERT(pWorker);
+
+ delete this;
+ if(pWorker)
+ {
+ HeapFree(pWorker->m_hHeap, HEAP_NO_SERIALIZE, this);
+ }
+ }
+ return l;
+ }
+};
+
+template <typename THandler>
+inline BOOL CreateRequestHandlerSync(__in IIsapiExtension *pExtension, __deref_out_opt IUnknown **ppOut)
+{
+ ATLENSURE(pExtension);
+ ATLENSURE(ppOut);
+
+ CIsapiWorker *pWorker = pExtension->GetThreadWorker();
+ ATLENSURE(pWorker);
+ void *pv = HeapAlloc(pWorker->m_hHeap, HEAP_NO_SERIALIZE, sizeof(PerThreadWrapper<THandler>));
+ if (!pv)
+ return FALSE;
+
+ PerThreadWrapper<THandler> *pHandler = new(pv) PerThreadWrapper<THandler>;
+ *ppOut = static_cast<IRequestHandler *>(pHandler);
+ pHandler->m_spExtension = pExtension;
+
+ (*ppOut)->AddRef();
+
+ return TRUE;
+}
+#pragma pop_macro("new")
+
+#define DECLARE_ASYNC_HANDLER() \
+ static DWORD GetHandlerFlags() \
+ { \
+ return ATLS_FLAG_ASYNC; \
+ } \
+ DWORD GetAsyncFlags() \
+ { \
+ return ATLSRV_INIT_USEASYNC; \
+ }
+
+#define DECLARE_ASYNC_HANDLER_EX() \
+ static DWORD GetHandlerFlags() \
+ { \
+ return ATLS_FLAG_ASYNC; \
+ } \
+ DWORD GetAsyncFlags() \
+ { \
+ return (ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX); \
+ }
+
+
+template <typename THandler>
+class IRequestHandlerImpl : public IRequestHandler
+{
+public:
+ HINSTANCE m_hInstHandler;
+ CComPtr<IServiceProvider> m_spServiceProvider;
+ CComPtr<IHttpServerContext> m_spServerContext;
+ CComPtr<IIsapiExtension> m_spExtension;
+ DWORD m_dwAsyncFlags;
+
+ IRequestHandlerImpl()
+ :m_hInstHandler(NULL)
+ {
+ m_dwAsyncFlags = 0;
+ }
+ virtual ~IRequestHandlerImpl()
+ {
+ }
+
+ HTTP_CODE GetFlags(__out DWORD *pdwStatus)
+ {
+ ATLASSERT(pdwStatus);
+ THandler *pT = static_cast<THandler *>(this);
+
+ *pdwStatus = pT->GetAsyncFlags();
+ if (pT->CachePage())
+ *pdwStatus |= ATLSRV_INIT_USECACHE;
+
+#ifdef _DEBUG
+ if (*pdwStatus & (ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX))
+ ATLASSERT(pT->GetHandlerFlags() & ATLS_FLAG_ASYNC);
+#endif
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE InitializeHandler(__in AtlServerRequest *pRequestInfo, __in IServiceProvider *pProvider)
+ {
+ ATLENSURE(pRequestInfo != NULL);
+ ATLENSURE(pProvider != NULL);
+ ATLENSURE(pRequestInfo->hInstDll != NULL);
+ ATLENSURE(pRequestInfo->pServerContext != NULL);
+
+ // Initialize our internal references to required services
+ m_hInstHandler = pRequestInfo->hInstDll;
+ m_spServiceProvider = pProvider;
+ m_spServerContext = pRequestInfo->pServerContext;
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE InitializeChild(__in AtlServerRequest *pRequestInfo, __in IServiceProvider *pProvider, IHttpRequestLookup * /*pLookup*/)
+ {
+ return InitializeHandler(pRequestInfo, pProvider);
+ }
+
+ void UninitializeHandler()
+ {
+ }
+
+ HTTP_CODE HandleRequest(
+ AtlServerRequest* /*pRequestInfo*/,
+ IServiceProvider* /*pServiceProvider*/)
+ {
+ return HTTP_SUCCESS;
+ }
+
+ DWORD GetAsyncFlags()
+ {
+ return m_dwAsyncFlags;
+ }
+
+ void SetAsyncFlags(__in DWORD dwAsyncFlags)
+ {
+ ATLASSERT((dwAsyncFlags & ~(ATLSRV_INIT_USEASYNC|ATLSRV_INIT_USEASYNC_EX)) == 0);
+ m_dwAsyncFlags = dwAsyncFlags;
+ }
+
+ BOOL CachePage()
+ {
+ return FALSE;
+ }
+
+ static DWORD GetHandlerFlags()
+ {
+ return ATLS_FLAG_NONE;
+ }
+
+ // Used to create new instance of this object. A pointer to this
+ // function is stored in the handler map in user's code.
+ static BOOL CreateRequestHandler(__in IIsapiExtension *pExtension, __deref_out_opt IUnknown **ppOut)
+ {
+ ATLASSERT(ppOut != NULL);
+ if (ppOut == NULL)
+ return false;
+
+ *ppOut = NULL;
+
+ if (THandler::GetHandlerFlags() & ATLS_FLAG_ASYNC)
+ {
+ THandler *pHandler = NULL;
+ ATLTRY(pHandler = new CComObjectNoLock<THandler>);
+ if (!pHandler)
+ return FALSE;
+ *ppOut = static_cast<IRequestHandler *>(pHandler);
+ pHandler->m_spExtension = pExtension;
+ (*ppOut)->AddRef();
+ }
+ else
+ {
+ if (!CreateRequestHandlerSync<THandler>(pExtension, ppOut))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ // Used to initialize the class
+ // function is stored in the handler map in user's code.
+ static BOOL InitRequestHandlerClass(IHttpServerContext *pContext, IIsapiExtension *pExt)
+ {
+ (pContext); // unused
+ (pExt); // unused
+ return TRUE;
+ }
+
+ // Used to uninitialize the class
+ // function is stored in the handler map in user's code.
+ static void UninitRequestHandlerClass()
+ {
+ return;
+ }
+};
+
+struct CRequestStats
+{
+ long m_lTotalRequests;
+ long m_lFailedRequests;
+ __int64 m_liTotalResponseTime;
+ long m_lAvgResponseTime;
+ long m_lCurrWaiting;
+ long m_lMaxWaiting;
+ long m_lActiveThreads;
+
+ CRequestStats() throw()
+ {
+ m_lTotalRequests = 0;
+ m_lFailedRequests = 0;
+ m_liTotalResponseTime = 0;
+ m_lAvgResponseTime = 0;
+ m_lCurrWaiting = 0;
+ m_lMaxWaiting = 0;
+ m_lActiveThreads = 0;
+ }
+
+ void RequestHandled(__in AtlServerRequest *pRequestInfo, __in BOOL bSuccess)
+ {
+ ATLENSURE(pRequestInfo);
+
+ InterlockedIncrement(&m_lTotalRequests);
+ if (!bSuccess)
+ InterlockedIncrement(&m_lFailedRequests);
+
+ long lTicks;
+
+#ifndef ATL_NO_MMSYS
+ lTicks = (long) (timeGetTime() - pRequestInfo->dwStartTicks);
+#else
+ lTicks = (long) (GetTickCount() - pRequestInfo->dwStartTicks);
+#endif
+ __int64 liTotalResponseTime = Add64(&m_liTotalResponseTime, lTicks);
+ long lAv = (long) (liTotalResponseTime / m_lTotalRequests);
+ InterlockedExchange(&m_lAvgResponseTime, lAv);
+
+ InterlockedDecrement(&m_lActiveThreads);
+ }
+
+ long GetTotalRequests() throw()
+ {
+ return m_lTotalRequests;
+ }
+
+ long GetFailedRequests() throw()
+ {
+ return m_lFailedRequests;
+ }
+
+ long GetAvgResponseTime() throw()
+ {
+ return m_lAvgResponseTime;
+ }
+
+ void OnRequestReceived() throw()
+ {
+ long nCurrWaiting = InterlockedIncrement(&m_lCurrWaiting);
+ AtlInterlockedUpdateMax(nCurrWaiting, &m_lMaxWaiting);
+ }
+
+ void OnRequestDequeued() throw()
+ {
+ InterlockedDecrement(&m_lCurrWaiting);
+ InterlockedIncrement(&m_lActiveThreads);
+ }
+
+ long GetCurrWaiting() throw()
+ {
+ return m_lCurrWaiting;
+ }
+
+ long GetMaxWaiting() throw()
+ {
+ return m_lMaxWaiting;
+ }
+
+ long GetActiveThreads() throw()
+ {
+ return m_lActiveThreads;
+ }
+
+private:
+ // not actually atomic, but it will add safely.
+
+ // the returned value is not 100% guaranteed to be
+ // correct, but should be correct more often than
+ // just reading the __int64
+ // the 2 cases where the return value will not be
+ // a valid result value from an add are:
+ // * multiple threads wrap the low part in rapid succession
+ // * different threads are adding values with different signs
+
+ // this is good enough for our use in RequestHandled -
+ // we always add positive values and we shouldn't wrap 32-bits often
+ inline __int64 Add64(__inout __int64* pn, long nAdd)
+ {
+ ATLENSURE(pn != NULL);
+
+#if defined(_WIN64) && defined(_M_CEE)
+
+ // Use System::Threading::Interlocked::Add because InterlockedExchangeAdd is an intrisinc not supported in managed code
+ // with 64bits compilers.
+ // Also, System::Threading::Interlocked::Add support 64bits operands.
+ return System::Threading::Interlocked::Add(*pn, nAdd);
+
+#else
+
+ long* pnLow = (long*)(LPBYTE(pn) + offsetof(LARGE_INTEGER, LowPart));
+ long* pnHigh = (long*)(LPBYTE(pn) + offsetof(LARGE_INTEGER, HighPart));
+
+ long nOrigLow = InterlockedExchangeAdd(pnLow, nAdd);
+ long nNewLow = nOrigLow + nAdd;
+ long nNewHigh = *pnHigh;
+ if (nAdd > 0 && nNewLow < nOrigLow)
+ nNewHigh = InterlockedIncrement(pnHigh);
+ else if (nAdd < 0 && nNewLow > nOrigLow)
+ nNewHigh = InterlockedDecrement(pnHigh);
+
+ LARGE_INTEGER li;
+ li.LowPart = nNewLow;
+ li.HighPart = nNewHigh;
+ return li.QuadPart;
+
+#endif
+ }
+};
+
+class CStdRequestStats : public CRequestStats
+{
+
+public:
+ HRESULT Initialize() throw()
+ {
+ return S_OK;
+ }
+
+ void Uninitialize() throw()
+ {
+ }
+};
+
+#define PERF_REQUEST_OBJECT 100
+
+struct CPerfRequestStatObject : public CPerfObject,
+ public CRequestStats
+{
+ DECLARE_PERF_CATEGORY_EX(PERF_REQUEST_OBJECT, IDS_PERFMON_REQUEST, IDS_PERFMON_REQUEST_HELP, PERF_DETAIL_NOVICE, 0, sizeof(CPerfRequestStatObject), MAX_PATH, -1);
+ BEGIN_COUNTER_MAP(CPerfRequestStatObject)
+ DEFINE_COUNTER(m_lTotalRequests, IDS_PERFMON_REQUEST_TOTAL, IDS_PERFMON_REQUEST_TOTAL_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_lFailedRequests, IDS_PERFMON_REQUEST_FAILED, IDS_PERFMON_REQUEST_FAILED_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_lTotalRequests, IDS_PERFMON_REQUEST_RATE, IDS_PERFMON_REQUEST_RATE_HELP, PERF_COUNTER_COUNTER, -1)
+ DEFINE_COUNTER(m_lAvgResponseTime, IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME, IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_lCurrWaiting, IDS_PERFMON_REQUEST_CURR_WAITING, IDS_PERFMON_REQUEST_CURR_WAITING_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_lMaxWaiting, IDS_PERFMON_REQUEST_MAX_WAITING, IDS_PERFMON_REQUEST_MAX_WAITING_HELP, PERF_COUNTER_RAWCOUNT, -1)
+ DEFINE_COUNTER(m_lActiveThreads, IDS_PERFMON_REQUEST_ACTIVE_THREADS, IDS_PERFMON_REQUEST_ACTIVE_THREADS, PERF_COUNTER_RAWCOUNT, -1)
+ END_COUNTER_MAP()
+};
+
+class CRequestPerfMon : public CPerfMon
+{
+public:
+ BEGIN_PERF_MAP(_T("ATL Server:Request"))
+ CHAIN_PERF_CATEGORY(CPerfRequestStatObject)
+ END_PERF_MAP()
+};
+
+class CPerfMonRequestStats
+{
+ CRequestPerfMon m_PerfMon;
+ CPerfRequestStatObject * m_pPerfObjectInstance;
+ CPerfRequestStatObject * m_pPerfObjectTotal;
+
+public:
+ CPerfMonRequestStats() throw()
+ {
+ m_pPerfObjectInstance = NULL;
+ m_pPerfObjectTotal = NULL;
+ }
+
+ HRESULT Initialize() throw()
+ {
+ HRESULT hr;
+
+ m_pPerfObjectInstance = NULL;
+ m_pPerfObjectTotal = NULL;
+
+ hr = m_PerfMon.Initialize();
+ if (SUCCEEDED(hr))
+ {
+ CPerfLock lock(&m_PerfMon);
+ if (FAILED(hr = lock.GetStatus()))
+ {
+ return hr;
+ }
+
+ HINSTANCE hInst = _AtlBaseModule.GetModuleInstance();
+ WCHAR szName[MAX_PATH];
+ if (GetModuleFileNameW(hInst, szName, MAX_PATH) == 0)
+ {
+ return E_FAIL;
+ }
+ szName[MAX_PATH-1] = 0;
+
+ hr = m_PerfMon.CreateInstanceByName(L"_Total", &m_pPerfObjectTotal);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = m_PerfMon.CreateInstanceByName(szName, &m_pPerfObjectInstance);
+ if (FAILED(hr))
+ {
+ m_PerfMon.ReleaseInstance(m_pPerfObjectTotal);
+ m_pPerfObjectTotal = NULL;
+ return hr;
+ }
+ }
+
+ return hr;
+ }
+
+ void Uninitialize() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ m_PerfMon.ReleaseInstance(m_pPerfObjectInstance);
+ if (m_pPerfObjectTotal != NULL)
+ m_PerfMon.ReleaseInstance(m_pPerfObjectTotal);
+
+ m_pPerfObjectInstance = NULL;
+ m_pPerfObjectTotal = NULL;
+
+ m_PerfMon.UnInitialize();
+ }
+
+ void RequestHandled(__in AtlServerRequest *pRequestInfo, __in BOOL bSuccess) throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ m_pPerfObjectInstance->RequestHandled(pRequestInfo, bSuccess);
+ if (m_pPerfObjectTotal != NULL)
+ m_pPerfObjectTotal->RequestHandled(pRequestInfo, bSuccess);
+ }
+
+ long GetTotalRequests() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ return m_pPerfObjectInstance->GetTotalRequests();
+
+ return 0;
+ }
+
+ long GetFailedRequests() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ return m_pPerfObjectInstance->GetFailedRequests();
+
+ return 0;
+ }
+
+ long GetAvgResponseTime() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ return m_pPerfObjectInstance->GetAvgResponseTime();
+
+ return 0;
+ }
+
+ void OnRequestReceived() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ m_pPerfObjectInstance->OnRequestReceived();
+ if (m_pPerfObjectTotal != NULL)
+ m_pPerfObjectTotal->OnRequestReceived();
+ }
+
+ void OnRequestDequeued() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ m_pPerfObjectInstance->OnRequestDequeued();
+ if (m_pPerfObjectTotal != NULL)
+ m_pPerfObjectTotal->OnRequestDequeued();
+ }
+
+ long GetCurrWaiting() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ return m_pPerfObjectInstance->GetCurrWaiting();
+
+ return 0;
+ }
+
+ long GetMaxWaiting() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ return m_pPerfObjectInstance->GetMaxWaiting();
+
+ return 0;
+ }
+
+ long GetActiveThreads() throw()
+ {
+ if (m_pPerfObjectInstance != NULL)
+ return m_pPerfObjectInstance->GetActiveThreads();
+
+ return 0;
+ }
+};
+
+class CNoRequestStats
+{
+protected:
+
+public:
+
+ HRESULT Initialize() throw()
+ {
+ return S_OK;
+ }
+
+ void Uninitialize() throw()
+ {
+ }
+
+ void RequestHandled(AtlServerRequest * /*pRequestInfo*/, BOOL /*bSuccess*/) throw()
+ {
+ }
+
+ long GetTotalRequests() throw()
+ {
+ return 0;
+ }
+
+ long GetFailedRequests() throw()
+ {
+ return 0;
+ }
+
+ long GetAvgResponseTime() throw()
+ {
+ return 0;
+ }
+
+ void OnRequestReceived() throw()
+ {
+ }
+
+ void OnRequestDequeued() throw()
+ {
+ }
+
+ long GetCurrWaiting() throw()
+ {
+ return 0;
+ }
+
+ long GetMaxWaiting() throw()
+ {
+ return 0;
+ }
+
+ long GetActiveThreads() throw()
+ {
+ return 0;
+ }
+};
+
+struct ATLServerDllInfo
+{
+ GETATLHANDLERBYNAME pfnGetHandler;
+ UNINITIALIZEATLHANDLERS pfnUninitHandlers;
+ INITIALIZEATLHANDLERS pfnInitHandlers;
+ IIsapiExtension *pExtension;
+ IHttpServerContext *pContext;
+};
+
+class CDllCachePeer
+{
+public:
+ struct DllInfo : public ATLServerDllInfo
+ {
+ DllInfo& operator=(__in const DllInfo& right) throw()
+ {
+ if (this != &right)
+ {
+ pfnGetHandler = right.pfnGetHandler;
+ pfnUninitHandlers = right.pfnUninitHandlers;
+ pfnInitHandlers = right.pfnInitHandlers;
+ pExtension = right.pExtension;
+ pContext = right.pContext;
+ }
+ return *this;
+ }
+ };
+
+ BOOL Add(__in HINSTANCE hInst, __out DllInfo *pInfo) throw(...)
+ {
+ ATLENSURE(pInfo!=NULL);
+ pInfo->pfnInitHandlers = (INITIALIZEATLHANDLERS) GetProcAddress(hInst, ATLS_FUNCID_INITIALIZEHANDLERS);
+
+ pInfo->pfnGetHandler = (GETATLHANDLERBYNAME) GetProcAddress(hInst, ATLS_FUNCID_GETATLHANDLERBYNAME);
+ if (!pInfo->pfnGetHandler)
+ return FALSE;
+
+ pInfo->pfnUninitHandlers = (UNINITIALIZEATLHANDLERS) GetProcAddress(hInst, ATLS_FUNCID_UNINITIALIZEHANDLERS);
+
+ if (pInfo->pfnInitHandlers)
+ {
+ pInfo->pfnInitHandlers(pInfo->pContext, pInfo->pExtension);
+ pInfo->pContext = NULL; // won't be valid after this call
+ }
+
+ return TRUE;
+ }
+
+ void Remove(HINSTANCE /*hInst*/, __in DllInfo *pInfo) throw(...)
+ {
+ ATLENSURE(pInfo!=NULL);
+ if (pInfo->pfnUninitHandlers)
+ (*pInfo->pfnUninitHandlers)();
+ }
+
+};
+
+inline bool operator==(__in const CDllCachePeer::DllInfo& left, __in const CDllCachePeer::DllInfo& right) throw()
+{
+ return ( (left.pfnGetHandler == right.pfnGetHandler) &&
+ (left.pfnUninitHandlers == right.pfnUninitHandlers) &&
+ (left.pfnInitHandlers == right.pfnInitHandlers) &&
+ (left.pExtension == right.pExtension) &&
+ (left.pContext == right.pContext)
+ );
+}
+
+
+
+// Helper function to impersonate the client
+// on the current thread
+inline BOOL AtlImpersonateClient(__in IHttpServerContext *pServerContext)
+{
+ ATLENSURE(pServerContext);
+
+ // impersonate the calling client on the current thread
+ HANDLE hToken;
+ _ATLTRY
+ {
+ if (!pServerContext->GetImpersonationToken(&hToken))
+ return FALSE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ if (!SetThreadToken(NULL, hToken))
+ return FALSE;
+ return TRUE;
+}
+
+// Helper class to set the thread impersonation token
+// This is mainly used internally to ensure that we
+// don't forget to revert to the process impersonation
+// level
+class CSetThreadToken
+{
+public:
+ CSetThreadToken() : m_bShouldRevert(FALSE) {}
+
+ BOOL Initialize(__in AtlServerRequest *pRequestInfo)
+ {
+ ATLENSURE(pRequestInfo);
+ m_bShouldRevert = AtlImpersonateClient(pRequestInfo->pServerContext);
+ return m_bShouldRevert;
+ }
+
+ ~CSetThreadToken() throw()
+ {
+ if( m_bShouldRevert && !RevertToSelf() )
+ {
+ _AtlRaiseException( (DWORD)EXCEPTION_ACCESS_VIOLATION );
+ }
+ }
+protected:
+ BOOL m_bShouldRevert;
+};
+
+
+// push_macro/pop_macro doesn't work in a template definition.
+#pragma push_macro("new")
+#undef new
+
+
+//Base is the user's class that derives from CComObjectRoot and whatever
+//interfaces the user wants to support on the object
+template <class Base>
+class _CComObjectHeapNoLock : public Base
+{
+ public:
+ typedef Base _BaseClass;
+ HANDLE m_hHeap;
+
+ _CComObjectHeapNoLock(void* = NULL, HANDLE hHeap = NULL)
+ {
+ m_hHeap = hHeap;
+ }
+ // Set refcount to -(LONG_MAX/2) to protect destruction and
+ // also catch mismatched Release in debug builds
+ ~_CComObjectHeapNoLock()
+ {
+ m_dwRef = -(LONG_MAX/2);
+ FinalRelease();
+#ifdef _ATL_DEBUG_INTERFACES
+ _AtlDebugInterfacesModule.DeleteNonAddRefThunk(_GetRawUnknown());
+#endif
+ }
+
+ //If InternalAddRef or InternalRelease is undefined then your class
+ //doesn't derive from CComObjectRoot
+ STDMETHOD_(ULONG, AddRef)() throw() {return InternalAddRef();}
+ STDMETHOD_(ULONG, Release)() throw()
+ {
+ ULONG l = InternalRelease();
+ if (l == 0)
+ {
+ HANDLE hHeap = m_hHeap;;
+ this->~_CComObjectHeapNoLock();
+ if (hHeap != NULL)
+ {
+ HeapFree(hHeap, 0, this);
+ }
+ }
+ return l;
+ }
+ //if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
+ STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) throw()
+ {return _InternalQueryInterface(iid, ppvObject);}
+
+ static HRESULT WINAPI CreateInstance(_CComObjectHeapNoLock<Base>** pp, HANDLE hHeap) throw();
+};
+
+
+
+template <class Base>
+HRESULT WINAPI _CComObjectHeapNoLock<Base>::CreateInstance(__deref_out _CComObjectHeapNoLock<Base>** pp, __in HANDLE hHeap) throw()
+{
+ ATLASSERT(pp != NULL);
+ if (pp == NULL)
+ return E_POINTER;
+ *pp = NULL;
+
+ HRESULT hRes = E_OUTOFMEMORY;
+ // Allocate a fixed block size to avoid fragmentation
+ void *pv = HeapAlloc(hHeap, HEAP_ZERO_MEMORY,
+ __max(sizeof(AtlServerRequest), sizeof(_CComObjectHeapNoLock<CServerContext>)));
+ if (pv == NULL)
+ {
+ return hRes;
+ }
+#pragma warning(push)
+#pragma warning(disable: 6280)
+ /* prefast noise VSW 493229 */
+ _CComObjectHeapNoLock<Base>* p = new(pv) _CComObjectHeapNoLock<CServerContext>(NULL, hHeap);
+#pragma warning(pop)
+
+ p->SetVoid(NULL);
+ p->InternalFinalConstructAddRef();
+ hRes = p->_AtlInitialConstruct();
+ if (SUCCEEDED(hRes))
+ hRes = p->FinalConstruct();
+ if (SUCCEEDED(hRes))
+ hRes = p->_AtlFinalConstruct();
+ p->InternalFinalConstructRelease();
+ if (hRes != S_OK)
+ {
+ p->~_CComObjectHeapNoLock();
+#pragma warning(push)
+#pragma warning(disable: 6280)
+ /* prefast noise VSW 493229 */
+ HeapFree(hHeap, 0, p);
+#pragma warning(pop)
+ p = NULL;
+ }
+ *pp = p;
+ return hRes;
+}
+
+inline CServerContext* CreateServerContext(__in HANDLE hRequestHeap) throw()
+{
+ _CComObjectHeapNoLock<CServerContext>* pContext;
+ _CComObjectHeapNoLock<CServerContext>::CreateInstance(&pContext, hRequestHeap);
+ return pContext;
+}
+#pragma pop_macro("new")
+
+// _AtlGetHandlerName
+// get handler name from stencil file. Ignore all server side comments
+// szFileName - the file from which to extract the handler name
+// szHandlerName - buffer into which handler name will be copied,
+// it is assumed to be of size MAX_PATH+ATL_MAX_HANDLER_NAME+2
+inline HTTP_CODE _AtlGetHandlerName(__in LPCSTR szFileName, __out_ecount(MAX_PATH+ATL_MAX_HANDLER_NAME+2) LPSTR szHandlerName)
+{
+ ATLASSERT(szFileName);
+ ATLENSURE(szHandlerName);
+
+ szHandlerName[0] = '\0';
+ CAtlFile cfFile;
+ HRESULT hr;
+
+ _ATLTRY
+ {
+ hr = cfFile.Create(CA2CTEX<MAX_PATH>(szFileName), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
+ if (FAILED(hr) || cfFile.m_h == NULL || GetFileType(cfFile.m_h) != FILE_TYPE_DISK)
+ {
+ if (hr == AtlHresultFromWin32(ERROR_FILE_NOT_FOUND))
+ return HTTP_NOT_FOUND;
+ else if (hr == AtlHresultFromWin32(ERROR_ACCESS_DENIED))
+ return HTTP_UNAUTHORIZED;
+ else
+ return AtlsHttpError(500, IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // CA2CTEX threw
+ }
+
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+ DWORD dwRead=0;
+ LPCSTR szHandler = "handler";
+ LPCSTR pszHandlerPos = NULL;
+ LPSTR pszHandlerName = szHandlerName;
+ char szBuf[4097];
+ LPSTR szCurly = NULL;
+ LPSTR pszBuf = NULL;
+ bool bInQuote = false;
+
+ // state:
+ // 0 = default/unknown
+ // 1 = have "{"
+ // 2 = have "{{" -- skip spaces
+ // 3 = have "{{" -- check "handler"
+ // 4 = have "handler" -- skip spaces
+ // 5 = have "handler" -- get name
+ // 6 = scan until first '}'
+ // 7 = better be '}'
+ // 8 = done
+ int nState = 0;
+
+ do
+ {
+ hr = cfFile.Read(szBuf, _countof(szBuf)-1, dwRead);
+ if (hr != S_OK)
+ {
+ return AtlsHttpError(500, ISE_SUBERR_READFILEFAIL); // failed reading
+ }
+
+ szBuf[dwRead] = '\0';
+ pszBuf = szBuf;
+
+ while (*pszBuf && nState != 8)
+ {
+ switch (nState)
+ {
+ case 0:
+ // 0 = default/unknown
+
+ // look for the first curly
+ szCurly = strchr(pszBuf, '{');
+ if (!szCurly)
+ {
+ // skip to the end of the buffer
+ pszBuf = szBuf+dwRead-1;
+ }
+ else
+ {
+ pszBuf = szCurly;
+ nState = 1;
+ }
+ break;
+ case 1:
+ // 1 = have "{"
+ if (*pszBuf == '{')
+ {
+ nState = 2;
+ }
+ else
+ {
+ nState = 0; // if the next character is not a '{', start over
+ }
+ break;
+ case 2:
+ if (!isspace(static_cast<unsigned char>(*pszBuf)))
+ {
+ pszHandlerPos = szHandler;
+ pszBuf--;
+ nState = 3;
+ }
+ break;
+ case 3:
+ // 3 = partial handler "h..."
+ if (*pszBuf != *pszHandlerPos)
+ {
+ // not a handler, skip tag
+ nState = 6;
+ }
+ else
+ {
+ pszHandlerPos++;
+ if (!*pszHandlerPos) // at the end of the "handler" part
+ nState = 4;
+ }
+ break;
+ case 4:
+ // 4 = have "handler" -- skip spaces
+ if (!isspace(static_cast<unsigned char>(*pszBuf)))
+ {
+ if (*pszBuf == '\"')
+ {
+ bInQuote = true;
+ }
+ else
+ {
+ pszBuf--;
+ }
+ nState = 5;
+ }
+ break;
+ case 5:
+ // 5 = have "handler" -- get name
+ if (isspace(static_cast<unsigned char>(*pszBuf)) && !bInQuote)
+ {
+ if (*(pszHandlerName-1) != '/')
+ {
+ // end of the name -- jump to getting the first '}'
+ nState = 6;
+ }
+ else
+ {
+ nState = 4;
+ }
+ }
+ else if (*pszBuf == '}')
+ {
+ // end of the name -- jump to getting the second '}'
+ nState = 7;
+ }
+ else if (*pszBuf == '\"')
+ {
+ if (bInQuote)
+ {
+ bInQuote = false;
+ }
+ else
+ {
+ hcErr = HTTP_FAIL;
+ nState = 8;
+ }
+ }
+ else
+ {
+ // ensure we don't overwrite the buffer
+ if (pszHandlerName-szHandlerName >= MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+1)
+ {
+ hcErr = HTTP_FAIL;
+ nState = 8;
+ }
+ else
+ {
+ *pszHandlerName++ = *pszBuf;
+ }
+ }
+ break;
+ case 6:
+ // 6 = scan until first '}'
+ if (*pszBuf == '}')
+ nState = 7;
+ break;
+ case 7:
+ // 7 = better be '}'
+ if (*pszBuf != '}')
+ {
+ hcErr = AtlsHttpError(500, ISE_SUBERR_BAD_HANDLER_TAG);
+ nState = 8;
+ }
+ if (*szHandlerName)
+ nState = 8;
+ else
+ nState = 0;
+ break;
+ default:
+ ATLASSERT(FALSE);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ pszBuf++;
+ }
+ } while (dwRead != 0 && nState != 8);
+
+ *pszHandlerName = '\0';
+
+ return hcErr;
+}
+
+// _AtlCrackHandler cracks a request path of the form dll_path/handler_name into its
+// constituent parts
+// szHandlerDllName - the full handler path of the form "dll_path/handler_name"
+// szDllPath - the DLL path (should be of length MAX_PATH)
+// szHandlerName - the handler name (should be of length ATL_MAX_HANDLER_NAME_LEN+1)
+//
+inline BOOL _AtlCrackHandler(
+ __in_z LPCSTR szHandlerDllName,
+ __out_ecount_part(*pdwszDllPathLen,*pdwszDllPathLen) LPSTR szDllPath,
+ __inout LPDWORD pdwDllPathLen,
+ __out_ecount_part(*pdwHandlerNameLen,*pdwHandlerNameLen) LPSTR szHandlerName,
+ __inout LPDWORD pdwHandlerNameLen)
+{
+ ATLENSURE( szHandlerDllName != NULL );
+ ATLASSERT( szDllPath != NULL );
+ ATLENSURE( pdwDllPathLen != NULL );
+ ATLASSERT( szHandlerName != NULL );
+ ATLASSERT( pdwHandlerNameLen != NULL );
+
+ BOOL bRet = TRUE;
+
+ // skip leading spaces
+ while (*szHandlerDllName && isspace(static_cast<unsigned char>(*szHandlerDllName)))
+ ++szHandlerDllName;
+
+ // get the handler name
+ LPCSTR szSlash = strchr(szHandlerDllName, '/');
+ LPCSTR szEnd = NULL;
+ LPCSTR szSlashEnd = NULL;
+
+ // if it is of the form <dll_name>/<handler_name>
+ if (szSlash)
+ {
+ szEnd = szSlash;
+
+ // skip trailing spaces on <dll_name>
+ while (szEnd>szHandlerDllName && isspace(static_cast<unsigned char>(*(szEnd-1))))
+ --szEnd;
+
+ szSlash++;
+ // skip leading whitespace
+ while (*szSlash && isspace(static_cast<unsigned char>(*szSlash)))
+ szSlash++;
+
+ // right trim szSlash;
+ szSlashEnd = szSlash;
+ while (*szSlashEnd && !isspace(static_cast<unsigned char>(*szSlashEnd)))
+ szSlashEnd++;
+ }
+ else // only the <dll_name>
+ {
+ szSlash = ATL_HANDLER_NAME_DEFAULT;
+ szSlashEnd = szSlash+sizeof(ATL_HANDLER_NAME_DEFAULT)-1;
+
+ // do it this way to handle paths with spaces
+ // (e.g. "some path\subdirectory one\subdirectory two\dll_name.dll")
+ szEnd = szHandlerDllName+strlen(szHandlerDllName);
+
+ // skip trailing spaces
+ while (szEnd>szHandlerDllName && isspace(static_cast<unsigned char>(*(szEnd-1))))
+ --szEnd;
+ }
+
+ // if the dll path is quoted, strip the quotes
+ if (*szHandlerDllName == '\"' && *(szEnd-1) == '\"' && szEnd > szHandlerDllName+2)
+ {
+ szHandlerDllName++;
+ szEnd--;
+ }
+
+ if (*pdwDllPathLen > (DWORD)(szEnd-szHandlerDllName) && (szEnd-szHandlerDllName >= 0))
+ {
+ Checked::memcpy_s(szDllPath, *pdwDllPathLen, szHandlerDllName, szEnd-szHandlerDllName);
+ szDllPath[szEnd-szHandlerDllName] = '\0';
+ *pdwDllPathLen = (DWORD)(szEnd-szHandlerDllName);
+ }
+ else
+ {
+ *pdwDllPathLen = (DWORD)(szEnd-szHandlerDllName)+1;
+ bRet = FALSE;
+ }
+
+ if (*pdwHandlerNameLen > (DWORD)(szSlashEnd-szSlash) && (szSlashEnd-szSlash >= 0))
+ {
+ Checked::memcpy_s(szHandlerName, *pdwHandlerNameLen, szSlash, (szSlashEnd-szSlash));
+ szHandlerName[szSlashEnd-szSlash] = '\0';
+ *pdwHandlerNameLen = (DWORD)(szSlashEnd-szSlash);
+ }
+ else
+ {
+ *pdwHandlerNameLen = (DWORD)(szSlashEnd-szSlash)+1;
+ bRet = FALSE;
+ }
+
+ return bRet;
+}
+
+inline __checkReturn __success(return==HTTP_SUCCESS) HTTP_CODE _AtlLoadRequestHandler(
+ __in LPCSTR szDllPath,
+ __in LPCSTR szHandlerName,
+ __in IHttpServerContext *pServerContext,
+ __out HINSTANCE *phInstance,
+ __deref_out_opt IRequestHandler **ppHandler,
+ __in IIsapiExtension *pExtension,
+ __in IDllCache *pDllCache) throw(...)
+{
+ ATLENSURE(phInstance!=NULL);
+ ATLENSURE(ppHandler!=NULL);
+ ATLENSURE(pDllCache!=NULL);
+ *phInstance = NULL;
+ *ppHandler = NULL;
+
+ ATLServerDllInfo DllInfo;
+ DllInfo.pExtension = pExtension;
+ DllInfo.pContext = pServerContext;
+ if (!IsFullPathA(szDllPath))
+ {
+ CHAR szFileName[MAX_PATH];
+ if (!GetScriptFullFileName(szDllPath, szFileName, pServerContext))
+ {
+ return HTTP_FAIL;
+ }
+
+ *phInstance = pDllCache->Load(szFileName, (void *)&DllInfo);
+ }
+ else
+ {
+ *phInstance = pDllCache->Load(szDllPath, (void *)&DllInfo);
+ }
+ if (!*phInstance)
+ {
+ ATLTRACE( "LoadLibrary failed: '%s' with error: %d\r\n", szDllPath, GetLastError() );
+ return AtlsHttpError(500, ISE_SUBERR_LOADLIB);
+ }
+
+ CComPtr<IUnknown> spUnk;
+
+ if (!DllInfo.pfnGetHandler ||
+ !DllInfo.pfnGetHandler(szHandlerName, pExtension, &spUnk) ||
+ FAILED(spUnk->QueryInterface(ppHandler)))
+ {
+ pDllCache->Free(*phInstance);
+ *phInstance = NULL;
+ return AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND);
+ }
+
+ return HTTP_SUCCESS;
+} // _AtlLoadRequestHandler
+
+
+class CTransferServerContext : public CComObjectRootEx<CComMultiThreadModel>,
+ public CWrappedServerContext
+{
+public:
+ char m_szFileName[MAX_PATH];
+ char m_szQueryString[ATL_URL_MAX_PATH_LENGTH+1];
+ CStringA m_strUrl;
+ IWriteStream *m_pStream;
+
+ BEGIN_COM_MAP(CTransferServerContext)
+ COM_INTERFACE_ENTRY(IHttpServerContext)
+ END_COM_MAP()
+
+ CTransferServerContext() throw()
+ {
+ m_pStream = NULL;
+ }
+
+ BOOL Initialize(__in CTransferServerContext *pOtherContext)
+ {
+ ATLENSURE(pOtherContext!=NULL);
+ return Initialize(pOtherContext->m_strUrl, pOtherContext->m_pStream, pOtherContext->m_spParent);
+ }
+
+ BOOL Initialize(__in LPCSTR szUrl, __in IWriteStream *pStream, __in IHttpServerContext *pParent) throw()
+ {
+ m_pStream = pStream;
+ m_spParent = pParent;
+
+ _ATLTRY
+ {
+ m_strUrl = szUrl; // we store the URL in case we need to initialize another context from this context
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ long nUrlLen = m_strUrl.GetLength();
+ m_szFileName[0] = '\0';
+
+ if (!IsFullPathA(szUrl))
+ {
+ DWORD dwLen = MAX_PATH;
+ BOOL bRet = TRUE;
+ _ATLTRY
+ {
+ bRet = m_spParent->GetServerVariable(
+ "APPL_PHYSICAL_PATH",
+ m_szFileName,
+ &dwLen);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+
+ if (!bRet)
+ {
+ return bRet;
+ }
+ }
+
+ // check for query params
+ LPCSTR szMark = strchr(szUrl, '?');
+ if (szMark)
+ {
+ size_t nPathLen = szMark - szUrl;
+ size_t nLen;
+
+ if ((nPathLen >= 0) && (nPathLen < MAX_PATH))
+ {
+ nLen = strlen(m_szFileName) + nPathLen;
+ if (nLen < MAX_PATH)
+ {
+#pragma warning(push)
+#pragma warning(disable: 22008)
+ /* Prefast false warning about unbound nPathLen in the below fragment -
+ we already have necessary checks few lines above
+ */
+ if (m_szFileName[0])
+ {
+ Checked::strcat_s(m_szFileName, MAX_PATH-nLen, szUrl);
+ }
+ else
+ {
+ Checked::memcpy_s(m_szFileName, MAX_PATH, szUrl, nPathLen);
+ m_szFileName[nPathLen] = '\0';
+ }
+#pragma warning(pop)
+ }
+ else
+ {
+ return FALSE; // path would overwrite buffer
+ }
+ }
+ else
+ {
+ return FALSE; // path would overwrite buffer
+ }
+
+ // save query params
+ nLen = strlen(szMark + 1);
+ if (nLen < ATL_URL_MAX_PATH_LENGTH)
+ {
+ Checked::strcpy_s(m_szQueryString, ATL_URL_MAX_PATH_LENGTH, szMark+1);
+ }
+ else
+ {
+ return FALSE; // url would overwrite buffer
+ }
+ }
+ else
+ {
+ // no query string
+ size_t nLen = strlen(m_szFileName) + nUrlLen;
+ if (nLen < MAX_PATH)
+ {
+ if (m_szFileName[0])
+ {
+ Checked::strcat_s(m_szFileName, MAX_PATH-nLen, szUrl);
+ }
+ else
+ {
+ Checked::strcpy_s(m_szFileName, MAX_PATH, szUrl);
+ }
+ }
+ else
+ {
+ return FALSE; // path would be too long
+ }
+ m_szQueryString[0] = '\0';
+ }
+
+ return TRUE;
+ }
+ BOOL WriteClient(__in_bcount(*pdwBytes) void *pvBuffer, __inout DWORD *pdwBytes)
+ {
+ ATLENSURE(m_pStream != NULL);
+
+ HRESULT hr = S_OK;
+ _ATLTRY
+ {
+ m_pStream->WriteStream((LPCSTR) pvBuffer, *pdwBytes, pdwBytes);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ return SUCCEEDED(hr);
+ }
+
+ LPCSTR GetQueryString()
+ {
+ ATLASSUME(m_spParent);
+ return m_szQueryString;
+ }
+
+ LPCSTR GetScriptPathTranslated()
+ {
+ ATLASSUME(m_spParent);
+ return m_szFileName;
+ }
+
+ LPCSTR GetPathTranslated()
+ {
+ ATLASSUME(m_spParent);
+ return m_szFileName;
+ }
+
+ // Asynchronous writes will not work properly in a child handler
+ BOOL AsyncWriteClient(void * /*pvBuffer*/, DWORD * /*pdwBytes*/)
+ {
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+ // These next few methods are to protect against attempting to parse form data twice
+ // We tell the new handler that it was a GET request
+ LPCSTR GetRequestMethod()
+ {
+ ATLASSUME(m_spParent);
+ return "GET";
+ }
+
+ // The handler should not query these methods -- they are only useful if attempting to
+ // parse form data, which is not allowed in child handlers.
+ BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
+ {
+ return FALSE;
+ }
+
+ BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
+ {
+ return FALSE;
+ }
+
+ DWORD GetTotalBytes()
+ {
+ ATLASSERT(FALSE);
+ return 0;
+ }
+
+ DWORD GetAvailableBytes()
+ {
+ ATLASSERT(FALSE);
+ return 0;
+ }
+
+ BYTE *GetAvailableData()
+ {
+ ATLASSERT(FALSE);
+ return NULL;
+ }
+
+ LPCSTR GetContentType()
+ {
+ ATLASSERT(FALSE);
+ return NULL;
+ }
+};
+
+class CAllocContextBase
+{
+public:
+ virtual HTTP_CODE Alloc(IHttpServerContext **ppNewContext) = 0;
+};
+
+ATL_NOINLINE inline HTTP_CODE _AtlRenderInclude(
+ __in IHttpServerContext *pServerContextNew,
+ __in LPCSTR szFileName,
+ __in LPCSTR szQueryParams,
+ __in WORD wCodePage,
+ __in CAllocContextBase *pAllocContext,
+ __in IServiceProvider *pServiceProvider,
+ __in_opt IHttpRequestLookup *pLookup,
+ __inout_opt CStencilState* pState = NULL)
+{
+ ATLENSURE(pServiceProvider!=NULL);
+ AtlServerRequest* pRequestInfo = NULL;
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+
+ // get a pointer to the ISAPI extension
+ CComPtr<IIsapiExtension> spExtension;
+ if (S_OK != pServiceProvider->QueryInterface(&spExtension) ||
+ !spExtension)
+ {
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+ // get a pointer to the extension's dll cache
+ CComPtr<IDllCache> spDllCache;
+ if (S_OK != pServiceProvider->QueryService(__uuidof(IDllCache),
+ __uuidof(IDllCache),
+ (void**)&spDllCache) ||
+ !spDllCache)
+ {
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+#ifdef _DEBUG
+ bool bAsyncAllowed = false;
+#endif
+ if (pState && pState->pIncludeInfo)
+ {
+ pRequestInfo = pState->pIncludeInfo;
+ pState->pIncludeInfo = NULL;
+#ifdef _DEBUG
+ bAsyncAllowed = true;
+#endif
+ }
+ else
+ {
+ ATLASSERT(spDllCache);
+ ATLASSERT(spExtension);
+
+ pRequestInfo = spExtension->CreateRequest();
+ if (pRequestInfo == NULL)
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+
+ pRequestInfo->dwRequestState = ATLSRV_STATE_BEGIN;
+ pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
+ pRequestInfo->pDllCache = spDllCache;
+ pRequestInfo->pExtension = spExtension;
+ pRequestInfo->pServerContext = pServerContextNew;
+ if (pState && pState->pParentInfo)
+ {
+ pRequestInfo->pUserData = pState->pParentInfo->pUserData;
+ }
+
+ // Extract the file extension of the included file by searching
+ // for the first '.' from the right.
+ // Can't use _tcsrchr because we have to use the stencil's codepage
+ LPCSTR szDot = NULL;
+ LPCSTR szMark = szFileName;
+ ATLENSURE(szMark!=NULL);
+ while (*szMark)
+ {
+ if (*szMark == '.')
+ szDot = szMark;
+
+ LPCSTR szNext = CharNextExA(wCodePage, szMark, 0);
+ if (szNext == szMark)
+ {
+ // embedded null
+ pRequestInfo->pServerContext = NULL;
+ spExtension->FreeRequest(pRequestInfo);
+ return HTTP_FAIL;
+ }
+ szMark = szNext;
+ }
+
+ if (szDot && AsciiStricmp(szDot, c_AtlSRFExtension) == 0)
+ {
+ hcErr = spExtension->LoadDispatchFile(szFileName, pRequestInfo);
+ if (hcErr)
+ {
+ pRequestInfo->pServerContext = NULL;
+ spExtension->FreeRequest(pRequestInfo);
+ return hcErr;
+ }
+ }
+ else if (szDot && AsciiStricmp(szDot, c_AtlDLLExtension) == 0)
+ {
+ // Get the handler name if they used the asdf.dll?Handler=Default notation
+ char szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1] = { '\0' };
+
+ LPCSTR szStart = strstr(szQueryParams, "Handler=");
+ if (szStart &&
+ ((szStart == szQueryParams) ||
+ ((szStart > szQueryParams) && (*(szStart-1) == '&'))))
+ {
+ szStart += 8; // Skip past "Handler" and the "="
+ LPCSTR szEnd = strchr(szStart, '&');
+ if (szEnd)
+ {
+ Checked::memcpy_s(szHandlerName, ATL_MAX_HANDLER_NAME_LEN+1, szStart, __min((szEnd-szStart), ATL_MAX_HANDLER_NAME_LEN));
+ szHandlerName[__min((szEnd-szStart), ATL_MAX_HANDLER_NAME_LEN)] = '\0';
+ }
+ else
+ {
+ if (!SafeStringCopy(szHandlerName, szStart))
+ {
+ // handler name too long
+ pRequestInfo->pServerContext = NULL;
+ spExtension->FreeRequest(pRequestInfo);
+ return HTTP_FAIL;
+ }
+ }
+ }
+ else
+ {
+ ATLASSERT( ATL_MAX_HANDLER_NAME_LEN >= sizeof(ATL_HANDLER_NAME_DEFAULT) );
+ Checked::memcpy_s(szHandlerName, ATL_MAX_HANDLER_NAME_LEN+1, ATL_HANDLER_NAME_DEFAULT, sizeof(ATL_HANDLER_NAME_DEFAULT));
+ }
+
+ pRequestInfo->dwRequestType = ATLSRV_REQUEST_DLL;
+
+ hcErr = spExtension->LoadRequestHandler(szFileName, szHandlerName, pRequestInfo->pServerContext,
+ &pRequestInfo->hInstDll, &pRequestInfo->pHandler);
+ if (hcErr)
+ {
+ pRequestInfo->pServerContext = NULL;
+ spExtension->FreeRequest(pRequestInfo);
+ return hcErr;
+ }
+ }
+ else
+ {
+ // unknown extension
+ pRequestInfo->pServerContext = NULL;
+ spExtension->FreeRequest(pRequestInfo);
+ return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+ DWORD dwStatus;
+ hcErr = pRequestInfo->pHandler->GetFlags(&dwStatus);
+
+ if (hcErr)
+ {
+ pRequestInfo->pHandler->UninitializeHandler();
+ pRequestInfo->pServerContext = NULL;
+ spExtension->FreeRequest(pRequestInfo);
+ return hcErr;
+ }
+
+ if (dwStatus & (ATLSRV_INIT_USEASYNC | ATLSRV_INIT_USEASYNC_EX))
+ {
+#ifdef _DEBUG
+ bAsyncAllowed = true;
+#endif
+ ATLENSURE(pAllocContext!=NULL);
+ hcErr = pAllocContext->Alloc(&pRequestInfo->pServerContext);
+ if (hcErr)
+ {
+ pRequestInfo->pHandler->UninitializeHandler();
+ if (pRequestInfo->pServerContext == pServerContextNew)
+ {
+ pRequestInfo->pServerContext = NULL;
+ }
+ spExtension->FreeRequest(pRequestInfo);
+ return hcErr;
+ }
+ }
+
+ hcErr = pRequestInfo->pHandler->InitializeChild(pRequestInfo, pServiceProvider, pLookup);
+ if (hcErr)
+ {
+ pRequestInfo->pHandler->UninitializeHandler();
+ if (pRequestInfo->pServerContext == pServerContextNew)
+ {
+ pRequestInfo->pServerContext = NULL;
+ }
+ spExtension->FreeRequest(pRequestInfo);
+ return hcErr;
+ }
+
+ pRequestInfo->pfnHandleRequest = &IRequestHandler::HandleRequest;
+ }
+
+ if (pRequestInfo)
+ {
+ if (!hcErr)
+ {
+ ATLASSERT(pRequestInfo->pfnHandleRequest != NULL);
+ hcErr = (pRequestInfo->pHandler->*pRequestInfo->pfnHandleRequest)(pRequestInfo, pServiceProvider);
+
+#ifdef _DEBUG
+ // must use ATLSRV_INIT_USEASYNC to use ASYNC returns
+ if (IsAsyncStatus(hcErr))
+ {
+ ATLASSERT(bAsyncAllowed);
+ }
+#endif
+
+ if (IsAsyncStatus(hcErr))
+ {
+ ATLASSERT(pState); // state is required for async
+ if (IsAsyncContinueStatus(hcErr))
+ {
+ pState->pIncludeInfo = pRequestInfo;
+ pRequestInfo->dwRequestState = ATLSRV_STATE_CONTINUE;
+ }
+ else if (IsAsyncDoneStatus(hcErr))
+ {
+ pRequestInfo->pHandler->UninitializeHandler();
+ if (pRequestInfo->pServerContext == pServerContextNew)
+ {
+ pRequestInfo->pServerContext = NULL;
+ }
+ spExtension->FreeRequest(pRequestInfo);
+ }
+ }
+ else
+ {
+ pRequestInfo->pHandler->UninitializeHandler();
+ if (pRequestInfo->pServerContext == pServerContextNew)
+ {
+ pRequestInfo->pServerContext = NULL;
+ }
+ spExtension->FreeRequest(pRequestInfo);
+ }
+ }
+ }
+ else
+ {
+ hcErr = AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+ return hcErr;
+}
+
+// CAllocTransferAsyncContext is an unsupported implementation detail, used
+// for implementing _AtlTransferRequest.
+class CAllocTransferAsyncContext :
+ public CAllocContextBase
+{
+public:
+ CAllocTransferAsyncContext(CTransferServerContext *pInitialContext):
+ m_pInitialContext(pInitialContext)
+ {
+ }
+
+ HTTP_CODE Alloc(IHttpServerContext** ppNewContext)
+ {
+ if (!ppNewContext)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ *ppNewContext = NULL;
+
+ CComObjectNoLock<CTransferServerContext>* pServerContext = NULL;
+ ATLTRY(pServerContext = new CComObjectNoLock<CTransferServerContext>);
+ if (pServerContext == NULL)
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ pServerContext->Initialize(m_pInitialContext);
+ pServerContext->AddRef();
+ *ppNewContext = pServerContext;
+ return HTTP_SUCCESS;
+ }
+private:
+ CTransferServerContext *m_pInitialContext;
+};
+
+inline HTTP_CODE _AtlTransferRequest(
+ __in AtlServerRequest *pRequest,
+ __in IServiceProvider *pServiceProvider,
+ __in IWriteStream *pWriteStream,
+ __in_opt IHttpRequestLookup *pLookup,
+ __in LPCSTR szNewUrl,
+ __in WORD nCodePage,
+ __in bool bContinueAfterProcess,
+ __inout_opt CStencilState *pState)
+{
+ CComObjectStackEx<CTransferServerContext> serverContext;
+ if (!serverContext.Initialize(szNewUrl, pWriteStream, pRequest->pServerContext))
+ return AtlsHttpError(500, 0);
+ CAllocTransferAsyncContext AsyncAllocObj(&serverContext);
+ HTTP_CODE hcErr = _AtlRenderInclude(static_cast<IHttpServerContext*>(&serverContext),
+ serverContext.m_szFileName,
+ serverContext.m_szQueryString,
+ nCodePage,
+ &AsyncAllocObj,
+ pServiceProvider,
+ pLookup,
+ pState);
+ if (hcErr == HTTP_SUCCESS && bContinueAfterProcess)
+ return hcErr;
+ return HTTP_SUCCESS_NO_PROCESS;
+}
+
+//
+// This function is now deprecated. ATL is not using it anymore.
+//
+inline ATL_DEPRECATED("Do not use this function.")
+void __cdecl AtlsSecErrHandlerFunc(int /* nCode */, void * /* pv */)
+{
+ //
+ // a buffer overflow has occurred in your code
+ //
+ ATLASSERT( FALSE );
+
+ //
+ // terminate process (safest thing to do)
+ //
+ TerminateProcess( GetCurrentProcess(), 1 );
+}
+
+//
+// Class CIsapiExtension
+// The main ISAPI Extension implementation.
+// Template parameters
+// ThreadPoolClass: Specifies the thread pool that will be used by the
+// extension to queue incoming requests. CThreadPool is the
+// default and is declared and implemented in ATLUTIL.H. This class
+// templatizes on a worker thread class. The worker thread class
+// represents an abstraction of a thread that will be used to
+// process requests as they are dequeued from the pool's work queue.
+// You would change this parameter if you wanted to use a completely
+// different thread pool, or, more commonly, if you wanted to use
+// a different worker thread class. Request processing code can
+// access a pointer to the worker thread class, which allows the
+// request handling code to easily access per-thread data.
+// CRequestStatClass: Specifies the class to be used to track request statistics
+// CNoRequestStats is the default which is a noop class.
+// You would change this parameter to provide a class that will
+// track request statistics for you. ATL provides CStdRequestStats
+// and CPerfRequestStatObject but these classes should be used
+// with caution because they require interlocked operations to
+// keep track of request statistics which can affect server performance.
+// HttpUserErrorTextProvider: This class provides error text messages
+// and headers, including resource IDs of error messages to the
+// isapi extension's error handling functions. You would change this
+// parameter if you wanted to provide your own error headers and/or
+// messages in response to error encountered during request processing.
+// CPageCacheStats, CStencilCacheStats: These two classes are used to keep
+// statistics about the page and stencil caches. You could change these
+// paramters if you wanted to track statistics for these caches. ATL
+// provides CPerfStatClass and CStdStatClass to store the stat data but
+// using these classes can affect server performance because they use
+// interlocked operations internally to store the data.
+template < class ThreadPoolClass=CThreadPool<CIsapiWorker>,
+ class CRequestStatClass=CNoRequestStats,
+ class HttpUserErrorTextProvider=CDefaultErrorProvider,
+ class WorkerThreadTraits=DefaultThreadTraits,
+ class CPageCacheStats=CNoStatClass,
+ class CStencilCacheStats=CNoStatClass>
+class CIsapiExtension :
+ public IServiceProvider, public IIsapiExtension, public IRequestStats
+{
+private:
+
+#ifndef ATL_NO_CRITICAL_ISAPI_ERROR
+
+ DWORD m_dwCriticalIsapiError;
+
+#endif // ATL_NO_CRITICAL_ISAPI_ERROR
+
+protected:
+ DWORD m_dwTlsIndex;
+
+ typedef CWorkerThread<WorkerThreadTraits> extWorkerType;
+
+ extWorkerType m_WorkerThread;
+ ThreadPoolClass m_ThreadPool;
+
+ CDllCache<extWorkerType, CDllCachePeer> m_DllCache;
+ CFileCache<extWorkerType, CPageCacheStats, CPageCachePeer> m_PageCache;
+ CComObjectGlobal<CStencilCache<extWorkerType, CStencilCacheStats > > m_StencilCache;
+ HttpUserErrorTextProvider m_UserErrorProvider;
+ HANDLE m_hRequestHeap;
+ CComCriticalSection m_critSec;
+
+ // Dynamic services stuff
+ struct ServiceNode
+ {
+ HINSTANCE hInst;
+ IUnknown *punk;
+ GUID guidService;
+ IID riid;
+
+ ServiceNode() throw()
+ {
+ }
+
+ ServiceNode(const ServiceNode& that) throw()
+ :hInst(that.hInst), punk(that.punk), guidService(that.guidService), riid(that.riid)
+ {
+ }
+ };
+
+ class CServiceEqualHelper
+ {
+ public:
+ static bool IsEqual(__in const ServiceNode& t1, __in const ServiceNode& t2) throw()
+ {
+ return (InlineIsEqualGUID(t1.guidService, t2.guidService) != 0 &&
+ InlineIsEqualGUID(t1.riid, t2.riid) != 0);
+ }
+ };
+
+ CSimpleArray<ServiceNode, CServiceEqualHelper> m_serviceMap;
+
+public:
+ CWin32Heap m_heap;
+
+ CRequestStatClass m_reqStats;
+
+ AtlServerRequest *CreateRequest()
+ {
+ // Allocate a fixed block size to avoid fragmentation
+ AtlServerRequest *pRequest = (AtlServerRequest *) HeapAlloc(m_hRequestHeap,
+ HEAP_ZERO_MEMORY, __max(sizeof(AtlServerRequest), sizeof(_CComObjectHeapNoLock<CServerContext>)));
+ if (!pRequest)
+ return NULL;
+ pRequest->cbSize = sizeof(AtlServerRequest);
+
+ return pRequest;
+ }
+
+ void FreeRequest(__inout AtlServerRequest *pRequest)
+ {
+ _ReleaseAtlServerRequest(pRequest);
+ HeapFree(m_hRequestHeap, 0, pRequest);
+ }
+
+ CIsapiExtension() throw()
+ {
+ m_hRequestHeap = NULL;
+#ifdef _DEBUG
+ m_bDebug = FALSE;
+#endif
+
+#ifndef ATL_NO_CRITICAL_ISAPI_ERROR
+
+ m_dwCriticalIsapiError = 0;
+
+#endif // ATL_NO_CRITICAL_ISAPI_ERROR
+ }
+
+ HTTP_CODE TransferRequest(
+ __in AtlServerRequest *pRequest,
+ __in IServiceProvider *pServiceProvider,
+ __in IWriteStream *pWriteStream,
+ __in_opt IHttpRequestLookup *pLookup,
+ __in LPCSTR szNewUrl,
+ __in WORD nCodePage,
+ __in bool bContinueAfterProcess,
+ __inout_opt CStencilState *pState)
+ {
+ HTTP_CODE hcErr;
+ _ATLTRY
+ {
+ hcErr = _AtlTransferRequest(pRequest, pServiceProvider, pWriteStream,
+ pLookup, szNewUrl, nCodePage, bContinueAfterProcess, pState);
+ }
+ _ATLCATCHALL()
+ {
+ hcErr = HTTP_FAIL;
+ }
+ return hcErr;
+ }
+
+#ifndef ATL_NO_CRITICAL_ISAPI_ERROR
+
+ DWORD ReturnCriticalError(__in EXTENSION_CONTROL_BLOCK *pECB)
+ {
+
+ _ATLTRY
+ {
+ ATLENSURE(pECB);
+ UINT uResId = 0;
+ LPCSTR szHeader = NULL;
+
+ CStringA strStatus;
+ CStringA strBody;
+ CStringA strFormat;
+ CStringA strError;
+
+ DWORD dwErr = GetCriticalIsapiError();
+ if (!strError.LoadString(dwErr))
+ {
+ strError.Format("Unknown Error %d", dwErr);
+ }
+
+#ifdef ATL_CRITICAL_ISAPI_ERROR_LOGONLY
+ // we've logged the real error - don't send detailed internal info to the user
+ m_UserErrorProvider.GetErrorText(500,
+ SUBERR_NONE,
+ &szHeader,
+ &uResId);
+
+ if (!uResId || !strBody.LoadString(uResId))
+ {
+ strBody = "<html><body>A server error has occurred.</body></html>";
+ }
+#else
+ m_UserErrorProvider.GetErrorText(500,
+ ISE_SUBERR_ISAPISTARTUPFAILED,
+ &szHeader,
+ &uResId);
+ if (!uResId || !strFormat.LoadString(uResId))
+ {
+ strFormat = "<html><body>A critical error has occurred initializing this ISAPI extension: %s</body></html>";
+ }
+ strBody.Format(strFormat, strError);
+#endif
+ strStatus.Format("500 %s", szHeader);
+
+ HSE_SEND_HEADER_EX_INFO hex;
+ hex.pszStatus = (LPCSTR)strStatus;
+ hex.pszHeader = NULL;
+ hex.cchStatus = (DWORD)strStatus.GetLength();
+ hex.cchHeader = 0;
+ hex.fKeepConn = FALSE;
+
+ pECB->ServerSupportFunction(pECB->ConnID,
+ HSE_REQ_SEND_RESPONSE_HEADER_EX,
+ &hex,
+ NULL,
+ NULL);
+
+ DWORD dwBodyLen = strBody.GetLength();
+ pECB->WriteClient(pECB->ConnID,
+ (void *) (LPCSTR) strBody,
+ &dwBodyLen,
+ NULL);
+ }
+ _ATLCATCHALL()
+ {
+ return HSE_STATUS_ERROR;
+ }
+ return HSE_STATUS_SUCCESS;
+ }
+
+#endif // ATL_NO_CRITICAL_ISAPI_ERROR
+
+ DWORD HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) throw()
+ {
+
+#ifndef ATL_NO_CRITICAL_ISAPI_ERROR
+
+ if (GetCriticalIsapiError() != 0)
+ {
+ return ReturnCriticalError(lpECB);
+ }
+
+#endif // ATL_NO_CRITICAL_ISAPI_ERROR
+
+ AtlServerRequest *pRequestInfo = NULL;
+
+ _ATLTRY
+ {
+ pRequestInfo = CreateRequest();
+ if (pRequestInfo == NULL)
+ return HSE_STATUS_ERROR;
+
+ CServerContext *pServerContext = NULL;
+ ATLTRY(pServerContext = CreateServerContext(m_hRequestHeap));
+ if (pServerContext == NULL)
+ {
+ FreeRequest(pRequestInfo);
+ return HSE_STATUS_ERROR;
+ }
+
+ pServerContext->Initialize(lpECB);
+ pServerContext->AddRef();
+
+ pRequestInfo->pServerContext = pServerContext;
+ pRequestInfo->dwRequestType = ATLSRV_REQUEST_UNKNOWN;
+ pRequestInfo->dwRequestState = ATLSRV_STATE_BEGIN;
+ pRequestInfo->pExtension = static_cast<IIsapiExtension *>(this);
+ pRequestInfo->pDllCache = static_cast<IDllCache *>(&m_DllCache);
+#ifndef ATL_NO_MMSYS
+ pRequestInfo->dwStartTicks = timeGetTime();
+#else
+ pRequestInfo->dwStartTicks = GetTickCount();
+#endif
+ pRequestInfo->pECB = lpECB;
+
+ m_reqStats.OnRequestReceived();
+
+ if (m_ThreadPool.QueueRequest(pRequestInfo))
+ return HSE_STATUS_PENDING;
+
+ if (pRequestInfo != NULL)
+ {
+ FreeRequest(pRequestInfo);
+ }
+
+ }
+ _ATLCATCHALL()
+ {
+ }
+
+
+
+ return HSE_STATUS_ERROR;
+ }
+
+
+ BOOL QueueRequest(__in AtlServerRequest * pRequestInfo)
+ {
+ return m_ThreadPool.QueueRequest(pRequestInfo);
+ }
+
+ CIsapiWorker *GetThreadWorker()
+ {
+ return (CIsapiWorker *) TlsGetValue(m_dwTlsIndex);
+ }
+
+ BOOL SetThreadWorker(__in CIsapiWorker *pWorker)
+ {
+ return TlsSetValue(m_dwTlsIndex, (void*)pWorker);
+ }
+
+ // Configuration functions -- override in base class if another value is desired
+ virtual LPCSTR GetExtensionDesc() throw() { return "VC Server Classes"; }
+ virtual int GetNumPoolThreads() throw() { return 0; }
+ virtual int GetPoolStackSize() throw() { return 0; }
+ virtual HANDLE GetIOCompletionHandle() throw() { return INVALID_HANDLE_VALUE; }
+ virtual DWORD GetDllCacheTimeout() throw() { return ATL_DLL_CACHE_TIMEOUT; }
+ virtual DWORD GetStencilCacheTimeout() throw() { return ATL_STENCIL_CACHE_TIMEOUT; }
+ virtual LONGLONG GetStencilLifespan() throw() { return ATL_STENCIL_LIFESPAN; }
+
+ BOOL OnThreadAttach()
+ {
+ return SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
+ }
+
+ void OnThreadTerminate()
+ {
+ CoUninitialize();
+ }
+
+#ifndef ATL_NO_CRITICAL_ISAPI_ERROR
+
+ BOOL SetCriticalIsapiError(__in DWORD dwErr = 1) throw()
+ {
+ m_dwCriticalIsapiError = dwErr;
+
+ // send the error to the event log
+ _ATLTRY
+ {
+ CStringA strBody;
+ CStringA strFormat;
+ CStringA strError;
+
+ // format an error message
+ if (!strError.LoadString(dwErr))
+ {
+ strError.Format("Unknown Error %d", dwErr);
+ }
+
+ if (!strFormat.LoadString(IDS_ATLSRV_CRITICAL_LOGMESSAGE))
+ {
+ strFormat = "A critical error has occurred initializing the ISAPI extension: %s";
+ }
+ strBody.Format(strFormat, strError);
+
+ // take the base module name as the app name
+ CPath path;
+ {
+ CStrBuf buf(path, MAX_PATH);
+ DWORD dwLen = ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(), buf, MAX_PATH);
+ if (dwLen == MAX_PATH)
+ buf.SetLength(MAX_PATH);
+ }
+ path.StripPath();
+
+ // log the event
+ HANDLE h = RegisterEventSource(NULL, path);
+ if (h)
+ {
+ LPCSTR szBody = strBody;
+ ReportEventA(h, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 1, 0, &szBody, NULL);
+ DeregisterEventSource(h);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+
+ return TRUE;
+ }
+
+ DWORD GetCriticalIsapiError() throw()
+ {
+ return m_dwCriticalIsapiError;
+ }
+
+#else
+
+ BOOL SetCriticalIsapiError(__in DWORD dwErr = 1) throw()
+ {
+ dwErr; // not used
+ return FALSE;
+ }
+
+ DWORD GetCriticalIsapiError() throw()
+ {
+ return 0;
+ }
+
+#endif // ATL_NO_CRITICAL_ISAPI_ERROR
+
+
+ BOOL GetExtensionVersion(__out HSE_VERSION_INFO* pVer) throw()
+ {
+ ATLASSERT(pVer!=NULL);
+ if(pVer==NULL)
+ {
+ return FALSE;
+ }
+ // allocate a Tls slot for storing per thread data
+ m_dwTlsIndex = TlsAlloc();
+
+ // create a private heap for request data
+ // this heap has to be thread safe to allow for
+ // async processing of requests
+ m_hRequestHeap = HeapCreate(0, 0, 0);
+ if (!m_hRequestHeap)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Failed creating request heap. Using process heap\n"));
+ m_hRequestHeap = GetProcessHeap();
+ if (!m_hRequestHeap)
+ {
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED);
+ }
+
+ }
+
+ // create a private heap (synchronized) for
+ // allocations. This reduces fragmentation overhead
+ // as opposed to the process heap
+ HANDLE hHeap = HeapCreate(0, 0, 0);
+ if (!hHeap)
+ {
+ ATLTRACE(atlTraceISAPI, 0, _T("Failed creating extension heap. Using process heap\n"));
+ hHeap = GetProcessHeap();
+ m_heap.Attach(hHeap, false);
+ }
+ else
+ {
+ m_heap.Attach(hHeap, true);
+ }
+ hHeap = NULL;
+
+ if (S_OK != m_reqStats.Initialize())
+ {
+ ATLTRACE(atlTraceISAPI,
+ 0,
+ _T("Initialization failed for request statistics perfmon support.\n")
+ _T("Check request statistics perfmon dll registration\n") );
+ }
+
+ if (S_OK != m_WorkerThread.Initialize())
+ {
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_WORKERINITFAILED);
+ }
+
+ if (m_critSec.Init() != S_OK)
+ {
+ HRESULT hrIgnore=m_WorkerThread.Shutdown();
+ (hrIgnore);
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_CRITSECINITFAILED);
+ }
+
+ if (S_OK != m_ThreadPool.Initialize(static_cast<IIsapiExtension*>(this), GetNumPoolThreads(), GetPoolStackSize(), GetIOCompletionHandle()))
+ {
+ HRESULT hrIgnore=m_WorkerThread.Shutdown();
+ (hrIgnore);
+ m_critSec.Term();
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_THREADPOOLFAILED);
+ }
+
+ if (FAILED(m_DllCache.Initialize(&m_WorkerThread, GetDllCacheTimeout())))
+ {
+ HRESULT hrIgnore=m_WorkerThread.Shutdown();
+ (hrIgnore);
+ m_ThreadPool.Shutdown();
+ m_critSec.Term();
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_DLLCACHEFAILED);
+ }
+
+ if (FAILED(m_PageCache.Initialize(&m_WorkerThread)))
+ {
+ HRESULT hrIgnore=m_WorkerThread.Shutdown();
+ (hrIgnore);
+ m_ThreadPool.Shutdown();
+ m_DllCache.Uninitialize();
+ m_critSec.Term();
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_PAGECACHEFAILED);
+ }
+
+ if (S_OK != m_StencilCache.Initialize(static_cast<IServiceProvider*>(this),
+ &m_WorkerThread,
+ GetStencilCacheTimeout(),
+ GetStencilLifespan()))
+ {
+ HRESULT hrIgnore=m_WorkerThread.Shutdown();
+ (hrIgnore);
+ m_ThreadPool.Shutdown();
+ m_DllCache.Uninitialize();
+ m_PageCache.Uninitialize();
+ m_critSec.Term();
+ return SetCriticalIsapiError(IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED);
+ }
+
+ pVer->dwExtensionVersion = HSE_VERSION;
+ Checked::strncpy_s(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN, GetExtensionDesc(), _TRUNCATE);
+ pVer->lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN - 1] = '\0';
+
+ return TRUE;
+ }
+
+ BOOL TerminateExtension(DWORD /*dwFlags*/) throw()
+ {
+ m_critSec.Lock();
+ for (int i=0; i < m_serviceMap.GetSize(); i++)
+ {
+ ATLASSUME(m_serviceMap[i].punk != NULL);
+ if(m_serviceMap[i].punk != NULL)
+ {
+ m_serviceMap[i].punk->Release();
+ }
+ m_DllCache.ReleaseModule(m_serviceMap[i].hInst);
+ }
+ m_critSec.Unlock();
+
+ m_ThreadPool.Shutdown();
+ m_StencilCache.Uninitialize();
+ m_DllCache.Uninitialize();
+ m_PageCache.Uninitialize();
+ HRESULT hrShutdown=m_WorkerThread.Shutdown();
+ m_reqStats.Uninitialize();
+ m_critSec.Term();
+
+ // free the request heap
+ if (m_hRequestHeap != GetProcessHeap())
+ HeapDestroy(m_hRequestHeap);
+
+ // free the Tls slot that we allocated
+ TlsFree(m_dwTlsIndex);
+
+ return SUCCEEDED(hrShutdown);
+ }
+
+ static void WINAPI AsyncCallback(LPEXTENSION_CONTROL_BLOCK /*lpECB*/,
+ __in PVOID pContext,
+ __in DWORD cbIO,
+ __in DWORD dwError) throw(...)
+ {
+ AtlServerRequest *pRequestInfo = reinterpret_cast<AtlServerRequest*>(pContext);
+ ATLENSURE(pRequestInfo);
+ if (pRequestInfo->m_hMutex)
+ {
+ // synchronize in case the previous async_noflush call isn't finished
+ // setting up state for the next call.
+ DWORD dwStatus = WaitForSingleObject(pRequestInfo->m_hMutex, ATLS_ASYNC_MUTEX_TIMEOUT);
+ if (dwStatus != WAIT_OBJECT_0 && dwStatus != WAIT_ABANDONED)
+ {
+ _ATLTRY
+ {
+ pRequestInfo->pExtension->RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE(_T("Warning: Uncaught user exception thrown and caught in AsyncCallback.\n"));
+ _ATLRETHROW;
+ }
+ return;
+ }
+ }
+
+ if (pRequestInfo->pfnAsyncComplete != NULL)
+ ATLTRY((*pRequestInfo->pfnAsyncComplete)(pRequestInfo, cbIO, dwError));
+
+ if (pRequestInfo->dwRequestState == ATLSRV_STATE_DONE)
+ {
+ pRequestInfo->pExtension->RequestComplete(pRequestInfo, HTTP_ERROR_CODE(HTTP_SUCCESS), 0);
+ }
+ else if (pRequestInfo->dwRequestState == ATLSRV_STATE_CACHE_DONE)
+ {
+ CloseHandle(pRequestInfo->hFile);
+ pRequestInfo->pFileCache->ReleaseFile(pRequestInfo->hEntry);
+ pRequestInfo->pExtension->RequestComplete(pRequestInfo, HTTP_ERROR_CODE(HTTP_SUCCESS), 0);
+ }
+ else
+ {
+ HANDLE hMutex = pRequestInfo->m_hMutex;
+ pRequestInfo->pExtension->QueueRequest(pRequestInfo);
+ if (hMutex)
+ ReleaseMutex(hMutex);
+ }
+ }
+
+ void HandleError(__in IHttpServerContext *pServerContext, __in DWORD dwStatus, __in DWORD dwSubStatus) throw()
+ {
+ RenderError(pServerContext, dwStatus, dwSubStatus, &m_UserErrorProvider);
+ }
+
+ void RequestComplete(__inout AtlServerRequest *pRequestInfo, __in DWORD dwStatus, __in DWORD dwSubStatus)
+ {
+ ATLASSERT(pRequestInfo);
+
+ if (pRequestInfo->pHandler != NULL)
+ pRequestInfo->pHandler->UninitializeHandler();
+
+ DWORD dwReqStatus = dwStatus;
+ if (!dwReqStatus)
+ dwReqStatus = 200;
+
+ if (dwStatus >= 400)
+ {
+ if (dwSubStatus != SUBERR_NO_PROCESS)
+ HandleError(pRequestInfo->pServerContext, dwStatus, dwSubStatus);
+ m_reqStats.RequestHandled(pRequestInfo, FALSE);
+ }
+ else
+ m_reqStats.RequestHandled(pRequestInfo, TRUE);
+
+ CComPtr<IHttpServerContext> spServerContext = pRequestInfo->pServerContext;
+
+ FreeRequest(pRequestInfo);
+
+ spServerContext->DoneWithSession(dwReqStatus);
+ }
+
+ HTTP_CODE GetHandlerName(__in LPCSTR szFileName, __out_ecount(MAX_PATH+ATL_MAX_HANDLER_NAME+2) LPSTR szHandlerName) throw()
+ {
+ return _AtlGetHandlerName(szFileName, szHandlerName);
+ }
+
+ HTTP_CODE LoadDispatchFile(__in LPCSTR szFileName, __out AtlServerRequest *pRequestInfo)
+ {
+ ATLASSERT(szFileName);
+ ATLASSERT(pRequestInfo);
+
+ CStencil *pStencil = NULL;
+ HCACHEITEM hStencil = NULL;
+
+ // Must have space for the path to the handler + the maximum size
+ // of the handler, plus the '/' plus the '\0'
+ CHAR szDllPath[MAX_PATH];
+ CHAR szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1];
+
+ pRequestInfo->pHandler = NULL;
+ pRequestInfo->hInstDll = NULL;
+
+ m_StencilCache.LookupStencil(szFileName, &hStencil);
+
+ // Stencil was found, check to see if it needs to be refreshed
+ if (hStencil)
+ {
+ m_StencilCache.GetStencil(hStencil, (void **) &pStencil);
+ pStencil->GetHandlerName(szDllPath, MAX_PATH, szHandlerName, ATL_MAX_HANDLER_NAME_LEN + 1);
+
+ CFileTime cftCurr;
+ CFileTime cftLastChecked;
+ cftCurr = CFileTime::GetCurrentTime();
+
+ pStencil->GetLastChecked(&cftLastChecked);
+
+ CFileTimeSpan span(ATL_STENCIL_CHECK_TIMEOUT * CFileTime::Millisecond);
+
+ if (cftLastChecked + span < cftCurr)
+ {
+ CComPtr<IStencilCacheControl> spCacheCtrl;
+ m_StencilCache.QueryInterface(__uuidof(IStencilCacheControl), reinterpret_cast<void**>(&spCacheCtrl));
+ if (spCacheCtrl)
+ {
+ CFileTime cftLastModified;
+ pStencil->GetLastModified(&cftLastModified);
+
+ // Resource based stencils have a last modified filetime of 0
+ if (cftLastModified != 0)
+ {
+ // for file base stencils, we check whether the file
+ // has been modified since being put in the cache
+ WIN32_FILE_ATTRIBUTE_DATA fad;
+ pStencil->SetLastChecked(&cftCurr);
+ BOOL bRet = GetFileAttributesExA(szFileName, GetFileExInfoStandard, &fad);
+
+ if ((bRet && cftLastModified < fad.ftLastWriteTime) ||
+ !bRet)
+ {
+ // the file has changed or an error has occurred trying to read the file,
+ // so remove it from the cache and force a reload
+ spCacheCtrl->RemoveStencil(hStencil);
+ pStencil = NULL;
+ hStencil = NULL;
+ }
+ }
+ }
+ }
+ }
+
+
+ if (!hStencil)
+ {
+ CHAR szHandlerDllName[MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+1];
+ *szHandlerDllName = '\0';
+
+ // not in the cache, so open the file
+ HTTP_CODE hcErr = GetHandlerName(szFileName, szHandlerDllName);
+ if (hcErr)
+ return hcErr;
+ DWORD dwDllPathLen = MAX_PATH;
+ DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
+ if (!_AtlCrackHandler(szHandlerDllName, szDllPath, &dwDllPathLen, szHandlerName, &dwHandlerNameLen))
+ {
+ return AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND);
+ }
+ ATLASSERT(*szHandlerName);
+ ATLASSERT(*szDllPath);
+ if (!*szHandlerName)
+ {
+ return AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND);
+ }
+ }
+ else
+ {
+ m_StencilCache.ReleaseStencil(hStencil);
+ }
+
+
+ return LoadRequestHandler(szDllPath, szHandlerName, pRequestInfo->pServerContext,
+ &pRequestInfo->hInstDll, &pRequestInfo->pHandler);
+ }
+
+ HTTP_CODE LoadDllHandler(__in LPCSTR szFileName, __in AtlServerRequest *pRequestInfo)
+ {
+ ATLASSERT(szFileName);
+ ATLENSURE(pRequestInfo);
+
+ _ATLTRY
+ {
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+ CHAR szHandler[ATL_MAX_HANDLER_NAME_LEN+1] = { 'D', 'e', 'f', 'a', 'u', 'l', 't', '\0' };
+ LPCSTR szQueryString = pRequestInfo->pServerContext->GetQueryString();
+ if (szQueryString != NULL)
+ {
+ LPCSTR szHdlr = strstr(szQueryString, "Handler=");
+ if (szHdlr != NULL)
+ {
+ if ((szHdlr == szQueryString) ||
+ ((szHdlr > szQueryString) && (*(szHdlr-1) == '&')))
+ {
+ int nCnt = 0;
+ LPSTR pszHandler = szHandler;
+ szHdlr += sizeof("Handler=")-1;
+ while (*szHdlr && *szHdlr != '&')
+ {
+ if (nCnt < ATL_MAX_HANDLER_NAME_LEN)
+ {
+ *pszHandler++ = *szHdlr++;
+ nCnt++;
+ }
+ else
+ {
+ hcErr = AtlsHttpError(500, ISE_SUBERR_HANDLER_NOT_FOUND);
+ break;
+ }
+ }
+ if (hcErr == HTTP_SUCCESS)
+ {
+ *pszHandler = '\0';
+ }
+ }
+ }
+ }
+
+ if (hcErr == HTTP_SUCCESS)
+ {
+ CHAR szFile[MAX_PATH+ATL_MAX_HANDLER_NAME_LEN+1];
+ if (SafeStringCopy(szFile, szFileName))
+ {
+ hcErr = LoadRequestHandler(szFile, szHandler, pRequestInfo->pServerContext, &pRequestInfo->hInstDll, &pRequestInfo->pHandler);
+ }
+ else
+ {
+ hcErr = AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ }
+ }
+
+ return hcErr;
+ }
+ _ATLCATCHALL()
+ {
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ }
+ }
+
+#pragma warning(push)
+#pragma warning(disable: 6014)
+ virtual __success(return) __checkReturn BOOL GetCacheServerContext(__in AtlServerRequest *pRequestInfo, __in IFileCache *pCache, __deref_out_opt IHttpServerContext **pCacheCtx)
+ {
+ ATLENSURE(pCacheCtx);
+ *pCacheCtx = NULL;
+
+ CComObjectNoLock<CCacheServerContext> *pCacheServerContext = NULL;
+ ATLTRY(pCacheServerContext = new CComObjectNoLock<CCacheServerContext>);
+ if (!pCacheServerContext)
+ return FALSE;
+
+ if (!pCacheServerContext->Initialize(pRequestInfo->pServerContext, pCache))
+ {
+ delete pCacheServerContext;
+ return FALSE;
+ }
+
+ pCacheServerContext->QueryInterface(__uuidof(IHttpServerContext), (void **) pCacheCtx);
+
+ if (*pCacheCtx)
+ return TRUE;
+
+ delete pCacheServerContext;
+ return FALSE;
+ }
+#pragma warning(pop)
+
+ virtual BOOL TransmitFromCache(__in AtlServerRequest* pRequestInfo, __out BOOL *pbAllowCaching)
+ {
+ ATLENSURE(pRequestInfo);
+ ATLENSURE(pbAllowCaching);
+
+ *pbAllowCaching = TRUE;
+
+ _ATLTRY
+ {
+ if (strcmp(pRequestInfo->pServerContext->GetRequestMethod(), "GET"))
+ return FALSE;
+
+ char szUrl[ATL_URL_MAX_URL_LENGTH + 1];
+ LPCSTR szPathInfo = pRequestInfo->pServerContext->GetPathInfo();
+ LPCSTR szQueryString = pRequestInfo->pServerContext->GetQueryString();
+
+ int nSize = 0;
+ LPSTR szTo = szUrl;
+ ATLENSURE(szPathInfo!=NULL);
+ while (*szPathInfo && nSize < ATL_URL_MAX_URL_LENGTH)
+ {
+ *szTo++ = *szPathInfo++;
+ nSize++;
+ }
+ if (nSize >= ATL_URL_MAX_URL_LENGTH)
+ {
+ return FALSE;
+ }
+ *szTo++ = '?';
+ nSize++;
+ ATLENSURE(szQueryString!=NULL);
+ while (*szQueryString && nSize < ATL_URL_MAX_URL_LENGTH)
+ {
+ *szTo++ = *szQueryString++;
+ nSize++;
+ }
+ if (nSize >= ATL_URL_MAX_URL_LENGTH)
+ {
+ return FALSE;
+ }
+ *szTo = '\0';
+
+ HCACHEITEM hEntry;
+
+ if (S_OK == m_PageCache.LookupFile(szUrl, &hEntry))
+ {
+ LPSTR szFileName;
+ CPageCachePeer::PeerInfo *pInfo;
+ m_PageCache.GetFile(hEntry, &szFileName, (void **)&pInfo);
+ ATLENSURE(pInfo);
+ CAtlFile file;
+ HRESULT hr = E_FAIL;
+
+ CA2CTEX<MAX_PATH> strFile(szFileName);
+ hr = file.Create(strFile,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED);
+
+ if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
+ {
+ m_PageCache.ReleaseFile(hEntry);
+ *pbAllowCaching = FALSE;
+ return FALSE;
+ }
+
+ pRequestInfo->pServerContext->SendResponseHeader(
+ pInfo->strHeader, pInfo->strStatus, FALSE);
+ HANDLE hFile = file.Detach();
+ BOOL bRet = FALSE;
+
+ pRequestInfo->dwRequestState = ATLSRV_STATE_CACHE_DONE;
+ pRequestInfo->hFile = hFile;
+ pRequestInfo->hEntry = hEntry;
+ pRequestInfo->pFileCache = &m_PageCache;
+
+ bRet = pRequestInfo->pServerContext->TransmitFile(
+ hFile, // The file to transmit
+ AsyncCallback, pRequestInfo, // The async callback and context
+ pInfo->strStatus, // HTTP status code
+ 0, // Send entire file
+ 0, // Start at the beginning of the file
+ NULL, 0, // Head and length
+ NULL, 0, // Tail and length
+ HSE_IO_ASYNC | HSE_IO_DISCONNECT_AFTER_SEND | HSE_IO_NODELAY // Send asynchronously
+ );
+
+ if (!bRet)
+ {
+ m_PageCache.ReleaseFile(hEntry);
+ CloseHandle(hFile);
+ *pbAllowCaching = FALSE;
+ return FALSE;
+ }
+ return TRUE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+
+ return FALSE;
+ }
+
+#if defined(_DEBUG) || defined(ATLS_ENABLE_DEBUGGING)
+
+ BOOL m_bDebug;
+ // F5 debugging support for VS7
+ BOOL ProcessDebug(__inout AtlServerRequest *pRequestInfo)
+{
+ ATLENSURE(pRequestInfo);
+ static GUID clsidDebugger[] = {
+ {0x70F65411, 0xFE8C, 0x4248, {0xBC,0xFF,0x70,0x1C,0x8B,0x2F,0x45,0x29}}, // debugger clsid
+ {0x62A78AC2, 0x7D9A, 0x4377, {0xB9,0x7E,0x69,0x65,0x91,0x9F,0xDD,0x02}}, // reserved
+ {0xCC23651F, 0x4574, 0x438F, {0xB4,0xAA,0xBC,0xB2,0x8B,0x6B,0x3E,0xCF}}, // reserved
+ {0xDBFDB1D0, 0x04A4, 0x4315, {0xB1,0x5C,0xF8,0x74,0xF6,0xB6,0xE9,0x0B}}, // reserved
+ {0xA4FCB474, 0x2687, 0x4924, {0xB0,0xAD,0x7C,0xAF,0x33,0x1D,0xB8,0x26}}, // reserved
+ {0xBEB261F6, 0xD5F0, 0x43BA, {0xBA,0xF4,0x8B,0x79,0x78,0x5F,0xFF,0xAF}}, // reserved
+ {0x8E2F5E28, 0xD4E2, 0x44C0, {0xAA,0x02,0xF8,0xC5,0xBE,0xB7,0x0C,0xAC}}, // reserved
+ {0x08100915, 0x0F41, 0x4CCF, {0x95,0x64,0xEB,0xAA,0x5D,0x49,0x44,0x6C}} // reserved
+ };
+ _ATLTRY
+ {
+ if (!_stricmp(pRequestInfo->pServerContext->GetRequestMethod(), "debug"))
+ {
+ // Debugger must be able to validate the client we are impersonating
+ // on an NT Domain so the client needs to use either NTLM or Negotiate.
+ DWORD dwAuthTypeSize = 64;
+ char szAuthType[64] = { 0 };
+
+ if ( !pRequestInfo->pServerContext->GetServerVariable("AUTH_TYPE", szAuthType, &dwAuthTypeSize) )
+ {
+ // error retrieving authentication type
+ RequestComplete(pRequestInfo, 501, 0);
+ return FALSE;
+ }
+
+ // if it's empty or not NTLM or negotiate we fail.
+ if ( !( *szAuthType != '\0 ' &&
+ ( !_stricmp(szAuthType, "NTLM") ||
+ !_stricmp(szAuthType, "Negotiate"))
+ ) )
+ {
+ // wrong authorization type or not authorized
+ RequestComplete(pRequestInfo, 401, 0);
+ return FALSE;
+ }
+
+ DWORD dwHeadersLen = 0;
+ CStringA strHeaders;
+ pRequestInfo->pServerContext->GetServerVariable("ALL_HTTP", NULL, &dwHeadersLen);
+ BOOL bRet = pRequestInfo->pServerContext->GetServerVariable("ALL_HTTP", strHeaders.GetBuffer(dwHeadersLen), &dwHeadersLen);
+ if (!bRet)
+ {
+ RequestComplete(pRequestInfo, 501, 0);
+ return FALSE;
+ }
+ strHeaders.ReleaseBuffer(dwHeadersLen - 1);
+ LPCSTR szCur = strHeaders;
+
+ while(*szCur)
+ {
+ if (!strncmp(szCur, "HTTP_COMMAND:", 13))
+ {
+ szCur += 13;
+ break;
+ }
+
+ szCur = strchr(szCur, '\n');
+ if (!szCur)
+ {
+ RequestComplete(pRequestInfo, 501, 0);
+ return FALSE;
+ }
+
+ szCur++;
+ }
+
+
+ if (!_strnicmp(szCur, "start-debug", sizeof("start-debug")-sizeof('\0')))
+ {
+ CCritSecLock Lock(m_critSec.m_sec);
+ if (m_bDebug)
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_ALREADY_DEBUGGING);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_ALREADY_DEBUGGING); // Already being debugged by another process
+ return FALSE;
+ }
+ CHttpRequest HttpRequest;
+ HttpRequest.Initialize(pRequestInfo->pServerContext);
+ HttpRequest.InitFromPost();
+ LPCSTR szString;
+ szString = HttpRequest.FormVars.Lookup("DebugSessionID");
+ if (!szString || !*szString)
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_INVALID_SESSION);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_INVALID_SESSION);
+ return FALSE;
+ }
+ CA2W szSessionID(szString);
+ if (!szSessionID)
+ {
+ HandleError(pRequestInfo->pServerContext, 500, ISE_SUBERR_OUTOFMEM);
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_OUTOFMEM);
+ return FALSE;
+ }
+ DWORD dwPid = GetCurrentProcessId();
+ LPWSTR szPoint = szSessionID;
+ while (szPoint && *szPoint && wcsncmp(szPoint, L"autoattachclsid=", 16))
+ {
+ szPoint = wcschr(szPoint, ';');
+ if (szPoint)
+ szPoint++;
+ }
+
+ if (!szPoint || !*szPoint)
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_BAD_ID);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_BAD_ID);
+ return FALSE;
+ }
+
+ szPoint += (sizeof("autoattachclsid=") - 1);
+ WCHAR szClsid[39];
+ szClsid[38] = '\0';
+ Checked::wcsncpy_s(szClsid, _countof(szClsid), szPoint, _TRUNCATE);
+ if (szClsid[38] != '\0')
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_BAD_ID);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_BAD_ID);
+ return FALSE;
+ }
+ szClsid[38] = '\0';
+
+ CLSID clsidDebugAutoAttach = CLSID_NULL;
+ HRESULT hr = CLSIDFromString(szClsid, &clsidDebugAutoAttach);
+
+ if (hr != S_OK)
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_BAD_ID);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_BAD_ID);
+ return FALSE;
+ }
+
+ size_t i=0,
+ nArrSize = sizeof(clsidDebugger)/sizeof(GUID);
+ for (i=0; i<nArrSize; i++)
+ {
+ if( InlineIsEqualGUID(clsidDebugAutoAttach, clsidDebugger[i]) )
+ break;
+ }
+ if (i >= nArrSize)
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_BAD_ID);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_BAD_ID);
+ return FALSE;
+ }
+
+ CComPtr<IDebugAutoAttach> spDebugAutoAttach;
+ hr = CoCreateInstance(clsidDebugAutoAttach, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER, __uuidof(IDebugAutoAttach), (void**)&spDebugAutoAttach);
+ if (FAILED(hr))
+ {
+ if (hr == E_ACCESSDENIED)
+ RequestComplete(pRequestInfo, 401, 0);
+ else
+ {
+ HandleError(pRequestInfo->pServerContext, 204, DBG_SUBERR_COCREATE);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_COCREATE);
+ }
+ return FALSE;
+ }
+ hr = spDebugAutoAttach->AutoAttach(GUID_NULL, dwPid, AUTOATTACH_PROGRAM_WIN32, 0, szSessionID);
+ if (FAILED(hr))
+ {
+ char szRetBuf[256];
+ int nLen = sprintf_s(szRetBuf, _countof(szRetBuf), "204 HRESULT=0x%.08X;ErrorString=Unable to attach to worker process", hr);
+ if (nLen > 0)
+ {
+ DWORD dwLen = nLen;
+ pRequestInfo->pServerContext->SendResponseHeader(NULL, szRetBuf, FALSE);
+ pRequestInfo->pServerContext->WriteClient(szRetBuf, &dwLen);
+ RequestComplete(pRequestInfo, 204, DBG_SUBERR_ATTACH);
+ }
+ return FALSE;
+ }
+ m_bDebug = TRUE;
+ HandleError(pRequestInfo->pServerContext, 200, SUBERR_NONE);
+ RequestComplete(pRequestInfo, 200, SUBERR_NONE);
+ return FALSE;
+ }
+ else if (!_strnicmp(szCur, "stop-debug", sizeof("stop-debug")-sizeof('\0')))
+ {
+ m_bDebug = FALSE;
+ HandleError(pRequestInfo->pServerContext, 200, SUBERR_NONE);
+ RequestComplete(pRequestInfo, 200, SUBERR_NONE);
+ return FALSE;
+ }
+ else
+ {
+ RequestComplete(pRequestInfo, 501, SUBERR_NONE); // Not Implemented
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+#endif // defined(_DEBUG) || defined(ATLS_ENABLE_DEBUGGING)
+
+ BOOL DispatchStencilCall(__inout AtlServerRequest *pRequestInfo)
+ {
+ ATLENSURE(pRequestInfo!=NULL);
+ CSetThreadToken sec;
+
+ m_reqStats.OnRequestDequeued();
+
+ if (!sec.Initialize(pRequestInfo))
+ {
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_IMPERSONATIONFAILED);
+ return FALSE;
+ }
+
+#if defined(_DEBUG) || defined(ATLS_ENABLE_DEBUGGING)
+ if (!ProcessDebug(pRequestInfo))
+ return TRUE;
+#endif // defined(ATLS_ENABLE_DEBUGGING)
+
+ if (pRequestInfo->m_hMutex)
+ {
+ // synchronize in case the previous async_noflush call isn't finished
+ // setting up state for the next call.
+ DWORD dwStatus = WaitForSingleObject(pRequestInfo->m_hMutex, ATLS_ASYNC_MUTEX_TIMEOUT);
+ if (dwStatus != WAIT_OBJECT_0 && dwStatus != WAIT_ABANDONED)
+ {
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
+ return FALSE;
+ }
+ }
+
+#ifdef _DEBUG
+ bool bAsyncAllowed = false;
+#endif
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+ if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
+ {
+ BOOL bAllowCaching = TRUE;
+ if (TransmitFromCache(pRequestInfo, &bAllowCaching)) // Page is in the cache, send it and bail
+ { // Async Callback will handle freeing pRequestInfo
+ return TRUE;
+ }
+
+ // get the srf filename
+ LPCSTR szFileName = pRequestInfo->pServerContext->GetScriptPathTranslated();
+
+ if (!szFileName)
+ {
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
+ return FALSE;
+ }
+
+ LPCSTR szDot = szFileName + strlen(szFileName) - 1;
+
+ // load a handler
+ if (AsciiStricmp(szDot - ATLS_EXTENSION_LEN, c_AtlSRFExtension) == 0)
+ {
+ pRequestInfo->dwRequestType = ATLSRV_REQUEST_STENCIL;
+ hcErr = LoadDispatchFile(szFileName, pRequestInfo);
+ }
+ else if (AsciiStricmp(szDot - ATLS_DLL_EXTENSION_LEN, c_AtlDLLExtension) == 0)
+ {
+ pRequestInfo->dwRequestType = ATLSRV_REQUEST_DLL;
+ hcErr = LoadDllHandler(szFileName, pRequestInfo);
+ }
+ else
+ {
+ hcErr = HTTP_FAIL;
+ }
+
+ if (hcErr)
+ {
+ RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
+ return TRUE;
+ }
+
+ pRequestInfo->pfnHandleRequest = &IRequestHandler::HandleRequest;
+
+ // initialize the handler
+ DWORD dwStatus = 0;
+
+ hcErr = pRequestInfo->pHandler->GetFlags(&dwStatus);
+ if (hcErr)
+ {
+ RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
+ return FALSE;
+ }
+
+ if (bAllowCaching && ((dwStatus & ATLSRV_INIT_USECACHE) != 0) &&
+ !strcmp(pRequestInfo->pServerContext->GetRequestMethod(), "GET"))
+ {
+ CComPtr<IHttpServerContext> spCacheCtx;
+ if (!GetCacheServerContext(pRequestInfo, &m_PageCache, &spCacheCtx) ||
+ !spCacheCtx)
+ {
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_OUTOFMEM);
+ return FALSE;
+ }
+
+ pRequestInfo->pServerContext->Release();
+ pRequestInfo->pServerContext = spCacheCtx.Detach();
+ }
+
+ if (dwStatus & (ATLSRV_INIT_USEASYNC | ATLSRV_INIT_USEASYNC_EX))
+ {
+#ifdef _DEBUG
+ bAsyncAllowed = true;
+#endif
+ if (!pRequestInfo->pServerContext->RequestIOCompletion(AsyncCallback, (DWORD *)pRequestInfo))
+ {
+ RequestComplete(pRequestInfo, 500, SUBERR_NONE);
+ return FALSE;
+ }
+ }
+
+ if (dwStatus & ATLSRV_INIT_USEASYNC_EX)
+ {
+ pRequestInfo->m_hMutex = CreateMutex(NULL, FALSE, NULL);
+ if (pRequestInfo->m_hMutex == NULL)
+ {
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_SYSOBJFAIL);
+ return FALSE;
+ }
+
+ DWORD dwMutexStatus = WaitForSingleObject(pRequestInfo->m_hMutex, 10000);
+ if (dwMutexStatus != WAIT_OBJECT_0 && dwMutexStatus != WAIT_ABANDONED)
+ {
+ RequestComplete(pRequestInfo, 500, ISE_SUBERR_UNEXPECTED);
+ return FALSE;
+ }
+ }
+ hcErr = pRequestInfo->pHandler->InitializeHandler(pRequestInfo, static_cast<IServiceProvider*>(this));
+ }
+#ifdef _DEBUG
+ else // pRequestInfo->dwRequestState != ATLSRV_STATE_BEGIN
+ {
+ bAsyncAllowed = true;
+ }
+#endif
+
+ ATLENSURE(pRequestInfo->pfnHandleRequest != NULL);
+
+ if (hcErr == HTTP_SUCCESS)
+ hcErr = (pRequestInfo->pHandler->*pRequestInfo->pfnHandleRequest)(pRequestInfo, static_cast<IServiceProvider*>(this));
+
+ if (hcErr == HTTP_SUCCESS_NO_CACHE)
+ {
+ CComPtr<IPageCacheControl> spControl;
+ HRESULT hr = pRequestInfo->pServerContext->QueryInterface(__uuidof(IPageCacheControl), reinterpret_cast<void**>(&spControl));
+ if (hr == S_OK)
+ spControl->Cache(FALSE);
+ }
+
+#ifdef _DEBUG
+ // must use ATLSRV_INIT_USEASYNC to use ASYNC returns
+ if (IsAsyncStatus(hcErr))
+ ATLASSERT(bAsyncAllowed);
+
+ // must use ATLSRV_INIT_USEASYNC_EX to use NOFLUSH returns
+ if (IsAsyncNoFlushStatus(hcErr))
+ ATLASSERT(pRequestInfo->m_hMutex);
+#endif
+
+ // save hMutex in case pRequestInfo is deleted by AsyncCallback after
+ // we call StartAsyncFlush but before we check to see if we need to
+ // call ReleaseMutex
+ HANDLE hMutex = pRequestInfo->m_hMutex;
+
+ if (IsAsyncStatus(hcErr))
+ {
+ if (IsAsyncDoneStatus(hcErr))
+ pRequestInfo->dwRequestState = ATLSRV_STATE_DONE;
+ else
+ pRequestInfo->dwRequestState = ATLSRV_STATE_CONTINUE;
+
+ if (IsAsyncFlushStatus(hcErr) && !StartAsyncFlush(pRequestInfo))
+ {
+ RequestComplete(pRequestInfo, 500, SUBERR_NONE);
+ pRequestInfo = NULL;
+ }
+ }
+ else
+ {
+ RequestComplete(pRequestInfo, HTTP_ERROR_CODE(hcErr), HTTP_SUBERROR_CODE(hcErr));
+ pRequestInfo = NULL;
+ }
+
+ if (hMutex)
+ ReleaseMutex(hMutex);
+
+ return TRUE;
+ }
+
+ BOOL StartAsyncFlush(__in AtlServerRequest *pRequestInfo)
+ {
+ ATLENSURE(pRequestInfo);
+ if (pRequestInfo->pszBuffer == NULL || pRequestInfo->dwBufferLen == 0)
+ {
+ ATLASSERT(FALSE);
+ return FALSE;
+ }
+
+ ATLENSURE(pRequestInfo->pServerContext);
+ BOOL bRet = TRUE;
+ _ATLTRY
+ {
+ bRet = pRequestInfo->pServerContext->AsyncWriteClient(
+ LPVOID(pRequestInfo->pszBuffer),
+ &pRequestInfo->dwBufferLen);
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+
+ return bRet;
+ }
+
+ long GetTotalRequests()
+ {
+ return m_reqStats.GetTotalRequests();
+ }
+
+ long GetFailedRequests()
+ {
+ return m_reqStats.GetFailedRequests();
+ }
+
+ long GetAvgResponseTime()
+ {
+ return m_reqStats.GetAvgResponseTime();
+ }
+
+ long GetCurrWaiting()
+ {
+ return m_reqStats.GetCurrWaiting();
+ }
+
+ long GetMaxWaiting()
+ {
+ return m_reqStats.GetMaxWaiting();
+ }
+
+ long GetActiveThreads()
+ {
+ return m_reqStats.GetActiveThreads();
+ }
+
+ __success(SUCCEEDED(return)) __checkReturn HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, __deref_out void **ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+ if (InlineIsEqualGUID(riid, __uuidof(IRequestStats)))
+ {
+ *ppv = static_cast<IRequestStats*>(this);
+ AddRef();
+ return S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
+ InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
+ {
+ *ppv = static_cast<IServiceProvider*>(this);
+ AddRef();
+ return S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IIsapiExtension)))
+ {
+ *ppv = static_cast<IIsapiExtension*>(this);
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+
+ virtual __checkReturn HRESULT STDMETHODCALLTYPE QueryService(
+ __in REFGUID guidService,
+ __in REFIID riid,
+ __deref_out void **ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (InlineIsEqualGUID(guidService, __uuidof(IDllCache)))
+ return m_DllCache.QueryInterface(riid, ppvObject);
+ else if (InlineIsEqualGUID(guidService, __uuidof(IStencilCache)))
+ return m_StencilCache.QueryInterface(riid, ppvObject);
+ else if (InlineIsEqualGUID(guidService, __uuidof(IThreadPoolConfig)))
+ return m_ThreadPool.QueryInterface(riid, ppvObject);
+ else if (InlineIsEqualGUID(guidService, __uuidof(IAtlMemMgr)))
+ {
+ *ppvObject = static_cast<IAtlMemMgr *>(&m_heap);
+ return S_OK;
+ }
+#ifndef ATL_NO_SOAP
+ else if (InlineIsEqualGUID(guidService, __uuidof(ISAXXMLReader)))
+ {
+ CIsapiWorker *p = GetThreadWorker();
+ ATLENSURE( p != NULL );
+ return p->m_spReader->QueryInterface(riid, ppvObject);
+ }
+#endif
+
+ // otherwise look it up in the servicemap
+ return GetService(guidService, riid, ppvObject);
+ }
+
+ virtual HRESULT AddService(REFGUID guidService, REFIID riid, IUnknown *punkService, HINSTANCE hInstance)
+ {
+ if (!punkService)
+ return E_INVALIDARG;
+
+ if (!m_DllCache.AddRefModule(hInstance))
+ return E_FAIL;
+
+ ServiceNode srvNode;
+ srvNode.hInst = hInstance;
+ srvNode.punk = punkService;
+ Checked::memcpy_s(&srvNode.guidService, sizeof(GUID), &guidService, sizeof(guidService));
+ Checked::memcpy_s(&srvNode.riid, sizeof(IID), &riid, sizeof(riid));
+
+ CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // if the service is already there, return S_FALSE
+ int nIndex = m_serviceMap.Find(srvNode);
+ if (nIndex >= 0)
+ return S_FALSE;
+
+ if (!m_serviceMap.Add(srvNode))
+ return E_OUTOFMEMORY;
+
+ punkService->AddRef();
+ return S_OK;
+ }
+
+ virtual HRESULT RemoveService(__in REFGUID guidService, __in REFIID riid)
+ {
+ ServiceNode srvNode;
+ Checked::memcpy_s(&srvNode.guidService, sizeof(GUID), &guidService, sizeof(guidService));
+ Checked::memcpy_s(&srvNode.riid, sizeof(IID), &riid, sizeof(riid));
+
+ CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ int nIndex = m_serviceMap.Find(srvNode);
+ if (nIndex < 0)
+ return S_FALSE;
+
+ ATLASSUME(m_serviceMap[nIndex].punk != NULL);
+ m_serviceMap[nIndex].punk->Release();
+
+ HINSTANCE hInstRemove = m_serviceMap[nIndex].hInst;
+
+ m_serviceMap.RemoveAt(nIndex);
+
+ if (!m_DllCache.ReleaseModule(hInstRemove))
+ return S_FALSE;
+
+ return S_OK;
+ }
+
+ __success(SUCCEEDED(return)) __checkReturn HRESULT GetService(__in REFGUID guidService, __in REFIID riid, __deref_out void **ppvObject) throw()
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ *ppvObject = NULL;
+ if (!m_serviceMap.GetSize())
+ return E_NOINTERFACE;
+
+ ServiceNode srvNode;
+ Checked::memcpy_s(&srvNode.guidService, sizeof(GUID), &guidService, sizeof(guidService));
+ Checked::memcpy_s(&srvNode.riid, sizeof(IID), &riid, sizeof(riid));
+
+ CComCritSecLock<CComCriticalSection> lock(m_critSec, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ int nIndex = m_serviceMap.Find(srvNode);
+ if (nIndex < 0)
+ return E_NOINTERFACE;
+
+ ATLASSUME(m_serviceMap[nIndex].punk != NULL);
+ return m_serviceMap[nIndex].punk->QueryInterface(riid, ppvObject);
+ }
+
+ HTTP_CODE LoadRequestHandler(__in LPCSTR szDllPath, __in LPCSTR szHandlerName, __in IHttpServerContext *pServerContext,
+ __out HINSTANCE *phInstance, __deref_out IRequestHandler **ppHandler)
+ {
+ return _AtlLoadRequestHandler(szDllPath, szHandlerName, pServerContext,
+ phInstance, ppHandler, this, static_cast<IDllCache*>(&m_DllCache));
+ } // LoadRequestHandler
+
+}; // class CIsapiExtension
+
+
+//===========================================================================================
+// IMPORTANT NOTE TO USERS:
+// DO NOT ASSUME *ANYTHING* ABOUT THE STRUCTURE OF THESE MAPS/ENTRIES/FUNCTIONS--THEY CAN
+// AND *WILL* CHANGE IN THE FUTURE. CORRECT USAGE MANDATES THAT YOU USE THE MACROS PROVIDED.
+// ABSOLUTELY NO GUARANTEES ABOUT BACKWARD COMPATABILITY ARE MADE FOR MANUALLY DEFINED
+// HANDLERS OR FUNCTIONS.
+//===========================================================================================
+
+typedef BOOL (*CREATEHANDLERFUNC)(IIsapiExtension *pExtension, IUnknown **ppOut);
+typedef BOOL (*INITHANDLERFUNC)(IHttpServerContext*, IIsapiExtension*);
+typedef void (*UNINITHANDLERFUNC)();
+
+struct _HANDLER_ENTRY
+{
+ LPCSTR szName;
+ CREATEHANDLERFUNC pfnCreate;
+ INITHANDLERFUNC pfnInit;
+ UNINITHANDLERFUNC pfnUninit;
+};
+// definitions of data segments and _HANDLER_ENTRY delimiters
+#pragma section("ATLS$A", read, shared)
+#pragma section("ATLS$Z", read, shared)
+#pragma section("ATLS$C", read, shared)
+extern "C"
+{
+__declspec(selectany) __declspec(allocate("ATLS$A")) ATL::_HANDLER_ENTRY * __phdlrA = NULL;
+__declspec(selectany) __declspec(allocate("ATLS$Z")) ATL::_HANDLER_ENTRY * __phdlrZ = NULL;
+}
+
+#if !defined(_M_IA64)
+#pragma comment(linker, "/merge:ATLS=.rdata")
+#endif
+
+#ifndef HANDLER_ENTRY_PRAGMA
+
+#if defined(_M_IX86)
+#define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:___phdlrEntry_" #class "_" #line));
+#elif defined(_M_IA64)
+#define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:__phdlrEntry_" #class "_" #line));
+#elif defined(_M_AMD64)
+#define HANDLER_ENTRY_PRAGMA(class, line) __pragma(comment(linker, "/include:__phdlrEntry_" #class "_" #line));
+#else
+#error Unknown Platform. define HANDLER_ENTRY_PRAGMA
+#endif
+
+#endif // HANDLER_ENTRY_PRAGMA
+
+// DECLARE_REQUEST_HANDLER macro
+#define __DECLARE_REQUEST_HANDLER_INTERNAL(handlerName, className, classQName, lineNum) \
+__declspec(selectany) ATL::_HANDLER_ENTRY __hdlrEntry_ ## className ## _ ## lineNum = { handlerName, classQName::CreateRequestHandler, classQName::InitRequestHandlerClass, classQName::UninitRequestHandlerClass }; \
+extern "C" __declspec(allocate("ATLS$C")) __declspec(selectany) \
+ATL::_HANDLER_ENTRY * const __phdlrEntry_ ## className ## _ ## lineNum = &__hdlrEntry_ ## className ## _ ## lineNum; \
+HANDLER_ENTRY_PRAGMA(className, lineNum) \
+__if_not_exists(GetAtlHandlerByName) \
+{ \
+extern "C" ATL_NOINLINE inline BOOL __declspec(dllexport) __stdcall GetAtlHandlerByName(LPCSTR szHandlerName, IIsapiExtension *pExtension, IUnknown **ppHandler) throw() \
+{ \
+ *ppHandler = NULL; \
+ ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
+ while (pEntry != &__phdlrZ) \
+ { \
+ if (*pEntry && (*pEntry)->szName) \
+ { \
+ if (strcmp((*pEntry)->szName, szHandlerName)==0) \
+ { \
+ return (*(*pEntry)->pfnCreate)(pExtension, ppHandler); \
+ } \
+ } \
+ pEntry++; \
+ } \
+ return FALSE; \
+} \
+extern "C" ATL_NOINLINE inline BOOL __declspec(dllexport) __stdcall InitializeAtlHandlers(IHttpServerContext *pContext, IIsapiExtension *pExt) throw() \
+{ \
+ ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
+ BOOL bRet = TRUE; \
+ while (pEntry != &__phdlrZ) \
+ { \
+ if (*pEntry && (*pEntry)->szName && (*pEntry)->pfnInit) \
+ { \
+ bRet = (*(*pEntry)->pfnInit)(pContext, pExt); \
+ if (!bRet) \
+ break; \
+ } \
+ pEntry++; \
+ } \
+ if (!bRet) \
+ { \
+ if (pEntry == &__phdlrA) \
+ return FALSE; \
+ do \
+ { \
+ pEntry--; \
+ if(*pEntry) \
+ (*(*pEntry)->pfnUninit)(); \
+ } \
+ while (pEntry != &__phdlrA); \
+ } \
+ return bRet; \
+} \
+extern "C" ATL_NOINLINE inline void __declspec(dllexport) __stdcall UninitializeAtlHandlers() throw() \
+{\
+ ATL::_HANDLER_ENTRY **pEntry = &__phdlrA; \
+ while (pEntry != &__phdlrZ) \
+ { \
+ if (*pEntry && (*pEntry)->szName && (*pEntry)->pfnUninit) \
+ { \
+ (*(*pEntry)->pfnUninit)(); \
+ } \
+ pEntry++; \
+ } \
+} \
+}
+
+#define __DECLARE_REQUEST_HANDLER(handlerName, className, classQName, lineNum) __DECLARE_REQUEST_HANDLER_INTERNAL(handlerName, className, classQName, lineNum)
+#define DECLARE_REQUEST_HANDLER(handlerName, className, classQName) __DECLARE_REQUEST_HANDLER(handlerName, className, classQName, __COUNTER__)
+
+#define BEGIN_HANDLER_MAP()
+#define HANDLER_ENTRY(handlerName, className) DECLARE_REQUEST_HANDLER(handlerName, className, className)
+#define HANDLER_ENTRY_EX(handlerName, className, classQName) DECLARE_REQUEST_HANDLER(handlerName, className, classQName)
+#define END_HANDLER_MAP()
+
+#define __HANDLER_ENTRY_SDL_INTERNAL(handlerString, handlerClass, handlerQClass, sdlClassName, lineNum)\
+_ATLSOAP_DECLARE_WSDL_SRF() \
+extern __declspec(selectany) const char const s_szClassName##sdlClassName##lineNum[]=handlerString;\
+typedef ATL::CSDLGenerator<handlerQClass, s_szClassName##sdlClassName##lineNum> sdlClassName; \
+HANDLER_ENTRY_EX(handlerString, handlerClass, handlerQClass)\
+HANDLER_ENTRY(#sdlClassName, sdlClassName)
+
+#define __HANDLER_ENTRY_SDL(handlerString, handlerClass, handlerQClass, sdlClassName, lineNum) __HANDLER_ENTRY_SDL_INTERNAL(handlerString, handlerClass, handlerQClass, sdlClassName, lineNum)
+#define HANDLER_ENTRY_SDL(handlerString, handlerClass, handlerQClass, sdlClassName) __HANDLER_ENTRY_SDL(handlerString, handlerClass, handlerQClass, sdlClassName, __COUNTER__)
+//
+// Use this class to check the authorization level of a client who is making
+// a request to this application. This class checks for the stronger authentication
+// levels (NTLM and Negotiate). You can call it directly from an implementation
+// of HandleRequest to check authorization before handling a request.
+#define MAX_AUTH_TYPE 50
+#define MAX_NAME_LEN 255
+
+template <class T>
+class CVerifyAuth
+{
+public:
+ HTTP_CODE IsAuthorized(__in AtlServerRequest *pInfo, __in_opt const SID* psidAuthGroup)
+ {
+ ATLENSURE(pInfo);
+ ATLENSURE(pInfo->pServerContext);
+ ATLASSERT(psidAuthGroup);
+ ATLASSERT(::IsValidSid((PSID) psidAuthGroup));
+
+ HTTP_CODE hcErr = HTTP_UNAUTHORIZED;
+ char szAuthType[MAX_AUTH_TYPE];
+ DWORD dwSize = MAX_AUTH_TYPE;
+ _ATLTRY
+ {
+ if (pInfo->pServerContext->GetServerVariable("AUTH_TYPE",
+ szAuthType, &dwSize))
+ {
+ if (szAuthType[0] && (!_stricmp(szAuthType, "NTLM")
+ || !_stricmp(szAuthType, "Negotiate")))
+ {
+ // if we were passed a group name
+ // we check to see that the logged on user is part
+ // of that group, else we just return success.
+ if (psidAuthGroup)
+ {
+ T* pT = static_cast<T*>(this);
+ if (pT->CheckAccount(pInfo->pServerContext, psidAuthGroup))
+ hcErr = HTTP_SUCCESS;
+ else
+ hcErr = pT->HandleError(pInfo);
+ }
+ else
+ hcErr = HTTP_SUCCESS;
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hcErr = HTTP_FAIL;
+ }
+
+ return hcErr;
+ }
+
+ virtual bool CheckAccount(IHttpServerContext *pContext, const SID *psidAuthGroup) throw()
+ {
+ (pContext); // unused
+ (psidAuthGroup); // unused
+ return false;
+ }
+
+ HTTP_CODE HandleError(AtlServerRequest *pRequestInfo) throw()
+ {
+ pRequestInfo; // unused
+ return HTTP_FAIL;
+ }
+
+ __checkReturn bool CheckAuthAccount(__in IHttpServerContext *pContext, __in const SID* psidAuthGroup) throw()
+ {
+ ATLASSERT(pContext);
+ ATLASSERT(psidAuthGroup);
+ if (!pContext || !psidAuthGroup)
+ return false;
+
+ HANDLE hToken = INVALID_HANDLE_VALUE;
+ bool bIsMember;
+ _ATLTRY
+ {
+ if (!pContext->GetImpersonationToken(&hToken) ||
+ hToken == INVALID_HANDLE_VALUE)
+ return false;
+
+ CAccessToken tok;
+ tok.Attach(hToken);
+ bool bRet = tok.CheckTokenMembership(CSid(psidAuthGroup), &bIsMember);
+ tok.Detach();
+
+ if (!bRet)
+ return false;
+ }
+ _ATLCATCHALL()
+ {
+ bIsMember = false;
+ }
+
+ return bIsMember;
+ }
+};
+
+// Checks that the user that is logging on is in the required group
+class CDefaultAuth :
+ public CVerifyAuth<CDefaultAuth>
+{
+public:
+ virtual bool CheckAccount(__in IHttpServerContext *pContext, __in const SID* psidAuthGroup) throw()
+ {
+ return CheckAuthAccount(pContext, psidAuthGroup);
+ }
+
+ HTTP_CODE HandleError(__in AtlServerRequest *pRequestInfo) throw()
+ {
+ ATLASSERT(pRequestInfo); // should always be valid
+ if(!pRequestInfo)
+ {
+ return HTTP_FAIL;
+ }
+
+ _ATLTRY
+ {
+ CHttpResponse response(pRequestInfo->pServerContext);
+ response.Write(GetErrorResponse());
+ response.Flush();
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+
+ return HTTP_SUCCESS_NO_PROCESS;
+ }
+
+ virtual LPCSTR GetErrorResponse()
+ {
+ static const char *szResponse = "<html><body>"
+ "<H1 align=center>NOT AUTHORIZED</H1><p>"
+ "</body></html>";
+ return szResponse;
+ }
+
+};
+
+} // namespace ATL
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif // __ATLISAPI_H__
diff --git a/include/atl/atlmime.h b/include/atl/atlmime.h
new file mode 100644
index 000000000..06d4fd2f4
--- /dev/null
+++ b/include/atl/atlmime.h
@@ -0,0 +1,2406 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLMIME_H__
+#define __ATLMIME_H__
+
+#pragma once
+
+#include <tchar.h>
+#include <time.h>
+#include <atlbase.h>
+#include <mlang.h>
+#include <atlfile.h>
+#include <atlcoll.h>
+#include <atlstr.h>
+#include <atlsmtputil.h>
+#include <atlenc.h>
+#include <atlspriv.h>
+
+#pragma warning(push)
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+
+#ifndef _CPPUNWIND
+#pragma warning (push)
+#pragma warning(disable: 4702) // unreachable code
+#endif // _CPPUNWIND
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+#ifndef ATLMIME_SEPARATOR
+#define ATLMIME_SEPARATOR "\r\n\r\n--"
+#endif//ATLMIME_SEPARATOR
+
+#ifndef ATLMIME_VERSION
+#define ATLMIME_VERSION "MIME-Version: 1.0"
+#endif//ATLMIME_VERSION
+
+#ifndef ATLMIME_EMAIL
+#define ATLMIME_EMAIL "email"
+#endif//ATLMIME_EMAIL
+
+extern __declspec(selectany) const DWORD ATL_MIME_BOUNDARYLEN = 32;
+extern __declspec(selectany) const DWORD ATL_MIME_DATE_LEN = 64;
+
+// Called when message is sent - sets the "Date:" field
+inline size_t SetRfc822Time(__out_ecount_part_z_opt(dwLen, return) LPSTR szDate, __in size_t dwLen) throw()
+{
+ // Max buffer size required(including NULL) - 38
+ const size_t s_dwMaxBufferLen = 38;
+ if (szDate == NULL)
+ {
+ return s_dwMaxBufferLen;
+ }
+
+ if (dwLen < 38)
+ {
+ return 0;
+ }
+ static const LPCSTR s_months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ static const LPCSTR s_days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+
+ SYSTEMTIME st;
+ DWORD dwTimeZoneId=TIME_ZONE_ID_UNKNOWN;
+ CHAR cDiff;
+ LONG ltzBias=0;
+ LONG ltzHour;
+ LONG ltzMinute;
+ TIME_ZONE_INFORMATION tzi;
+
+ GetLocalTime(&st);
+
+ // Gets TIME_ZONE_INFORMATION
+ memset(&tzi, 0, sizeof(tzi));
+ dwTimeZoneId = GetTimeZoneInformation(&tzi);
+ switch (dwTimeZoneId)
+ {
+ case TIME_ZONE_ID_STANDARD:
+ ltzBias = tzi.Bias + tzi.StandardBias;
+ break;
+
+ case TIME_ZONE_ID_DAYLIGHT:
+ ltzBias = tzi.Bias + tzi.DaylightBias;
+ break;
+
+ case TIME_ZONE_ID_UNKNOWN:
+ default:
+ ltzBias = tzi.Bias;
+ break;
+ }
+
+ // Set Hour Minutes and time zone dif
+ ltzHour = ltzBias / 60;
+ ltzMinute = ltzBias % 60;
+ cDiff = (ltzHour < 0) ? '+' : '-';
+
+ int nDay = (st.wDayOfWeek > 6) ? 0 : st.wDayOfWeek;
+ int nMonth = st.wMonth = (WORD)((st.wMonth < 1 || st.wMonth > 12) ? 0 : st.wMonth - 1);
+
+
+ // Constructs RFC 822 format: "ddd, dd mmm yyyy hh:mm:ss +/- hhmm\0"
+ sprintf_s(szDate, dwLen, "Date: %3s, %d %3s %4d %02d:%02d:%02d %c%02d%02d",
+ s_days[nDay], // "ddd"
+ st.wDay, // "dd"
+ s_months[nMonth], // "mmm"
+ st.wYear, // "yyyy"
+ st.wHour, // "hh"
+ st.wMinute, // "mm"
+ st.wSecond, // "ss"
+ cDiff, // "+" / "-"
+ abs (ltzHour), // "hh"
+ abs (ltzMinute)); // "mm"
+ return s_dwMaxBufferLen;
+}
+
+inline DWORD GetContentTypeFromFileName(LPCTSTR szFileName, CSimpleString& strContentType) throw()
+{
+ if (szFileName == NULL)
+ {
+ return ERROR_INVALID_DATA;
+ }
+
+ DWORD dwErr = ERROR_PATH_NOT_FOUND;
+ _ATLTRY
+ {
+ // get the file extension
+ TCHAR szExt[_MAX_EXT];
+ Checked::tsplitpath_s(szFileName, NULL, 0, NULL, 0, NULL, 0, szExt, _countof(szExt));
+ if (*szExt)
+ {
+ // Query the content type from the registry
+ CRegKey rkContentType;
+ dwErr = rkContentType.Open(HKEY_CLASSES_ROOT, szExt, KEY_READ);
+ if (dwErr == ERROR_SUCCESS)
+ {
+ ULONG nChars=0;
+ dwErr = rkContentType.QueryStringValue(_T("Content Type"), NULL, &nChars);
+ if (dwErr == ERROR_SUCCESS)
+ {
+ LPTSTR szBuf = strContentType.GetBuffer(nChars);
+ dwErr = rkContentType.QueryStringValue(_T("Content Type"), szBuf, &nChars);
+ strContentType.ReleaseBuffer(nChars);
+ }
+ }
+ }
+
+ if (dwErr != ERROR_SUCCESS)
+ {
+ // default to application/octet-stream
+ strContentType.SetString(_T("application/octet-stream"), sizeof("application/octet-stream")-1);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ dwErr = ERROR_OUTOFMEMORY;
+ }
+
+ return dwErr;
+}
+
+// CMimeBodyPart is an abstract base class for the body parts
+// CMimeAttachment, CMimeText, CMimeHeader.
+class CMimeBodyPart
+{
+public:
+
+ virtual ~CMimeBodyPart() = 0 {}
+
+ // WriteData - pure virtual method to dump the data for a body part.
+ virtual BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) = 0;
+
+ // GetContentType - pure virtual method to get the content of a body part
+ virtual LPCSTR GetContentType() = 0;
+
+ // GetCharset - virtual method to get the character set of a body part
+ // (defaults to ATLSMTP_DEFAULT_CSET).
+ virtual LPCSTR GetCharset()
+ {
+ return ATLSMTP_DEFAULT_CSET;
+ }
+
+ virtual CMimeBodyPart* Copy() = 0;
+
+protected:
+
+ // MakeMimeHeader - pure virutal method to create a MIME header for a
+ // body part.
+ virtual BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) = 0;
+}; // class CMimeBodyPart
+
+
+// This enum is used with the X-Priority part of the message header
+enum ATL_MIME_PRIORITY
+{
+ ATL_MIME_HIGH_PRIORITY = 1,
+ ATL_MIME_NORMAL_PRIORITY = 3,
+ ATL_MIME_LOW_PRIORITY = 5,
+ ATL_MIME_PRIORITY_ERROR = 0
+};
+
+
+// CMimeHeader describes the basic RFC 822 message header.
+// It also serves as the base class for the CMimeMessage object.
+class CMimeHeader : public CMimeBodyPart
+{
+protected:
+
+ // Pointer to MLANG's IMultiLanguage interface.
+ // This is used in doing conversion from code pages
+ // to MIME-compatible character sets.
+ CComPtr<IMultiLanguage> m_spMultiLanguage;
+
+ //Basic Header Parts
+ CStringA m_strFrom;
+ CStringA m_strTo;
+ CStringA m_strCc;
+ CStringA m_strBcc;
+ CStringA m_strSubject;
+
+ //Extended Header Parts
+ ATL_MIME_PRIORITY m_nPriority;
+ CStringA m_XHeader;
+
+ //Display Names
+ CStringA m_strSenderName;
+
+ //MIME Character Sets
+ char m_szSubjectCharset[ATL_MAX_ENC_CHARSET_LENGTH];
+ char m_szSenderCharset[ATL_MAX_ENC_CHARSET_LENGTH];
+
+ //Recipient and CC charsets are encoded in the Add methods
+
+public:
+
+ CMimeHeader() throw()
+ :m_nPriority(ATL_MIME_NORMAL_PRIORITY)
+ {
+ m_szSubjectCharset[0] = '\0';
+ m_szSenderCharset[0] = '\0';
+ }
+
+ ~CMimeHeader() throw()
+ {
+ }
+
+ // Initialize MLang for multilanguage support
+ inline BOOL Initialize(IMultiLanguage* pMultiLanguage = NULL) throw()
+ {
+ if (pMultiLanguage != NULL)
+ {
+ m_spMultiLanguage = pMultiLanguage;
+ }
+ else
+ {
+ HRESULT hr = m_spMultiLanguage.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
+ if (hr != S_OK)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ // Get the content type
+ virtual inline LPCSTR GetContentType() throw()
+ {
+ return "text/plain";
+ }
+
+ // Get the character set
+ virtual inline LPCSTR GetCharset() throw()
+ {
+ return "iso-8859-1";
+ }
+
+ virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
+ {
+ CAutoPtr<CMimeHeader> pNewHeader;
+ ATLTRY(pNewHeader.Attach(new CMimeHeader));
+ if (pNewHeader)
+ *pNewHeader = *this;
+
+ return pNewHeader.Detach();
+ }
+
+ const CMimeHeader& operator=(const CMimeHeader& that) throw( ... )
+ {
+ if (this != &that)
+ {
+ m_spMultiLanguage = that.m_spMultiLanguage;
+ m_strFrom = that.m_strFrom;
+ m_strTo = that.m_strTo;
+ m_strCc = that.m_strCc;
+ m_strSubject = that.m_strSubject;
+
+ m_nPriority = that.m_nPriority;
+ m_XHeader = that.m_XHeader;
+
+ m_strSenderName = that.m_strSenderName;
+
+ Checked::strcpy_s(m_szSubjectCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szSubjectCharset);
+ Checked::strcpy_s(m_szSenderCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szSenderCharset);
+ }
+
+ return *this;
+ }
+
+ // Set the priority of the message
+ inline BOOL SetPriority(ATL_MIME_PRIORITY nPriority) throw()
+ {
+ if (nPriority < 0)
+ return FALSE;
+ m_nPriority = nPriority;
+ return TRUE;
+ }
+
+ // Get the priority of the message
+ inline ATL_MIME_PRIORITY GetPriority() throw()
+ {
+ return m_nPriority;
+ }
+
+ // Set the display (friendly) name for the header
+ inline BOOL SetSenderName(LPCTSTR szName, UINT uiCodePage = 0) throw()
+ {
+ if (szName == NULL)
+ return FALSE;
+
+ CHeapPtr<char> szNamePtr;
+ UINT nLen(0);
+
+ BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szName, &szNamePtr, &nLen);
+ if (bRet)
+ {
+ _ATLTRY
+ {
+ m_strSenderName.Empty();
+ m_strSenderName.Append(szNamePtr, (int) nLen);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ bRet = AtlMimeCharsetFromCodePage(m_szSenderCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
+ }
+
+ return bRet;
+ }
+
+ // Get the display (friendly) name for the sender
+ inline LPCSTR GetSenderName() throw()
+ {
+ return m_strSenderName;
+ }
+
+ // Append a user defined header (should not contain CRLF)
+ inline BOOL AppendUserDefinedHeader(LPCTSTR szHeaderName, LPCTSTR szHeader, UINT uiCodePage = 0) throw()
+ {
+ if ((szHeader == NULL) || (szHeaderName == NULL))
+ return FALSE;
+
+ _ATLTRY
+ {
+ CHeapPtr<char> szName;
+ UINT nLen(0);
+
+ BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szHeader, &szName, &nLen);
+ if (bRet)
+ {
+ // get the charset
+ char szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
+ bRet = AtlMimeCharsetFromCodePage(szCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
+
+ if (bRet)
+ {
+ CStringA str;
+ str.Append(szName, (int)nLen);
+
+ // encode the string
+ CHeapPtr<char> szBuf;
+ DWORD dwReqLen = QEncodeGetRequiredLength(str.GetLength(),
+ ATL_MAX_ENC_CHARSET_LENGTH);
+
+ if (szBuf.Allocate(dwReqLen) == false)
+ {
+ return FALSE;
+ }
+
+ DWORD dwLength(0);
+ BOOL bEncoded = FALSE;
+ if (!GetEncodedString(str, szCharset, szBuf, dwReqLen, dwLength, bEncoded))
+ {
+ return FALSE;
+ }
+
+ // add to m_XHeader
+ m_XHeader += CT2CA(szHeaderName);
+ m_XHeader.Append(": ", 2);
+ m_XHeader.Append(szBuf, dwLength);
+ m_XHeader.Append("\r\n", 2);
+ }
+ }
+
+ return bRet;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Add a recipient ("To:" line)
+ inline BOOL AddRecipient(LPCTSTR szAddress, LPCTSTR szName = NULL, UINT uiCodePage = 0) throw()
+ {
+ return AddRecipientHelper(m_strTo, szAddress, szName, uiCodePage);
+ }
+
+ // Get the recipients string ("To:" line)
+ inline LPCSTR GetRecipients() throw()
+ {
+ return m_strTo;
+ }
+
+ // Clear all recipients ("To:" line)
+ inline BOOL ClearRecipients() throw()
+ {
+ m_strTo.Empty();
+ return TRUE;
+ }
+
+ // Add a recipient ("CC:" line)
+ inline BOOL AddCc(LPCTSTR szAddress, LPCTSTR szName = NULL, UINT uiCodePage = 0) throw()
+ {
+ return AddRecipientHelper(m_strCc, szAddress, szName, uiCodePage);
+ }
+
+ // Get the recipients string ("CC:" line)
+ inline LPCSTR GetCc() throw()
+ {
+ return m_strCc;
+ }
+
+ // Clear the recipients string ("CC:" line)
+ inline BOOL ClearCc() throw()
+ {
+ m_strCc.Empty();
+ return TRUE;
+ }
+
+ // Add a Bcc recipient (not output as part of message)
+ inline BOOL AddBcc(LPCTSTR szAddress) throw()
+ {
+ if (szAddress == NULL)
+ {
+ return FALSE;
+ }
+
+ _ATLTRY
+ {
+ CStringA str = m_strBcc;
+
+ if (m_strBcc.GetLength() > 0)
+ str.Append(",", 1);
+
+ str += CT2CA(szAddress);
+
+ m_strBcc = str;
+
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Get the recipients string (Bcc part)
+ inline LPCSTR GetBcc() throw()
+ {
+ return m_strBcc;
+ }
+
+ // Clear the recipients string (Bcc part)
+ inline BOOL ClearBcc() throw()
+ {
+ m_strBcc.Empty();
+ return TRUE;
+ }
+
+
+ inline DWORD GetRequiredRecipientsStringLength() throw()
+ {
+ DWORD dwRet = m_strTo.GetLength();
+ if (m_strCc.GetLength())
+ {
+ dwRet += dwRet ? 1 : 0;
+ dwRet += m_strCc.GetLength();
+ }
+ if (m_strBcc.GetLength())
+ {
+ dwRet += dwRet ? 1 : 0;
+ dwRet += m_strBcc.GetLength();
+ }
+ dwRet++;
+ return dwRet;
+ }
+
+ // returns the recipients string to be (addresses only, in comma separated format)
+ ATL_NOINLINE BOOL GetRecipientsString(__out_ecount_part_z(*pdwLen, *pdwLen) LPSTR szRecip, __inout LPDWORD pdwLen) throw()
+ {
+ if ( (szRecip == NULL) || (pdwLen == NULL) )
+ {
+ return FALSE;
+ }
+
+ if ( *pdwLen < GetRequiredRecipientsStringLength())
+ {
+ *pdwLen = GetRequiredRecipientsStringLength();
+ return FALSE;
+ }
+
+ DWORD dwMaxLen = *pdwLen;
+ *pdwLen = 0;
+
+ DWORD dwLen = 0;
+ DWORD dwTotalLen = 0;
+ if (m_strTo.GetLength() > 0)
+ {
+ dwLen = *pdwLen - dwTotalLen;
+ if (AtlMimeMakeRecipientsString(m_strTo, szRecip, &dwLen) != TRUE)
+ {
+ return FALSE;
+ }
+ szRecip+= dwLen;
+ dwTotalLen = dwLen;
+ }
+
+ if (m_strCc.GetLength() > 0)
+ {
+ if (dwTotalLen)
+ {
+ *szRecip++ = ',';
+ dwTotalLen++;
+ }
+ dwLen = *pdwLen - dwTotalLen;
+ if (AtlMimeMakeRecipientsString(m_strCc, szRecip, &dwLen) != TRUE)
+ {
+ return FALSE;
+ }
+ szRecip+= dwLen;
+ dwTotalLen+= dwLen;
+ }
+
+ if (m_strBcc.GetLength() > 0)
+ {
+ dwLen = m_strBcc.GetLength();
+ if (dwTotalLen)
+ {
+ *szRecip++ = ',';
+ dwTotalLen++;
+ }
+ dwLen = *pdwLen - dwTotalLen;
+ Checked::memcpy_s(szRecip, dwMaxLen-dwTotalLen, m_strBcc, dwLen);
+ szRecip+= dwLen;
+ dwTotalLen+= dwLen;
+ }
+
+ *szRecip = '\0';
+ *pdwLen = dwTotalLen;
+
+ return TRUE;
+ }
+
+
+ // Get the sender
+ inline LPCSTR GetSender() throw()
+ {
+ return m_strFrom;
+ }
+
+ // Set the sender
+ inline BOOL SetSender(LPCTSTR szSender) throw()
+ {
+ if (szSender == NULL)
+ return FALSE;
+
+ _ATLTRY
+ {
+ m_strFrom = CT2CA(szSender);
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Set the subject
+ inline BOOL SetSubject(LPCTSTR szSubject, UINT uiCodePage = 0) throw()
+ {
+ if (szSubject == NULL)
+ return FALSE;
+
+ _ATLTRY
+ {
+ CHeapPtr<char> szName;
+ UINT nLen(0);
+
+ BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szSubject, &szName, &nLen);
+ if (bRet)
+ {
+ m_strSubject.Empty();
+ m_strSubject.Append(szName, (int)nLen);
+ bRet = AtlMimeCharsetFromCodePage(m_szSubjectCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
+ }
+
+ return bRet;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Get the subject
+ inline LPCSTR GetSubject() throw()
+ {
+ return (LPCSTR)m_strSubject;
+ }
+
+ // Dump the header to hFile
+ virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR /*szBoundary*/, DWORD dwFlags = 0) throw()
+ {
+ if (pOverlapped == NULL)
+ {
+ return FALSE;
+ }
+
+ int nMaxSendLen = GetRequiredBufferSize(ATLSMTP_MAX_LINE_LENGTH-4);
+ CHeapPtr<char> spSendBuffer;
+ if (!spSendBuffer.Allocate(nMaxSendLen))
+ return FALSE;
+
+ // choose QEncode here, because the max QEncodeGetRequiredLength will always
+ // return a value greater than BEncodeGetRequiredLength
+ int nBufLen = __max(QEncodeGetRequiredLength(m_strSubject.GetLength(),
+ ATL_MAX_ENC_CHARSET_LENGTH),
+ QEncodeGetRequiredLength(m_strSenderName.GetLength(),
+ ATL_MAX_ENC_CHARSET_LENGTH)+m_strFrom.GetLength()+2);
+
+ CHeapPtr<char> spBuf;
+ if (!spBuf.Allocate(nBufLen))
+ return FALSE;
+
+ int nMaxLen = nBufLen;
+ DWORD dwOffset = 0;
+
+ char szDate[ATL_MIME_DATE_LEN];
+
+ SetRfc822Time(szDate, ATL_MIME_DATE_LEN);
+ char *pSendBuffer = spSendBuffer;
+
+ DWORD dwLength = (DWORD) strlen(szDate);
+
+ if(dwLength > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
+ return FALSE;
+
+ Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szDate, dwLength);
+ dwOffset += dwLength;
+ *(pSendBuffer+dwOffset++) = '\r';
+ *(pSendBuffer+dwOffset++) = '\n';
+
+ int dwHeaderPartLength = 0;
+ *spBuf = '\0';
+
+ // Get the sender name
+ BOOL bRet = TRUE;
+ BOOL bEncoded = FALSE;
+ if (m_strSenderName.GetLength() > 0)
+ {
+ bRet = GetEncodedString(m_strSenderName, m_szSenderCharset, spBuf, nBufLen, dwLength, bEncoded);
+ dwHeaderPartLength += dwLength;
+ }
+
+ // Get the sender email address
+ if (bRet && m_strFrom.GetLength() > 0)
+ {
+
+ if (dwHeaderPartLength != 0)
+ {
+ if(dwHeaderPartLength + 1 > nBufLen)
+ return FALSE;
+
+ *(spBuf+dwHeaderPartLength++) = ' ';
+ }
+
+ if(dwHeaderPartLength + m_strFrom.GetLength() + 2 > nBufLen)
+ return FALSE;
+
+ *(spBuf+dwHeaderPartLength++) = '<';
+ if (dwHeaderPartLength < 0 || dwHeaderPartLength > nMaxLen)
+ {
+ return FALSE;
+ }
+ Checked::memcpy_s(spBuf+dwHeaderPartLength, nMaxLen-dwHeaderPartLength, (LPCSTR)m_strFrom, m_strFrom.GetLength());
+ dwHeaderPartLength+= m_strFrom.GetLength();
+ *(spBuf+dwHeaderPartLength++) = '>';
+ }
+
+ // Output the "From: " line
+ if (bRet && dwHeaderPartLength != 0)
+ {
+ const char szFrom[] = "From: ";
+ if(sizeof(szFrom)/sizeof(szFrom[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
+ return FALSE;
+ if (dwOffset > static_cast<DWORD>(nMaxSendLen))
+ {
+ return FALSE;
+ }
+ Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szFrom, _countof(szFrom)-1);
+ dwOffset+= (sizeof(szFrom)/sizeof(szFrom[0])-1) ;
+ DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
+ bRet = FormatField((LPBYTE)(char*)spBuf, dwHeaderPartLength, (LPBYTE)(pSendBuffer+dwOffset), &dwWritten, dwFlags);
+ dwOffset += dwWritten;
+ *(pSendBuffer+dwOffset++) = '\r';
+ *(pSendBuffer+dwOffset++) = '\n';
+ }
+
+ // Output the subject
+ if (bRet && m_strSubject.GetLength() > 0)
+ {
+ dwLength = 0;
+ bRet = GetEncodedString(m_strSubject, m_szSubjectCharset, spBuf, nBufLen, dwLength, bEncoded);
+ if (bRet && dwLength != 0)
+ {
+ const char szSubject[] = "Subject: ";
+ if(sizeof(szSubject)/sizeof(szSubject[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
+ return FALSE;
+ if (dwOffset > static_cast<DWORD>(nMaxSendLen))
+ {
+ return FALSE;
+ }
+ Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szSubject, _countof(szSubject)-1);
+ dwOffset+= (sizeof(szSubject)/sizeof(szSubject[0])-1);
+ DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
+ bRet = FormatField((LPBYTE)(char*)spBuf, dwLength, (LPBYTE)(pSendBuffer+dwOffset), &dwWritten, dwFlags);
+ dwOffset += dwWritten;
+ *(pSendBuffer+dwOffset++) = '\r';
+ *(pSendBuffer+dwOffset++) = '\n';
+ }
+ }
+
+ // Output the "To:" line
+ if (bRet && m_strTo.GetLength() > 0)
+ {
+ const char szTo[] = "To: ";
+ if(sizeof(szTo)/sizeof(szTo[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
+ return FALSE;
+ if (dwOffset > static_cast<DWORD>(nMaxSendLen))
+ {
+ return FALSE;
+ }
+ Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szTo, _countof(szTo)-1);
+ dwOffset+= (sizeof(szTo)/sizeof(szTo[0]) -1);
+ DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
+ bRet = FormatRecipients((LPBYTE)((LPCSTR)m_strTo), m_strTo.GetLength(), (LPBYTE)(pSendBuffer+dwOffset), &dwWritten);
+ dwOffset+= dwWritten;
+ *(pSendBuffer+dwOffset++) = '\r';
+ *(pSendBuffer+dwOffset++) = '\n';
+ }
+
+ // Output the "CC:" line
+ if (bRet && m_strCc.GetLength() > 0)
+ {
+ const char szCC[] = "CC: ";
+ if(sizeof(szCC)/sizeof(szCC[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
+ return FALSE;
+ if (dwOffset > static_cast<DWORD>(nMaxSendLen))
+ {
+ return FALSE;
+ }
+ Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szCC, _countof(szCC)-1);
+ dwOffset+= (sizeof(szCC)/sizeof(szCC[0]) -1);
+ DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
+ bRet = FormatRecipients((LPBYTE)((LPCSTR)m_strCc), m_strCc.GetLength(), (LPBYTE)(pSendBuffer+dwOffset), &dwWritten);
+ dwOffset+= dwWritten;
+ *(pSendBuffer+dwOffset++) = '\r';
+ *(pSendBuffer+dwOffset++) = '\n';
+ }
+
+ // Send the header
+ if (bRet && dwOffset)
+ bRet = AtlSmtpSendAndWait(hFile, pSendBuffer, dwOffset, pOverlapped);
+
+ return bRet;
+ }
+
+protected:
+
+ // Make the mime header
+ virtual inline BOOL MakeMimeHeader(CStringA& /*header*/, LPCSTR /*szBoundary*/) throw()
+ {
+ // The message header does not have its own MIME header
+ ATLASSERT(FALSE);
+ return TRUE;
+ }
+
+ // Get an encoded string for a header field
+ inline BOOL GetEncodedString(__in CStringA& headerString, __in LPCSTR szCharset, __out_ecount_part_z(nBufLen, dwLength) LPSTR szBuf, __in int nBufLen, __out DWORD& dwLength, __out BOOL& bEncoded) throw()
+ {
+// BOOL bEncoded = FALSE;
+ bEncoded = FALSE;
+ if (m_spMultiLanguage.p)
+ {
+ // only encode if there are 8bit characters
+ int nExtendedChars = GetExtendedChars(headerString, headerString.GetLength());
+ if (nExtendedChars)
+ {
+ // choose smallest encoding
+ if (((nExtendedChars*100)/headerString.GetLength()) < 17)
+ {
+ int nEncCnt = 0;
+ if (!QEncode((LPBYTE)((LPCSTR)headerString), headerString.GetLength(), szBuf, &nBufLen, szCharset, &nEncCnt))
+ {
+ return FALSE;
+ }
+
+ //if no unsafe characters were encountered, just output it
+ if (nEncCnt != 0)
+ {
+ bEncoded = TRUE;
+ }
+ }
+ else
+ {
+ if (!BEncode((LPBYTE)((LPCSTR)headerString), headerString.GetLength(), szBuf, &nBufLen, szCharset))
+ {
+ return FALSE;
+ }
+
+ bEncoded = TRUE;
+ }
+ }
+ }
+
+ if (!bEncoded)
+ {
+ // there was no encoding
+ dwLength = (DWORD) headerString.GetLength();
+ if(dwLength > DWORD(nBufLen))
+ return FALSE;
+ Checked::memcpy_s(szBuf, nBufLen, headerString, dwLength);
+ }
+ else
+ {
+ dwLength = nBufLen;
+ }
+ return TRUE;
+ }
+
+
+ // Helper function for adding recipients
+ inline BOOL AddRecipientHelper(CStringA& str, LPCTSTR szAddress, LPCTSTR szName = NULL, UINT uiCodePage = 0) throw()
+ {
+ if ((szAddress == NULL) && (szName == NULL))
+ {
+ return FALSE;
+ }
+
+ _ATLTRY
+ {
+ if (szName)
+ {
+ CHeapPtr<char> szNamePtr;
+ UINT nLen(0);
+
+ BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szName, &szNamePtr, &nLen);
+ if (bRet)
+ {
+ CStringA Name(szNamePtr, (int)nLen);
+
+ char szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
+
+ if (!AtlMimeCharsetFromCodePage(szCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
+ {
+ return FALSE;
+ }
+
+ CFixedStringT<CStringA, 256> strBuf;
+
+ int nBufLen = QEncodeGetRequiredLength(Name.GetLength(),
+ ATL_MAX_ENC_CHARSET_LENGTH)+1;
+
+ char * szBuf = strBuf.GetBuffer(nBufLen);
+ if (szBuf == NULL)
+ {
+ return FALSE;
+ }
+
+ DWORD dwLength = 0;
+ BOOL bEncoded = FALSE;
+ if (!GetEncodedString(Name, szCharset, szBuf, nBufLen, dwLength, bEncoded))
+ {
+ strBuf.ReleaseBuffer();
+ return FALSE;
+ }
+
+ strBuf.ReleaseBuffer(dwLength);
+
+ // append comma if there are existing recipients
+ if (str.GetLength() != 0)
+ {
+ str.Append(", ", 2);
+ }
+
+ if (bEncoded == FALSE)
+ {
+ // need to escape the string if no encoding
+ strBuf.Replace("\\", "\\\\");
+ strBuf.Replace("\"", "\\\"");
+
+ // wrap the unescaped name in quotes
+ str.Append("\"", 1);
+ }
+ str += strBuf;
+ if (bEncoded == FALSE)
+ {
+ // close quote
+ str.Append("\"", 1);
+ }
+ }
+ else
+ {
+ return bRet;
+ }
+ }
+
+ if (szAddress)
+ {
+ if (szName)
+ {
+ str.Append(" ", 1);
+ }
+ else
+ {
+ // append comma if there are existing recipients
+ if (str.GetLength() != 0)
+ {
+ str.Append(", ", 2);
+ }
+ }
+ str.Append("<", 1);
+ str += CT2CA(szAddress);
+ str.Append(">", 1);
+ }
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Get the formatted header information
+ inline BOOL FormatField(LPBYTE pbSrcData, int nSrcLen, LPBYTE pbDest,
+ DWORD* pnBufLen, DWORD dwFlags = 0) throw()
+ {
+ if(pnBufLen == NULL)
+ return FALSE;
+
+ int nRead = 0;
+
+ // 9 is the length of the maximum field name : "Subject :"
+ // we set that here for simplicity
+ int nLineLen = 9;
+ DWORD nWritten = 0;
+
+ //subtract 2 from these because it's easier for when we have
+ //to break lines with a CRLF (and tab if necessary)
+ int nMaxLineLength = ATLSMTP_MAX_LINE_LENGTH-3;
+ while (nRead < nSrcLen)
+ {
+ //if we're at the end of the line, break it
+ if (nLineLen == nMaxLineLength)
+ {
+ if( nWritten + 2 > *pnBufLen)
+ return FALSE;
+
+ *pbDest++ = '\r';
+ *pbDest++ = '\n';
+ nWritten+= 2;
+ nLineLen = -1;
+
+ if ((dwFlags & ATLSMTP_FORMAT_SMTP))
+ {
+ if(nWritten + 1 > *pnBufLen)
+ return FALSE;
+
+ *pbDest++ = '\t';
+ nWritten++;
+ nLineLen++;
+ }
+ }
+
+ //if we hit a CRLF, reset nLineLen
+ if (*pbSrcData == '\n' && nRead > 0 && *(pbSrcData-1) == '\r')
+ {
+ nLineLen = -1;
+ }
+
+ if(nWritten + 1 > *pnBufLen)
+ return FALSE;
+
+ *pbDest++ = *pbSrcData++;
+ nRead++;
+ nWritten++;
+ nLineLen++;
+ }
+
+ *pnBufLen = (DWORD)nWritten;
+
+ return TRUE;
+ }
+
+
+ // Get the formatted recipient information
+ inline BOOL FormatRecipients(LPBYTE pbSrcData, int nSrcLen, LPBYTE pbDest,
+ DWORD* pnBufLen) throw()
+ {
+
+ if(pnBufLen == NULL)
+ return FALSE;
+
+ int nRead = 0;
+ DWORD nWritten = 0;
+
+ while (nRead < nSrcLen)
+ {
+ if (*pbSrcData == ',')
+ {
+ if(nWritten + 4 > *pnBufLen)
+ return FALSE;
+
+ *pbDest++ = *pbSrcData++;
+ nRead++;
+ if (nRead+1 <= nSrcLen && *pbSrcData == ' ')
+ {
+ pbSrcData++;
+ nRead++;
+ }
+ *pbDest++ = '\r';
+ *pbDest++ = '\n';
+ *pbDest++ = '\t';
+ nWritten+= 4;
+
+ continue;
+ }
+
+ if(nWritten + 1 > *pnBufLen)
+ return FALSE;
+
+ *pbDest++ = *pbSrcData++;
+ nRead++;
+ nWritten++;
+ }
+
+ *pnBufLen = nWritten;
+
+ return TRUE;
+ }
+
+ // Get the required buffer size for the header
+ inline int GetRequiredBufferSize(int nMaxLineLength) throw()
+ {
+ const static DWORD DATELINE = 27;
+ const static DWORD FROMLINE = 10;
+ const static DWORD TOLINE = 6;
+ const static DWORD CCLINE = 6;
+ const static DWORD SUBJECTLINE = 11;
+
+ //data lengths (QEncoding potentially takes up more space than BEncoding,
+ //so default to it)
+ int nRequiredLength = QEncodeGetRequiredLength(m_strSenderName.GetLength(), ATL_MAX_ENC_CHARSET_LENGTH)
+ +QEncodeGetRequiredLength(m_strSubject.GetLength(), ATL_MAX_ENC_CHARSET_LENGTH);
+ nRequiredLength += m_strFrom.GetLength()+m_strTo.GetLength()+m_strCc.GetLength();
+
+ //Add space for date
+ nRequiredLength += DATELINE;
+
+ //Add space for From: line
+ nRequiredLength += FROMLINE;
+
+ //Add space for To: line
+ nRequiredLength += TOLINE;
+
+ //Add space for Cc: line
+ nRequiredLength += CCLINE;
+
+ //Add space for Subject: line
+ nRequiredLength += SUBJECTLINE;
+
+ //Add space for line breaks and tabs
+ nRequiredLength += 3*(nRequiredLength/nMaxLineLength);
+
+ //Trailing CRLF
+ nRequiredLength += 2;
+
+ return nRequiredLength;
+ }
+
+}; // class CMimeHeader
+
+
+// CMimeAttachment is an abstract base class for MIME message attachments.
+// It serves as a base class for CMimeFileAttachment and CMimeRawAttachment
+class CMimeAttachment : public CMimeBodyPart
+{
+protected:
+
+ // the encoding scheme (ATLSMTP_BASE64_ENCODE, ATLSMTP_UUENCODE, ATLSMTP_QP_ENCODE)
+ int m_nEncodingScheme;
+
+ // the content type of the attachment
+ CStringA m_ContentType;
+
+ // the character set
+ char m_szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
+
+ // the encode string ("base64", "quoted-printable", "uuencode")
+ char *m_pszEncodeString;
+
+ // the display name of the attachment
+ TCHAR m_szDisplayName[_MAX_FNAME];
+
+public:
+ CMimeAttachment() throw()
+ :m_nEncodingScheme(ATLSMTP_BASE64_ENCODE), m_pszEncodeString(NULL)
+ {
+ m_szCharset[0] = 0;
+ m_szDisplayName[0] = 0;
+ }
+
+ virtual ~CMimeAttachment() throw()
+ {
+ }
+
+ // CMimeFileAttachment and CMimeRawAttachment have to handle their own dumping
+ virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) = 0;
+
+ // Set the encoding scheme of the attachment
+ inline BOOL SetEncodingScheme(int nScheme) throw()
+ {
+ if (nScheme != ATLSMTP_BASE64_ENCODE && nScheme != ATLSMTP_UUENCODE && nScheme != ATLSMTP_QP_ENCODE)
+ {
+ return FALSE;
+ }
+
+ m_nEncodingScheme = nScheme;
+ return TRUE;
+ }
+
+ // Set the Content-Type of the attachment
+ inline BOOL SetContentType(LPCTSTR szContent) throw()
+ {
+ _ATLTRY
+ {
+ m_ContentType = CT2CA(szContent);
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Get the content type of the attachment
+ virtual inline LPCSTR GetContentType() throw()
+ {
+ return m_ContentType;
+ }
+
+ // Get the character set of the attachment
+ virtual inline LPCSTR GetCharset() throw()
+ {
+ return m_szCharset;
+ }
+
+ virtual ATL_NOINLINE CMimeBodyPart* Copy() = 0;
+
+ const CMimeAttachment& operator=(const CMimeAttachment& that) throw( ... )
+ {
+ if (this != &that)
+ {
+ m_nEncodingScheme = that.m_nEncodingScheme;
+ m_ContentType = that.m_ContentType;
+ Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szCharset);
+ m_pszEncodeString = that.m_pszEncodeString;
+ Checked::tcscpy_s(m_szDisplayName, _countof(m_szDisplayName), that.m_szDisplayName);
+ }
+
+ return *this;
+ }
+
+protected:
+
+ // Make the MIME header for the attachment
+ virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
+ {
+ // if no display name is specified, default to "rawdata"
+ return MakeMimeHeader(header, szBoundary, _T("rawdata"));
+ }
+
+ // Make the MIME header with the specified filename
+ virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary, LPCTSTR szFileName)
+ {
+ ATLENSURE(szBoundary != NULL);
+ ATLASSERT(szFileName != NULL);
+ ATLASSUME(m_pszEncodeString != NULL);
+
+ char szBegin[256];
+ if (*szBoundary)
+ {
+ // this is not the only body part
+ Checked::memcpy_s(szBegin, 256, ATLMIME_SEPARATOR, sizeof(ATLMIME_SEPARATOR));
+ Checked::memcpy_s(szBegin+6, 250, szBoundary, ATL_MIME_BOUNDARYLEN);
+ *(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = '\0';
+ }
+ else
+ {
+ // this is the only body part, so output the MIME header
+ Checked::memcpy_s(szBegin, 256, ATLMIME_VERSION, sizeof(ATLMIME_VERSION));
+ }
+
+ // Get file name with the path stripped out
+ TCHAR szFile[MAX_PATH+1];
+ TCHAR szExt[_MAX_EXT+1];
+ Checked::tsplitpath_s(szFileName, NULL, 0, NULL, 0, szFile, _countof(szFile), szExt, _countof(szExt));
+ Checked::tcscat_s(szFile, _countof(szFile), szExt);
+
+ _ATLTRY
+ {
+ CT2CAEX<MAX_PATH+1> szFileNameA(szFile);
+
+ CStringA szDisplayName(szFile);
+ if (m_szDisplayName[0] != '\0')
+ {
+ szDisplayName = CT2CAEX<_MAX_FNAME+1>(m_szDisplayName);
+ }
+
+ header.Format("%s\r\nContent-Type: %s;\r\n\tcharset=\"%s\"\r\n\tname=\"%s\"\r\n"
+ "Content-Transfer-Encoding: %s\r\nContent-Disposition: attachment;\r\n\tfilename=\"%s\"\r\n\r\n",
+ szBegin, (LPCSTR) m_ContentType, m_szCharset, (LPCSTR) szDisplayName, m_pszEncodeString, (LPCSTR) szFileNameA);
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Get encoding information
+ inline BOOL GetEncodingInformation(int* pnRequiredLength, int* pnLineLength)
+ {
+ ATLENSURE(pnRequiredLength != NULL);
+ ATLENSURE(pnLineLength != NULL);
+
+ switch(m_nEncodingScheme)
+ {
+ case ATLSMTP_BASE64_ENCODE:
+ m_pszEncodeString = "base64";
+ *pnLineLength = ATLSMTP_MAX_BASE64_LINE_LENGTH;
+ *pnRequiredLength = Base64EncodeGetRequiredLength(ATLSMTP_MAX_BASE64_LINE_LENGTH);
+ break;
+ case ATLSMTP_UUENCODE:
+ m_pszEncodeString ="uuencode";
+ *pnLineLength = ATLSMTP_MAX_UUENCODE_LINE_LENGTH;
+ *pnRequiredLength = UUEncodeGetRequiredLength(ATLSMTP_MAX_UUENCODE_LINE_LENGTH);
+ break;
+ case ATLSMTP_QP_ENCODE:
+ m_pszEncodeString = "quoted-printable";
+ *pnLineLength = ATLSMTP_MAX_QP_LINE_LENGTH;
+ *pnRequiredLength = QPEncodeGetRequiredLength(ATLSMTP_MAX_QP_LINE_LENGTH);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+}; // class CMimeAttachment
+
+
+// CMimeFileAttachment represents a MIME file attachment body part
+class CMimeFileAttachment : public CMimeAttachment
+{
+
+protected:
+ // The filename
+ TCHAR m_szFileName[MAX_PATH+1];
+
+public:
+ CMimeFileAttachment() throw()
+ {
+ m_szFileName[0] = 0;
+ }
+
+ virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
+ {
+ CAutoPtr<CMimeFileAttachment> pNewAttachment;
+ ATLTRY(pNewAttachment.Attach(new CMimeFileAttachment));
+ if (pNewAttachment)
+ *pNewAttachment = *this;
+
+ return pNewAttachment.Detach();
+ }
+
+ const CMimeFileAttachment& operator=(const CMimeFileAttachment& that) throw( ... )
+ {
+ if (this != &that)
+ {
+ CMimeAttachment::operator=(that);
+ Checked::tcscpy_s(m_szFileName, _countof(m_szFileName), that.m_szFileName);
+ }
+
+ return *this;
+ }
+
+
+ // Initialize the file attachment
+ // szFileName - the actual file name
+ // szDisplayName - the display name for the file (optional)
+ // pMultiLanguage - the IMulitLanguage pointer for codepage to charset conversion (optional)
+ // uiCodePage - the code page (optional)
+ inline BOOL Initialize(LPCTSTR szFileName, LPCTSTR szDisplayName = NULL, IMultiLanguage* pMultiLanguage = NULL, UINT uiCodePage = 0) throw()
+ {
+ if (!AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
+ return FALSE;
+
+ if( _tcslen(szFileName) > MAX_PATH )
+ {
+ return FALSE;
+ }
+ Checked::tcscpy_s(m_szFileName, _countof(m_szFileName), szFileName);
+
+ if (szDisplayName)
+ {
+ // use the user-specified display name
+ size_t nLen = _tcslen(szDisplayName)+1;
+ if (nLen <= _countof(m_szDisplayName))
+ {
+ Checked::tcscpy_s(m_szDisplayName, _countof(m_szDisplayName), szDisplayName);
+ }
+ else
+ {
+ Checked::tcsncpy_s(m_szDisplayName, _countof(m_szDisplayName), szDisplayName, _countof(m_szDisplayName) - 4);
+ Checked::tcscpy_s(m_szDisplayName + _countof(m_szDisplayName) - 4, 4, _T("..."));
+ }
+ }
+ else
+ {
+ // otherwise there is no display name
+ *m_szDisplayName = '\0';
+ }
+ return TRUE;
+ }
+
+ // Dump the data for the file attachment
+ virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) throw()
+ {
+ if ((pOverlapped == NULL) || (szBoundary == NULL))
+ {
+ return FALSE;
+ }
+
+ int nLineLength = 0;
+ int nRequiredLength = 0;
+
+ if (!GetEncodingInformation(&nRequiredLength, &nLineLength))
+ return FALSE;
+
+ //Try to open the file that is being attached
+ CAtlFile readFile;
+ if (FAILED(readFile.Create(m_szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)))
+ return FALSE;
+
+ //Make the mime header
+ CStringA header;
+ if (!MakeMimeHeader(header, szBoundary, m_szFileName))
+ {
+ return FALSE;
+ }
+
+ //Try to send the mime header
+ if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)header), header.GetLength(), pOverlapped))
+ {
+ return FALSE;
+ }
+
+ int nGetLines = ATLSMTP_GET_LINES;
+
+ nRequiredLength *= nGetLines;
+
+ //dwToGet is the total number of characters to attempt to get
+ DWORD dwToGet = (DWORD)nGetLines*nLineLength;
+
+ //allocate the data array
+ CHeapPtr<BYTE> spData;
+ if (!spData.Allocate(dwToGet+1))
+ return FALSE;
+
+// if double buffering is defined, create two buffers
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ CHeapPtr<char> buffer1;
+ if (!buffer1.Allocate(nRequiredLength+3))
+ return FALSE;
+
+ CHeapPtr<char> buffer2;
+ if (!buffer2.Allocate(nRequiredLength+3))
+ return FALSE;
+
+ char* currBuffer = buffer1;
+ char* prevBuffer = NULL;
+ int nCurrBuffer = 0;
+ DWORD dwPrevLength = 0;
+#else
+ CHeapPtr<char> currBuffer;
+ if (!currBuffer.Allocate(nRequiredLength+3))
+ return FALSE;
+
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ int nEncodedLength = nRequiredLength;
+ BOOL bRet = FALSE;
+ DWORD dwRead = 0;
+ DWORD dwTotalRead = 0;
+ DWORD dwCurrRead = 0;
+
+ do
+ {
+ do
+ {
+ //Read a chunk of data from the file increment buffer offsets and amount to read
+ //based on what's already been read in this iteration of the loop
+ HRESULT hr = readFile.Read(((LPBYTE)spData)+dwCurrRead, dwToGet-dwCurrRead, dwRead);
+ if (FAILED(hr))
+ {
+ if (hr != AtlHresultFromWin32(ERROR_MORE_DATA))
+ {
+ return FALSE;
+ }
+ }
+ dwCurrRead += dwRead;
+
+ } while (dwRead != 0 && dwCurrRead < dwToGet);
+
+ //reset nEncodedLength
+ nEncodedLength = nRequiredLength;
+ switch (m_nEncodingScheme)
+ {
+ case ATLSMTP_BASE64_ENCODE:
+ //if we are at the end of input (dwCurrRead < dwToGet), output the trailing padding if necessary
+ //(ATL_FLAG_NONE)
+ bRet = Base64Encode(spData, dwCurrRead, currBuffer, &nEncodedLength,
+ (dwCurrRead < dwToGet ? ATL_BASE64_FLAG_NONE: ATL_BASE64_FLAG_NOPAD));
+ //Base64Encoding needs explicit CRLF added
+ if (dwCurrRead < dwToGet)
+ {
+ currBuffer[nEncodedLength++] = '\r';
+ currBuffer[nEncodedLength++] = '\n';
+ }
+ break;
+ case ATLSMTP_UUENCODE:
+ //if we are at the beginning of the input, output the header (ATL_UUENCODE_HEADER)
+ //if we are the end of input (dwCurrRead < dwToGet), output the 'end'
+ //we are encoding for purposes of sending mail, so stuff dots (ATL_UUENCODE_DOT)
+ bRet = UUEncode(spData, dwCurrRead, currBuffer, &nEncodedLength, m_szFileName,
+ (dwTotalRead > 0 ? 0 : ATLSMTP_UUENCODE_HEADER) |
+ (dwCurrRead < dwToGet ? ATLSMTP_UUENCODE_END : 0) |
+ ((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_UUENCODE_DOT : 0));
+ break;
+ case ATLSMTP_QP_ENCODE:
+ //we are encoding for purposes of sending mail, so stuff dots
+ bRet = QPEncode(spData, dwCurrRead, currBuffer, &nEncodedLength,
+ ((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_QPENCODE_DOT : 0) |
+ (dwCurrRead < dwToGet ? 0 : ATLSMTP_QPENCODE_TRAILING_SOFT));
+ break;
+ }
+ //try to send the encoded data
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ if (bRet)
+ {
+ bRet = AtlSmtpSendOverlapped(hFile, currBuffer, nEncodedLength,
+ prevBuffer, dwPrevLength, pOverlapped);
+ }
+
+ //swap the buffers
+ dwPrevLength = nEncodedLength;
+ prevBuffer = currBuffer;
+ currBuffer = (nCurrBuffer == 0 ? buffer2 : buffer1);
+ nCurrBuffer = (nCurrBuffer == 0 ? 1 : 0);
+#else
+ if (bRet)
+ {
+ bRet = AtlSmtpSendAndWait(hFile, currBuffer, nEncodedLength, pOverlapped);
+ }
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ dwTotalRead += dwCurrRead;
+ if (dwRead != 0)
+ dwCurrRead = 0;
+
+ nEncodedLength = nRequiredLength;
+
+ } while (dwRead != 0 && bRet);
+
+ //ensure that the last Send sent all the data
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ DWORD dwWritten = 0, dwErr = 0;
+ if (!GetOverlappedResult(hFile, pOverlapped, &dwWritten, TRUE))
+ {
+ if ((dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
+ {
+ bRet = FALSE;
+ }
+ else if (dwWritten < dwPrevLength)
+ {
+ bRet = AtlSmtpSendAndWait(hFile, prevBuffer+dwWritten,
+ dwPrevLength-dwWritten, pOverlapped);
+ }
+ }
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ //for uuencoding, if the last chunk read was of size dwToGet, but it was also the end of the file,
+ //the "end" keyword will not get encoded, so a check is necessary
+ if (m_nEncodingScheme == ATLSMTP_UUENCODE && dwCurrRead == dwToGet)
+ {
+ bRet = UUEncode(spData, 0, currBuffer, &nEncodedLength, m_szFileName,
+ (dwFlags & ATLSMTP_FORMAT_SMTP ? ATLSMTP_UUENCODE_DOT : 0) |
+ ATLSMTP_UUENCODE_END);
+ if (bRet)
+ {
+ bRet = AtlSmtpSendAndWait(hFile, currBuffer, nEncodedLength, pOverlapped);
+ }
+ }
+
+ return bRet;
+ }
+}; // class CMimeFileAttachment
+
+// CMimeRawAttachment represents a file attachment MIME body part.
+// The data provided is not a file, but a blob of raw data.
+class CMimeRawAttachment : public CMimeAttachment
+{
+protected:
+
+ //the raw data
+ void* m_pvRaw;
+
+ //the length
+ DWORD m_dwLength;
+
+ //whether or not we own it
+ bool m_bShared;
+
+public:
+ CMimeRawAttachment() throw()
+ :m_dwLength(0), m_bShared(false), m_pvRaw(NULL)
+ {
+ }
+
+ ~CMimeRawAttachment() throw()
+ {
+ //If we own the raw data, free it
+ if (!m_bShared && m_pvRaw)
+ free(m_pvRaw);
+ }
+
+ virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
+ {
+ CAutoPtr<CMimeRawAttachment> pNewAttachment;
+ ATLTRY(pNewAttachment.Attach(new CMimeRawAttachment));
+ if (pNewAttachment)
+ *pNewAttachment = *this;
+
+ return pNewAttachment.Detach();
+ }
+
+ const CMimeRawAttachment& operator=(const CMimeRawAttachment& that) throw( ... )
+ {
+ if (this != &that)
+ {
+ CMimeAttachment::operator=(that);
+ if (!m_bShared && m_pvRaw)
+ free(m_pvRaw);
+
+ m_bShared = that.m_bShared;
+ m_dwLength = that.m_dwLength;
+
+ if (m_bShared)
+ {
+ m_pvRaw = that.m_pvRaw;
+ }
+ else
+ {
+ m_pvRaw = malloc(m_dwLength);
+ if (m_pvRaw)
+ {
+ Checked::memcpy_s(m_pvRaw, m_dwLength, that.m_pvRaw, m_dwLength);
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ // Initialize the attachment
+ // pData - the data
+ // nDataLength - the size of pData in BYTEs
+ // bCopyData - flag specifying whether CMimeRawAttachment should make a copy of the data (optional)
+ // pMultiLanguage - the IMultiLanguage pointer for codepage to character set conversion (optional)
+ // uiCodePage - the codepage (optional)
+ inline BOOL Initialize(void* pData, DWORD nDataLength, BOOL bCopyData = TRUE, LPCTSTR szDisplayName = NULL,
+ IMultiLanguage* pMultiLanguage = NULL, UINT uiCodePage = 0) throw()
+ {
+ // if we're already attached to some data, and it's not shared, free it
+ if (m_pvRaw && !m_bShared)
+ free(m_pvRaw);
+ m_pvRaw = NULL;
+
+ m_dwLength = nDataLength;
+ if (bCopyData)
+ {
+ m_pvRaw = calloc(sizeof(BYTE),m_dwLength);
+ if (!m_pvRaw)
+ {
+ return FALSE;
+ }
+ Checked::memcpy_s(m_pvRaw, m_dwLength, pData, m_dwLength);
+ m_bShared = false;
+ }
+ else
+ {
+ m_pvRaw = pData;
+ m_bShared = true;
+ }
+
+ if (!AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
+ return FALSE;
+
+ if (szDisplayName)
+ {
+ // use the user-specified display name
+ Checked::tcscpy_s(m_szDisplayName, _countof(m_szDisplayName), szDisplayName);
+ m_szDisplayName[_countof(m_szDisplayName)-1] = 0;
+ }
+ else
+ {
+ // no display name
+ *m_szDisplayName = '\0';
+ }
+ return TRUE;
+ }
+
+ // Output the data--similar to CFileAttachment::WriteData
+ // See CFileAttachment::WriteData for comments
+ virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) throw()
+ {
+ if ((pOverlapped == NULL) || (szBoundary == NULL))
+ {
+ return FALSE;
+ }
+
+ if (!m_pvRaw)
+ return FALSE;
+
+ int nLineLength = 0, nRequiredLength = 0;
+ if (!GetEncodingInformation(&nRequiredLength, &nLineLength))
+ return FALSE;
+
+ CStringA header;
+
+ if (!MakeMimeHeader(header, szBoundary))
+ {
+ return FALSE;
+ }
+
+ if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)header), header.GetLength(), pOverlapped))
+ {
+ return FALSE;
+ }
+
+ int nGetLines = ATLSMTP_GET_LINES;
+ DWORD dwCurrChunk = 0;
+ nRequiredLength *= nGetLines;
+ DWORD dwToGet = (DWORD)nGetLines*nLineLength;
+ int nDestLen = nRequiredLength;
+ BOOL bRet = FALSE;
+ DWORD dwRead = 0;
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ CHeapPtr<char> buffer1;
+ if (!buffer1.Allocate(nRequiredLength+3))
+ return FALSE;
+
+ CHeapPtr<char> buffer2;
+ if (!buffer2.Allocate(nRequiredLength+3))
+ return FALSE;
+
+ char* currBuffer = buffer1;
+ char* prevBuffer = NULL;
+ int nCurrBuffer = 0;
+ DWORD dwPrevLength = 0;
+#else
+ CHeapPtr<char> currBuffer;
+ if (!currBuffer.Allocate(nRequiredLength+3))
+ return FALSE;
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ do
+ {
+ if ((m_dwLength-dwRead) <= dwToGet)
+ dwCurrChunk = m_dwLength-dwRead;
+ else
+ dwCurrChunk = dwToGet;
+ switch(m_nEncodingScheme)
+ {
+ case ATLSMTP_BASE64_ENCODE:
+ bRet = Base64Encode(((LPBYTE)(m_pvRaw))+dwRead, dwCurrChunk, currBuffer, &nDestLen,
+ (dwRead < m_dwLength) ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOPAD);
+ if (dwRead+dwCurrChunk == m_dwLength)
+ {
+ currBuffer[nDestLen++] = '\r';
+ currBuffer[nDestLen++] = '\n';
+ }
+ break;
+ case ATLSMTP_UUENCODE:
+ bRet = UUEncode(((LPBYTE)(m_pvRaw))+dwRead, dwCurrChunk, currBuffer, &nDestLen, _T("rawdata"),
+ (dwRead > 0 ? 0 : ATLSMTP_UUENCODE_HEADER) |
+ (dwRead+dwCurrChunk == m_dwLength ? ATLSMTP_UUENCODE_END : 0) |
+ ((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_UUENCODE_DOT : 0));
+ break;
+ case ATLSMTP_QP_ENCODE:
+ bRet = QPEncode(((LPBYTE)(m_pvRaw))+dwRead, dwCurrChunk, currBuffer, &nDestLen,
+ ((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_QPENCODE_DOT : 0) |
+ (dwRead+dwCurrChunk == m_dwLength ? 0 : ATLSMTP_QPENCODE_TRAILING_SOFT));
+ break;
+ }
+ if (!bRet)
+ break;
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ bRet = AtlSmtpSendOverlapped(hFile, currBuffer, nDestLen, prevBuffer, dwPrevLength, pOverlapped);
+ dwPrevLength = (DWORD)nDestLen;
+ prevBuffer = currBuffer;
+ currBuffer = (nCurrBuffer == 0 ? buffer2 : buffer1);
+ nCurrBuffer = (nCurrBuffer == 0 ? 1 : 0);
+#else
+ bRet = AtlSmtpSendAndWait(hFile, currBuffer, nDestLen, pOverlapped);
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ nDestLen = nRequiredLength;
+ dwRead += dwCurrChunk;
+ } while (bRet && (dwRead < m_dwLength));
+
+ //ensure all data is sent from prevBuffer
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ DWORD dwWritten = 0, dwErr = 0;
+ if (!GetOverlappedResult(hFile, pOverlapped, &dwWritten, TRUE))
+ {
+ if ((dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
+ bRet = FALSE;
+ else if (dwWritten < dwPrevLength)
+ bRet = AtlSmtpSendAndWait(hFile, prevBuffer+dwWritten, dwPrevLength-dwWritten, pOverlapped);
+ }
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ return bRet;
+ }
+}; // class CMimeRawAttachment
+
+
+// CMimeText - represents a text body part in MIME body
+class CMimeText : public CMimeBodyPart
+{
+protected:
+
+ // the text
+ CHeapPtr<char> m_szText;
+
+ // the character set
+ char m_szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
+
+ // the text length
+ int m_nTextLen;
+
+public:
+ CMimeText() throw()
+ :m_nTextLen(0)
+ {
+ Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, ATLSMTP_DEFAULT_CSET);
+ }
+
+ virtual ~CMimeText() throw()
+ {
+ }
+
+ // Get the content type
+ virtual inline LPCSTR GetContentType() throw()
+ {
+ return "text/plain";
+ }
+
+ // Get the character set
+ virtual inline LPCSTR GetCharset() throw()
+ {
+ return m_szCharset;
+ }
+
+ virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
+ {
+ CAutoPtr<CMimeText> pNewText;
+ ATLTRY(pNewText.Attach(new CMimeText));
+ if (pNewText)
+ *pNewText = *this;
+
+ return pNewText.Detach();
+ }
+
+ const CMimeText& operator=(const CMimeText& that) throw( ... )
+ {
+ if (this != &that)
+ {
+ m_nTextLen = that.m_nTextLen;
+ Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szCharset);
+ m_szText.Free();
+ if (m_szText.AllocateBytes(m_nTextLen) != false)
+ {
+ Checked::memcpy_s((char *)m_szText, m_nTextLen, (char *)that.m_szText, m_nTextLen);
+ }
+ }
+
+ return *this;
+ }
+
+ // Initialize the body part
+ // szText - the text (required)
+ // nTextLen - the text length in bytes (optional--if not specified a _tcslen will be done)
+ // pMultiLanguage - the IMultiLanguagte pointer for converting codepages to MIME character sets (optional)
+ // uiCodePage - the codepage
+ inline BOOL Initialize(LPCTSTR szText, int nTextLen = -1, IMultiLanguage* pMultiLanguage = NULL, UINT uiCodePage = 0) throw()
+ {
+ BOOL bRet = TRUE;
+
+ // if IMultiLanguage is there, respect the codepage
+ if (pMultiLanguage)
+ {
+ CHeapPtr<char> szTextPtr;
+ UINT nLen(0);
+
+ bRet = AtlMimeConvertString(pMultiLanguage, uiCodePage, szText, &szTextPtr, &nLen);
+ if (bRet)
+ {
+ m_szText.Free();
+ m_szText.Attach(szTextPtr.Detach());
+ m_nTextLen = nLen;
+ }
+ }
+ else // no multilanguage support
+ {
+ if (nTextLen < 0)
+ {
+ nTextLen = (int) _tcslen(szText);
+ nTextLen*= sizeof(TCHAR);
+ }
+
+ m_szText.Free();
+ if (m_szText.AllocateBytes(nTextLen) != false)
+ {
+ Checked::memcpy_s((char *)m_szText, nTextLen, szText, nTextLen);
+ m_nTextLen = nTextLen;
+ }
+ }
+
+ if (bRet)
+ {
+ bRet = AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
+ }
+
+ return bRet;
+ }
+
+ // Dump the data to hFile
+ virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) throw()
+ {
+ if ((pOverlapped == NULL) || (szBoundary == NULL))
+ {
+ return FALSE;
+ }
+
+ CStringA strHeader;
+ char sendBuffer[ATLSMTP_READBUFFER_SIZE];
+ LPSTR pSendBuffer = sendBuffer;
+ LPSTR szText = m_szText;
+
+ if (!MakeMimeHeader(strHeader, szBoundary))
+ {
+ return FALSE;
+ }
+
+ //copy the header into the sendbuffer
+ int nWritten = strHeader.GetLength();
+ if(nWritten > ATLSMTP_READBUFFER_SIZE)
+ return FALSE;
+
+ Checked::memcpy_s(pSendBuffer, ATLSMTP_READBUFFER_SIZE, (LPCSTR)strHeader, nWritten);
+ pSendBuffer+= nWritten;
+ int nRead = 0;
+ int nLineLen = 0;
+
+ //subtract 2 from these because it's easier for when we have
+ //to break lines with a CRLF
+ int nMaxLineLength = ATLSMTP_MAX_LINE_LENGTH-2;
+ int nMaxBufferSize = ATLSMTP_READBUFFER_SIZE-2;
+ while (nRead <= m_nTextLen)
+ {
+ //if the buffer is full or we've reached the end of the text,
+ //send it
+ if (nWritten >= nMaxBufferSize || nRead == m_nTextLen)
+ {
+ if (!AtlSmtpSendAndWait(hFile, sendBuffer, nWritten, pOverlapped))
+ return FALSE;
+ nWritten = 0;
+ pSendBuffer = sendBuffer;
+ if (nRead == m_nTextLen)
+ {
+ break; // job done, no need to run the code below
+ }
+ }
+
+ //if we're at the end of the line, break it
+ if (nLineLen == nMaxLineLength)
+ {
+ if(nWritten + 2 > ATLSMTP_READBUFFER_SIZE)
+ return FALSE;
+ *pSendBuffer++ = '\r';
+ *pSendBuffer++ = '\n';
+ nWritten+= 2;
+ nLineLen = -1;
+ continue;
+ }
+
+ //stuff dots at the start of the line
+ if (nLineLen == 0 && (dwFlags & ATLSMTP_FORMAT_SMTP) && *szText == '.')
+ {
+ if(nWritten + 1 > ATLSMTP_READBUFFER_SIZE)
+ return FALSE;
+ *pSendBuffer++ = '.';
+ nWritten++;
+ nLineLen++;
+ continue;
+ }
+
+ //if we hit a CRLF, reset nLineLen
+ if (*szText == '\n' && nRead > 0 && *(szText-1) == '\r')
+ nLineLen = -1;
+
+ if(nWritten + 1 > ATLSMTP_READBUFFER_SIZE)
+ return FALSE;
+ *pSendBuffer++ = (*szText++);
+ nRead++;
+ nWritten++;
+ nLineLen++;
+ }
+
+ return TRUE;
+ }
+
+protected:
+
+ // Make the MIME header
+ virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
+ {
+ char szBegin[256];
+ if (*szBoundary)
+ {
+ // this is not the only body part
+ Checked::memcpy_s(szBegin, sizeof(szBegin), ATLMIME_SEPARATOR, sizeof(ATLMIME_SEPARATOR));
+ Checked::memcpy_s(szBegin+6, sizeof(szBegin)-6, szBoundary, ATL_MIME_BOUNDARYLEN);
+ *(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = '\0';
+ }
+ else
+ {
+ // this is the only body part, so output the full MIME header
+ Checked::memcpy_s(szBegin, sizeof(szBegin), ATLMIME_VERSION, sizeof(ATLMIME_VERSION));
+ }
+
+ _ATLTRY
+ {
+ header.Format("%s\r\nContent-Type: text/plain;\r\n\tcharset=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n",
+ szBegin, m_szCharset);
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+}; // class CMimeText
+
+
+// CMimeMessage - the MIME message class. Represents a full MIME message
+class CMimeMessage : public CMimeHeader
+{
+protected:
+
+ // The list of the MIME body parts
+ CAutoPtrList<CMimeBodyPart> m_BodyParts;
+
+ // The display name of the message
+ char m_szDisplayName[MAX_PATH+1];
+
+public:
+ CMimeMessage(IMultiLanguage *pMultiLanguage = NULL) throw()
+ {
+ Initialize(pMultiLanguage);
+ Checked::memcpy_s(m_szDisplayName, MAX_PATH+1, ATLMIME_EMAIL, sizeof(ATLMIME_EMAIL));
+ }
+
+ virtual ~CMimeMessage() throw()
+ {
+ RemoveParts();
+ }
+
+ void RemoveParts() throw()
+ {
+ m_BodyParts.RemoveAll();
+ }
+
+
+ virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
+ {
+ CAutoPtr<CMimeMessage> pNewMessage;
+ ATLTRY(pNewMessage.Attach(new CMimeMessage));
+ if (pNewMessage)
+ *pNewMessage = *this;
+
+ return pNewMessage.Detach();
+ }
+
+
+ const CMimeMessage& operator=(const CMimeMessage& that) throw( ... )
+ {
+ if (this != &that)
+ {
+ CMimeHeader::operator=(that);
+ Checked::strcpy_s(m_szDisplayName, MAX_PATH+1, that.m_szDisplayName);
+
+ RemoveParts();
+ POSITION pos = that.m_BodyParts.GetHeadPosition();
+ while (pos != NULL)
+ {
+ CAutoPtr<CMimeBodyPart> pCopy(that.m_BodyParts.GetNext(pos)->Copy());
+ if (pCopy)
+ {
+ m_BodyParts.AddTail(pCopy);
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ // Set the display name of the message
+ inline BOOL SetDisplayName(LPCTSTR szDisplayName) throw()
+ {
+ if (szDisplayName == NULL)
+ {
+ return FALSE;
+ }
+
+ _ATLTRY
+ {
+ CT2CA szDisplayNameA(szDisplayName);
+ if (szDisplayNameA == NULL || strlen(szDisplayNameA) > MAX_PATH)
+ return FALSE;
+ Checked::strcpy_s(m_szDisplayName, MAX_PATH+1, szDisplayNameA);
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Add some text to the message at position nPos in the body parts list
+ // szText - the text
+ // nTextLen - the size of the text in bytes (optional - if not specified a _tcslen will be done)
+ // nPos - the position in the message at which to insert the text (optional)
+ // uiCodePage - the codepage (optional)
+ inline BOOL AddText(LPCTSTR szText, int nTextLen = -1, int nPos = 1, UINT uiCodePage = 0) throw()
+ {
+ if (szText == NULL)
+ return FALSE;
+
+ if (nPos < 1)
+ {
+ nPos = 1;
+ }
+
+ CAutoPtr<CMimeBodyPart> spNewText;
+ CMimeText *pNewText = NULL;
+ ATLTRY(spNewText.Attach(pNewText = new CMimeText()));
+ if (!spNewText || !pNewText)
+ return FALSE;
+
+ BOOL bRet = pNewText->Initialize(szText, nTextLen, m_spMultiLanguage, uiCodePage);
+ if (bRet)
+ {
+ _ATLTRY
+ {
+ POSITION currPos = m_BodyParts.FindIndex(nPos-1);
+
+ if (!currPos)
+ {
+ if (!m_BodyParts.AddTail(spNewText))
+ bRet = FALSE;
+ }
+ else
+ {
+ if (!m_BodyParts.InsertBefore(currPos, spNewText))
+ bRet = FALSE;
+ }
+
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+
+ return bRet;
+ }
+
+ // Dump the data
+ virtual BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary=NULL, DWORD dwFlags = 0) throw()
+ {
+ if (pOverlapped == NULL)
+ {
+ return FALSE;
+ }
+
+ // Make the MIME boundary for this message
+ char szBoundaryBuf[ATL_MIME_BOUNDARYLEN+1];
+ if(MakeBoundary(szBoundaryBuf,ATL_MIME_BOUNDARYLEN+1) == FALSE)
+ return FALSE;
+
+ // if the passed boundary is valid, this is an attached message
+ if (szBoundary && *szBoundary != '\0')
+ {
+ _ATLTRY
+ {
+ // output the MIME header for a message attachment
+ CStringA strHeader;
+ strHeader.Format("\r\n\r\n--%s\r\nContent-Type: message/rfc822\r\n\tname=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n"
+ "Content-Disposition: attachment;\r\n\tfilename=\"%s\"\r\n\r\n",
+ szBoundary, m_szDisplayName, m_szDisplayName);
+
+ if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)strHeader), strHeader.GetLength(), pOverlapped))
+ {
+ return FALSE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ if (!CMimeHeader::WriteData(hFile, pOverlapped, szBoundaryBuf, dwFlags))
+ return FALSE;
+
+ // Create and output the header
+ CStringA strHeader;
+
+ if (!MakeMimeHeader(strHeader, szBoundaryBuf))
+ {
+ return FALSE;
+ }
+
+ if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)strHeader), strHeader.GetLength(), pOverlapped))
+ {
+ return FALSE;
+ }
+
+ CMimeBodyPart* pCurrPart;
+ POSITION currPos = m_BodyParts.GetHeadPosition();
+
+ //Dump the body parts
+ while (currPos != NULL)
+ {
+ pCurrPart = m_BodyParts.GetAt(currPos);
+ if (!pCurrPart->WriteData(hFile, pOverlapped, szBoundaryBuf, dwFlags))
+ {
+ return FALSE;
+ }
+ m_BodyParts.GetNext(currPos);
+ }
+
+ char szBuf[ATL_MIME_BOUNDARYLEN+(sizeof("\r\n\r\n--%s--\r\n"))];
+ //output a trailing boundary
+ if (*szBoundaryBuf)
+ {
+ int nBufLen = sprintf_s(szBuf, ATL_MIME_BOUNDARYLEN+(sizeof("\r\n\r\n--%s--\r\n")),
+ "\r\n\r\n--%s--\r\n", szBoundaryBuf);
+ if ((nBufLen < 0) || (!AtlSmtpSendAndWait(hFile, szBuf, nBufLen, pOverlapped)))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ // Attach a file.
+ // szFileName - the filename
+ // szDisplayName - the display name (optional)
+ // szContentType - the content type (optional - defaults to NULL -- lookup will be attempted, otherwise default to application/octet-stream)
+ // nEncodingScheme - the encoding scheme to use for the attachment (optional - defaults to base64
+ // uiCodePage - the codepage (optional)
+ inline BOOL AttachFile(LPCTSTR szFileName, LPCTSTR szDisplayName = NULL, LPCTSTR szContentType = NULL,
+ int nEncodingScheme = ATLSMTP_BASE64_ENCODE, UINT uiCodepage = 0)
+ {
+ if (szFileName == NULL)
+ return FALSE;
+
+ CAutoPtr<CMimeBodyPart> spFileAttach;
+ CMimeFileAttachment* pFileAttach = NULL;
+ ATLTRY(spFileAttach.Attach(pFileAttach = new CMimeFileAttachment()));
+ if (!spFileAttach || !pFileAttach)
+ return FALSE;
+
+ BOOL bRet = pFileAttach->Initialize(szFileName, szDisplayName, m_spMultiLanguage, uiCodepage);
+
+ if (bRet)
+ bRet = pFileAttach->SetEncodingScheme(nEncodingScheme);
+
+ CString strContentType;
+ if (bRet && (szContentType == NULL))
+ {
+ if (GetContentTypeFromFileName(szFileName, strContentType) != ERROR_OUTOFMEMORY)
+ {
+ szContentType = strContentType;
+ }
+ else
+ {
+ bRet = FALSE;
+ }
+ }
+
+ _ATLTRY
+ {
+ if (bRet)
+ {
+ bRet = pFileAttach->SetContentType(szContentType);
+ if (bRet)
+ {
+ if (!m_BodyParts.AddTail(spFileAttach))
+ {
+ bRet = FALSE;
+ }
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+
+ return bRet;
+ }
+
+ // Attach some raw data
+ // pRawData - the data
+ // nDataLength - the size of the data in bytes
+ // nEncodingScheme - the encoding scheme to use for the attachment (optional - defaults to base64
+ // uiCodePage - the codepage (optional)
+ inline BOOL AttachRaw(void* pRawData, DWORD dwDataLength, int nEncodingScheme = ATLSMTP_BASE64_ENCODE, BOOL bCopyData = TRUE,
+ LPCTSTR szDisplayName = NULL, LPCTSTR szContentType = _T("application/octet-stream"), UINT uiCodepage = 0)
+ {
+ if (!pRawData)
+ return FALSE;
+
+ CAutoPtr<CMimeBodyPart> spRawAttach;
+ CMimeRawAttachment* pRawAttach;
+ ATLTRY(spRawAttach.Attach(pRawAttach = new CMimeRawAttachment()));
+ if (!spRawAttach)
+ {
+ return FALSE;
+ }
+
+ BOOL bRet = pRawAttach->Initialize(pRawData, dwDataLength, bCopyData, szDisplayName, m_spMultiLanguage, uiCodepage);
+
+ if (bRet)
+ bRet = pRawAttach->SetEncodingScheme(nEncodingScheme);
+ if (bRet)
+ bRet = pRawAttach->SetContentType(szContentType);
+
+ _ATLTRY
+ {
+ if (bRet)
+ if(!m_BodyParts.AddTail(spRawAttach))
+ bRet = FALSE;
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+
+ return bRet;
+ }
+
+ // Attach a CMimeMessage
+ // pMsg - pointer to the Msg object
+ inline BOOL AttachMessage(CMimeMessage* pMsg) throw( ... )
+ {
+ if (!pMsg)
+ return FALSE;
+
+ _ATLTRY
+ {
+ CAutoPtr<CMimeBodyPart> spMsg(pMsg->Copy());
+ if (!m_BodyParts.AddTail(spMsg))
+ return FALSE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+protected:
+ // Make the MIME header
+ virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
+ {
+ _ATLTRY
+ {
+ if (!*szBoundary)
+ {
+ header.Format("X-Priority: %d\r\n%s", m_nPriority, (LPCSTR) m_XHeader);
+ }
+ else if (m_BodyParts.GetCount() > 1)
+ {
+ header.Format("X-Priority: %d\r\n%sMIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"%s\"\r\n",
+ m_nPriority, (LPCSTR) m_XHeader, szBoundary);
+ }
+ return TRUE;
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ // Make the MIME boundary
+ inline BOOL MakeBoundary(__out_ecount_z(nBufLen) LPSTR szBoundary, __in int nBufLen)
+ {
+ ATLENSURE(szBoundary != NULL);
+
+ if(nBufLen < 1)
+ {
+ return FALSE;
+ }
+
+ if (m_BodyParts.GetCount() < 2)
+ {
+ *szBoundary = '\0';
+ }
+ else
+ {
+ int ret = sprintf_s(szBoundary, nBufLen, "------=_Next_Part_%.10u.%.3u", GetTickCount(), rand()%1000);
+ if (ret == -1 || ret >= nBufLen)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+}; // class CMimeMessage
+
+} // namespace ATL
+#pragma pack(pop)
+
+#ifndef _CPPUNWIND
+#pragma warning (pop)
+#endif //_CPPUNWIND
+
+#pragma warning(pop)
+
+#endif // __ATLMIME_H__
diff --git a/include/atl/atlperf.h b/include/atl/atlperf.h
new file mode 100644
index 000000000..a1e8d9ea4
--- /dev/null
+++ b/include/atl/atlperf.h
@@ -0,0 +1,663 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLPERF_H__
+#define __ATLPERF_H__
+
+#pragma once
+
+#ifndef __cplusplus
+ #error ATL requires C++ compilation (use a .cpp suffix)
+#endif
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlstr.h>
+#include <atlfile.h>
+#include <atlsync.h>
+#include <winperf.h>
+#include <atlcoll.h>
+#include <atlsecurity.h>
+
+#ifndef _ATL_PERF_NOXML
+#include <atlenc.h>
+#include <oaidl.h>
+#include <xmldomdid.h>
+
+/* xmldsodid and mshtmdid both have the same identifiers defined, with differing values. So we are renaming the XML ones since there are less identifiers dependent on those. */
+#ifdef DISPID_XOBJ_MIN
+/* in case the HTM one was included first, we'll undef these first */
+#define _ATL_MSHTMDID_INCLUDED_ALREADY
+#undef DISPID_XOBJ_MIN
+#undef DISPID_XOBJ_MAX
+#undef DISPID_XOBJ_BASE
+#endif
+
+#include <xmldsodid.h>
+#include <msxmldid.h>
+
+/* re-undef the clashing names, and their direct dependents */
+#undef DISPID_XOBJ_MIN
+#undef DISPID_XOBJ_MAX
+#undef DISPID_XOBJ_BASE
+#undef DISPID_XMLDSO
+#undef DISPID_XMLELEMENTCOLLECTION
+
+/* re-def the clashing names, and their direct dependents, with un-clashed values */
+#define DISPID_XMLDSO_XOBJ_MIN 0x00010000
+#define DISPID_XMLDSO_XOBJ_MAX 0x0001FFFF
+#define DISPID_XMLDSO_XOBJ_BASE DISPID_XMLDSO_XOBJ_MIN
+#define DISPID_XMLDSO DISPID_XMLDSO_XOBJ_BASE
+#define DISPID_XMLELEMENTCOLLECTION DISPID_XMLDSO_XOBJ_BASE
+#ifdef _ATL_MSHTMDID_INCLUDED_ALREADY
+
+/* redef the clashing names to the HTM values */
+#define DISPID_XOBJ_MIN 0x80010000
+#define DISPID_XOBJ_MAX 0x8001FFFF
+#define DISPID_XOBJ_BASE DISPID_XOBJ_MIN
+#endif
+#include <msxml.h>
+#endif
+
+#pragma warning(push)
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL
+{
+
+const DWORD ATLPERF_SIZE_MASK = 0x00000300;
+const DWORD ATLPERF_TYPE_MASK = 0x00000C00;
+const DWORD ATLPERF_TEXT_MASK = 0x00010000;
+
+#ifndef ATLPERF_DEFAULT_MAXINSTNAMELENGTH
+#define ATLPERF_DEFAULT_MAXINSTNAMELENGTH 64
+#endif
+
+// base class for user-defined perf objects
+struct CPerfObject
+{
+ // implementation
+
+ ULONG m_nAllocSize;
+ DWORD m_dwCategoryId;
+ DWORD m_dwInstance;
+ ULONG m_nRefCount;
+ ULONG m_nInstanceNameOffset; // byte offset from beginning of PerfObject to LPWSTR szInstanceName
+};
+
+class CPerfMon
+{
+public:
+ virtual ~CPerfMon() throw();
+
+#ifdef _ATL_PERF_REGISTER
+ // registration
+ HRESULT Register(
+ LPCTSTR szOpenFunc,
+ LPCTSTR szCollectFunc,
+ LPCTSTR szCloseFunc,
+ HINSTANCE hDllInstance = _AtlBaseModule.GetModuleInstance()) throw();
+ HRESULT RegisterStrings(
+ LANGID wLanguage = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
+ HINSTANCE hResInstance = _AtlBaseModule.GetResourceInstance()) throw();
+ HRESULT RegisterAllStrings(HINSTANCE hResInstance = NULL) throw();
+ HRESULT Unregister() throw();
+#endif
+
+ HRESULT Initialize() throw();
+ void UnInitialize() throw();
+ HRESULT CreateInstance(
+ DWORD dwCategoryId,
+ DWORD dwInstance,
+ LPCWSTR szInstanceName,
+ CPerfObject** ppInstance) throw();
+ HRESULT CreateInstanceByName(
+ DWORD dwCategoryId,
+ LPCWSTR szInstanceName,
+ CPerfObject** ppInstance) throw();
+
+ template <class T>
+ HRESULT CreateInstance(
+ DWORD dwInstance,
+ LPCWSTR szInstanceName,
+ T** ppInstance) throw()
+ {
+ // Ensure T derives from CPerfObject
+ static_cast<CPerfObject*>(*ppInstance);
+
+ return CreateInstance(
+ T::kCategoryId,
+ dwInstance,
+ szInstanceName,
+ reinterpret_cast<CPerfObject**>(ppInstance)
+ );
+ }
+
+ template <class T>
+ HRESULT CreateInstanceByName(
+ LPCWSTR szInstanceName,
+ T** ppInstance) throw()
+ {
+ // Ensure T derives from CPerfObject
+ static_cast<CPerfObject*>(*ppInstance);
+
+ return CreateInstanceByName(
+ T::kCategoryId,
+ szInstanceName,
+ reinterpret_cast<CPerfObject**>(ppInstance)
+ );
+ }
+
+ HRESULT ReleaseInstance(CPerfObject* pInstance) throw();
+ HRESULT LockPerf(DWORD dwTimeout = INFINITE) throw();
+ void UnlockPerf() throw();
+
+#ifndef _ATL_PERF_NOXML
+ HRESULT PersistToXML(IStream *pStream, BOOL bFirst=TRUE, BOOL bLast=TRUE) throw(...);
+ HRESULT LoadFromXML(IStream *pStream) throw(...);
+#endif
+
+ // implementation
+
+public:
+ // PerfMon entry point helpers
+ DWORD Open(LPWSTR lpDeviceNames) throw();
+ DWORD Collect(__in_z LPWSTR lpwszValue, __deref_inout_bcount(*pcbBytes) LPVOID* lppData, __inout LPDWORD lpcbBytes, __inout LPDWORD lpcObjectTypes) throw();
+ DWORD Close() throw();
+
+ // map building routines
+ HRESULT AddCategoryDefinition(
+ DWORD dwCategoryId,
+ LPCTSTR szCategoryName,
+ LPCTSTR szHelpString,
+ DWORD dwDetailLevel,
+ INT nDefaultCounter,
+ BOOL bInstanceLess,
+ UINT nStructSize,
+ UINT nMaxInstanceNameLen = ATLPERF_DEFAULT_MAXINSTNAMELENGTH) throw();
+ HRESULT AddCounterDefinition(
+ DWORD dwCounterId,
+ LPCTSTR szCounterName,
+ LPCTSTR szHelpString,
+ DWORD dwDetailLevel,
+ DWORD dwCounterType,
+ ULONG nMaxCounterSize,
+ UINT nOffset,
+ INT nDefaultScale) throw();
+
+ // macro helpers
+ HRESULT RegisterCategory(
+ WORD wLanguage,
+ HINSTANCE hResInstance,
+ UINT* pSampleRes,
+ DWORD dwCategoryId,
+ UINT nNameString,
+ UINT nHelpString,
+ DWORD dwDetail,
+ BOOL bInstanceless,
+ UINT nStructSize,
+ UINT nMaxInstanceNameLen,
+ INT nDefaultCounter) throw();
+ HRESULT RegisterCategory(
+ WORD wLanguage,
+ HINSTANCE hResInstance,
+ UINT* pSampleRes,
+ DWORD dwCategoryId,
+ LPCTSTR szNameString,
+ LPCTSTR szHelpString,
+ DWORD dwDetail,
+ BOOL bInstanceless,
+ UINT nStructSize,
+ UINT nMaxInstanceNameLen,
+ INT nDefaultCounter) throw();
+ HRESULT RegisterCounter(
+ WORD wLanguage,
+ HINSTANCE hResInstance,
+ DWORD dwCounterId,
+ UINT nNameString,
+ UINT nHelpString,
+ DWORD dwDetail,
+ DWORD dwCounterType,
+ ULONG nMaxCounterSize,
+ UINT nOffset,
+ INT nDefaultScale) throw();
+ HRESULT RegisterCounter(
+ WORD wLanguage,
+ HINSTANCE hResInstance,
+ DWORD dwCounterId,
+ LPCTSTR szNameString,
+ LPCTSTR szHelpString,
+ DWORD dwDetail,
+ DWORD dwCounterType,
+ ULONG nMaxCounterSize,
+ UINT nOffset,
+ INT nDefaultScale) throw();
+
+protected:
+ void ClearMap() throw();
+
+ virtual LPCTSTR GetAppName() const throw() = 0;
+ virtual HRESULT CreateMap(WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes = NULL) throw();
+ virtual void OnBlockAlloc(CAtlFileMappingBase* /*pNewBlock*/) { }
+#ifdef _ATL_PERF_REGISTER
+ static BOOL CALLBACK EnumResLangProc(HINSTANCE hModule, LPCTSTR szType, LPCTSTR szName, LANGID wIDLanguage, LPARAM lParam);
+#endif
+
+ // implementation helpers
+ struct CounterInfo
+ {
+ CounterInfo() : m_dwCounterId(0), m_dwDetailLevel(0), m_nNameId(0),
+ m_nHelpId(0), m_dwCounterType(0), m_nDefaultScale(0),
+ m_nMaxCounterSize(0), m_nDataOffset(0)
+ {
+ ZeroMemory (&m_cache, sizeof(m_cache));
+ }
+ // implementation
+
+ DWORD m_dwCounterId;
+ CString m_strName;
+ CString m_strHelp;
+ DWORD m_dwDetailLevel;
+
+ // the ids that correspond to the name and help strings stored in the registry
+ UINT m_nNameId;
+ UINT m_nHelpId;
+
+ // counter data
+
+ DWORD m_dwCounterType;
+ LONG m_nDefaultScale;
+
+ // the maximum size of the string counter data in characters, including the null terminator
+ // ignored if not a string counter
+ ULONG m_nMaxCounterSize;
+
+ ULONG m_nDataOffset;
+
+ // cached data to be copied into request
+ PERF_COUNTER_DEFINITION m_cache;
+ };
+
+ struct CategoryInfo
+ {
+ // implementation
+
+ DWORD m_dwCategoryId;
+ CString m_strName;
+ CString m_strHelp;
+ DWORD m_dwDetailLevel;
+
+ // the ids that correspond to the name and help strings stored in the registry
+ UINT m_nNameId;
+ UINT m_nHelpId;
+
+ // category data
+
+ LONG m_nDefaultCounter;
+ LONG m_nInstanceLess; // PERF_NO_INSTANCES if instanceless
+
+ // the size of the struct not counting the name and string counters
+ ULONG m_nStructSize;
+
+ // in characters including the null terminator
+ ULONG m_nMaxInstanceNameLen;
+
+ ULONG m_nAllocSize;
+
+ // cached data to be copied into request
+ PERF_OBJECT_TYPE m_cache;
+ ULONG m_nCounterBlockSize;
+
+ // counters
+ UINT _GetNumCounters() throw();
+ CounterInfo* _GetCounterInfo(UINT nIndex) throw();
+
+ CAtlArray<CounterInfo> m_counters;
+ };
+
+ LPBYTE _AllocData(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, size_t nBytesNeeded) throw();
+ template<typename T> T* _AllocStruct(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, T*) throw()
+ {
+ return reinterpret_cast<T*>(_AllocData(pData, nBytesAvail, pnBytesUsed, sizeof(T)));
+ }
+
+ UINT _GetNumCategoriesAndCounters() throw();
+ CategoryInfo* _GetCategoryInfo(UINT nIndex) throw();
+ UINT _GetNumCategories() throw();
+ CPerfObject* _GetFirstInstance(CAtlFileMappingBase* pBlock) throw();
+ CPerfObject* _GetNextInstance(CPerfObject* pInstance) throw();
+ CAtlFileMappingBase* _GetNextBlock(CAtlFileMappingBase* pBlock) throw();
+ CAtlFileMappingBase* _OpenNextBlock(CAtlFileMappingBase* pPrev) throw();
+ CAtlFileMappingBase* _AllocNewBlock(CAtlFileMappingBase* pPrev, BOOL* pbExisted = NULL) throw();
+ HRESULT _OpenAllBlocks() throw();
+ DWORD& _GetBlockId(CAtlFileMappingBase* pBlock) throw(...);
+ DWORD* _GetBlockId_NoThrow(CAtlFileMappingBase* pBlock) throw();
+ CategoryInfo* _FindCategoryInfo(DWORD dwCategoryId) throw();
+ CounterInfo* _FindCounterInfo(CategoryInfo* pCategoryInfo, DWORD dwCounterId) throw();
+ CounterInfo* _FindCounterInfo(DWORD dwCategoryId, DWORD dwCounterId) throw();
+ BOOL _WantCategoryType(__in_z LPWSTR lpwszValue, __in DWORD dwPerfId) throw(...);
+ void _FillCategoryType(CategoryInfo* pCategoryInfo) throw();
+ void _FillCounterDef(CounterInfo* pCounterInfo, ULONG* pnCounterBlockSize) throw();
+ HRESULT CPerfMon::_CollectInstance(
+ CategoryInfo* pCategoryInfo,
+ LPBYTE& pData,
+ ULONG nBytesAvail,
+ ULONG* pnBytesUsed,
+ CPerfObject* pInstance,
+ PERF_OBJECT_TYPE* pObjectType,
+ PERF_COUNTER_DEFINITION* pCounterDefs
+ ) throw();
+ HRESULT _CollectInstance(
+ CategoryInfo* pCategoryInfo,
+ LPBYTE& pData,
+ ULONG nBytesAvail,
+ ULONG* pnBytesUsed,
+ PERF_OBJECT_TYPE* pObjectType,
+ PERF_COUNTER_DEFINITION* pCounterDefs
+ ) throw();
+ HRESULT _CollectCategoryType(
+ CategoryInfo* pCategoryInfo,
+ LPBYTE pData,
+ ULONG nBytesAvail,
+ ULONG* pnBytesUsed) throw();
+ HRESULT _LoadMap(DWORD* pData) throw();
+ HRESULT _SaveMap() throw();
+ HRESULT _GetAttribute(
+ IXMLDOMNode *pNode,
+ LPCWSTR szAttrName,
+ BSTR *pbstrVal) throw();
+ HRESULT CPerfMon::_CreateInstance(
+ DWORD dwCategoryId,
+ DWORD dwInstance,
+ LPCWSTR szInstanceName,
+ CPerfObject** ppInstance,
+ bool bByName) throw();
+
+#ifdef _ATL_PERF_REGISTER
+ void _AppendStrings(
+ LPTSTR& pszNew,
+ CAtlArray<CString>& astrStrings,
+ ULONG iFirstIndex
+ ) throw();
+ HRESULT _AppendRegStrings(
+ CRegKey& rkLang,
+ LPCTSTR szValue,
+ CAtlArray<CString>& astrStrings,
+ ULONG nNewStringSize,
+ ULONG iFirstIndex,
+ ULONG iLastIndex) throw();
+ HRESULT _RemoveRegStrings(
+ CRegKey& rkLang,
+ LPCTSTR szValue,
+ ULONG iFirstIndex,
+ ULONG iLastIndex) throw();
+ HRESULT _ReserveStringRange(DWORD& dwFirstCounter, DWORD& dwFirstHelp) throw();
+ HRESULT _UnregisterStrings() throw();
+ HRESULT _RegisterAllStrings(UINT nRes, HINSTANCE hResInstance) throw();
+#endif
+private:
+ CAtlArray<CategoryInfo> m_categories;
+ CAutoPtrArray<CAtlFileMappingBase> m_aMem;
+ CMutex m_lock;
+ ULONG m_nAllocSize;
+ ULONG m_nHeaderSize;
+ ULONG m_nSchemaSize;
+ CSecurityDesc m_sd;
+};
+
+class CPerfLock
+{
+public:
+ CPerfLock(CPerfMon* pPerfMon, DWORD dwTimeout = INFINITE)
+ {
+ ATLENSURE(pPerfMon != NULL);
+ m_pPerfMon = pPerfMon;
+ m_hrStatus = m_pPerfMon->LockPerf(dwTimeout);
+ }
+
+ ~CPerfLock() throw()
+ {
+ if (SUCCEEDED(m_hrStatus))
+ m_pPerfMon->UnlockPerf();
+ }
+
+ HRESULT GetStatus() const throw()
+ {
+ return m_hrStatus;
+ }
+
+private:
+ CPerfMon* m_pPerfMon;
+ HRESULT m_hrStatus;
+};
+
+////////////////////////////////////////////////////////////////////////
+// map macros
+
+// empty definition just for ease of use with code wizards, etc.
+#define BEGIN_PERFREG_MAP()
+
+// empty definition just for ease of use with code wizards, etc.
+#define END_PERFREG_MAP()
+
+#if !defined(_ATL_PERF_REGISTER) | defined(_ATL_PERF_NOEXPORT)
+#define PERFREG_ENTRY(className)
+#endif
+
+#ifdef _ATL_PERF_REGISTER
+#define BEGIN_PERF_MAP(AppName) \
+ private: \
+ LPCTSTR GetAppName() const throw() { return AppName; } \
+ HRESULT CreateMap(WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes = NULL) throw() \
+ { \
+ if (pSampleRes) \
+ *pSampleRes = 0; \
+ ClearMap();
+
+#define BEGIN_COUNTER_MAP(categoryclass) \
+ public: \
+ typedef categoryclass _PerfCounterClass; \
+ static HRESULT CreateMap(CPerfMon* pPerf, WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes) throw() \
+ { \
+ HRESULT hr = RegisterCategory(pPerf, wLanguage, hResInstance, pSampleRes); \
+ if (FAILED(hr)) \
+ return hr;
+
+#define DECLARE_PERF_CATEGORY_EX(dwCategoryId, namestring, helpstring, detail, instanceless, structsize, maxinstnamelen, defcounter) \
+ static HRESULT RegisterCategory(CPerfMon* pPerf, WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes) throw() \
+ { \
+ return pPerf->RegisterCategory(wLanguage, hResInstance, pSampleRes, dwCategoryId, namestring, helpstring, detail, instanceless, structsize, maxinstnamelen, defcounter); \
+ } \
+ /* NOTE: put a semicolon after your call to DECLARE_PERF_CATEGORY*(...) */ \
+ /* this is needed for the code wizards to parse things properly */ \
+ static const DWORD kCategoryId = dwCategoryId
+
+#define CHAIN_PERF_CATEGORY(categoryclass) \
+ if (FAILED(categoryclass::CreateMap(this, wLanguage, hResInstance, pSampleRes))) \
+ return E_FAIL;
+
+// CAssertValidField ensures that the member variable that's being passed to
+// DEFINE_COUNTER[_EX] is the proper type. only 32-bit integral types can be used with
+// PERF_SIZE_DWORD and only 64-bit integral types can be used with PERF_SIZE_LARGE
+template< DWORD t_dwSize >
+class CAssertValidField
+{
+};
+
+template<>
+class CAssertValidField< PERF_SIZE_DWORD >
+{
+public:
+ template< class C > static void AssertValidFieldType( ULONG C::* ) throw() { }
+ template< class C > static void AssertValidFieldType( LONG C::* ) throw() { }
+};
+
+template<>
+class CAssertValidField< PERF_SIZE_LARGE >
+{
+public:
+ template< class C > static void AssertValidFieldType( ULONGLONG C::* ) throw() { }
+ template< class C > static void AssertValidFieldType( LONGLONG C::* ) throw() { }
+};
+
+#define DEFINE_COUNTER_EX(member, dwCounterId, namestring, helpstring, detail, countertype, maxcountersize, defscale) \
+ CAssertValidField< (countertype) & ATLPERF_SIZE_MASK >::AssertValidFieldType( &_PerfCounterClass::member ); \
+ hr = pPerf->RegisterCounter(wLanguage, hResInstance, dwCounterId, namestring, helpstring, detail, countertype, maxcountersize, (ULONG) offsetof(_PerfCounterClass, member), defscale); \
+ if (FAILED(hr)) \
+ return hr;
+
+#define END_PERF_MAP() \
+ return S_OK; \
+ }
+
+#define END_COUNTER_MAP() \
+ return S_OK; \
+ }
+
+#else // _ATL_PERF_REGISTER
+
+#define BEGIN_PERF_MAP(AppName) \
+ private: \
+ LPCTSTR GetAppName() const throw() { return AppName; }
+
+#define BEGIN_COUNTER_MAP(objectclass)
+
+#define DECLARE_PERF_CATEGORY_EX(dwCategoryId, namestring, helpstring, detail, instanceless, structsize, maxinstnamelen, defcounter) \
+ /* NOTE: put a semicolon after your call to DECLARE_PERF_CATEGORY*(...) */ \
+ /* this is needed for the code wizards to parse things properly */ \
+ static const DWORD kCategoryId = dwCategoryId
+
+#define CHAIN_PERF_CATEGORY(objectclass)
+#define DEFINE_COUNTER_EX(member, dwCounterId, namestring, helpstring, detail, countertype, maxcountersize, defscale)
+
+#define END_PERF_MAP()
+#define END_COUNTER_MAP()
+
+#endif // _ATL_PERF_REGISTER
+
+#define DECLARE_PERF_CATEGORY(objectclass, dwCategoryId, namestring, helpstring, defcounter) \
+ DECLARE_PERF_CATEGORY_EX(dwCategoryId, namestring, helpstring, PERF_DETAIL_NOVICE, 0, sizeof(objectclass), ATLPERF_DEFAULT_MAXINSTNAMELENGTH, defcounter)
+#define DECLARE_PERF_CATEGORY_NO_INSTANCES(objectclass, dwCategoryId, namestring, helpstring, defcounter) \
+ DECLARE_PERF_CATEGORY_EX(dwCategoryId, namestring, helpstring, PERF_DETAIL_NOVICE, PERF_NO_INSTANCES, sizeof(objectclass), 0, defcounter)
+
+#define DEFINE_COUNTER(member, namestring, helpstring, countertype, defscale) \
+ DEFINE_COUNTER_EX(member, 0, namestring, helpstring, PERF_DETAIL_NOVICE, countertype, 0, defscale)
+
+#pragma deprecated( DECLARE_PERF_OBJECT_EX )
+#pragma deprecated( DECLARE_PERF_OBJECT )
+#pragma deprecated( DECLARE_PERF_OBJECT_NO_INSTANCES )
+#pragma deprecated( CHAIN_PERF_OBJECT )
+#define DECLARE_PERF_OBJECT_EX DECLARE_PERF_CATEGORY_EX
+#define DECLARE_PERF_OBJECT DECLARE_PERF_CATEGORY
+#define DECLARE_PERF_OBJECT_NO_INSTANCES DECLARE_PERF_CATEGORY_NO_INSTANCES
+#define CHAIN_PERF_OBJECT CHAIN_PERF_CATEGORY
+
+////////////////////////////////////////////////////////////////////////
+// automagic registration stuff
+
+#if defined(_ATL_PERF_REGISTER) & !defined(_ATL_PERF_NOEXPORT)
+
+// define _ATL_PERF_NOEXPORT if you don't want to use the PERFREG map and don't want these
+// functions exported from your DLL
+
+// Perf register map stuff
+// this is for ease of integration with the module attribute and for the
+// perfmon wizard
+
+#pragma section("ATLP$A", read, shared)
+#pragma section("ATLP$Z", read, shared)
+#pragma section("ATLP$C", read, shared)
+extern "C"
+{
+__declspec(selectany) __declspec(allocate("ATLP$A")) CPerfMon * __pperfA = NULL;
+__declspec(selectany) __declspec(allocate("ATLP$Z")) CPerfMon * __pperfZ = NULL;
+}
+
+#if !defined(_M_IA64)
+#pragma comment(linker, "/merge:ATLP=.rdata")
+#endif
+
+#if defined(_M_IA64) || defined(_M_AMD64)
+ #define ATLPERF_FUNCID_OPEN "OpenPerfMon"
+ #define ATLPERF_FUNCID_COLLECT "CollectPerfMon"
+ #define ATLPERF_FUNCID_CLOSE "ClosePerfMon"
+#elif defined(_M_IX86)
+ #define ATLPERF_FUNCID_OPEN "_OpenPerfMon@4"
+ #define ATLPERF_FUNCID_COLLECT "_CollectPerfMon@16"
+ #define ATLPERF_FUNCID_CLOSE "_ClosePerfMon@0"
+#else
+#if !defined(ATLPERF_FUNCID_OPEN) || !defined(ATLPERF_FUNCID_COLLECT) || !defined (ATLPERF_FUNCID_CLOSE)
+#error "Unknown platform. Define ATLPERF_FUNCID_OPEN, ATLPERF_FUNCID_COLLECT, ATLPERF_FUNCID_CLOSE"
+#endif
+#endif
+
+HRESULT RegisterPerfMon(HINSTANCE hDllInstance = _AtlBaseModule.GetModuleInstance()) throw();
+HRESULT UnregisterPerfMon() throw();
+
+extern "C" DWORD __declspec(dllexport) WINAPI OpenPerfMon(LPWSTR lpDeviceNames) throw();
+extern "C" DWORD __declspec(dllexport) WINAPI CollectPerfMon(LPWSTR lpwszValue, LPVOID* lppData,
+ LPDWORD lpcbBytes, LPDWORD lpcObjectTypes) throw();
+extern "C" DWORD __declspec(dllexport) WINAPI ClosePerfMon() throw();
+
+// this class handles integrating the registration with CComModule
+class _CAtlPerfSetFuncPtr
+{
+public:
+ _CAtlPerfSetFuncPtr()
+ {
+ _pPerfRegFunc = RegisterPerfMon;
+ _pPerfUnRegFunc = UnregisterPerfMon;
+ }
+};
+
+extern "C" { __declspec(selectany) _CAtlPerfSetFuncPtr g_atlperfinit; }
+
+#if defined(_M_IX86)
+#pragma comment(linker, "/INCLUDE:_g_atlperfinit")
+#elif defined(_M_IA64) || defined(_M_AMD64)
+#pragma comment(linker, "/INCLUDE:g_atlperfinit")
+#else
+#pragma message("Unknown platform. Make sure the linker includes g_atlperfinit")
+#endif
+
+#ifndef PERF_ENTRY_PRAGMA
+
+#if defined(_M_IX86)
+#define PERF_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:___pperf_" #class));
+#elif defined(_M_IA64)
+#define PERF_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:__pperf_" #class));
+#elif defined(_M_AMD64)
+#define PERF_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:__pperf_" #class));
+#else
+#error Unknown Platform. define PERF_ENTRY_PRAGMA
+#endif
+
+#endif // PERF_ENTRY_PRAGMA
+
+#define PERFREG_ENTRY(className) \
+ __declspec(selectany) className __perf_##className; \
+ extern "C" __declspec(allocate("ATLP$C")) __declspec(selectany) CPerfMon * const __pperf_##className = \
+ static_cast<CPerfMon*>(&__perf_##className); \
+ PERF_ENTRY_PRAGMA(className)
+
+#endif // _ATL_PERF_NOEXPORT
+
+} // namespace ATL
+
+
+#include <atlperf.inl>
+
+#pragma pack(pop)
+#pragma warning(pop)
+
+#endif // __ATLPERF_H__
diff --git a/include/atl/atlperf.inl b/include/atl/atlperf.inl
new file mode 100644
index 000000000..398fd1b0a
--- /dev/null
+++ b/include/atl/atlperf.inl
@@ -0,0 +1,2894 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLPERF_INL__
+#define __ATLPERF_INL__
+
+#pragma once
+
+#ifndef __ATLPERF_H__
+ #error atlperf.inl requires atlperf.h to be included first
+#endif
+
+#pragma warning(push)
+
+#ifndef _CPPUNWIND
+#pragma warning(disable: 4702) // unreachable code
+#endif
+
+namespace ATL
+{
+
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfCounter = _T("Counter");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfFirstCounter = _T("First Counter");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfLastCounter = _T("Last Counter");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfHelp = _T("Help");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfFirstHelp = _T("First Help");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfLastHelp = _T("Last Help");
+
+extern __declspec(selectany) const WCHAR * const c_szAtlPerfGlobal = L"Global";
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfLibrary = _T("Library");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfOpen = _T("Open");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfCollect = _T("Collect");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfClose = _T("Close");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfLanguages = _T("Languages");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfMap = _T("Map");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerformance = _T("Performance");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfServicesKey = _T("SYSTEM\\CurrentControlSet\\Services\\%s");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerformanceKey = _T("SYSTEM\\CurrentControlSet\\Services\\%s\\Performance");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerfLibKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
+extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerfLibLangKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\%3.3x");
+
+inline CPerfMon::CounterInfo* CPerfMon::CategoryInfo::_GetCounterInfo(UINT nIndex) throw()
+{
+ ATLASSERT(nIndex < _GetNumCounters());
+ return &m_counters[nIndex];
+}
+
+inline UINT CPerfMon::CategoryInfo::_GetNumCounters() throw()
+{
+ return (UINT) m_counters.GetCount();
+}
+
+inline CPerfMon::~CPerfMon() throw()
+{
+ UnInitialize();
+}
+
+inline HRESULT CPerfMon::CreateMap(LANGID language, HINSTANCE hResInstance, UINT* pSampleRes) throw()
+{
+ (language); // unused
+ (hResInstance); // unused
+ (pSampleRes); // unused
+ return S_OK;
+}
+
+inline UINT CPerfMon::_GetNumCategoriesAndCounters() throw()
+{
+ UINT nResult = _GetNumCategories();
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ nResult += _GetCategoryInfo(i)->_GetNumCounters();
+ }
+
+ return nResult;
+}
+
+inline CPerfMon::CategoryInfo* CPerfMon::_GetCategoryInfo(UINT nIndex) throw()
+{
+ ATLASSERT(nIndex < _GetNumCategories());
+ return &m_categories[nIndex];
+}
+
+inline UINT CPerfMon::_GetNumCategories() throw()
+{
+ return (UINT) m_categories.GetCount();
+}
+
+inline CPerfObject* CPerfMon::_GetFirstInstance(CAtlFileMappingBase* pBlock)
+{
+ ATLENSURE(pBlock != NULL);
+
+ // should never happen if Initialize succeeded
+ // are you checking return codes?
+ ATLASSERT(pBlock->GetData() != NULL);
+
+ return reinterpret_cast<CPerfObject*>(LPBYTE(pBlock->GetData()) + m_nHeaderSize);
+}
+
+inline CPerfObject* CPerfMon::_GetNextInstance(CPerfObject* pInstance)
+{
+ ATLENSURE_RETURN_VAL(pInstance != NULL, NULL);
+ ATLENSURE_RETURN_VAL(pInstance->m_nAllocSize != (ULONG)-1, NULL);
+ ATLASSERT(pInstance->m_nAllocSize != (ULONG)0);
+
+ return reinterpret_cast<CPerfObject*>(LPBYTE(pInstance) + pInstance->m_nAllocSize);
+}
+
+inline CAtlFileMappingBase* CPerfMon::_GetNextBlock(CAtlFileMappingBase* pBlock) throw()
+{
+ // calling _GetNextBlock(NULL) will return the first block
+ DWORD dwNextBlockIndex = 0;
+ DWORD* pDw= _GetBlockId_NoThrow(pBlock);
+ if (pDw)
+ {
+ dwNextBlockIndex = *pDw +1;
+ }
+ if (m_aMem.GetCount() == dwNextBlockIndex)
+ return NULL;
+ return m_aMem[dwNextBlockIndex];
+}
+
+inline CAtlFileMappingBase* CPerfMon::_OpenNextBlock(CAtlFileMappingBase* pPrev) throw()
+{
+ CAutoPtr<CAtlFileMappingBase> spMem;
+ CAtlFileMappingBase* pMem = NULL;
+ ATLTRY(spMem.Attach(new CAtlFileMappingBase));
+ if (spMem == NULL)
+ return NULL;
+
+ // create a unique name for the shared mem segment based on the index
+ DWORD dwNextBlockIndex;
+ DWORD* pDw= _GetBlockId_NoThrow(pPrev);
+ if (pDw)
+ {
+ dwNextBlockIndex = *pDw +1;
+ }
+ else
+ {
+ // use the system allocation granularity (65536 currently. may be different in the future)
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ m_nAllocSize = si.dwAllocationGranularity;
+
+ dwNextBlockIndex = 0;
+ }
+
+ _ATLTRY
+ {
+ CString strName;
+ strName.Format(_T("Global\\ATLPERF_%s_%3.3d"), GetAppName(), dwNextBlockIndex);
+
+ HRESULT hr = spMem->OpenMapping(strName, m_nAllocSize, 0, FILE_MAP_READ);
+ if (FAILED(hr))
+ return NULL;
+
+ pMem = spMem;
+ m_aMem.Add(spMem);
+ }
+ _ATLCATCHALL()
+ {
+ return NULL;
+ }
+
+ return pMem;
+}
+
+inline CAtlFileMappingBase* CPerfMon::_AllocNewBlock(CAtlFileMappingBase* pPrev, BOOL* pbExisted /* == NULL */) throw()
+{
+ CAtlFileMappingBase* pMem = NULL;
+ _ATLTRY
+ {
+ CSecurityAttributes sa;
+ sa.Set(m_sd);
+
+ CAutoPtr<CAtlFileMappingBase> spMem;
+ spMem.Attach(new CAtlFileMappingBase);
+ if (spMem == NULL)
+ {
+ return NULL;
+ }
+
+ // create a unique name for the shared mem segment based on the index
+ DWORD dwNextBlockIndex;
+ if (pPrev != NULL)
+ {
+ dwNextBlockIndex = _GetBlockId(pPrev) +1;
+ }
+ else
+ {
+ // use the system allocation granularity (65536 currently. may be different in the future)
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ m_nAllocSize = si.dwAllocationGranularity;
+
+ dwNextBlockIndex = 0;
+ }
+
+ BOOL bExisted = FALSE;
+ CString strName;
+ strName.Format(_T("Global\\ATLPERF_%s_%3.3d"), GetAppName(), dwNextBlockIndex);
+
+ HRESULT hr = spMem->MapSharedMem(m_nAllocSize, strName, &bExisted, &sa);
+ if (FAILED(hr))
+ {
+ return NULL;
+ }
+
+ if(!bExisted)
+ {
+ memset(spMem->GetData(), 0, m_nAllocSize);
+ // save the index of this block
+ // don't for first block since we don't know m_nSchemaSize yet
+ if (dwNextBlockIndex)
+ {
+ _GetBlockId(spMem) = dwNextBlockIndex;
+ }
+ }
+ else
+ {
+ CSid owner;
+ CDacl dacl;
+
+ m_sd.GetOwner(&owner);
+ m_sd.GetDacl(&dacl);
+
+ // prevent us from using an object someone else has opened
+ if (::SetSecurityInfo(spMem->GetHandle(), SE_KERNEL_OBJECT,
+ DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
+ const_cast<SID*>(owner.GetPSID()),
+ NULL,
+ const_cast<ACL*>(dacl.GetPACL()),
+ NULL) != ERROR_SUCCESS)
+ {
+ return NULL;
+ }
+ }
+
+ if (pbExisted)
+ {
+ *pbExisted = bExisted;
+ }
+
+ pMem = spMem;
+ m_aMem.Add(spMem);
+
+ OnBlockAlloc(pMem);
+ }
+ _ATLCATCHALL()
+ {
+ return NULL;
+ }
+
+ return pMem;
+}
+
+inline HRESULT CPerfMon::_OpenAllBlocks() throw()
+{
+ HRESULT hr;
+
+ // if we haven't opened any yet, initialize
+ if (m_aMem.GetCount() == 0)
+ {
+ CAtlFileMappingBase* pMem = _OpenNextBlock(NULL);
+ if (pMem == NULL)
+ return S_OK;
+
+ hr = _LoadMap(LPDWORD(pMem->GetData()));
+ if (FAILED(hr))
+ {
+ m_aMem.RemoveAll();
+ return hr;
+ }
+
+ m_nSchemaSize = *LPDWORD(pMem->GetData());
+ m_nHeaderSize = m_nSchemaSize + sizeof(DWORD);
+ m_nHeaderSize = AtlAlignUp(m_nHeaderSize,16);
+ }
+
+ // open any new blocks
+ CAtlFileMappingBase* pMem = m_aMem[m_aMem.GetCount()-1];
+ while (pMem)
+ pMem = _OpenNextBlock(pMem);
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::_LoadMap(DWORD* pData) throw()
+{
+ _ATLTRY
+ {
+ HRESULT hr;
+
+ ClearMap();
+
+ DWORD dwDataSize = *pData++; // blob size
+ DWORD dwNumItems = *pData++; // number of items
+
+ // see if we have name data
+ DWORD* pNameData = NULL;
+ if (dwDataSize > (2+dwNumItems*9) * sizeof(DWORD))
+ pNameData = pData + dwNumItems*9; // blob size and item count already skipped. skip item data
+
+ for (DWORD i=0; i<dwNumItems; i++)
+ {
+ DWORD dwIsObject = *pData++;
+ DWORD dwPerfId = *pData++;
+ DWORD dwDetailLevel = *pData++;
+
+ CString strName;
+ if (pNameData)
+ {
+ strName = CString(LPWSTR(pNameData+1), *pNameData);
+ pNameData += AtlAlignUp(sizeof(WCHAR) * *pNameData, sizeof(DWORD))/sizeof(DWORD) + 1;
+ }
+
+ if (dwIsObject)
+ {
+ DWORD dwDefaultCounter = *pData++;
+ DWORD dwInstanceLess = *pData++;
+ DWORD dwStructSize = *pData++;
+ DWORD dwMaxInstanceNameLen = *pData++;
+
+ hr = AddCategoryDefinition(
+ dwPerfId,
+ strName,
+ NULL,
+ dwDetailLevel,
+ dwDefaultCounter,
+ dwInstanceLess,
+ dwStructSize,
+ dwMaxInstanceNameLen);
+ if (FAILED(hr))
+ {
+ ClearMap();
+ return hr;
+ }
+
+ DWORD dwNameId = *pData++;
+ DWORD dwHelpId = *pData++;
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1);
+ pCategoryInfo->m_nNameId = dwNameId;
+ pCategoryInfo->m_nHelpId = dwHelpId;
+ }
+ else
+ {
+ DWORD dwCounterType = *pData++;
+ DWORD dwMaxCounterSize = *pData++;
+ DWORD dwDataOffset = *pData++;
+ DWORD dwDefaultScale = *pData++;
+
+ hr = AddCounterDefinition(
+ dwPerfId,
+ strName,
+ NULL,
+ dwDetailLevel,
+ dwCounterType,
+ dwMaxCounterSize,
+ dwDataOffset,
+ dwDefaultScale);
+ if (FAILED(hr))
+ {
+ ClearMap();
+ return hr;
+ }
+
+ DWORD dwNameId = *pData++;
+ DWORD dwHelpId = *pData++;
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1);
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(pCategoryInfo->_GetNumCounters()-1);
+ pCounterInfo->m_nNameId = dwNameId;
+ pCounterInfo->m_nHelpId = dwHelpId;
+ }
+ }
+
+ // fill in cache data
+ ULONG* pnCounterBlockSize = NULL; // pointer to the object's counter block size
+ for (DWORD i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+ // align at 8 bytes per Q262335
+ pCategoryInfo->m_nCounterBlockSize = (ULONG) AtlAlignUp(sizeof(PERF_COUNTER_BLOCK), 8);
+ pnCounterBlockSize = &pCategoryInfo->m_nCounterBlockSize;
+ _FillCategoryType(pCategoryInfo);
+ for (DWORD j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+ _FillCounterDef(pCounterInfo, pnCounterBlockSize);
+ }
+ // align at 8 bytes per Q262335
+ pCategoryInfo->m_nCounterBlockSize = (ULONG) AtlAlignUp(pCategoryInfo->m_nCounterBlockSize, 8);
+ }
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+}
+
+inline HRESULT CPerfMon::_SaveMap() throw()
+{
+ _ATLTRY
+ {
+ // figure out how much memory we need
+ size_t nSize = (2 + 9*_GetNumCategoriesAndCounters()) * sizeof(DWORD);
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ // if any of the entries have names, they'd better all have names
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+ if (!pCategoryInfo->m_strName.IsEmpty())
+ {
+ nSize += sizeof(DWORD) + AtlAlignUp(sizeof(WCHAR) * pCategoryInfo->m_strName.GetLength(), sizeof(DWORD));
+ for (UINT j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+ nSize += sizeof(DWORD) + AtlAlignUp(sizeof(WCHAR) * pCounterInfo->m_strName.GetLength(), sizeof(DWORD));
+ }
+ }
+ }
+
+ CHeapPtr<BYTE> blob;
+ if (!blob.Allocate(nSize))
+ return E_OUTOFMEMORY;
+
+ // start with blob size and number of items in the blob
+ DWORD* pCurrent = reinterpret_cast<DWORD*>(blob.m_pData);
+ memset(pCurrent, 0, nSize);
+ *pCurrent++ = (DWORD) nSize; // blob size
+ *pCurrent++ = _GetNumCategoriesAndCounters(); // number of items
+ size_t nSizeLast = nSize;
+ nSize -= 2 * sizeof(DWORD);
+ if(nSize > nSizeLast) return E_FAIL;
+
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ // add all the relevant runtime info to the blob for each item
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+
+ *pCurrent++ = TRUE; // is object
+ *pCurrent++ = pCategoryInfo->m_dwCategoryId;
+ *pCurrent++ = pCategoryInfo->m_dwDetailLevel;
+ *pCurrent++ = pCategoryInfo->m_nDefaultCounter;
+ *pCurrent++ = pCategoryInfo->m_nInstanceLess;
+ *pCurrent++ = pCategoryInfo->m_nStructSize;
+ *pCurrent++ = pCategoryInfo->m_nMaxInstanceNameLen;
+ *pCurrent++ = pCategoryInfo->m_nNameId;
+ *pCurrent++ = pCategoryInfo->m_nHelpId;
+ nSizeLast = nSize;
+ nSize -= 9 * sizeof(DWORD);
+ if(nSize > nSizeLast) return E_FAIL;
+
+ for (UINT j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+
+ *pCurrent++ = FALSE; // is object
+ *pCurrent++ = pCounterInfo->m_dwCounterId;
+ *pCurrent++ = pCounterInfo->m_dwDetailLevel;
+ *pCurrent++ = pCounterInfo->m_dwCounterType;
+ *pCurrent++ = pCounterInfo->m_nMaxCounterSize;
+ *pCurrent++ = pCounterInfo->m_nDataOffset;
+ *pCurrent++ = pCounterInfo->m_nDefaultScale;
+ *pCurrent++ = pCounterInfo->m_nNameId;
+ *pCurrent++ = pCounterInfo->m_nHelpId;
+ nSizeLast = nSize;
+ nSize -= 9 * sizeof(DWORD);
+ if(nSize > nSizeLast) return E_FAIL;
+ }
+ }
+
+ // add names to the blob
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+ // copy the len of the string (in characters) then the wide-char version of the string
+ // pad the string to a dword boundary
+ int nLen = pCategoryInfo->m_strName.GetLength();
+ *pCurrent++ = nLen;
+ nSizeLast = nSize;
+ nSize -= sizeof(DWORD);
+ if(nSize > nSizeLast) return E_FAIL;
+
+ Checked::memcpy_s(pCurrent, nSize, CT2CW(pCategoryInfo->m_strName), sizeof(WCHAR)*nLen);
+ pCurrent += AtlAlignUp(sizeof(WCHAR) * nLen, sizeof(DWORD))/sizeof(DWORD);
+ nSizeLast = nSize;
+ nSize -= sizeof(WCHAR)*nLen;
+ if(nSize > nSizeLast) return E_FAIL;
+
+ for (UINT j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+ // copy the len of the string (in characters) then the wide-char version of the string
+ // pad the string to a dword boundary
+ int nCounterLen = pCounterInfo->m_strName.GetLength();
+ *pCurrent++ = nCounterLen;
+ nSizeLast = nSize;
+ nSize -= sizeof(DWORD);
+ if(nSize > nSizeLast) return E_FAIL;
+
+ Checked::memcpy_s(pCurrent, nSize, CT2CW(pCounterInfo->m_strName), sizeof(WCHAR)*nCounterLen);
+ pCurrent += AtlAlignUp(sizeof(WCHAR) * nCounterLen, sizeof(DWORD))/sizeof(DWORD);
+ nSizeLast = nSize;
+ nSize -= sizeof(WCHAR)*nCounterLen;
+ if(nSize > nSizeLast) return E_FAIL;
+ }
+ }
+
+ CRegKey rkApp;
+ CString str;
+ DWORD dwErr;
+
+ str.Format(c_szAtlPerfPerformanceKey, GetAppName());
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ rkApp.SetBinaryValue(c_szAtlPerfMap, blob, *LPDWORD(blob.m_pData));
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+}
+
+inline CPerfMon::CategoryInfo* CPerfMon::_FindCategoryInfo(DWORD dwCategoryId) throw()
+{
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+ if (pCategoryInfo->m_dwCategoryId == dwCategoryId)
+ return pCategoryInfo;
+ }
+
+ return NULL;
+}
+
+inline CPerfMon::CounterInfo* CPerfMon::_FindCounterInfo(CategoryInfo* pCategoryInfo, DWORD dwCounterId)
+{
+ ATLENSURE_RETURN_VAL(pCategoryInfo != NULL, NULL);
+
+ for (DWORD i=0; i<pCategoryInfo->_GetNumCounters(); i++)
+ {
+ CounterInfo* pCounter = pCategoryInfo->_GetCounterInfo(i);
+ if (pCounter->m_dwCounterId == dwCounterId)
+ return pCounter;
+ }
+
+ return NULL;
+}
+
+inline CPerfMon::CounterInfo* CPerfMon::_FindCounterInfo(DWORD dwCategoryId, DWORD dwCounterId) throw()
+{
+ CategoryInfo* pCategoryInfo = _FindCategoryInfo(dwCategoryId);
+ if (pCategoryInfo != NULL)
+ return _FindCounterInfo(pCategoryInfo, dwCounterId);
+
+ return NULL;
+}
+
+inline BOOL CPerfMon::_WantCategoryType(__in_z LPWSTR szValue, __in DWORD dwCategoryId) throw(...)
+{
+ ATLASSERT(szValue != NULL);
+
+ if (lstrcmpiW(c_szAtlPerfGlobal, szValue) == 0)
+ return TRUE;
+
+ CString strList(szValue);
+ int nStart = 0;
+
+ CString strNum = strList.Tokenize(_T(" "), nStart);
+ while (!strNum.IsEmpty())
+ {
+ if (_ttoi(strNum) == int(dwCategoryId))
+ return TRUE;
+
+ strNum = strList.Tokenize(_T(" "), nStart);
+ }
+
+ return FALSE;
+}
+
+inline LPBYTE CPerfMon::_AllocData(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, size_t nBytesNeeded)
+{
+ ATLENSURE_RETURN_VAL(pnBytesUsed != NULL, NULL);
+ ULONG newSize = *pnBytesUsed+static_cast<ULONG>(nBytesNeeded);
+
+ if ((newSize < *pnBytesUsed) || (newSize < (ULONG) nBytesNeeded) || (nBytesAvail < newSize))
+ return NULL;
+
+ LPBYTE p = pData;
+ pData += nBytesNeeded;
+ *pnBytesUsed += (ULONG) nBytesNeeded;
+
+ return p;
+}
+
+inline DWORD& CPerfMon::_GetBlockId(CAtlFileMappingBase* pBlock)
+{
+ DWORD* pDw = _GetBlockId_NoThrow(pBlock);
+ ATLENSURE(pDw);
+ return *pDw;
+}
+
+inline DWORD* CPerfMon::_GetBlockId_NoThrow(CAtlFileMappingBase* pBlock)
+{
+ if (pBlock == NULL)
+ return NULL;
+
+ return LPDWORD(LPBYTE(pBlock->GetData()) + m_nSchemaSize);
+}
+
+inline void CPerfMon::_FillCategoryType(CategoryInfo* pCategoryInfo) throw()
+{
+ PERF_OBJECT_TYPE& type = pCategoryInfo->m_cache;
+ type.DefinitionLength = sizeof(PERF_OBJECT_TYPE) + sizeof(PERF_COUNTER_DEFINITION) * pCategoryInfo->_GetNumCounters();
+ type.TotalByteLength = type.DefinitionLength; // we will add the instance definitions/counter blocks as we go
+ type.HeaderLength = sizeof(PERF_OBJECT_TYPE);
+ type.ObjectNameTitleIndex = pCategoryInfo->m_nNameId;
+ type.ObjectNameTitle = NULL;
+ type.ObjectHelpTitleIndex = pCategoryInfo->m_nHelpId;
+ type.ObjectHelpTitle = NULL;
+ type.DetailLevel = pCategoryInfo->m_dwDetailLevel;
+ type.NumCounters = pCategoryInfo->_GetNumCounters();
+ type.DefaultCounter = pCategoryInfo->m_nDefaultCounter;
+ if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES)
+ type.NumInstances = PERF_NO_INSTANCES;
+ else
+ type.NumInstances = 0; // this will be calculated as objects are processed
+ type.CodePage = 0;
+ type.PerfTime.QuadPart = 0;
+ QueryPerformanceFrequency (&(type.PerfFreq));
+}
+
+inline void CPerfMon::_FillCounterDef(CounterInfo* pCounterInfo, ULONG* pnCounterBlockSize) throw()
+{
+ PERF_COUNTER_DEFINITION& def = pCounterInfo->m_cache;
+
+ def.ByteLength = sizeof(PERF_COUNTER_DEFINITION);
+ def.CounterNameTitleIndex = pCounterInfo->m_nNameId;
+ def.CounterNameTitle = NULL;
+ def.CounterHelpTitleIndex = pCounterInfo->m_nHelpId;
+ def.CounterHelpTitle = NULL;
+ def.DefaultScale = pCounterInfo->m_nDefaultScale;
+ def.DetailLevel = pCounterInfo->m_dwDetailLevel;
+ def.CounterType = pCounterInfo->m_dwCounterType;
+ DWORD dwAlignOfCounter=0;
+ switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK)
+ {
+ case PERF_SIZE_DWORD:
+ def.CounterSize = sizeof(DWORD);
+ dwAlignOfCounter = sizeof(DWORD);
+ break;
+ case PERF_SIZE_LARGE:
+ def.CounterSize = sizeof(__int64);
+ dwAlignOfCounter = sizeof(__int64);
+ break;
+ case PERF_SIZE_ZERO:
+ def.CounterSize = 0;
+ dwAlignOfCounter = 0;
+ break;
+ case PERF_SIZE_VARIABLE_LEN:
+ ATLASSERT((pCounterInfo->m_dwCounterType & ATLPERF_TYPE_MASK) == PERF_TYPE_TEXT);
+ if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
+ {
+ def.CounterSize = (DWORD) AtlAlignUp(pCounterInfo->m_nMaxCounterSize * sizeof(WCHAR), sizeof(DWORD));
+ }
+ else
+ {
+ def.CounterSize = (DWORD) AtlAlignUp(pCounterInfo->m_nMaxCounterSize * sizeof(char), sizeof(DWORD));
+ }
+ break;
+ }
+ *pnCounterBlockSize = AtlAlignUp(*pnCounterBlockSize, dwAlignOfCounter);
+ def.CounterOffset = *pnCounterBlockSize;
+ *pnCounterBlockSize += def.CounterSize;
+}
+
+inline HRESULT CPerfMon::_CollectInstance(
+ CategoryInfo* pCategoryInfo,
+ LPBYTE& pData,
+ ULONG nBytesAvail,
+ ULONG* pnBytesUsed,
+ CPerfObject* _pInstance,
+ PERF_OBJECT_TYPE* pObjectType,
+ PERF_COUNTER_DEFINITION* pCounterDefs
+ ) throw()
+{
+ DWORD dwInstance = _pInstance->m_dwInstance;
+
+ // grab a snapshot of the object
+ USES_ATL_SAFE_ALLOCA;
+ CPerfObject* pInstance = (CPerfObject*) _ATL_SAFE_ALLOCA(_pInstance->m_nAllocSize, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+ if (pInstance == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ Checked::memcpy_s(pInstance, _pInstance->m_nAllocSize, _pInstance, _pInstance->m_nAllocSize);
+
+ // if it was changed or deleted between when we first saw it and when we copied
+ // it, then forget about whatever happens to be there for this collection period
+ if (pInstance->m_dwCategoryId != pCategoryInfo->m_dwCategoryId ||
+ dwInstance != pInstance->m_dwInstance ||
+ pInstance->m_nRefCount == 0)
+ return S_OK;
+
+ // we have a copy of something that claims to be the object type we're expecting
+ // put it into the data blob
+ PERF_INSTANCE_DEFINITION* pInstanceDef = NULL;
+
+ if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES)
+ pObjectType->NumInstances = PERF_NO_INSTANCES;
+ else
+ {
+ pObjectType->NumInstances++;
+
+ // create an instance definition
+ pInstanceDef = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_INSTANCE_DEFINITION*) NULL);
+ if (pInstanceDef == NULL)
+ return E_OUTOFMEMORY;
+
+ pInstanceDef->ParentObjectTitleIndex = 0;
+ pInstanceDef->ParentObjectInstance = 0;
+ pInstanceDef->UniqueID = PERF_NO_UNIQUE_ID;
+
+ // handle the instance name
+ LPCWSTR szInstNameSrc = LPCWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset);
+ pInstanceDef->NameLength = (ULONG)(wcslen(szInstNameSrc)+1)*sizeof(WCHAR);
+ // align at 8 bytes per Q262335
+ ULONG nNameAlloc = (ULONG) AtlAlignUp(pInstanceDef->NameLength, 8);
+ LPWSTR szInstNameDest = (LPWSTR) _AllocData(pData, nBytesAvail, pnBytesUsed, nNameAlloc);
+ if (szInstNameDest == NULL)
+ return E_OUTOFMEMORY;
+
+ Checked::memcpy_s(szInstNameDest, nNameAlloc, szInstNameSrc, pInstanceDef->NameLength);
+ pInstanceDef->NameOffset = ULONG(LPBYTE(szInstNameDest) - LPBYTE(pInstanceDef));
+
+ pInstanceDef->ByteLength = DWORD(sizeof(PERF_INSTANCE_DEFINITION) + nNameAlloc);
+ }
+
+ // create the counter block + data
+ LPBYTE pCounterData = _AllocData(pData, nBytesAvail, pnBytesUsed, pCategoryInfo->m_nCounterBlockSize);
+ if (pCounterData == NULL)
+ return E_OUTOFMEMORY;
+
+ // fill in the counter block header for the data
+ PERF_COUNTER_BLOCK* pCounterBlock = (PERF_COUNTER_BLOCK*) pCounterData;
+ pCounterBlock->ByteLength = pCategoryInfo->m_nCounterBlockSize;
+
+ // fill in the data
+ for (ULONG i=0; i<pObjectType->NumCounters; i++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(i);
+ PERF_COUNTER_DEFINITION& def = pCounterDefs[i];
+ LPBYTE pSrc = LPBYTE(pInstance)+pCounterInfo->m_nDataOffset;
+ LPBYTE pDest = pCounterData+def.CounterOffset;
+ switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK)
+ {
+ case PERF_SIZE_DWORD:
+ *LPDWORD(pDest) = *LPDWORD(pSrc);
+ break;
+ case PERF_SIZE_LARGE:
+ *(ULONGLONG*)(pDest) = *(ULONGLONG*)(pSrc);
+ break;
+ case PERF_SIZE_VARIABLE_LEN:
+ if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
+ {
+ LPCWSTR szSrc = reinterpret_cast<LPCWSTR>(pSrc);
+ LPWSTR szDest = reinterpret_cast<LPWSTR>(pDest);
+ size_t nLen = __min(wcslen(szSrc), pCounterInfo->m_nMaxCounterSize-1);
+ Checked::wcsncpy_s(szDest, pCounterInfo->m_nMaxCounterSize-1, szSrc, nLen);
+ szDest[nLen] = 0;
+ }
+ else
+ {
+ LPCSTR szSrc = reinterpret_cast<LPCSTR>(pSrc);
+ LPSTR szDest = reinterpret_cast<LPSTR>(pDest);
+ size_t nLen = __min(strlen(szSrc), pCounterInfo->m_nMaxCounterSize-1);
+ Checked::strncpy_s(szDest, pCounterInfo->m_nMaxCounterSize-1, szSrc, nLen);
+ szDest[nLen] = 0;
+ }
+ break;
+ }
+ }
+
+ if (pInstanceDef != NULL)
+ pObjectType->TotalByteLength += pInstanceDef->ByteLength;
+ pObjectType->TotalByteLength += pCounterBlock->ByteLength;
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::_CollectInstance(
+ CategoryInfo* pCategoryInfo,
+ LPBYTE& pData,
+ ULONG nBytesAvail,
+ ULONG* pnBytesUsed,
+ PERF_OBJECT_TYPE* pObjectType,
+ PERF_COUNTER_DEFINITION* pCounterDefs
+ ) throw()
+{
+ // specialization to collect an instanceless object with no instance data
+ ATLASSERT(pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES);
+ pObjectType->NumInstances = PERF_NO_INSTANCES;
+
+ // create the counter block + data
+ LPBYTE pCounterData = _AllocData(pData, nBytesAvail, pnBytesUsed, pCategoryInfo->m_nCounterBlockSize);
+ if (pCounterData == NULL)
+ return E_OUTOFMEMORY;
+
+ // fill in the counter block header for the data
+ PERF_COUNTER_BLOCK* pCounterBlock = (PERF_COUNTER_BLOCK*) pCounterData;
+ pCounterBlock->ByteLength = pCategoryInfo->m_nCounterBlockSize;
+
+ // fill in the data
+ for (ULONG i=0; i<pObjectType->NumCounters; i++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(i);
+ PERF_COUNTER_DEFINITION& def = pCounterDefs[i];
+ LPBYTE pDest = pCounterData+def.CounterOffset;
+ switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK)
+ {
+ case PERF_SIZE_DWORD:
+ *LPDWORD(pDest) = 0;
+ break;
+ case PERF_SIZE_LARGE:
+ *PULONGLONG(pDest) = 0;
+ break;
+ case PERF_SIZE_VARIABLE_LEN:
+ if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
+ memset(pDest, 0, pCounterInfo->m_nMaxCounterSize*sizeof(WCHAR));
+ else
+ memset(pDest, 0, pCounterInfo->m_nMaxCounterSize*sizeof(CHAR));
+ break;
+ }
+ }
+
+ pObjectType->TotalByteLength += pCounterBlock->ByteLength;
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::_CollectCategoryType(
+ CategoryInfo* pCategoryInfo,
+ LPBYTE pData,
+ ULONG nBytesAvail,
+ ULONG* pnBytesUsed
+ ) throw()
+{
+ ATLENSURE_RETURN(pCategoryInfo != NULL);
+ ATLASSERT(pnBytesUsed != NULL);
+
+ // write the object definition out
+ PERF_OBJECT_TYPE* pObjectType = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_OBJECT_TYPE*) NULL);
+ if (pObjectType == NULL)
+ return E_OUTOFMEMORY;
+
+ Checked::memcpy_s(pObjectType, sizeof(PERF_OBJECT_TYPE), &pCategoryInfo->m_cache, sizeof(PERF_OBJECT_TYPE));
+
+ // save a pointer to the first counter entry and counter definition.
+ // we'll need them when we create the PERF_COUNTER_BLOCK data
+ PERF_COUNTER_DEFINITION* pCounterDefs = reinterpret_cast<PERF_COUNTER_DEFINITION*>(pData);
+
+ // write the counter definitions out
+ for (DWORD i=0; i<pCategoryInfo->_GetNumCounters(); i++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(i);
+
+ PERF_COUNTER_DEFINITION* pCounterDef = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_COUNTER_DEFINITION*) NULL);
+ if (pCounterDef == NULL)
+ return E_OUTOFMEMORY;
+
+ Checked::memcpy_s(pCounterDef, sizeof(PERF_COUNTER_DEFINITION), &pCounterInfo->m_cache, sizeof(PERF_COUNTER_DEFINITION));
+
+ // set PerfTime and PerfFreq for PERF_ELAPSED_TIME counter.
+ if(pCounterDef->CounterType == PERF_ELAPSED_TIME)
+ {
+ LARGE_INTEGER currTime;
+ if (FALSE != QueryPerformanceCounter(&currTime))
+ pObjectType->PerfTime = currTime;
+ else
+ pObjectType->PerfTime.QuadPart = 0;
+ QueryPerformanceFrequency (&(pObjectType->PerfFreq));
+ }
+ }
+
+ // search for objects of the appropriate type and write out their instance/counter data
+ bool bGotInstance = false;
+
+ CAtlFileMappingBase* pCurrentBlock = _GetNextBlock(NULL);
+ if (pCurrentBlock != NULL)
+ {
+ CPerfObject* pInstance = _GetFirstInstance(pCurrentBlock);
+ while (pInstance && pInstance->m_nAllocSize != 0)
+ {
+ if (pInstance->m_dwCategoryId == pCategoryInfo->m_dwCategoryId)
+ {
+ bGotInstance = true;
+ HRESULT hr = _CollectInstance(pCategoryInfo, pData, nBytesAvail,
+ pnBytesUsed, pInstance, pObjectType, pCounterDefs);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ pInstance = _GetNextInstance(pInstance);
+ ATLENSURE_RETURN(pInstance!= NULL);
+
+ if (pInstance->m_nAllocSize == (ULONG) -1)
+ {
+ pCurrentBlock = _GetNextBlock(pCurrentBlock);
+ if (pCurrentBlock == NULL)
+ pInstance = NULL;
+ else
+ pInstance = _GetFirstInstance(pCurrentBlock);
+ }
+ }
+ }
+
+ if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES && !bGotInstance)
+ {
+ // we have an instanceless (singleton) object with no data. send zeroed data
+ HRESULT hr = _CollectInstance(pCategoryInfo, pData, nBytesAvail,
+ pnBytesUsed, pObjectType, pCounterDefs);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ return S_OK;
+}
+
+inline DWORD CPerfMon::Open(LPWSTR szDeviceNames) throw()
+{
+ (szDeviceNames); // unused
+
+ return 0;
+}
+
+inline DWORD CPerfMon::Collect(
+ __in_z LPWSTR szValue,
+ __deref_inout_bcount(*pcbBytes) LPVOID* ppData,
+ __inout LPDWORD pcbBytes,
+ __inout LPDWORD pcObjectTypes
+ ) throw()
+{
+
+
+
+
+
+ _ATLTRY
+ {
+ if (FAILED(_OpenAllBlocks()))
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+
+ LPBYTE pData = LPBYTE(*ppData);
+ ULONG nBytesLeft = *pcbBytes;
+ *pcbBytes = 0;
+
+ if (_GetNumCategories() == 0)
+ {
+ // nothing is providing data. we need to load the map directly
+ // from the registry in order to provide category/counter data
+ CRegKey rkApp;
+ DWORD dwErr;
+ CString strAppKey;
+
+ strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
+
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, strAppKey, KEY_READ);
+ if (dwErr != ERROR_SUCCESS)
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+
+ ULONG nBytes = 0;
+ dwErr = rkApp.QueryBinaryValue(c_szAtlPerfMap, NULL, &nBytes);
+ if (dwErr != ERROR_SUCCESS)
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+
+ CHeapPtr<DWORD> buf;
+ if (!buf.Allocate((nBytes+3)/4))
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+
+ dwErr = rkApp.QueryBinaryValue(c_szAtlPerfMap, buf, &nBytes);
+ if (dwErr != ERROR_SUCCESS)
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+
+ if (FAILED(_LoadMap(buf)))
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+ }
+
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+ if (_WantCategoryType(szValue, pCategoryInfo->m_nNameId))
+ {
+ ULONG nBytesUsed = 0;
+ HRESULT hr = _CollectCategoryType(pCategoryInfo, pData, nBytesLeft, &nBytesUsed);
+ if (hr == E_OUTOFMEMORY)
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_MORE_DATA;
+ }
+ else if (FAILED(hr))
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+
+ (*pcObjectTypes)++;
+ (*pcbBytes) += nBytesUsed;
+ nBytesLeft -= nBytesUsed;
+ pData += nBytesUsed;
+ }
+ }
+
+ *ppData = pData;
+ return ERROR_SUCCESS;
+ }
+ _ATLCATCHALL()
+ {
+ *pcbBytes = 0;
+ *pcObjectTypes = 0;
+ return ERROR_SUCCESS;
+ }
+}
+
+inline DWORD CPerfMon::Close() throw()
+{
+ UnInitialize();
+ return ERROR_SUCCESS;
+}
+
+#ifdef _ATL_PERF_REGISTER
+#pragma warning (push)
+#pragma warning(disable : 4996)
+
+inline void CPerfMon::_AppendStrings(
+ LPTSTR& pszNew,
+ CAtlArray<CString>& astrStrings,
+ ULONG iFirstIndex
+ ) throw()
+{
+ for (UINT iString = 0; iString < astrStrings.GetCount(); iString++)
+ {
+ INT nFormatChars = _stprintf(pszNew, _T("%d"), iFirstIndex+2*iString);
+ pszNew += nFormatChars + 1;
+ _tcscpy(pszNew, astrStrings[iString]);
+ pszNew += astrStrings[iString].GetLength() + 1;
+ }
+}
+
+#pragma warning (pop)
+
+inline HRESULT CPerfMon::_AppendRegStrings(
+ CRegKey& rkLang,
+ LPCTSTR szValue,
+ CAtlArray<CString>& astrStrings,
+ ULONG nNewStringSize,
+ ULONG iFirstIndex,
+ ULONG iLastIndex
+ ) throw()
+{
+ _ATLTRY
+ {
+ // load the existing strings, add the new data, and resave the strings
+ ULONG nCharsOrig = 0;
+ ULONG nCharsNew;
+ DWORD dwErr;
+
+ dwErr = rkLang.QueryMultiStringValue(szValue, NULL, &nCharsOrig);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ nCharsNew = nCharsOrig + nNewStringSize;
+
+ CString strOrig;
+ dwErr = rkLang.QueryMultiStringValue(szValue, CStrBuf(strOrig, nCharsOrig, CStrBuf::SET_LENGTH), &nCharsOrig);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+ LPCTSTR pszOrig = strOrig;
+
+ CString strNew;
+ CStrBuf szNew(strNew, nCharsNew, CStrBuf::SET_LENGTH);
+ LPTSTR pszNew = szNew;
+
+ bool bNewStringsAdded = false;
+
+ while (*pszOrig != '\0')
+ {
+ ULONG iIndex = _ttoi(pszOrig);
+ int nLen = (int) _tcslen(pszOrig) + 1; // get the length of the index and null
+ nLen += (int) _tcslen(pszOrig+nLen) + 1; // add the length of the description and null
+
+ if (!bNewStringsAdded && iIndex >= iFirstIndex)
+ {
+ LPTSTR pszOld =pszNew;
+ _AppendStrings(pszNew, astrStrings, iFirstIndex);
+ bNewStringsAdded = true;
+ ULONG nCharsNewLast = nCharsNew;
+ nCharsNew -= ULONG(pszNew-pszOld);
+ if(nCharsNew > nCharsNewLast)
+ {
+ return E_FAIL;
+ }
+ }
+
+ if (iIndex < iFirstIndex || iIndex > iLastIndex)
+ {
+ Checked::memmove_s(pszNew, nCharsNew, pszOrig, nLen*sizeof(TCHAR));
+ pszNew += nLen;
+ }
+ pszOrig += nLen;
+ }
+ if (!bNewStringsAdded)
+ _AppendStrings(pszNew, astrStrings, iFirstIndex);
+
+ *pszNew++ = '\0'; // must have 2 null terminators at end of multi_sz
+
+ dwErr = rkLang.SetMultiStringValue(szValue, strNew);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+}
+
+inline HRESULT CPerfMon::_RemoveRegStrings(
+ CRegKey& rkLang,
+ LPCTSTR szValue,
+ ULONG iFirstIndex,
+ ULONG iLastIndex
+ ) throw()
+{
+ _ATLTRY
+ {
+ // load the existing strings, remove the data, and resave the strings
+ DWORD nChars = 0;
+ DWORD dwErr;
+
+ dwErr = rkLang.QueryMultiStringValue(szValue, NULL, &nChars);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ CString str;
+ CStrBuf szBuf(str, nChars, CStrBuf::SET_LENGTH);
+ DWORD nMaxLen = nChars*sizeof(TCHAR);
+
+ dwErr = rkLang.QueryMultiStringValue(szValue, szBuf, &nChars);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ LPCTSTR pszRead = szBuf;
+ LPTSTR pszWrite = szBuf;
+ while (*pszRead != '\0')
+ {
+ ULONG iIndex = _ttoi(pszRead);
+ int nLen = (int) _tcslen(pszRead) + 1; // get the length of the index and null
+ nLen += (int) _tcslen(pszRead+nLen) + 1; // add the length of the description and null
+ if (iIndex < iFirstIndex || iIndex > iLastIndex)
+ {
+ Checked::memmove_s(pszWrite, nMaxLen , pszRead, nLen*sizeof(TCHAR));
+ UINT nMaxLenLast = nMaxLen;
+ nMaxLen -= nLen*sizeof(TCHAR);
+ if(nMaxLen > nMaxLenLast) return E_FAIL;
+ pszWrite += nLen;
+ }
+ pszRead += nLen;
+ }
+ *pszWrite++ = '\0'; // must have 2 null terminators at end of multi_sz
+
+ dwErr = rkLang.SetMultiStringValue(szValue, szBuf);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+}
+
+inline HRESULT CPerfMon::_ReserveStringRange(DWORD& dwFirstCounter, DWORD& dwFirstHelp) throw()
+{
+ CRegKey rkApp;
+ CString strAppKey;
+ DWORD dwErr;
+
+ _ATLTRY
+ {
+ strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ DWORD nNumStrings = _GetNumCategoriesAndCounters();
+
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, strAppKey);
+ if (dwErr == ERROR_SUCCESS)
+ {
+ // see if we already have a sufficient range reserved
+ DWORD dwFirstAppCounter;
+ DWORD dwFirstAppHelp;
+ DWORD dwLastAppCounter;
+ DWORD dwLastAppHelp;
+
+ if (rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstAppCounter) == ERROR_SUCCESS &&
+ rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstAppHelp) == ERROR_SUCCESS &&
+ rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastAppCounter) == ERROR_SUCCESS &&
+ rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastAppHelp) == ERROR_SUCCESS &&
+ dwLastAppCounter-dwFirstAppCounter+2 >= 2*nNumStrings &&
+ dwLastAppHelp-dwFirstAppHelp+2 >= 2*nNumStrings)
+ {
+ dwFirstCounter = dwFirstAppCounter;
+ dwFirstHelp = dwFirstAppHelp;
+ return S_OK;
+ }
+ }
+
+ CRegKey rkPerfLib;
+
+ dwErr = rkPerfLib.Open(HKEY_LOCAL_MACHINE, c_szAtlPerfPerfLibKey);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ if (!rkApp)
+ {
+ dwErr = rkApp.Create(HKEY_LOCAL_MACHINE, strAppKey);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+ }
+
+ // figure out the counter range
+ DWORD dwLastCounter;
+ DWORD dwLastHelp;
+
+ dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwFirstCounter = dwLastCounter + 2;
+ dwFirstHelp = dwLastHelp + 2;
+ dwLastCounter += 2*nNumStrings;
+ dwLastHelp += 2*nNumStrings;
+
+ dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ // register the used counter range
+ dwErr = rkApp.SetDWORDValue(c_szAtlPerfFirstCounter, dwFirstCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetDWORDValue(c_szAtlPerfFirstHelp, dwFirstHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::Register(
+ LPCTSTR szOpenFunc,
+ LPCTSTR szCollectFunc,
+ LPCTSTR szCloseFunc,
+ HINSTANCE hDllInstance /* == _AtlBaseModule.GetModuleInstance() */
+ ) throw()
+{
+ ATLASSERT(szOpenFunc != NULL);
+ ATLASSERT(szCollectFunc != NULL);
+ ATLASSERT(szCloseFunc != NULL);
+
+ CString str;
+ DWORD dwErr;
+ HRESULT hr;
+ hr = CreateMap(LANGIDFROMLCID(GetThreadLocale()), hDllInstance);
+ if (FAILED(hr)){
+ hr = CreateMap(LANGIDFROMLCID(1033), hDllInstance);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ CString strAppKey;
+ _ATLTRY
+ {
+ strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ // if we're already registered, unregister so we can redo registration
+ _UnregisterStrings();
+
+ // reserve a range for our counter and help strings
+ DWORD dwFirstCounter = 0;
+ DWORD dwFirstHelp = 0;
+ hr = _ReserveStringRange(dwFirstCounter, dwFirstHelp);
+ if (FAILED(hr))
+ return hr;
+
+ DWORD dwCurrentName = dwFirstCounter;
+ DWORD dwCurrentHelp = dwFirstHelp;
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+
+ pCategoryInfo->m_nNameId = dwCurrentName;
+ dwCurrentName += 2;
+ pCategoryInfo->m_nHelpId = dwCurrentHelp;
+ dwCurrentHelp += 2;
+
+ for (UINT j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+
+ pCounterInfo->m_nNameId = dwCurrentName;
+ dwCurrentName += 2;
+ pCounterInfo->m_nHelpId = dwCurrentHelp;
+ dwCurrentHelp += 2;
+ }
+ }
+
+ // register the app entry points
+ CRegKey rkApp;
+
+ dwErr = rkApp.Create(HKEY_LOCAL_MACHINE, strAppKey);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ _ATLTRY
+ {
+ DWORD dwFLen = GetModuleFileName(hDllInstance, CStrBuf(str, MAX_PATH), MAX_PATH);
+ if( dwFLen == 0 )
+ return AtlHresultFromLastError();
+ else if( dwFLen == MAX_PATH )
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfLibrary, str);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfOpen, szOpenFunc);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfCollect, szCollectFunc);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfClose, szCloseFunc);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfLanguages, _T(""));
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ hr = _SaveMap();
+ if (FAILED(hr))
+ return hr;
+
+ // if the dll is disabled, reenable it since we just reregistered it
+ rkApp.DeleteValue(_T("Disable Performance Counters"));
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::RegisterStrings(
+ LANGID language /* = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) */,
+ HINSTANCE hResInstance /* = _AtlBaseModule.GetResourceInstance() */
+ ) throw()
+{
+ _ATLTRY
+ {
+ CString str;
+ DWORD dwErr;
+ HRESULT hr;
+ CRegKey rkLang;
+ CRegKey rkApp;
+
+ LANGID wPrimaryLanguage = (LANGID) PRIMARYLANGID(language);
+
+ if (language == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
+ {
+ //First try current thread locale
+ language = LANGIDFROMLCID(GetThreadLocale());
+ wPrimaryLanguage = (LANGID) PRIMARYLANGID(language);
+ }
+ str.Format(c_szAtlPerfPerfLibLangKey, wPrimaryLanguage);
+ dwErr = rkLang.Open(HKEY_LOCAL_MACHINE, str);
+ if (dwErr == ERROR_FILE_NOT_FOUND)
+ {
+ // failed using current thread, so try default system lcid
+ language = GetSystemDefaultLangID();
+ wPrimaryLanguage = (LANGID) PRIMARYLANGID(language);
+ str.Format(c_szAtlPerfPerfLibLangKey, wPrimaryLanguage);
+ dwErr = rkLang.Open(HKEY_LOCAL_MACHINE, str);
+ }
+ if (dwErr == ERROR_FILE_NOT_FOUND)
+ return S_FALSE; // the language isn't installed on the system
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ hr = CreateMap(language, hResInstance);
+ if (FAILED(hr))
+ return hr;
+
+ // load list of language strings already registered
+ str.Format(c_szAtlPerfPerformanceKey, GetAppName());
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ DWORD dwLangsLen = 0;
+ CString strLangs;
+
+ dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, NULL, &dwLangsLen);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ ULONG nLangsBuffSize = dwLangsLen+4;
+ CStrBuf szLangs(strLangs, nLangsBuffSize, CStrBuf::SET_LENGTH); // reserve room for adding new language
+ dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, szLangs, &dwLangsLen);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+ dwLangsLen--; // don't count '\0'
+
+ // see if this language has already been registered and if so, return
+ TCHAR szNewLang[5];
+ _sntprintf_s(szNewLang, _countof(szNewLang), _countof(szNewLang)-1, _T("%3.3x "), wPrimaryLanguage);
+ if (strLangs.Find(szNewLang) != -1)
+ return S_OK;
+
+ // load the strings we want to append and figure out how much extra space is needed for them
+ // (including up to 5-digit index values and 2 null separators)
+ CAtlArray<CString> astrCounters;
+ CAtlArray<CString> astrHelp;
+ ULONG nNewCounterSize = 0;
+ ULONG nNewHelpSize = 0;
+
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+
+ astrCounters.Add(pCategoryInfo->m_strName);
+ astrHelp.Add(pCategoryInfo->m_strHelp);
+
+ for (UINT j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+
+ astrCounters.Add(pCounterInfo->m_strName);
+ astrHelp.Add(pCounterInfo->m_strHelp);
+ }
+ }
+
+ for (size_t i=0; i<astrCounters.GetCount(); i++)
+ {
+ nNewCounterSize += astrCounters[i].GetLength() + 7;
+ nNewHelpSize += astrHelp[i].GetLength() + 7;
+ }
+
+ DWORD dwFirstCounter;
+ DWORD dwFirstHelp;
+ DWORD dwLastCounter;
+ DWORD dwLastHelp;
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ hr = _AppendRegStrings(rkLang, c_szAtlPerfCounter, astrCounters, nNewCounterSize, dwFirstCounter, dwLastCounter);
+ if (FAILED(hr))
+ return hr;
+
+ hr = _AppendRegStrings(rkLang, c_szAtlPerfHelp, astrHelp, nNewHelpSize, dwFirstHelp, dwLastHelp);
+ if (FAILED(hr))
+ return hr;
+
+ // add the language to the list of installed languages
+ Checked::tcscpy_s(szLangs+dwLangsLen, nLangsBuffSize-dwLangsLen, szNewLang);
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfLanguages, szLangs);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+}
+
+inline BOOL CPerfMon::EnumResLangProc(
+ HINSTANCE hModule,
+ LPCTSTR szType,
+ LPCTSTR szName,
+ LANGID wIDLanguage,
+ LPARAM lParam
+ ) throw()
+{
+ hModule; // unused
+ szType; // unused
+ szName; // unused
+
+ CAtlArray<LANGID>* pLangs = reinterpret_cast<CAtlArray<LANGID>*>(lParam);
+ _ATLTRY
+ {
+ pLangs->Add(wIDLanguage);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline HRESULT CPerfMon::RegisterAllStrings(
+ HINSTANCE hResInstance /* = NULL */
+ ) throw()
+{
+ HRESULT hrReturn = S_FALSE;
+ HRESULT hr;
+
+ UINT nRes;
+ hr = CreateMap(0, hResInstance, &nRes);
+ if (FAILED(hr))
+ return hr;
+
+ if (nRes == 0)
+ return RegisterStrings(0, hResInstance);
+
+ if (hResInstance != NULL)
+ return _RegisterAllStrings(nRes, hResInstance);
+
+ for (int i = 0; hResInstance = _AtlBaseModule.GetHInstanceAt(i), hResInstance != NULL; i++)
+ {
+ hr = _RegisterAllStrings(nRes, hResInstance);
+ if (FAILED(hr))
+ return hr;
+ if (hr == S_OK)
+ hrReturn = S_OK;
+ }
+
+ return hrReturn;
+}
+
+inline HRESULT CPerfMon::_RegisterAllStrings(
+ UINT nRes,
+ HINSTANCE hResInstance
+ ) throw()
+{
+ HRESULT hrReturn = S_FALSE;
+ HRESULT hr;
+
+ CAtlArray<LANGID> langs;
+ if (!EnumResourceLanguages(hResInstance, RT_STRING, MAKEINTRESOURCE((nRes>>4)+1), EnumResLangProc, reinterpret_cast<LPARAM>(&langs)))
+ return AtlHresultFromLastError();
+
+ for (UINT i=0; i<langs.GetCount(); i++)
+ {
+ hr = RegisterStrings(langs[i], hResInstance);
+ if (FAILED(hr))
+ return hr;
+ if (hr == S_OK)
+ hrReturn = S_OK;
+ }
+
+ return hrReturn;
+}
+
+inline HRESULT CPerfMon::_UnregisterStrings() throw()
+{
+ _ATLTRY
+ {
+ CString str;
+ HRESULT hr;
+ DWORD dwErr;
+
+ // unregister the PerfMon counter and help strings
+ CRegKey rkApp;
+
+ str.Format(c_szAtlPerfPerformanceKey, GetAppName());
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
+ //The register strings was unregistered.
+ if (dwErr == ERROR_FILE_NOT_FOUND)
+ return S_OK;
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ DWORD dwFirstAppCounter;
+ DWORD dwFirstAppHelp;
+ DWORD dwLastAppCounter;
+ DWORD dwLastAppHelp;
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstAppCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstAppHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastAppCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastAppHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ // iterate through the installed languages and delete them all
+ DWORD nChars = 0;
+ dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, NULL, &nChars);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ CString strLangs;
+ dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, CStrBuf(strLangs, nChars, CStrBuf::SET_LENGTH), &nChars);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ int nIndex = 0;
+ CString strLang = strLangs.Tokenize(_T(" "), nIndex);
+ while (!strLang.IsEmpty())
+ {
+ CRegKey rkLang;
+
+ dwErr = rkLang.Open(HKEY_LOCAL_MACHINE, CString(c_szAtlPerfPerfLibKey) + _T("\\") + strLang);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ hr = _RemoveRegStrings(rkLang, c_szAtlPerfCounter, dwFirstAppCounter, dwLastAppCounter);
+ if (FAILED(hr))
+ return hr;
+
+ hr = _RemoveRegStrings(rkLang, c_szAtlPerfHelp, dwFirstAppHelp, dwLastAppHelp);
+ if (FAILED(hr))
+ return hr;
+
+ strLang = strLangs.Tokenize(_T(" "), nIndex);
+ }
+
+ dwErr = rkApp.SetStringValue(c_szAtlPerfLanguages, _T(""));
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ return S_OK;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+}
+
+inline HRESULT CPerfMon::Unregister() throw()
+{
+ CString str;
+ HRESULT hr;
+ DWORD dwErr;
+
+ CRegKey rkPerfLib;
+ CRegKey rkApp;
+
+ hr = _UnregisterStrings();
+ if (FAILED(hr))
+ return hr;
+
+ dwErr = rkPerfLib.Open(HKEY_LOCAL_MACHINE, c_szAtlPerfPerfLibKey);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ _ATLTRY
+ {
+ str.Format(c_szAtlPerfPerformanceKey, GetAppName());
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
+ // The performance counter was unregistered
+ if (dwErr == ERROR_FILE_NOT_FOUND)
+ return S_OK;
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ DWORD dwLastCounter;
+ DWORD dwLastHelp;
+ DWORD dwFirstAppCounter;
+ DWORD dwFirstAppHelp;
+ DWORD dwLastAppCounter;
+ DWORD dwLastAppHelp;
+
+ dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstAppCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstAppHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastAppCounter);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastAppHelp);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ // rewind the Last Help/Last Counter values if possible
+ if (dwLastCounter == dwLastAppCounter)
+ {
+ dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastCounter, dwFirstAppCounter-2);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+ }
+
+ if (dwLastHelp == dwLastAppHelp)
+ {
+ dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastHelp, dwFirstAppHelp-2);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+ }
+ rkApp.Close();
+
+ // delete the app key
+ CRegKey rkServices;
+
+ _ATLTRY
+ {
+ str.Format(c_szAtlPerfServicesKey, GetAppName());
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+ dwErr = rkServices.Open(HKEY_LOCAL_MACHINE, str);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ dwErr = rkServices.RecurseDeleteKey(c_szAtlPerfPerformance);
+ if (dwErr != ERROR_SUCCESS)
+ return AtlHresultFromWin32(dwErr);
+
+ return S_OK;
+}
+#endif
+
+inline HRESULT CPerfMon::Initialize() throw()
+{
+ CMutex tempLock;
+ CString strAppName;
+ HRESULT hr;
+
+ _ATLTRY
+ {
+ strAppName = GetAppName();
+
+ ATLASSUME(m_aMem.GetCount() == 0);
+
+ CAccessToken at;
+ if (!at.GetEffectiveToken(TOKEN_QUERY))
+ return E_FAIL;
+
+
+ CSid self;
+ if (!at.GetUser(&self))
+ return E_FAIL;
+
+ // set up security information for creating the mutex
+ CDacl dacl;
+
+
+ dacl.AddAllowedAce(Sids::NetworkService(),GENERIC_READ);
+ dacl.AddAllowedAce(Sids::Admins(), GENERIC_ALL);
+ dacl.AddAllowedAce(Sids::System(), GENERIC_ALL);
+ dacl.AddAllowedAce(self, GENERIC_ALL);
+
+ m_sd.SetDacl(dacl);
+ m_sd.SetOwner(self);
+
+ CSecurityAttributes sa;
+ sa.Set(m_sd);
+
+ // create a mutex to handle syncronizing access to the shared memory area
+ CString strMutexName;
+ strMutexName.Format(_T("Global\\ATLPERF_%s_LOCK"), strAppName);
+ tempLock.Create(&sa, FALSE, strMutexName);
+ if (tempLock.m_h == NULL)
+ return AtlHresultFromLastError();
+
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ // prevent us from using an object someone else has opened
+ if (::SetSecurityInfo(tempLock, SE_KERNEL_OBJECT,
+ DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
+ const_cast<SID*>(self.GetPSID()),
+ NULL,
+ const_cast<ACL*>(dacl.GetPACL()),
+ NULL) != ERROR_SUCCESS)
+ return E_FAIL;
+ }
+
+ // now set up the dacl for creating shared memory segments and store it
+ dacl.AddAllowedAce(Sids::Interactive(), GENERIC_READ);
+ m_sd.SetDacl(dacl);
+
+ // create a shared memory area to share data between the app being measured and the client doing the measuring
+ {
+ CMutexLock lock(tempLock);
+
+ BOOL bExisted = FALSE;
+
+ CAtlFileMappingBase* pMem;
+ pMem = _AllocNewBlock(NULL, &bExisted);
+ if (pMem == NULL)
+ return E_OUTOFMEMORY;
+
+ if (!bExisted)
+ {
+ // copy the map from the registry to the shared memory
+ CRegKey rkApp;
+ DWORD dwErr;
+ CString strAppKey;
+
+ strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
+
+ dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, strAppKey, KEY_READ);
+ if (dwErr != ERROR_SUCCESS)
+ {
+ m_aMem.RemoveAll();
+ return AtlHresultFromWin32(dwErr);
+ }
+
+ ULONG nBytes = m_nAllocSize;
+ dwErr = rkApp.QueryBinaryValue(c_szAtlPerfMap, pMem->GetData(), &nBytes);
+ if (dwErr != ERROR_SUCCESS)
+ {
+ m_aMem.RemoveAll();
+ return AtlHresultFromWin32(dwErr);
+ }
+ }
+
+ hr = _LoadMap(LPDWORD(pMem->GetData()));
+ if (FAILED(hr))
+ {
+ m_aMem.RemoveAll();
+ return hr;
+ }
+
+ m_nSchemaSize = *LPDWORD(pMem->GetData());
+ m_nHeaderSize = m_nSchemaSize + sizeof(DWORD);
+ m_nHeaderSize = AtlAlignUp(m_nHeaderSize,16);
+ }
+
+ m_lock.Attach(tempLock.Detach());
+ }
+ _ATLCATCHALL()
+ {
+ m_aMem.RemoveAll();
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+inline void CPerfMon::UnInitialize() throw()
+{
+ if (m_lock.m_h != NULL)
+ m_lock.Close();
+ m_aMem.RemoveAll();
+ ClearMap();
+}
+
+inline HRESULT CPerfMon::_CreateInstance(
+ DWORD dwCategoryId,
+ DWORD dwInstance,
+ LPCWSTR szInstanceName,
+ CPerfObject** ppInstance,
+ bool bByName
+ ) throw()
+{
+ CPerfObject* pEmptyBlock = NULL;
+
+ if (ppInstance == NULL)
+ return E_POINTER;
+
+ CAtlFileMappingBase* pCurrentBlock = _GetNextBlock(NULL);
+ if (pCurrentBlock == NULL || pCurrentBlock->GetData() == NULL || m_lock.m_h == NULL)
+ return E_UNEXPECTED; // Initialize must succeed before calling CreateInstance
+
+ *ppInstance = NULL;
+
+ CategoryInfo* pCategoryInfo = _FindCategoryInfo(dwCategoryId);
+ if (pCategoryInfo == NULL)
+ return E_INVALIDARG;
+ if (szInstanceName == NULL && bByName)
+ return E_INVALIDARG;
+ if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES &&
+ (dwInstance != 0 || szInstanceName != NULL))
+ return E_INVALIDARG;
+
+ CPerfLock lock(this);
+ if (FAILED(lock.GetStatus()))
+ return lock.GetStatus();
+
+ CPerfObject* pInstance = _GetFirstInstance(pCurrentBlock);
+ ULONG nMaxInstance = 0;
+ ULONG nUsedSpace = 0;
+
+ // walk all of the existing objects trying to find one that matches the request
+ while (pInstance->m_nAllocSize != 0)
+ {
+ nUsedSpace += pInstance->m_nAllocSize;
+
+ if (pInstance->m_dwCategoryId == dwCategoryId)
+ {
+ nMaxInstance = __max(nMaxInstance, pInstance->m_dwInstance);
+
+ // check to see if we've found the one the caller wants
+ if (!bByName && pInstance->m_dwInstance == dwInstance &&
+ (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES || dwInstance != 0))
+ {
+ *ppInstance = pInstance;
+ pInstance->m_nRefCount++;
+ return S_OK;
+ }
+ if (bByName)
+ {
+ LPWSTR szInstName = (LPWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset));
+ if (wcsncmp(szInstName, szInstanceName, pCategoryInfo->m_nMaxInstanceNameLen-1) == 0)
+ {
+ *ppInstance = pInstance;
+ pInstance->m_nRefCount++;
+ return S_OK;
+ }
+ }
+ }
+
+ if (pInstance->m_nAllocSize == pCategoryInfo->m_nAllocSize && pInstance->m_nRefCount == 0)
+ pEmptyBlock = pInstance;
+
+ pInstance = _GetNextInstance(pInstance);
+ ATLENSURE_RETURN(pInstance!= NULL);
+
+ if (pInstance->m_nAllocSize == 0 &&
+ m_nHeaderSize + nUsedSpace + pCategoryInfo->m_nAllocSize + sizeof(CPerfObject) > m_nAllocSize)
+ {
+ // we've reached the end of the block and have no room to allocate an object of this
+ // type. cap the block with a sentinel
+ pInstance->m_nAllocSize = (ULONG) -1;
+ }
+
+ // check for an end-of-shared-mem sentinel
+ if (pInstance->m_nAllocSize == (ULONG) -1)
+ {
+ nUsedSpace = 0;
+ CAtlFileMappingBase* pNextBlock = _GetNextBlock(pCurrentBlock);
+ if (pNextBlock == NULL)
+ {
+ // we've reached the last block of shared mem.
+ // the instance hasn't been found, so either use a
+ // previously freed instance block (pEmptyBlock) or allocate a new
+ // shared mem block to hold the new instance
+ if (pEmptyBlock == NULL)
+ {
+ pNextBlock = _AllocNewBlock(pCurrentBlock);
+ if (pNextBlock == NULL)
+ return E_OUTOFMEMORY;
+ }
+ else
+ break;
+ }
+ pCurrentBlock = pNextBlock;
+ pInstance = _GetFirstInstance(pCurrentBlock);
+ }
+ }
+
+ // allocate a new object
+ if (pEmptyBlock != NULL)
+ pInstance = pEmptyBlock;
+ else
+ pInstance->m_nAllocSize = pCategoryInfo->m_nAllocSize;
+
+ if (dwInstance == 0 && pCategoryInfo->m_nInstanceLess != PERF_NO_INSTANCES)
+ pInstance->m_dwInstance = nMaxInstance + 1;
+ else
+ pInstance->m_dwInstance = dwInstance;
+
+ pInstance->m_nRefCount = 1;
+
+ // copy the instance name, truncate if necessary
+ if (pCategoryInfo->m_nInstanceLess != PERF_NO_INSTANCES)
+ {
+ ULONG nNameLen = (ULONG)__min(wcslen(szInstanceName), pCategoryInfo->m_nMaxInstanceNameLen-1);
+ ULONG nNameBytes = (nNameLen+1) * sizeof(WCHAR);
+ pInstance->m_nInstanceNameOffset = pInstance->m_nAllocSize-nNameBytes;
+ Checked::memcpy_s(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset, pInstance->m_nAllocSize-pInstance->m_nInstanceNameOffset, szInstanceName, nNameBytes);
+ LPWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset)[nNameLen] = 0;
+ }
+
+ // copy the CategoryId last: it won't be collected until this is set
+ pInstance->m_dwCategoryId = pCategoryInfo->m_dwCategoryId;
+
+ *ppInstance = pInstance;
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::CreateInstance(
+ DWORD dwCategoryId,
+ DWORD dwInstance,
+ LPCWSTR szInstanceName,
+ CPerfObject** ppInstance
+ ) throw()
+{
+ return _CreateInstance(dwCategoryId, dwInstance, szInstanceName, ppInstance, false);
+}
+
+inline HRESULT CPerfMon::CreateInstanceByName(
+ DWORD dwCategoryId,
+ LPCWSTR szInstanceName,
+ CPerfObject** ppInstance
+ ) throw()
+{
+ return _CreateInstance(dwCategoryId, 0, szInstanceName, ppInstance, true);
+}
+
+inline HRESULT CPerfMon::ReleaseInstance(CPerfObject* pInstance) throw()
+{
+ ATLASSERT(pInstance != NULL);
+ if (pInstance == NULL)
+ return E_INVALIDARG;
+
+ CPerfLock lock(this);
+ if (FAILED(lock.GetStatus()))
+ return lock.GetStatus();
+
+ if (--pInstance->m_nRefCount == 0)
+ {
+ pInstance->m_dwInstance = 0;
+ pInstance->m_dwCategoryId = 0;
+ }
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::LockPerf(DWORD dwTimeout /* == INFINITE */) throw()
+{
+ if (m_lock.m_h == NULL)
+ return E_UNEXPECTED;
+
+ DWORD dwRes = WaitForSingleObject(m_lock.m_h, dwTimeout);
+ if (dwRes == WAIT_ABANDONED || dwRes == WAIT_OBJECT_0)
+ return S_OK;
+ if (dwRes == WAIT_TIMEOUT)
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+ return AtlHresultFromLastError();
+}
+
+inline void CPerfMon::UnlockPerf() throw()
+{
+ m_lock.Release();
+}
+
+// map building routines
+inline HRESULT CPerfMon::AddCategoryDefinition(
+ DWORD dwCategoryId,
+ LPCTSTR szCategoryName,
+ LPCTSTR szHelpString,
+ DWORD dwDetailLevel,
+ INT nDefaultCounter,
+ BOOL bInstanceLess,
+ UINT nStructSize,
+ UINT nMaxInstanceNameLen) throw()
+{
+ // must have one and only one of these
+ ATLASSERT(!bInstanceLess ^ !nMaxInstanceNameLen);
+
+ // get the things that can fail out of the way first
+ CString strName;
+ CString strHelp;
+ _ATLTRY
+ {
+ strName = szCategoryName;
+ strHelp = szHelpString;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (!m_categories.SetCount(m_categories.GetCount()+1))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ // category has been added, set the data
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1);
+
+ pCategoryInfo->m_dwCategoryId = dwCategoryId;
+ pCategoryInfo->m_dwDetailLevel = dwDetailLevel;
+ pCategoryInfo->m_nDefaultCounter = nDefaultCounter;
+ pCategoryInfo->m_nInstanceLess = bInstanceLess ? PERF_NO_INSTANCES : 0;
+ pCategoryInfo->m_nStructSize = nStructSize;
+ pCategoryInfo->m_nMaxInstanceNameLen = nMaxInstanceNameLen;
+ pCategoryInfo->m_nAllocSize = nStructSize + nMaxInstanceNameLen*sizeof(WCHAR);
+ pCategoryInfo->m_strName = strName;
+ pCategoryInfo->m_strHelp = strHelp;
+ pCategoryInfo->m_nNameId = 0;
+ pCategoryInfo->m_nHelpId = 0;
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::AddCounterDefinition(
+ DWORD dwCounterId,
+ LPCTSTR szCounterName,
+ LPCTSTR szHelpString,
+ DWORD dwDetailLevel,
+ DWORD dwCounterType,
+ ULONG nMaxCounterSize,
+ UINT nOffset,
+ INT nDefaultScale) throw()
+{
+ // must add category BEFORE adding counter!
+ ATLASSERT(_GetNumCategories() > 0);
+
+ CounterInfo counter;
+
+ counter.m_dwCounterId = dwCounterId;
+ _ATLTRY
+ {
+ counter.m_strName = szCounterName;
+ counter.m_strHelp = szHelpString;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+ counter.m_dwDetailLevel = dwDetailLevel;
+ counter.m_dwCounterType = dwCounterType;
+ counter.m_nDefaultScale = nDefaultScale;
+ counter.m_nMaxCounterSize = nMaxCounterSize;
+ counter.m_nDataOffset = nOffset;
+
+ counter.m_nNameId = 0;
+ counter.m_nHelpId = 0;
+
+ // add the counter to the category
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1);
+ _ATLTRY
+ {
+ pCategoryInfo->m_counters.Add(counter);
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (counter.m_nMaxCounterSize > 0)
+ {
+ ATLASSERT(counter.m_dwCounterType & PERF_TYPE_TEXT);
+ pCategoryInfo->m_nAllocSize += counter.m_nMaxCounterSize * sizeof(WCHAR);
+ }
+
+ return S_OK;
+}
+
+inline HRESULT CPerfMon::RegisterCategory(
+ WORD wLanguage,
+ HINSTANCE hResInstance,
+ UINT* pSampleRes,
+ DWORD dwCategoryId,
+ UINT nNameString,
+ UINT nHelpString,
+ DWORD dwDetail,
+ BOOL bInstanceless,
+ UINT nStructSize,
+ UINT nMaxInstanceNameLen,
+ INT nDefaultCounter) throw()
+{
+ if (pSampleRes)
+ *pSampleRes = nNameString;
+
+ CString strName;
+ CString strHelp;
+
+ _ATLTRY
+ {
+
+ if (!strName.LoadString(hResInstance, nNameString, wLanguage) ||
+ !strHelp.LoadString(hResInstance, nHelpString, wLanguage))
+ {
+ return E_FAIL;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return RegisterCategory(
+ wLanguage,
+ hResInstance,
+ pSampleRes,
+ dwCategoryId,
+ strName,
+ strHelp,
+ dwDetail,
+ bInstanceless,
+ nStructSize,
+ nMaxInstanceNameLen,
+ nDefaultCounter);
+}
+
+inline HRESULT CPerfMon::RegisterCategory(
+ WORD /* wLanguage */,
+ HINSTANCE /* hResInstance */,
+ UINT* /* pSampleRes */,
+ DWORD dwCategoryId,
+ LPCTSTR szNameString,
+ LPCTSTR szHelpString,
+ DWORD dwDetail,
+ BOOL bInstanceless,
+ UINT nStructSize,
+ UINT nMaxInstanceNameLen,
+ INT nDefaultCounter) throw()
+{
+ return AddCategoryDefinition(
+ dwCategoryId,
+ szNameString,
+ szHelpString,
+ dwDetail,
+ nDefaultCounter,
+ bInstanceless,
+ nStructSize,
+ nMaxInstanceNameLen);
+}
+
+inline HRESULT CPerfMon::RegisterCounter(
+ WORD wLanguage,
+ HINSTANCE hResInstance,
+ DWORD dwCounterId,
+ UINT nNameString,
+ UINT nHelpString,
+ DWORD dwDetail,
+ DWORD dwCounterType,
+ ULONG nMaxCounterSize,
+ UINT nOffset,
+ INT nDefaultScale) throw()
+{
+ CString strName;
+ CString strHelp;
+
+ _ATLTRY
+ {
+
+ if (!strName.LoadString(hResInstance, nNameString, wLanguage) ||
+ !strHelp.LoadString(hResInstance, nHelpString, wLanguage))
+ {
+ return E_FAIL;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return RegisterCounter(
+ wLanguage,
+ hResInstance,
+ dwCounterId,
+ strName,
+ strHelp,
+ dwDetail,
+ dwCounterType,
+ nMaxCounterSize,
+ nOffset,
+ nDefaultScale);
+}
+
+inline HRESULT CPerfMon::RegisterCounter(
+ WORD /* wLanguage */,
+ HINSTANCE /* hResInstance */,
+ DWORD dwCounterId,
+ LPCTSTR szNameString,
+ LPCTSTR szHelpString,
+ DWORD dwDetail,
+ DWORD dwCounterType,
+ ULONG nMaxCounterSize,
+ UINT nOffset,
+ INT nDefaultScale) throw()
+{
+ return AddCounterDefinition(
+ dwCounterId,
+ szNameString,
+ szHelpString,
+ dwDetail,
+ dwCounterType,
+ nMaxCounterSize,
+ nOffset,
+ nDefaultScale);
+}
+
+inline void CPerfMon::ClearMap() throw()
+{
+ m_categories.RemoveAll();
+}
+
+#ifndef _ATL_PERF_NOXML
+
+ATL_NOINLINE inline HRESULT CPerfMon::PersistToXML(IStream *pStream, BOOL bFirst/*=TRUE*/, BOOL bLast/*=TRUE*/) throw(...)
+{
+ ATLASSERT(pStream != NULL);
+ if (pStream == NULL)
+ return E_INVALIDARG;
+
+ CPerfLock lock(this);
+ if (FAILED(lock.GetStatus()))
+ return ERROR_SUCCESS;
+
+ CStringA strXML;
+ HRESULT hr = S_OK;
+ ULONG nLen = 0;
+
+ if (bFirst)
+ {
+ strXML = "<?xml version=\"1.0\" ?>\r\n<perfPersist>\r\n";
+ hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
+ if (hr != S_OK)
+ return hr;
+ }
+
+ strXML.Format("\t<perfmon name=\"%s\">\r\n", CT2CA(GetAppName()));
+ hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
+
+ for (UINT i=0; i<_GetNumCategories(); i++)
+ {
+ CategoryInfo* pCategoryInfo = _GetCategoryInfo(i);
+
+ CAtlFileMappingBase *pCurrentBlock = _GetNextBlock(NULL);
+ CPerfObject *pInstance = _GetFirstInstance(pCurrentBlock);
+
+ strXML.Format("\t\t<perfObject perfid=\"%d\">\r\n",
+ pCategoryInfo->m_dwCategoryId, pCategoryInfo->m_nNameId, pCategoryInfo->m_nHelpId);
+
+ hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
+ if (hr != S_OK)
+ return E_FAIL;
+
+ while (pInstance && pInstance->m_nAllocSize)
+ {
+ if (pInstance->m_dwCategoryId == pCategoryInfo->m_dwCategoryId)
+ {
+ if (pCategoryInfo->m_nInstanceLess != PERF_NO_INSTANCES)
+ {
+ // handle the instance name
+ LPCWSTR wszInstNameSrc = LPCWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset);
+ int nInstLen = (int) wcslen(wszInstNameSrc);
+
+ // convert to UTF8
+ int nLength = AtlUnicodeToUTF8(wszInstNameSrc, nInstLen, NULL, 0);
+ CHeapPtr<CHAR> szUTF8;
+ if ((nLength < 0) || (nLength+1<nLength) || !szUTF8.Allocate(nLength+1))
+ return E_OUTOFMEMORY;
+ nLength = AtlUnicodeToUTF8(wszInstNameSrc, nInstLen, szUTF8, nLength);
+ szUTF8[nLength] = '\0';
+
+ strXML.Format("\t\t\t<instance name=\"%s\" id=\"%d\">\r\n", szUTF8, pInstance->m_dwInstance);
+ hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
+ if (hr != S_OK)
+ return hr;
+ }
+
+ for (UINT j=0; j<pCategoryInfo->_GetNumCounters(); j++)
+ {
+ CounterInfo *pCounterInfo = pCategoryInfo->_GetCounterInfo(j);
+ switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK)
+ {
+ case PERF_SIZE_DWORD:
+ {
+ strXML.Format("\t\t\t\t<counter type=\"perf_size_dword\" value=\"%d\" offset=\"%d\"/>\r\n",
+ *LPDWORD(LPBYTE(pInstance)+pCounterInfo->m_nDataOffset),
+ pCounterInfo->m_nDataOffset);
+ break;
+ }
+ case PERF_SIZE_LARGE:
+ {
+ strXML.Format("\t\t\t\t<counter type=\"perf_size_large\" value=\"%d\" offset=\"%d\"/>\r\n",
+ *PULONGLONG(LPBYTE(pInstance)+pCounterInfo->m_nDataOffset),
+ pCounterInfo->m_nDataOffset);
+ break;
+ }
+ case PERF_SIZE_VARIABLE_LEN:
+ {
+ CHeapPtr<CHAR> szUTF8;
+ LPBYTE pSrc = LPBYTE(pInstance)+pCounterInfo->m_nDataOffset;
+ if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
+ {
+ ULONG nTextLen = (ULONG)wcslen(LPCWSTR(pSrc));
+ // convert to UTF8
+ nLen = AtlUnicodeToUTF8(LPCWSTR(pSrc), nTextLen, NULL, 0);
+ if (!szUTF8.Allocate(nLen+1))
+ return E_OUTOFMEMORY;
+
+ nLen = AtlUnicodeToUTF8(LPCWSTR(pSrc), nTextLen, szUTF8, nLen);
+ szUTF8[nLen] = '\0';
+ strXML.Format("\t\t\t\t<counter type=\"perf_size_variable_len_unicode\" value=\"%s\" offset=\"%d\"/>\r\n",
+ szUTF8,
+ pCounterInfo->m_nDataOffset);
+ }
+ else
+ {
+ ULONG nTextLen = (ULONG)strlen(LPCSTR(pSrc));
+ if (!szUTF8.Allocate(nTextLen+1))
+ return E_OUTOFMEMORY;
+ Checked::strcpy_s(szUTF8, nTextLen+1, LPCSTR(pSrc));
+ strXML.Format("\t\t\t\t<counter type=\"perf_size_variable_len_ansi\" value=\"%s\" offset=\"%d\"/>\r\n",
+ szUTF8,
+ pCounterInfo->m_nDataOffset);
+ }
+ break;
+ }
+ default:
+ // error:
+ return E_FAIL;
+ }
+ hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
+ if (hr != S_OK)
+ return hr;
+ }
+
+ if (pCategoryInfo->m_nInstanceLess != PERF_NO_INSTANCES)
+ {
+ hr = pStream->Write("\t\t\t</instance>\r\n", sizeof("\t\t\t</instance>\r\n")-1, &nLen);
+ if (hr != S_OK)
+ return hr;
+ }
+ }
+
+ pInstance = _GetNextInstance(pInstance);
+ ATLENSURE_RETURN(pInstance!= NULL);
+
+ if (pInstance->m_nAllocSize == (ULONG)-1)
+ {
+ pCurrentBlock = _GetNextBlock(pCurrentBlock);
+ if (pCurrentBlock == NULL)
+ pInstance = NULL;
+ else
+ pInstance = _GetFirstInstance(pCurrentBlock);
+ }
+ }
+
+ hr = pStream->Write("\t\t</perfObject>\r\n", sizeof("\t\t</perfObject>\r\n")-1, &nLen);
+ if (hr != S_OK)
+ return hr;
+ }
+
+ hr = pStream->Write("\t</perfmon>\r\n", sizeof("\t</perfmon>\r\n")-1, &nLen);
+ if (hr != S_OK)
+ return hr;
+
+ if (hr == S_OK && bLast)
+ hr = pStream->Write("</perfPersist>", sizeof("</perfPersist>")-1, &nLen);
+
+ return hr;
+}
+
+// This function is very lenient with inappropriate XML
+ATL_NOINLINE inline HRESULT CPerfMon::LoadFromXML(IStream *pStream) throw(...)
+{
+ ATLASSERT(pStream != NULL);
+ if (pStream == NULL)
+ return E_INVALIDARG;
+
+ // Get a lock
+ CPerfLock lock(this);
+ if (FAILED(lock.GetStatus()))
+ return ERROR_SUCCESS;
+
+ CComPtr<IXMLDOMDocument> spdoc;
+
+ // load the xml
+ HRESULT hr = CoCreateInstance(__uuidof(DOMDocument), NULL, CLSCTX_INPROC, __uuidof(IXMLDOMDocument), (void **) &spdoc);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ spdoc->put_async(VARIANT_FALSE);
+
+ CComPtr<IPersistStreamInit> spSI;
+ hr = spdoc->QueryInterface(&spSI);
+ if (hr != S_OK)
+ return hr;
+ hr = spSI->Load(pStream);
+ if (hr != S_OK)
+ return hr;
+
+ // validate that it is a perfPersist stream
+ CComPtr<IXMLDOMElement> spRoot;
+
+ hr = spdoc->get_documentElement(&spRoot);
+ if (hr != S_OK)
+ return hr;
+
+ CComBSTR bstrName;
+ hr = spRoot->get_baseName(&bstrName);
+ if (wcscmp(bstrName, L"perfPersist"))
+ return S_FALSE;
+
+ // find the appropriate perfmon node
+ CComPtr<IXMLDOMNode> spChild;
+ hr = spRoot->get_firstChild(&spChild);
+ while (hr == S_OK)
+ {
+ bstrName.Empty();
+ hr = spChild->get_baseName(&bstrName);
+ if (hr == S_OK)
+ {
+ if (!wcscmp(bstrName, L"perfmon"))
+ {
+ bstrName.Empty();
+ hr = _GetAttribute(spChild, L"name", &bstrName);
+ if (hr == S_OK)
+ {
+ if (!_tcscmp(CW2CT(bstrName), GetAppName()))
+ break;
+ }
+ }
+ }
+
+ CComPtr<IXMLDOMNode> spNext;
+ hr = spChild->get_nextSibling(&spNext);
+ spChild.Attach(spNext.Detach());
+ }
+
+ // there is no perfmon node in the XML for the current CPerfMon class
+ if (hr != S_OK)
+ return S_FALSE;
+
+ CComPtr<IXMLDOMNode> spPerfRoot;
+ spPerfRoot.Attach(spChild.Detach());
+
+ // iterate over the objects in the perfmon subtree
+ // this is the loop that does the real work
+ hr = spPerfRoot->get_firstChild(&spChild);
+ while (hr == S_OK)
+ {
+ // see if it's a perfObject
+ bstrName.Empty();
+ hr = spChild->get_baseName(&bstrName);
+ if (hr != S_OK || wcscmp(bstrName, L"perfObject"))
+ return S_FALSE;
+
+ // get the perfid
+ bstrName.Empty();
+ hr = _GetAttribute(spChild, L"perfid", &bstrName);
+ DWORD dwPerfId = _wtoi(bstrName);
+
+ // iterate over children
+ CComPtr<IXMLDOMNode> spInstChild;
+ hr = spChild->get_firstChild(&spInstChild);
+ while (hr == S_OK)
+ {
+ // see if it's a instance
+ bstrName.Empty();
+ hr = spInstChild->get_baseName(&bstrName);
+ if (hr != S_OK || wcscmp(bstrName, L"instance"))
+ return S_FALSE;
+
+ // get the instance name
+ bstrName.Empty();
+ hr = _GetAttribute(spInstChild, L"name", &bstrName);
+ if (hr != S_OK)
+ return S_FALSE;
+
+ // get the instance id
+ bstrName.Empty();
+ hr = _GetAttribute(spChild, L"id", &bstrName);
+ if (hr != S_OK)
+ return S_FALSE;
+ DWORD dwInstance = _wtoi(bstrName);
+
+ // create the instance
+ CPerfObject *pInstance = NULL;
+ hr = CreateInstance(dwPerfId, dwInstance++, bstrName, &pInstance);
+ if (hr != S_OK)
+ return S_FALSE;
+
+ // iterate over the counters and set the data
+ CComPtr<IXMLDOMNode> spCntrChild;
+ hr = spInstChild->get_firstChild(&spCntrChild);
+ while (hr == S_OK)
+ {
+ // get the base name
+ bstrName.Empty();
+ hr = spCntrChild->get_baseName(&bstrName);
+ if (hr != S_OK || wcscmp(bstrName, L"counter"))
+ return S_FALSE;
+
+ // get the type
+ bstrName.Empty();
+ hr = _GetAttribute(spCntrChild, L"type", &bstrName);
+ if (hr != S_OK)
+ return S_FALSE;
+
+ DWORD dwType;
+ if (!wcscmp(bstrName, L"perf_size_dword"))
+ dwType = PERF_SIZE_DWORD;
+ else if (!wcscmp(bstrName, L"perf_size_large"))
+ dwType = PERF_SIZE_LARGE;
+ else if (!wcscmp(bstrName, L"perf_size_variable_len_ansi"))
+ dwType = PERF_SIZE_VARIABLE_LEN;
+ else if (!wcscmp(bstrName, L"perf_size_variable_len_unicode"))
+ dwType = PERF_SIZE_VARIABLE_LEN | PERF_TEXT_UNICODE;
+ else
+ return S_FALSE;
+
+ // get the value
+ bstrName.Empty();
+ hr = _GetAttribute(spCntrChild, L"value", &bstrName);
+ if (hr != S_OK)
+ return S_FALSE;
+
+ CComBSTR bstrOffset;
+ hr = _GetAttribute(spCntrChild, L"offset", &bstrOffset);
+ if (hr != S_OK)
+ return S_FALSE;
+
+ WCHAR *pStop = NULL;
+ DWORD dwOffset = wcstoul(bstrOffset, &pStop, 10);
+
+ if (dwType == PERF_SIZE_DWORD) // add it as a DWORD
+ {
+ DWORD dwVal = wcstoul(bstrName, &pStop, 10);
+ *LPDWORD(LPBYTE(pInstance)+dwOffset) = dwVal;
+ }
+ else if (dwType == PERF_SIZE_LARGE) // add it is a ULONGLONG
+ {
+ ULONGLONG qwVal = _wcstoui64(bstrName, &pStop, 10);
+ *PULONGLONG(LPBYTE(pInstance)+dwOffset) = qwVal;
+ }
+ else if (dwType == PERF_SIZE_VARIABLE_LEN) // add it as an ansi string
+ {
+ AtlW2AHelper(LPSTR(LPBYTE(pInstance)+dwOffset), bstrName, bstrName.Length(), ATL::_AtlGetConversionACP());
+ }
+ else // add it as a unicode string
+ {
+ Checked::memcpy_s(LPBYTE(pInstance)+dwOffset, pInstance->m_nAllocSize-dwOffset, bstrName, bstrName.Length()*sizeof(WCHAR));
+ }
+
+ CComPtr<IXMLDOMNode> spCntrNext;
+ hr = spCntrChild->get_nextSibling(&spCntrNext);
+ spCntrChild.Attach(spCntrNext.Detach());
+ }
+
+ CComPtr<IXMLDOMNode> spInstNext;
+ hr = spInstChild->get_nextSibling(&spInstNext);
+ spInstChild.Attach(spInstNext.Detach());
+ }
+
+ CComPtr<IXMLDOMNode> spNext;
+ hr = spChild->get_nextSibling(&spNext);
+ spChild.Attach(spNext.Detach());
+ }
+
+ return S_OK;
+}
+
+// a little utility function to retrieve a named attribute from a node
+ATL_NOINLINE inline HRESULT CPerfMon::_GetAttribute(IXMLDOMNode *pNode, LPCWSTR szAttrName, BSTR *pbstrVal) throw()
+{
+ ATLENSURE_RETURN(pNode != NULL);
+ ATLASSERT(szAttrName != NULL);
+ ATLENSURE_RETURN(pbstrVal != NULL);
+
+ *pbstrVal = NULL;
+ CComPtr<IXMLDOMNamedNodeMap> spAttrs;
+
+ HRESULT hr = pNode->get_attributes(&spAttrs);
+ if (hr != S_OK)
+ return hr;
+
+ CComPtr<IXMLDOMNode> spAttr;
+
+ hr = spAttrs->getNamedItem((BSTR) szAttrName, &spAttr);
+ if (hr != S_OK)
+ return hr;
+
+ CComVariant varVal;
+ hr = spAttr->get_nodeValue(&varVal);
+ if (hr != S_OK)
+ return hr;
+
+ hr = varVal.ChangeType(VT_BSTR);
+ if (hr != S_OK)
+ return hr;
+
+ *pbstrVal = varVal.bstrVal;
+ varVal.vt = VT_EMPTY;
+
+ return S_OK;
+}
+
+#endif // _ATL_PERF_NOXML
+
+#if defined(_ATL_PERF_REGISTER) & !defined(_ATL_PERF_NOEXPORT)
+
+ATL_NOINLINE inline HRESULT RegisterPerfMon(HINSTANCE hDllInstance /* = _AtlBaseModule.GetModuleInstance() */) throw()
+{
+ CPerfMon **ppPerf = &__pperfA;
+ HRESULT hr = S_OK;
+ while (ppPerf != &__pperfZ)
+ {
+ if (*ppPerf != NULL)
+ {
+ hr = (*ppPerf)->Register(_T( ATLPERF_FUNCID_OPEN ), _T( ATLPERF_FUNCID_COLLECT ), _T( ATLPERF_FUNCID_CLOSE ), hDllInstance);
+ if (FAILED(hr))
+ return hr;
+ hr = (*ppPerf)->RegisterAllStrings(hDllInstance);
+ if (FAILED(hr))
+ return hr;
+ }
+ ppPerf++;
+ }
+ return S_OK;
+}
+
+ATL_NOINLINE inline HRESULT UnregisterPerfMon() throw()
+{
+ CPerfMon **ppPerf = &__pperfA;
+ HRESULT hr = S_OK;
+ while (ppPerf != &__pperfZ)
+ {
+ if (*ppPerf != NULL)
+ {
+ hr = (*ppPerf)->Unregister();
+ if (FAILED(hr))
+ return hr;
+ }
+ ppPerf++;
+ }
+ return S_OK;
+}
+
+extern "C" ATL_NOINLINE inline DWORD __declspec(dllexport) WINAPI OpenPerfMon(LPWSTR lpDeviceNames) throw()
+{
+ CPerfMon **ppPerf = &__pperfA;
+ DWORD dwErr = 0;
+ while (ppPerf != &__pperfZ)
+ {
+ if (*ppPerf != NULL)
+ {
+ dwErr = (*ppPerf)->Open(lpDeviceNames);
+ if (dwErr != 0)
+ return dwErr;
+ }
+ ppPerf++;
+ }
+ return 0;
+}
+
+extern "C" ATL_NOINLINE inline DWORD __declspec(dllexport) WINAPI CollectPerfMon(LPWSTR lpwszValue, LPVOID* lppData,
+ LPDWORD lpcbBytes, LPDWORD lpcObjectTypes) throw()
+{
+ DWORD dwOrigBytes = *lpcbBytes;
+ DWORD dwBytesRemaining = *lpcbBytes;
+ CPerfMon **ppPerf = &__pperfA;
+ DWORD dwErr = 0;
+ while (ppPerf != &__pperfZ)
+ {
+ if (*ppPerf != NULL)
+ {
+ dwErr = (*ppPerf)->Collect(lpwszValue, lppData, lpcbBytes, lpcObjectTypes);
+ if (dwErr != 0)
+ return dwErr;
+ dwBytesRemaining -= *lpcbBytes;
+ *lpcbBytes = dwBytesRemaining;
+ }
+ ppPerf++;
+ }
+ *lpcbBytes = dwOrigBytes - dwBytesRemaining;
+ return 0;
+}
+
+extern "C" ATL_NOINLINE inline DWORD __declspec(dllexport) WINAPI ClosePerfMon() throw()
+{
+ CPerfMon **ppPerf = &__pperfA;
+ while (ppPerf != &__pperfZ)
+ {
+ if (*ppPerf != NULL)
+ {
+ (*ppPerf)->Close();
+ }
+ ppPerf++;
+ }
+ return 0;
+}
+
+#endif // defined(_ATL_PERF_REGISTER) & !defined(_ATL_PERF_NOEXPORT)
+
+} // namespace ATL
+
+#pragma warning(pop)
+
+#endif // __ATLPERF_INL__
diff --git a/include/atl/atlrx.h b/include/atl/atlrx.h
new file mode 100644
index 000000000..1f2b07a1f
--- /dev/null
+++ b/include/atl/atlrx.h
@@ -0,0 +1,2013 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLRX_H__
+#define __ATLRX_H__
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcoll.h>
+#include <mbstring.h>
+
+#ifndef ATL_REGEXP_MIN_STACK
+#define ATL_REGEXP_MIN_STACK 256
+#endif
+
+/*
+ Regular Expression Grammar
+
+ R - top level grammar rule
+ RE - regular expression
+ AltE - Alternative expression
+ E - expression
+ SE - simple expression
+
+ R -> RE
+ '^'RE (matches begining of string)
+
+ RE -> AltE RE
+ AltE
+
+
+ AltE -> E
+ E '|' AltE
+ E -> SE (RepeatOp '?'?)?
+ SE -> Arg
+ Group
+ CharClass
+ '\'Abbrev (see below)
+ '\'EscapedChar (any character including reserved symbols)
+ '\'Digit+ (Arg back reference)
+ '!' (not)
+ '.' (any char)
+ '$' (end of input)
+ Symbol (any non-reserved character)
+ Arg -> '{'RE'}'
+ Group -> '('RE')'
+ CharClass -> '[' '^'? CharSet ']'
+ CharSet -> CharItem+
+ CharItem -> Char('-'Char)?
+ RepeatOp -> '*'
+ '+'
+ '?'
+ Abbrev -> Abbreviation defined in CAtlRECharTraits
+ Abbrev Expansion Meaning
+ a ([a-zA-Z0-9]) alpha numeric
+ b ([ \\t]) white space (blank)
+ c ([a-zA-Z]) alpha
+ d ([0-9]) digit
+ h ([0-9a-fA-F]) hex digit
+ n (\r|(\r?\n)) newline
+ q (\"[^\"]*\")|(\'[^\']*\') quoted string
+ w ([a-zA-Z]+) simple word
+ z ([0-9]+) integer
+*/
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+//Convertion utility classes used to convert char* to RECHAR.
+//Used by rx debugging printing.
+template <typename RECHARTYPE=char>
+class CAToREChar
+{
+public:
+ CAToREChar(const char* psz) throw()
+ : m_psz(psz)
+ {
+ }
+ operator const RECHARTYPE*() const throw() { return m_psz; }
+ const char* m_psz;
+};
+
+template<>
+class CAToREChar<wchar_t>
+{
+public:
+ CAToREChar(const char* psz) throw()
+ : m_a2w(psz)
+ {
+ }
+ operator const wchar_t*() const throw() { return (wchar_t*)m_a2w; }
+
+private:
+ CA2W m_a2w;
+};
+
+class CAtlRECharTraitsA
+{
+public:
+ typedef char RECHARTYPE;
+
+ static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+ {
+#ifndef ATL_NO_CHECK_BIT_FIELD
+ ATLASSERT(UseBitFieldForRange());
+#endif
+ return static_cast<size_t>(static_cast<unsigned char>(*sz));
+ }
+ static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+ {
+ return (RECHARTYPE *) (sz+1);
+ }
+
+ static int Strncmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+ {
+ return strncmp(szLeft, szRight, nCount);
+ }
+
+ static int Strnicmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+ {
+ return _strnicmp(szLeft, szRight, nCount);
+ }
+
+ _ATL_INSECURE_DEPRECATE("CAtlRECharTraitsA::Strlwr must be passed a buffer size.")
+ static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+ {
+ #pragma warning (push)
+ #pragma warning(disable : 4996)
+ return _strlwr(sz);
+ #pragma warning (pop)
+ }
+
+ static RECHARTYPE *Strlwr(RECHARTYPE *sz, int nSize) throw()
+ {
+ Checked::strlwr_s(sz, nSize);
+ return sz;
+ }
+
+ static long Strtol(const RECHARTYPE *sz, RECHARTYPE **szEnd, int nBase) throw()
+ {
+ return strtol(sz, szEnd, nBase);
+ }
+
+ static int Isdigit(RECHARTYPE ch) throw()
+ {
+ return isdigit(static_cast<unsigned char>(ch));
+ }
+
+ static const RECHARTYPE** GetAbbrevs()
+ {
+ static const RECHARTYPE *s_szAbbrevs[] =
+ {
+ "a([a-zA-Z0-9])", // alpha numeric
+ "b([ \\t])", // white space (blank)
+ "c([a-zA-Z])", // alpha
+ "d([0-9])", // digit
+ "h([0-9a-fA-F])", // hex digit
+ "n(\r|(\r?\n))", // newline
+ "q(\"[^\"]*\")|(\'[^\']*\')", // quoted string
+ "w([a-zA-Z]+)", // simple word
+ "z([0-9]+)", // integer
+ NULL
+ };
+
+ return s_szAbbrevs;
+ }
+
+ static BOOL UseBitFieldForRange() throw()
+ {
+ return TRUE;
+ }
+
+ static int ByteLen(const RECHARTYPE *sz) throw()
+ {
+ return int(strlen(sz));
+ }
+};
+
+class CAtlRECharTraitsW
+{
+public:
+ typedef WCHAR RECHARTYPE;
+
+ static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+ {
+#ifndef ATL_NO_CHECK_BIT_FIELD
+ ATLASSERT(UseBitFieldForRange());
+#endif
+ return static_cast<size_t>(*sz);
+ }
+ static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+ {
+ return (RECHARTYPE *) (sz+1);
+ }
+
+ static int Strncmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+ {
+ return wcsncmp(szLeft, szRight, nCount);
+ }
+
+ static int Strnicmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+ {
+ return _wcsnicmp(szLeft, szRight, nCount);
+ }
+
+ _ATL_INSECURE_DEPRECATE("CAtlRECharTraitsW::Strlwr must be passed a buffer size.")
+ static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+ {
+ #pragma warning (push)
+ #pragma warning(disable : 4996)
+ return _wcslwr(sz);
+ #pragma warning (pop)
+ }
+
+ static RECHARTYPE *Strlwr(RECHARTYPE *sz, int nSize) throw()
+ {
+ Checked::wcslwr_s(sz, nSize);
+ return sz;
+ }
+
+ static long Strtol(const RECHARTYPE *sz, RECHARTYPE **szEnd, int nBase) throw()
+ {
+ return wcstol(sz, szEnd, nBase);
+ }
+
+ static int Isdigit(RECHARTYPE ch) throw()
+ {
+ return iswdigit(ch);
+ }
+
+ static const RECHARTYPE** GetAbbrevs()
+ {
+ static const RECHARTYPE *s_szAbbrevs[] =
+ {
+ L"a([a-zA-Z0-9])", // alpha numeric
+ L"b([ \\t])", // white space (blank)
+ L"c([a-zA-Z])", // alpha
+ L"d([0-9])", // digit
+ L"h([0-9a-fA-F])", // hex digit
+ L"n(\r|(\r?\n))", // newline
+ L"q(\"[^\"]*\")|(\'[^\']*\')", // quoted string
+ L"w([a-zA-Z]+)", // simple word
+ L"z([0-9]+)", // integer
+ NULL
+ };
+
+ return s_szAbbrevs;
+ }
+
+ static BOOL UseBitFieldForRange() throw()
+ {
+ return FALSE;
+ }
+
+ static int ByteLen(const RECHARTYPE *sz) throw()
+ {
+ return int(wcslen(sz)*sizeof(WCHAR));
+ }
+};
+
+class CAtlRECharTraitsMB
+{
+public:
+ typedef unsigned char RECHARTYPE;
+
+ static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+ {
+#ifndef ATL_NO_CHECK_BIT_FIELD
+ ATLASSERT(UseBitFieldForRange());
+#endif
+
+ return static_cast<size_t>(*sz);
+ }
+
+ static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+ {
+ return _mbsinc(sz);
+ }
+
+ static int Strncmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+ {
+ return _mbsncmp(szLeft, szRight, nCount);
+ }
+
+ static int Strnicmp(const RECHARTYPE *szLeft, const RECHARTYPE *szRight, size_t nCount) throw()
+ {
+ return _mbsnicmp(szLeft, szRight, nCount);
+ }
+
+ _ATL_INSECURE_DEPRECATE("CAtlRECharTraitsMB::Strlwr must be passed a buffer size.")
+ static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+ {
+ #pragma warning (push)
+ #pragma warning(disable : 4996)
+ return _mbslwr(sz);
+ #pragma warning (pop)
+ }
+
+ static RECHARTYPE *Strlwr(RECHARTYPE *sz, int nSize) throw()
+ {
+ Checked::mbslwr_s(sz, nSize);
+ return sz;
+ }
+
+ static long Strtol(const RECHARTYPE *sz, RECHARTYPE **szEnd, int nBase) throw()
+ {
+ return strtol((const char *) sz, (char **) szEnd, nBase);
+ }
+
+ static int Isdigit(RECHARTYPE ch) throw()
+ {
+ return _ismbcdigit((unsigned int) ch);
+ }
+
+ static const RECHARTYPE** GetAbbrevs()
+ {
+ return reinterpret_cast<const RECHARTYPE **>(CAtlRECharTraitsA::GetAbbrevs());
+ }
+
+ static BOOL UseBitFieldForRange() throw()
+ {
+ return FALSE;
+ }
+
+ static int ByteLen(const RECHARTYPE *sz) throw()
+ {
+ return (int)strlen((const char *) sz);
+ }
+};
+
+#ifndef _UNICODE
+typedef CAtlRECharTraitsA CAtlRECharTraits;
+#else // _UNICODE
+typedef CAtlRECharTraitsW CAtlRECharTraits;
+#endif // !_UNICODE
+// Note: If you want to use CAtlRECharTraitsMB you must pass it in
+// as a template argument
+
+template <class CharTraits=CAtlRECharTraits>
+class CAtlRegExp; // forward declaration
+
+template <class CharTraits=CAtlRECharTraits>
+class CAtlREMatchContext
+{
+public:
+ friend CAtlRegExp<CharTraits>;
+ typedef typename CharTraits::RECHARTYPE RECHAR;
+
+ struct MatchGroup
+ {
+ const RECHAR *szStart;
+ const RECHAR *szEnd;
+ };
+
+ UINT m_uNumGroups;
+
+ MatchGroup m_Match;
+
+ void GetMatch(UINT nIndex, const RECHAR **szStart, const RECHAR **szEnd)
+ {
+ ATLENSURE(szStart != NULL);
+ ATLENSURE(szEnd != NULL);
+ ATLENSURE(nIndex >=0 && nIndex < m_uNumGroups);
+ *szStart = m_Matches[nIndex].szStart;
+ *szEnd = m_Matches[nIndex].szEnd;
+ }
+
+ void GetMatch(UINT nIndex, MatchGroup *pGroup)
+ {
+
+ ATLENSURE(pGroup != NULL);
+ ATLENSURE(nIndex >=0&&(static_cast<UINT>(nIndex))< m_uNumGroups);
+ pGroup->szStart = m_Matches[nIndex].szStart;
+ pGroup->szEnd = m_Matches[nIndex].szEnd;
+ }
+
+protected:
+ CAutoVectorPtr<void *> m_Mem;
+ CAutoVectorPtr<MatchGroup> m_Matches;
+ CAtlArray<void *> m_stack;
+ size_t m_nTos;
+
+public:
+ CAtlREMatchContext(size_t nInitStackSize=ATL_REGEXP_MIN_STACK)
+ {
+ m_uNumGroups = 0;
+ m_nTos = 0;
+ m_stack.SetCount(nInitStackSize);
+ m_Match.szStart = NULL;
+ m_Match.szEnd = NULL;
+ }
+
+protected:
+ BOOL Initialize(UINT uRequiredMem, UINT uNumGroups) throw()
+ {
+ m_nTos = 0;
+
+ m_uNumGroups = 0;
+ m_Matches.Free();
+
+ if (!m_Matches.Allocate(uNumGroups))
+ return FALSE;
+
+ m_uNumGroups = uNumGroups;
+
+ m_Mem.Free();
+
+ if (!m_Mem.Allocate(uRequiredMem))
+ return FALSE;
+
+ memset(m_Mem.m_p, 0x00, uRequiredMem*sizeof(void *));
+
+ memset(m_Matches, 0x00, m_uNumGroups * sizeof(MatchGroup));
+ return TRUE;
+ }
+
+ BOOL Push(void *p)
+ {
+ m_nTos++;
+ if (m_stack.GetCount() <= (UINT) m_nTos)
+ {
+ if (!m_stack.SetCount((m_nTos+1)*2))
+ {
+ m_nTos--;
+ return FALSE;
+ }
+ }
+ m_stack[m_nTos] = p;
+ return TRUE;
+ }
+
+ BOOL Push(size_t n)
+ {
+ return Push((void *) n);
+ }
+
+ void *Pop() throw()
+ {
+ if (m_nTos==0)
+ {
+ // stack underflow
+ // this should never happen at match time.
+ // (the parsing succeeded when it shouldn't have)
+ ATLASSERT(FALSE);
+ return NULL;
+ }
+ void *p = m_stack[m_nTos];
+ m_nTos--;
+ return p;
+ }
+};
+
+enum REParseError {
+ REPARSE_ERROR_OK = 0, // No error occurred
+ REPARSE_ERROR_OUTOFMEMORY, // Out of memory
+ REPARSE_ERROR_BRACE_EXPECTED, // A closing brace was expected
+ REPARSE_ERROR_PAREN_EXPECTED, // A closing parenthesis was expected
+ REPARSE_ERROR_BRACKET_EXPECTED, // A closing bracket was expected
+ REPARSE_ERROR_UNEXPECTED, // An unspecified fatal error occurred
+ REPARSE_ERROR_EMPTY_RANGE, // A range expression was empty
+ REPARSE_ERROR_INVALID_GROUP, // A backreference was made to a group
+ // that did not exist
+ REPARSE_ERROR_INVALID_RANGE, // An invalid range was specified
+ REPARSE_ERROR_EMPTY_REPEATOP, // A possibly empty * or + was detected
+ REPARSE_ERROR_INVALID_INPUT, // The input string was invalid
+};
+
+template <class CharTraits /* =CAtlRECharTraits */>
+class CAtlRegExp
+{
+public:
+ CAtlRegExp() throw()
+ {
+ m_uNumGroups = 0;
+ m_uRequiredMem = 0;
+ m_bCaseSensitive = TRUE;
+ m_LastError = REPARSE_ERROR_OK;
+ }
+
+ typedef typename CharTraits::RECHARTYPE RECHAR;
+
+ // CAtlRegExp::Parse
+ // Parses the regular expression
+ // returns REPARSE_ERROR_OK if successful, an REParseError otherwise
+ REParseError Parse(const RECHAR *szRE, BOOL bCaseSensitive=TRUE)
+ {
+ ATLASSERT(szRE);
+ if (!szRE)
+ return REPARSE_ERROR_INVALID_INPUT;
+
+ Reset();
+
+ m_bCaseSensitive = bCaseSensitive;
+
+ const RECHAR *szInput = szRE;
+
+ if (!bCaseSensitive)
+ {
+ // copy the string
+ int nSize = CharTraits::ByteLen(szRE)+sizeof(RECHAR);
+ szInput = (const RECHAR *) malloc(nSize);
+ if (!szInput)
+ return REPARSE_ERROR_OUTOFMEMORY;
+
+ Checked::memcpy_s((char *) szInput, nSize, szRE, nSize);
+
+ CharTraits::Strlwr(const_cast<RECHAR *>(szInput), nSize/sizeof(RECHAR));
+ }
+ const RECHAR *sz = szInput;
+
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return REPARSE_ERROR_OUTOFMEMORY;
+
+ if (*sz == '^')
+ {
+ if (AddInstruction(RE_FAIL) < 0)
+ return REPARSE_ERROR_OUTOFMEMORY;
+ sz++;
+ }
+ else
+ {
+ if (AddInstruction(RE_ADVANCE) < 0)
+ return REPARSE_ERROR_OUTOFMEMORY;
+ }
+
+ bool bEmpty = true;
+ ParseRE(&sz, bEmpty);
+ if (!GetLastParseError())
+ {
+ GetInstruction(nCall).call.nTarget = 2;
+
+ if (AddInstruction(RE_MATCH) < 0)
+ return REPARSE_ERROR_OUTOFMEMORY;
+ }
+
+ if (szInput != szRE)
+ free((void *) szInput);
+
+ return GetLastParseError();
+ }
+
+ BOOL Match(const RECHAR *szIn, CAtlREMatchContext<CharTraits> *pContext, const RECHAR **ppszEnd=NULL)
+ {
+ ATLASSERT(szIn);
+ ATLASSERT(pContext);
+
+ if (!szIn || !pContext)
+ return FALSE;
+
+ if (ppszEnd)
+ *ppszEnd = NULL;
+
+ const RECHAR *szInput = szIn;
+
+ if (!m_bCaseSensitive)
+ {
+ int nSize = CharTraits::ByteLen(szIn)+sizeof(RECHAR);
+ szInput = (const RECHAR *) malloc(nSize);
+ if (!szInput)
+ return FALSE;
+
+ Checked::memcpy_s((char *) szInput, nSize, szIn, nSize);
+ CharTraits::Strlwr(const_cast<RECHAR *>(szInput), nSize/sizeof(RECHAR));
+ }
+
+ if (!pContext->Initialize(m_uRequiredMem, m_uNumGroups))
+ {
+ if (szInput != szIn)
+ free((void *) szInput);
+ return FALSE;
+ }
+
+ size_t ip = 0;
+
+ const RECHAR *sz = szInput;
+ const RECHAR *szCurrInput = szInput;
+
+#pragma warning(push)
+#pragma warning(disable:4127) // conditional expression is constant
+
+ while (1)
+ {
+#ifdef ATLRX_DEBUG
+ OnDebugEvent(ip, szInput, sz, pContext);
+#endif
+ if (ip == 0)
+ pContext->m_Match.szStart = sz;
+
+ switch (GetInstruction(ip).type)
+ {
+ case RE_NOP:
+ ip++;
+ break;
+
+ case RE_SYMBOL:
+ if (GetInstruction(ip).symbol.nSymbol == static_cast<size_t>(static_cast<_TUCHAR>(*sz)))
+ {
+ sz = CharTraits::Next(sz);
+ ip++;
+ }
+ else
+ {
+ ip = (size_t) pContext->Pop();
+ }
+ break;
+
+ case RE_ANY:
+ if (*sz)
+ {
+ sz = CharTraits::Next(sz);
+ ip++;
+ }
+ else
+ {
+ ip = (size_t) pContext->Pop();
+ }
+ break;
+
+ case RE_GROUP_START:
+ pContext->m_Matches[GetInstruction(ip).group.nGroup].szStart = sz;
+ ip++;
+ break;
+
+ case RE_GROUP_END:
+ pContext->m_Matches[GetInstruction(ip).group.nGroup].szEnd = sz;
+ ip++;
+ break;
+
+ case RE_PUSH_CHARPOS:
+ pContext->Push((void *) sz);
+ ip++;
+ break;
+
+ case RE_POP_CHARPOS:
+ sz = (RECHAR *) pContext->Pop();
+ ip++;
+ break;
+
+ case RE_CALL:
+ pContext->Push(ip+1);
+ ip = GetInstruction(ip).call.nTarget;
+ break;
+
+ case RE_JMP:
+ ip = GetInstruction(ip).jmp.nTarget;
+ break;
+
+ case RE_RETURN:
+ ip = (size_t) pContext->Pop();
+ break;
+
+ case RE_PUSH_MEMORY:
+ pContext->Push((void *) (pContext->m_Mem[GetInstruction(ip).memory.nIndex]));
+ ip++;
+ break;
+
+ case RE_POP_MEMORY:
+ pContext->m_Mem[GetInstruction(ip).memory.nIndex] = pContext->Pop();
+ ip++;
+ break;
+
+ case RE_STORE_CHARPOS:
+ pContext->m_Mem[GetInstruction(ip).memory.nIndex] = (void *) sz;
+ ip++;
+ break;
+
+ case RE_GET_CHARPOS:
+ sz = (RECHAR *) pContext->m_Mem[GetInstruction(ip).memory.nIndex];
+ ip++;
+ break;
+
+ case RE_STORE_STACKPOS:
+ pContext->m_Mem[GetInstruction(ip).memory.nIndex] = (void *) pContext->m_nTos;
+ ip++;
+ break;
+
+ case RE_GET_STACKPOS:
+ pContext->m_nTos = (size_t) pContext->m_Mem[GetInstruction(ip).memory.nIndex];
+ ip++;
+ break;
+
+ case RE_RET_NOMATCH:
+ if (sz == (RECHAR *) pContext->m_Mem[GetInstruction(ip).memory.nIndex])
+ {
+ // do a return
+ ip = (size_t) pContext->Pop();
+ }
+ else
+ ip++;
+ break;
+
+ case RE_ADVANCE:
+ sz = CharTraits::Next(szCurrInput);
+ szCurrInput = sz;
+ if (*sz == '\0')
+ goto Error;
+ ip = 0;
+ pContext->m_nTos = 0;
+ break;
+
+ case RE_FAIL:
+ goto Error;
+
+ case RE_RANGE:
+ {
+ if (*sz == '\0')
+ {
+ ip = (size_t) pContext->Pop();
+ break;
+ }
+
+ RECHAR *pBits = reinterpret_cast<RECHAR *>((&m_Instructions[ip]+1));
+ size_t u = CharTraits::GetBitFieldForRangeArrayIndex(sz);
+ if (pBits[u >> 3] & 1 << (u & 0x7))
+ {
+ ip += InstructionsPerRangeBitField();
+ ip++;
+ sz = CharTraits::Next(sz);
+ }
+ else
+ {
+ ip = (size_t) pContext->Pop();
+ }
+ }
+ break;
+
+ case RE_NOTRANGE:
+ {
+ if (*sz == '\0')
+ {
+ ip = (size_t) pContext->Pop();
+ break;
+ }
+
+ RECHAR *pBits = reinterpret_cast<RECHAR *>((&m_Instructions[ip]+1));
+ size_t u = static_cast<size_t>(static_cast<_TUCHAR>(* ((RECHAR *) sz)));
+ if (pBits[u >> 3] & 1 << (u & 0x7))
+ {
+ ip = (size_t) pContext->Pop();
+ }
+ else
+ {
+ ip += InstructionsPerRangeBitField();
+ ip++;
+ sz = CharTraits::Next(sz);
+ }
+ }
+ break;
+
+ case RE_RANGE_EX:
+ {
+ if (*sz == '\0')
+ {
+ ip = (size_t) pContext->Pop();
+ break;
+ }
+
+ BOOL bMatch = FALSE;
+ size_t inEnd = GetInstruction(ip).range.nTarget;
+ ip++;
+
+ while (ip < inEnd)
+ {
+ if (static_cast<size_t>(static_cast<_TUCHAR>(*sz)) >= GetInstruction(ip).memory.nIndex &&
+ static_cast<size_t>(static_cast<_TUCHAR>(*sz)) <= GetInstruction(ip+1).memory.nIndex)
+ {
+ // if we match, we jump to the end
+ sz = CharTraits::Next(sz);
+ ip = inEnd;
+ bMatch = TRUE;
+ }
+ else
+ {
+ ip += 2;
+ }
+ }
+ if (!bMatch)
+ {
+ ip = (size_t) pContext->Pop();
+ }
+ }
+ break;
+
+ case RE_NOTRANGE_EX:
+ {
+ if (*sz == '\0')
+ {
+ ip = (size_t) pContext->Pop();
+ break;
+ }
+
+ BOOL bMatch = TRUE;
+ size_t inEnd = GetInstruction(ip).range.nTarget;
+ ip++;
+
+ while (ip < inEnd)
+ {
+ if (static_cast<size_t>(static_cast<_TUCHAR>(*sz)) >= GetInstruction(ip).memory.nIndex &&
+ static_cast<size_t>(static_cast<_TUCHAR>(*sz)) <= GetInstruction(ip+1).memory.nIndex)
+ {
+ ip = (size_t) pContext->Pop();
+ bMatch = FALSE;
+ break;
+ }
+ else
+ {
+ // if we match, we jump to the end
+ ip += 2;
+ }
+ }
+ if (bMatch)
+ sz = CharTraits::Next(sz);
+ }
+ break;
+
+ case RE_PREVIOUS:
+ {
+ BOOL bMatch = FALSE;
+ if (m_bCaseSensitive)
+ {
+ bMatch = !CharTraits::Strncmp(sz, pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart,
+ pContext->m_Matches[GetInstruction(ip).prev.nGroup].szEnd-pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart);
+ }
+ else
+ {
+ bMatch = !CharTraits::Strnicmp(sz, pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart,
+ pContext->m_Matches[GetInstruction(ip).prev.nGroup].szEnd-pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart);
+ }
+ if (bMatch)
+ {
+ sz += pContext->m_Matches[GetInstruction(ip).prev.nGroup].szEnd-pContext->m_Matches[GetInstruction(ip).prev.nGroup].szStart;
+ ip++;
+ break;
+ }
+ ip = (size_t) pContext->Pop();
+ }
+ break;
+
+ case RE_MATCH:
+ pContext->m_Match.szEnd = sz;
+ if (!m_bCaseSensitive)
+ FixupMatchContext(pContext, szIn, szInput);
+ if (ppszEnd)
+ *ppszEnd = szIn + (sz - szInput);
+ if (szInput != szIn)
+ free((void *) szInput);
+ return TRUE;
+ break;
+
+ case RE_PUSH_GROUP:
+ pContext->Push((void *) pContext->m_Matches[GetInstruction(ip).group.nGroup].szStart);
+ pContext->Push((void *) pContext->m_Matches[GetInstruction(ip).group.nGroup].szEnd);
+ ip++;
+ break;
+
+ case RE_POP_GROUP:
+ pContext->m_Matches[GetInstruction(ip).group.nGroup].szEnd = (const RECHAR *) pContext->Pop();
+ pContext->m_Matches[GetInstruction(ip).group.nGroup].szStart = (const RECHAR *) pContext->Pop();
+ ip++;
+ break;
+
+ default:
+ ATLASSERT(FALSE);
+ break;
+ }
+ }
+
+#pragma warning(pop) // 4127
+
+ ATLASSERT(FALSE);
+Error:
+ pContext->m_Match.szEnd = sz;
+ if (!m_bCaseSensitive)
+ FixupMatchContext(pContext, szIn, szInput);
+ if (ppszEnd)
+ *ppszEnd = szIn + (sz - szInput);
+ if (szInput != szIn)
+ free((void *) szInput);
+ return FALSE;
+ }
+
+protected:
+ REParseError m_LastError;
+
+ REParseError GetLastParseError() throw()
+ {
+ return m_LastError;
+ }
+
+ void SetLastParseError(REParseError Error) throw()
+ {
+ m_LastError = Error;
+ }
+ // CAtlRegExp::Reset
+ // Removes all instructions to allow reparsing into the same instance
+ void Reset() throw()
+ {
+ m_Instructions.RemoveAll();
+ m_uRequiredMem = 0;
+ m_bCaseSensitive = TRUE;
+ m_uNumGroups = 0;
+ SetLastParseError(REPARSE_ERROR_OK);
+ }
+
+
+ enum REInstructionType {
+ RE_NOP,
+ RE_GROUP_START,
+ RE_GROUP_END,
+ RE_SYMBOL,
+ RE_ANY,
+ RE_RANGE,
+ RE_NOTRANGE,
+ RE_RANGE_EX,
+ RE_NOTRANGE_EX,
+ RE_PLUS,
+ RE_NG_PLUS,
+ RE_QUESTION,
+ RE_NG_QUESTION,
+ RE_JMP,
+ RE_PUSH_CHARPOS,
+ RE_POP_CHARPOS,
+ RE_CALL,
+ RE_RETURN,
+ RE_STAR_BEGIN,
+ RE_NG_STAR_BEGIN,
+ RE_PUSH_MEMORY,
+ RE_POP_MEMORY,
+ RE_STORE_CHARPOS,
+ RE_STORE_STACKPOS,
+ RE_GET_CHARPOS,
+ RE_GET_STACKPOS,
+ RE_RET_NOMATCH,
+ RE_PREVIOUS,
+ RE_FAIL,
+ RE_ADVANCE,
+ RE_MATCH,
+ RE_PUSH_GROUP,
+ RE_POP_GROUP,
+ };
+
+ struct INSTRUCTION_SYMBOL
+ {
+ size_t nSymbol;
+ };
+
+ struct INSTRUCTION_JMP
+ {
+ size_t nTarget;
+ };
+
+ struct INSTRUCTION_GROUP
+ {
+ size_t nGroup;
+ };
+
+ struct INSTRUCTION_CALL
+ {
+ size_t nTarget;
+ };
+
+ struct INSTRUCTION_MEMORY
+ {
+ size_t nIndex;
+ };
+
+ struct INSTRUCTION_PREVIOUS
+ {
+ size_t nGroup;
+ };
+
+ struct INSTRUCTION_RANGE_EX
+ {
+ size_t nTarget;
+ };
+
+ struct INSTRUCTION
+ {
+ REInstructionType type;
+ union
+ {
+ INSTRUCTION_SYMBOL symbol;
+ INSTRUCTION_JMP jmp;
+ INSTRUCTION_GROUP group;
+ INSTRUCTION_CALL call;
+ INSTRUCTION_MEMORY memory;
+ INSTRUCTION_PREVIOUS prev;
+ INSTRUCTION_RANGE_EX range;
+ };
+ };
+
+ inline int InstructionsPerRangeBitField() throw()
+ {
+ return (256/8) / sizeof(INSTRUCTION) + (((256/8) % sizeof(INSTRUCTION)) ? 1 : 0);
+ }
+
+ CAtlArray<INSTRUCTION> m_Instructions;
+
+ UINT m_uNumGroups;
+ UINT m_uRequiredMem;
+ BOOL m_bCaseSensitive;
+
+
+ // class used internally to restore
+ // parsing state when unwinding
+ class CParseState
+ {
+ public:
+ int m_nNumInstructions;
+ UINT m_uNumGroups;
+ UINT m_uRequiredMem;
+
+ CParseState(CAtlRegExp *pRegExp) throw()
+ {
+ m_nNumInstructions = (int) pRegExp->m_Instructions.GetCount();
+ m_uNumGroups = pRegExp->m_uNumGroups;
+ m_uRequiredMem = pRegExp->m_uRequiredMem;
+ }
+
+ void Restore(CAtlRegExp *pRegExp)
+ {
+ pRegExp->m_Instructions.SetCount(m_nNumInstructions);
+ pRegExp->m_uNumGroups = m_uNumGroups;
+ pRegExp->m_uRequiredMem = m_uRequiredMem;
+ }
+ };
+
+ int AddInstruction(REInstructionType type)
+ {
+ if (!m_Instructions.SetCount(m_Instructions.GetCount()+1))
+ {
+ SetLastParseError(REPARSE_ERROR_OUTOFMEMORY);
+ return -1;
+ }
+
+ m_Instructions[m_Instructions.GetCount()-1].type = type;
+ return (int) m_Instructions.GetCount()-1;
+ }
+
+ BOOL PeekToken(const RECHAR **ppszRE, int ch) throw()
+ {
+ if (**ppszRE != ch)
+ return FALSE;
+ return TRUE;
+ }
+
+ BOOL MatchToken(const RECHAR **ppszRE, int ch) throw()
+ {
+ if (!PeekToken(ppszRE, ch))
+ return FALSE;
+ *ppszRE = CharTraits::Next(*ppszRE);
+ return TRUE;
+ }
+
+ INSTRUCTION &GetInstruction(size_t nIndex) throw()
+ {
+ return m_Instructions[nIndex];
+ }
+
+ // ParseArg: parse grammar rule Arg
+ int ParseArg(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ int nPushGroup = AddInstruction(RE_PUSH_GROUP);
+ if (nPushGroup < 0)
+ return -1;
+
+ GetInstruction(nPushGroup).group.nGroup = m_uNumGroups;
+
+ int p = AddInstruction(RE_GROUP_START);
+ if (p < 0)
+ return -1;
+ GetInstruction(p).group.nGroup = m_uNumGroups++;
+
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return -1;
+
+ int nPopGroup = AddInstruction(RE_POP_GROUP);
+ if (nPopGroup < 0)
+ return -1;
+ GetInstruction(nPopGroup).group.nGroup = GetInstruction(nPushGroup).group.nGroup;
+
+ if (AddInstruction(RE_RETURN) < 0)
+ return -1;
+
+ int nAlt = ParseRE(ppszRE, bEmpty);
+ if (nAlt < 0)
+ {
+ if (GetLastParseError())
+ return -1;
+
+ if (!PeekToken(ppszRE, '}'))
+ {
+ SetLastParseError(REPARSE_ERROR_BRACE_EXPECTED);
+ return -1;
+ }
+
+ // in the case of an empty group, we add a nop
+ nAlt = AddInstruction(RE_NOP);
+ if (nAlt < 0)
+ return -1;
+ }
+
+ GetInstruction(nCall).call.nTarget = nAlt;
+
+ if (!MatchToken(ppszRE, '}'))
+ {
+ SetLastParseError(REPARSE_ERROR_BRACE_EXPECTED);
+ return -1;
+ }
+
+ int nEnd = AddInstruction(RE_GROUP_END);
+ if (nEnd < 0)
+ return -1;
+ GetInstruction(nEnd).group.nGroup = GetInstruction(p).group.nGroup;
+ return nPushGroup;
+ }
+
+ // ParseGroup: parse grammar rule Group
+ int ParseGroup(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return -1;
+
+ if (AddInstruction(RE_RETURN) < 0)
+ return -1;
+
+ int nAlt = ParseRE(ppszRE, bEmpty);
+ if (nAlt < 0)
+ {
+ if (GetLastParseError())
+ return -1;
+
+ if (!PeekToken(ppszRE, ')'))
+ {
+ SetLastParseError(REPARSE_ERROR_PAREN_EXPECTED);
+ return -1;
+ }
+
+ // in the case of an empty group, we add a nop
+ nAlt = AddInstruction(RE_NOP);
+ if (nAlt < 0)
+ return -1;
+ }
+
+ GetInstruction(nCall).call.nTarget = nAlt;
+
+ if (!MatchToken(ppszRE, ')'))
+ {
+ SetLastParseError(REPARSE_ERROR_PAREN_EXPECTED);
+ return -1;
+ }
+
+ return nCall;
+ }
+
+ RECHAR GetEscapedChar(RECHAR ch) throw()
+ {
+ if (ch == 't')
+ return '\t';
+ return ch;
+ }
+
+ // ParseCharItem: parse grammar rule CharItem
+ int ParseCharItem(const RECHAR **ppszRE, RECHAR *pchStartChar, RECHAR *pchEndChar) throw()
+ {
+ if (**ppszRE == '\\')
+ {
+ *ppszRE = CharTraits::Next(*ppszRE);
+ *pchStartChar = GetEscapedChar(**ppszRE);
+ }
+ else
+ *pchStartChar = **ppszRE;
+ *ppszRE = CharTraits::Next(*ppszRE);
+
+ if (!MatchToken(ppszRE, '-'))
+ {
+ *pchEndChar = *pchStartChar;
+ return 0;
+ }
+
+ // check for unterminated range
+ if (!**ppszRE || PeekToken(ppszRE, ']'))
+ {
+ SetLastParseError(REPARSE_ERROR_BRACKET_EXPECTED);
+ return -1;
+ }
+
+ *pchEndChar = **ppszRE;
+ *ppszRE = CharTraits::Next(*ppszRE);
+
+ if (*pchEndChar < *pchStartChar)
+ {
+ SetLastParseError(REPARSE_ERROR_INVALID_RANGE);
+ return -1;
+ }
+ return 0;
+ }
+
+ int AddInstructions(int nNumInstructions)
+ {
+ size_t nCurr = m_Instructions.GetCount();
+ if (!m_Instructions.SetCount(nCurr+nNumInstructions))
+ {
+ SetLastParseError(REPARSE_ERROR_OUTOFMEMORY);
+ return -1;
+ }
+ return (int) nCurr;
+ }
+
+ // ParseCharSet: parse grammar rule CharSet
+ int ParseCharSet(const RECHAR **ppszRE, BOOL bNot)
+ {
+ int p = -1;
+
+ unsigned char *pBits = NULL;
+
+ if (CharTraits::UseBitFieldForRange())
+ {
+ // we use a bit field to represent the characters
+ // a 1 bit means match against the character
+ // the last 5 bits are used as an index into
+ // the byte array, and the first 3 bits
+ // are used to index into the selected byte
+
+ p = AddInstruction(bNot ? RE_NOTRANGE : RE_RANGE);
+ if (p < 0)
+ return -1;
+
+ // add the required space to hold the character
+ // set. We use one bit per character for ansi
+ if (AddInstructions(InstructionsPerRangeBitField()) < 0)
+ return -1;
+
+ pBits = (unsigned char *) (&m_Instructions[p+1]);
+ memset(pBits, 0x00, 256/8);
+ }
+ else
+ {
+ p = AddInstruction(bNot ? RE_NOTRANGE_EX : RE_RANGE_EX);
+ if (p < 0)
+ return -1;
+ }
+
+ RECHAR chStart;
+ RECHAR chEnd;
+
+ while (**ppszRE && **ppszRE != ']')
+ {
+ if (ParseCharItem(ppszRE, &chStart, &chEnd))
+ return -1;
+
+ if (CharTraits::UseBitFieldForRange())
+ {
+ for (int i=chStart; i<=chEnd; i++)
+ pBits[i >> 3] |= 1 << (i & 0x7);
+ }
+ else
+ {
+ int nStart = AddInstruction(RE_NOP);
+ if (nStart < 0)
+ return -1;
+
+ int nEnd = AddInstruction(RE_NOP);
+ if (nEnd < 0)
+ return -1;
+
+ GetInstruction(nStart).memory.nIndex = (int) chStart;
+ GetInstruction(nEnd).memory.nIndex = (int) chEnd;
+ }
+ }
+
+ if (!CharTraits::UseBitFieldForRange())
+ GetInstruction(p).range.nTarget = m_Instructions.GetCount();
+
+ return p;
+ }
+
+ // ParseCharClass: parse grammar rule CharClass
+ int ParseCharClass(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ bEmpty = false;
+ if (MatchToken(ppszRE, ']'))
+ {
+ SetLastParseError(REPARSE_ERROR_EMPTY_RANGE);
+ return -1;
+ }
+
+ BOOL bNot = FALSE;
+ if (MatchToken(ppszRE, '^'))
+ bNot = TRUE;
+
+ if (MatchToken(ppszRE, ']'))
+ {
+ SetLastParseError(REPARSE_ERROR_EMPTY_RANGE);
+ return -1;
+ }
+
+ int p = ParseCharSet(ppszRE, bNot);
+ if (p < 0)
+ return p;
+ if (!MatchToken(ppszRE, ']'))
+ {
+ SetLastParseError(REPARSE_ERROR_BRACKET_EXPECTED);
+ return -1;
+ }
+
+ return p;
+ }
+
+ int AddMemInstruction(REInstructionType type)
+ {
+ int p = AddInstruction(type);
+ if (p < 0)
+ return p;
+ GetInstruction(p).memory.nIndex = m_uRequiredMem++;
+ return p;
+ }
+
+ // helper for parsing !SE
+ int ParseNot(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ int nStoreCP = AddMemInstruction(RE_STORE_CHARPOS);
+ int nStoreSP = AddMemInstruction(RE_STORE_STACKPOS);
+
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return -1;
+
+ int nGetCP = AddInstruction(RE_GET_CHARPOS);
+ if (nGetCP < 0)
+ return -1;
+ GetInstruction(nGetCP).memory.nIndex = GetInstruction(nStoreCP).memory.nIndex;
+
+ int nGetSP = AddInstruction(RE_GET_STACKPOS);
+ if (nGetSP < 0)
+ return -1;
+ GetInstruction(nGetSP).memory.nIndex = GetInstruction(nStoreSP).memory.nIndex;
+
+ int nJmp = AddInstruction(RE_JMP);
+ if (nJmp < 0)
+ return -1;
+
+ int nSE = ParseSE(ppszRE, bEmpty);
+ if (nSE < 0)
+ return nSE;
+
+ // patch the call
+ GetInstruction(nCall).call.nTarget = nSE;
+
+ int nGetCP1 = AddInstruction(RE_GET_CHARPOS);
+ if (nGetCP1 < 0)
+ return -1;
+ GetInstruction(nGetCP1).memory.nIndex = GetInstruction(nStoreCP).memory.nIndex;
+
+ int nGetSP1 = AddInstruction(RE_GET_STACKPOS);
+ if (nGetSP1 < 0)
+ return -1;
+ GetInstruction(nGetSP1).memory.nIndex = GetInstruction(nStoreSP).memory.nIndex;
+
+ int nRet = AddInstruction(RE_RETURN);
+ if (nRet < 0)
+ return -1;
+
+ GetInstruction(nJmp).jmp.nTarget = nRet+1;
+
+ return nStoreCP;
+ }
+
+ // ParseAbbrev: parse grammar rule Abbrev
+ int ParseAbbrev(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ const RECHAR **szAbbrevs = CharTraits::GetAbbrevs();
+
+ while (*szAbbrevs)
+ {
+ if (**ppszRE == **szAbbrevs)
+ {
+ const RECHAR *szAbbrev = (*szAbbrevs)+1;
+ int p = ParseE(&szAbbrev, bEmpty);
+ if (p < 0)
+ {
+ SetLastParseError(REPARSE_ERROR_UNEXPECTED);
+ return p;
+ }
+ *ppszRE = CharTraits::Next(*ppszRE);
+ return p;
+ }
+ szAbbrevs++;
+ }
+ return -1;
+ }
+
+ // ParseSE: parse grammar rule SE (simple expression)
+ int ParseSE(const RECHAR **ppszRE, bool &bEmpty)
+ {
+
+ if (MatchToken(ppszRE, '{'))
+ return ParseArg(ppszRE, bEmpty);
+ if (MatchToken(ppszRE, '('))
+ return ParseGroup(ppszRE, bEmpty);
+ if (MatchToken(ppszRE, '['))
+ return ParseCharClass(ppszRE, bEmpty);
+
+ if (MatchToken(ppszRE, '\\'))
+ {
+ if (!CharTraits::Isdigit(**ppszRE))
+ {
+ // check for abbreviations
+ int p;
+ p = ParseAbbrev(ppszRE, bEmpty);
+ if (p >= 0)
+ return p;
+
+ if (GetLastParseError())
+ return -1;
+
+ // escaped char
+ p = AddInstruction(RE_SYMBOL);
+ if (p < 0)
+ return -1;
+ GetInstruction(p).symbol.nSymbol = (int) **ppszRE;
+ *ppszRE = CharTraits::Next(*ppszRE);
+ return p;
+ }
+ // previous match
+ bEmpty = false;
+ int nPrev = AddInstruction(RE_PREVIOUS);
+ if (nPrev < 0)
+ return -1;
+
+ UINT uValue = (UINT) CharTraits::Strtol(*ppszRE, (RECHAR **) ppszRE, 10);
+ if (uValue >= m_uNumGroups)
+ {
+ SetLastParseError(REPARSE_ERROR_INVALID_GROUP);
+ return -1;
+ }
+ GetInstruction(nPrev).prev.nGroup = (size_t) uValue;
+ return nPrev;
+ }
+
+ if (MatchToken(ppszRE, '!'))
+ return ParseNot(ppszRE, bEmpty);
+
+ if (**ppszRE == '}' || **ppszRE == ']' || **ppszRE == ')')
+ {
+ return -1;
+ }
+
+ if (**ppszRE == '\0')
+ {
+ return -1;
+ }
+
+ int p;
+ if (**ppszRE == '.')
+ {
+ p = AddInstruction(RE_ANY);
+ if (p < 0)
+ return -1;
+ bEmpty = false;
+ }
+ else if (**ppszRE == '$' && (*ppszRE)[1] == '\0')
+ {
+ p = AddInstruction(RE_SYMBOL);
+ if (p < 0)
+ return -1;
+ GetInstruction(p).symbol.nSymbol = 0;
+ bEmpty = false;
+ }
+ else
+ {
+ p = AddInstruction(RE_SYMBOL);
+ if (p < 0)
+ return -1;
+ GetInstruction(p).symbol.nSymbol = (int) **ppszRE;
+ bEmpty = false;
+ }
+ *ppszRE = CharTraits::Next(*ppszRE);
+ return p;
+ }
+
+ // ParseE: parse grammar rule E (expression)
+ int ParseE(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ CParseState ParseState(this);
+ const RECHAR *sz = *ppszRE;
+
+ int nSE;
+
+ int nFirst = ParseSE(ppszRE, bEmpty);
+ if (nFirst < 0)
+ return nFirst;
+
+ REInstructionType type = RE_MATCH;
+
+ if (MatchToken(ppszRE, '*'))
+ if(MatchToken(ppszRE, '?'))
+ type = RE_NG_STAR_BEGIN;
+ else
+ type = RE_STAR_BEGIN;
+
+
+ else if (MatchToken(ppszRE, '+'))
+ if(MatchToken(ppszRE, '?'))
+ type = RE_NG_PLUS;
+ else
+ type = RE_PLUS;
+
+ else if (MatchToken(ppszRE, '?'))
+ if(MatchToken(ppszRE, '?'))
+ type = RE_NG_QUESTION;
+ else
+ type = RE_QUESTION;
+
+
+ if (type == RE_MATCH)
+ return nFirst;
+
+ if (type == RE_STAR_BEGIN || type == RE_QUESTION|| type == RE_NG_STAR_BEGIN || type == RE_NG_QUESTION)
+ {
+ ParseState.Restore(this);
+ }
+ else
+ {
+ m_uNumGroups = ParseState.m_uNumGroups;
+ }
+ *ppszRE = sz;
+
+ int nE;
+
+ if (type == RE_NG_STAR_BEGIN || type == RE_NG_PLUS || type == RE_NG_QUESTION) // Non-Greedy
+ {
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return -1;
+
+ bEmpty = false;
+
+ nSE = ParseSE(ppszRE, bEmpty);
+ if (nSE < 0)
+ return nSE;
+
+ if (bEmpty && (type == RE_NG_STAR_BEGIN || type == RE_NG_PLUS))
+ {
+ SetLastParseError(REPARSE_ERROR_EMPTY_REPEATOP);
+ return -1;
+ }
+ bEmpty = true;
+
+ *ppszRE = CharTraits::Next(*ppszRE);
+ *ppszRE = CharTraits::Next(*ppszRE);
+
+ if (type == RE_NG_STAR_BEGIN || type == RE_NG_PLUS)
+ {
+ int nJmp = AddInstruction(RE_JMP);
+ if (nJmp < 0)
+ return -1;
+ GetInstruction(nCall).call.nTarget = nJmp+1;
+ GetInstruction(nJmp).jmp.nTarget = nCall;
+ }
+ else
+ GetInstruction(nCall).call.nTarget = nSE+1;
+
+ if (type == RE_NG_PLUS)
+ nE = nFirst;
+ else
+ nE = nCall;
+ }
+ else // Greedy
+ {
+
+ int nPushMem = AddInstruction(RE_PUSH_MEMORY);
+ if (nPushMem < 0)
+ return -1;
+
+ int nStore = AddInstruction(RE_STORE_CHARPOS);
+ if (nStore < 0)
+ return -1;
+
+ if (AddInstruction(RE_PUSH_CHARPOS) < 0)
+ return -1;
+
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return -1;
+
+ if (AddInstruction(RE_POP_CHARPOS) < 0)
+ return -1;
+
+ int nPopMem = AddInstruction(RE_POP_MEMORY);
+ if (nPopMem < 0)
+ return -1;
+
+ int nJmp = AddInstruction(RE_JMP);
+ if (nJmp < 0)
+ return -1;
+
+ GetInstruction(nPushMem).memory.nIndex = m_uRequiredMem++;
+ GetInstruction(nStore).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+ GetInstruction(nCall).call.nTarget = nJmp+1;
+ GetInstruction(nPopMem).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+
+ bEmpty = false;
+
+ nSE = ParseSE(ppszRE, bEmpty);
+ if (nSE < 0)
+ return nSE;
+
+ if (bEmpty && (type == RE_STAR_BEGIN || type == RE_PLUS))
+ {
+ SetLastParseError(REPARSE_ERROR_EMPTY_REPEATOP);
+ return -1;
+ }
+
+ if (type != RE_PLUS && type != RE_NG_PLUS)
+ bEmpty = true;
+
+ *ppszRE = CharTraits::Next(*ppszRE);
+
+
+ int nRetNoMatch = AddInstruction(RE_RET_NOMATCH);
+ if (nRetNoMatch < 0)
+ return -1;
+
+ int nStore1 = AddInstruction(RE_STORE_CHARPOS);
+ if (nStore1 < 0)
+ return -1;
+
+ GetInstruction(nRetNoMatch).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+ GetInstruction(nStore1).memory.nIndex = GetInstruction(nPushMem).memory.nIndex;
+
+ if (type != RE_QUESTION)
+ {
+ int nJmp1 = AddInstruction(RE_JMP);
+ if (nJmp1 < 0)
+ return -1;
+ GetInstruction(nJmp1).jmp.nTarget = nPushMem;
+ }
+
+ GetInstruction(nJmp).jmp.nTarget = m_Instructions.GetCount();
+ if (type == RE_PLUS)
+ nE = nFirst;
+ else
+ nE = nPushMem;
+ }
+
+ return nE;
+ }
+
+
+ // ParseAltE: parse grammar rule AltE
+ int ParseAltE(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ const RECHAR *sz = *ppszRE;
+ CParseState ParseState(this);
+
+ int nPush = AddInstruction(RE_PUSH_CHARPOS);
+ if (nPush < 0)
+ return -1;
+
+ int nCall = AddInstruction(RE_CALL);
+ if (nCall < 0)
+ return -1;
+
+ GetInstruction(nCall).call.nTarget = nPush+4;
+ if (AddInstruction(RE_POP_CHARPOS) < 0)
+ return -1;
+
+ int nJmpNext = AddInstruction(RE_JMP);
+ if (nJmpNext < 0)
+ return -1;
+
+ int nE = ParseE(ppszRE, bEmpty);
+ if (nE < 0)
+ {
+ if (GetLastParseError())
+ return -1;
+ ParseState.Restore(this);
+ return nE;
+ }
+
+ int nJmpEnd = AddInstruction(RE_JMP);
+ if (nJmpEnd < 0)
+ return -1;
+
+ GetInstruction(nJmpNext).jmp.nTarget = nJmpEnd+1;
+
+ if (!MatchToken(ppszRE, '|'))
+ {
+ ParseState.Restore(this);
+ *ppszRE = sz;
+
+ return ParseE(ppszRE, bEmpty);
+ }
+
+ bool bEmptyAltE;
+ int nAltE = ParseAltE(ppszRE, bEmptyAltE);
+ GetInstruction(nJmpEnd).jmp.nTarget = m_Instructions.GetCount();
+ GetInstruction(nJmpNext).jmp.nTarget = nAltE;
+ if (nAltE < 0)
+ {
+ if (GetLastParseError())
+ return -1;
+ ParseState.Restore(this);
+ return nAltE;
+ }
+ bEmpty = bEmpty | bEmptyAltE;
+ return nPush;
+ }
+
+ // ParseRE: parse grammar rule RE (regular expression)
+ int ParseRE(const RECHAR **ppszRE, bool &bEmpty)
+ {
+ if (**ppszRE == '\0')
+ return -1;
+
+ int p = ParseAltE(ppszRE, bEmpty);
+ if (p < 0)
+ return p;
+
+ bool bEmptyRE = true;
+ ParseRE(ppszRE, bEmptyRE);
+ if (GetLastParseError())
+ return -1;
+ bEmpty = bEmpty && bEmptyRE;
+ return p;
+ }
+
+ //pointers to the matched string and matched groups, currently point into an internal allocated
+ //buffer that hold a copy of the input string.
+ //This function fix these pointers to point into the original, user supplied buffer (first param to Match method).
+ //Example: If a ptr (szStart) currently point to <internal buffer>+3, it is fixed to <user supplied buffer>+3
+ void FixupMatchContext(CAtlREMatchContext<CharTraits> *pContext, const RECHAR *szOrig, const RECHAR *szNew)
+ {
+ ATLENSURE(pContext);
+ ATLASSERT(szOrig);
+ ATLASSERT(szNew);
+
+ pContext->m_Match.szStart = szOrig + (pContext->m_Match.szStart - szNew);
+ pContext->m_Match.szEnd = szOrig + (pContext->m_Match.szEnd - szNew);
+ for (UINT i=0; i<pContext->m_uNumGroups; i++)
+ {
+ if (pContext->m_Matches[i].szStart==NULL || pContext->m_Matches[i].szEnd==NULL)
+ {
+ continue; //Do not fix unmatched groups.
+ }
+ pContext->m_Matches[i].szStart = szOrig + (pContext->m_Matches[i].szStart - szNew);
+ pContext->m_Matches[i].szEnd = szOrig + (pContext->m_Matches[i].szEnd - szNew);
+ }
+ }
+ // implementation
+ // helpers for dumping and debugging the rx engine
+public:
+#ifdef ATL_REGEXP_DUMP
+ size_t DumpInstruction(size_t ip)
+ {
+ printf("%08x ", ip);
+ switch (GetInstruction(ip).type)
+ {
+ case RE_NOP:
+ printf("NOP\n");
+ ip++;
+ break;
+
+ case RE_SYMBOL:
+ AtlprintfT<RECHAR>(CAToREChar<RECHAR>("Symbol %c\n"),GetInstruction(ip).symbol.nSymbol);
+ ip++;
+ break;
+
+ case RE_ANY:
+ printf("Any\n");
+ ip++;
+ break;
+
+ case RE_RANGE:
+ printf("Range\n");
+ ip++;
+ ip += InstructionsPerRangeBitField();
+ break;
+
+ case RE_NOTRANGE:
+ printf("NOT Range\n");
+ ip++;
+ ip += InstructionsPerRangeBitField();
+ break;
+
+ case RE_RANGE_EX:
+ printf("RangeEx %08x\n", GetInstruction(ip).range.nTarget);
+ ip++;
+ break;
+
+ case RE_NOTRANGE_EX:
+ printf("NotRangeEx %08x\n", GetInstruction(ip).range.nTarget);
+ ip++;
+ break;
+
+ case RE_GROUP_START:
+ printf("Start group %d\n", GetInstruction(ip).group.nGroup);
+ ip++;
+ break;
+
+ case RE_GROUP_END:
+ printf("Group end %d\n", GetInstruction(ip).group.nGroup);
+ ip++;
+ break;
+
+ case RE_PUSH_CHARPOS:
+ printf("Push char pos\n");
+ ip++;
+ break;
+
+ case RE_POP_CHARPOS:
+ printf("Pop char pos\n");
+ ip++;
+ break;
+
+ case RE_STORE_CHARPOS:
+ printf("Store char pos %d\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_GET_CHARPOS:
+ printf("Get char pos %d\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_STORE_STACKPOS:
+ printf("Store stack pos %d\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_GET_STACKPOS:
+ printf("Get stack pos %d\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_CALL:
+ printf("Call %08x\n", GetInstruction(ip).call.nTarget);
+ ip++;
+ break;
+
+ case RE_JMP:
+ printf("Jump %08x\n", GetInstruction(ip).jmp.nTarget);
+ ip++;
+ break;
+
+ case RE_RETURN:
+ printf("return\n");
+ ip++;
+ break;
+
+ case RE_PUSH_MEMORY:
+ printf("Push memory %08x\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_POP_MEMORY:
+ printf("Pop memory %08x\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_RET_NOMATCH:
+ printf("Return no match %08x\n", GetInstruction(ip).memory.nIndex);
+ ip++;
+ break;
+
+ case RE_MATCH:
+ printf("END\n");
+ ip++;
+ break;
+
+ case RE_ADVANCE:
+ printf("ADVANCE\n");
+ ip++;
+ break;
+
+ case RE_FAIL:
+ printf("FAIL\n");
+ ip++;
+ break;
+
+ case RE_PREVIOUS:
+ printf("Prev %d\n", GetInstruction(ip).prev.nGroup);
+ ip++;
+ break;
+
+ case RE_PUSH_GROUP:
+ printf("Push group %d\n", GetInstruction(ip).group.nGroup);
+ ip++;
+ break;
+
+ case RE_POP_GROUP:
+ printf("Pop group %d\n", GetInstruction(ip).group.nGroup);
+ ip++;
+ break;
+
+
+ default:
+ printf("????\n");
+ ip++;
+ break;
+ }
+ return ip;
+ }
+
+ void Dump(size_t ipCurrent = 0)
+ {
+ size_t ip = 0;
+
+ while (ip < m_Instructions.GetCount())
+ {
+ if (ip == ipCurrent)
+ printf("->");
+ ip = DumpInstruction(ip);
+ }
+ }
+#endif
+
+#ifdef ATLRX_DEBUG
+ void cls( HANDLE hConsole )
+ {
+ COORD coordScreen = { 0, 0 }; /* here's where we'll home the
+ cursor */
+ BOOL bSuccess;
+ DWORD cCharsWritten;
+ CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
+ DWORD dwConSize; /* number of character cells in
+ the current buffer */
+
+ /* get the number of character cells in the current buffer */
+
+ bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
+ dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+
+ /* fill the entire screen with blanks */
+
+ bSuccess = FillConsoleOutputCharacter( hConsole, (TCHAR) ' ',
+ dwConSize, coordScreen, &cCharsWritten );
+
+ /* get the current text attribute */
+
+ bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
+
+ /* now set the buffer's attributes accordingly */
+
+ bSuccess = FillConsoleOutputAttribute( hConsole, csbi.wAttributes,
+ dwConSize, coordScreen, &cCharsWritten );
+
+ /* put the cursor at (0, 0) */
+
+ bSuccess = SetConsoleCursorPosition( hConsole, coordScreen );
+ return;
+ }
+
+ void DumpStack(CAtlREMatchContext<CharTraits> *pContext)
+ {
+ for (size_t i=pContext->m_nTos; i>0; i--)
+ {
+ if (pContext->m_stack[i] < (void *) m_Instructions.GetCount())
+ printf("0x%p\n", pContext->m_stack[i]);
+ else
+ {
+ // assume a pointer into the input
+ AtlprintfT<RECHAR>(CAToREChar<RECHAR>("%s\n"), pContext->m_stack[i]);
+ }
+ }
+ }
+
+ void DumpMemory(CAtlREMatchContext<CharTraits> *pContext)
+ {
+ for (UINT i=0; i<m_uRequiredMem; i++)
+ {
+ AtlprintfT<RECHAR>(CAToREChar<RECHAR>("%d: %s\n"), i, pContext->m_Mem.m_p[i]);
+ }
+ }
+
+ virtual void OnDebugEvent(size_t ip, const RECHAR *szIn, const RECHAR *sz, CAtlREMatchContext<CharTraits> *pContext)
+ {
+ cls(GetStdHandle(STD_OUTPUT_HANDLE));
+ printf("----------Code---------\n");
+ Dump(ip);
+ printf("----------Input---------\n");
+ AtlprintfT<RECHAR>(CAToREChar<RECHAR>("%s\n"), szIn);
+ for (int s=0; szIn+s < sz; s++)
+ {
+ printf(" ");
+ }
+ printf("^\n");
+ printf("----------Memory---------\n");
+ DumpMemory(pContext);
+ printf("----------Stack---------\n");
+ DumpStack(pContext);
+ getchar();
+ }
+#endif
+
+};
+
+} // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLRX_H__
diff --git a/include/atl/atlserr.h b/include/atl/atlserr.h
new file mode 100644
index 000000000..1e90016fb
--- /dev/null
+++ b/include/atl/atlserr.h
@@ -0,0 +1,174 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSERR_H__
+#define __ATLSERR_H__
+
+#pragma once
+#pragma pack(push,_ATL_PACKING)
+namespace ATL{
+
+#define VALIDATION_S_OK 0x00000000
+#define VALIDATION_S_EMPTY 0x00000001
+#define VALIDATION_E_PARAMNOTFOUND 0x00000002
+#define VALIDATION_E_LENGTHMIN 0x80000083
+#define VALIDATION_E_LENGTHMAX 0x80000084
+#define VALIDATION_E_INVALIDLENGTH 0x80000080
+#define VALIDATION_E_INVALIDPARAM 0x80000005
+#define VALIDATION_E_FAIL 0x80000006
+
+#define VALIDATION_SUCCEEDED(x) (((x == VALIDATION_S_OK) || (x == VALIDATION_S_EMPTY )))
+
+typedef DWORD HTTP_CODE;
+
+#define HTTP_ERROR(err, sub) ((HTTP_CODE)(DWORD_PTR)MAKELONG((WORD)err, (WORD)sub))
+#define HTTP_ERROR_CODE(err) ((DWORD)LOWORD(err))
+#define HTTP_SUBERROR_CODE(err) ((DWORD)HIWORD(err))
+#define HTTP_SUCCESS HTTP_ERROR(0, 0)
+
+#define SUBERR_NONE 0
+#define ISE_SUBERR_BADSRF 1
+#define ISE_SUBERR_HNDLFAIL 2
+#define ISE_SUBERR_SYSOBJFAIL 3
+#define ISE_SUBERR_READFILEFAIL 4
+#define ISE_SUBERR_LOADFILEFAIL 6
+#define ISE_SUBERR_LOADLIB 7
+#define ISE_SUBERR_HANDLERIF 8
+#define ISE_SUBERR_OUTOFMEM 9
+#define ISE_SUBERR_UNEXPECTED 10
+#define ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET 11
+#define ISE_SUBERR_STENCIL_MISMATCHWHILE 12
+#define ISE_SUBERR_STENCIL_MISMATCHIF 13
+#define ISE_SUBERR_STENCIL_UNEXPECTEDTYPE 14
+#define ISE_SUBERR_STENCIL_INVALIDINDEX 15
+#define ISE_SUBERR_STENCIL_INDEXOUTOFRANGE 16
+#define ISE_SUBERR_STENCIL_PARSE_FAIL 17
+#define ISE_SUBERR_STENCIL_LOAD_FAIL 18
+#define ISE_SUBERR_HANDLER_NOT_FOUND 19
+#define ISE_SUBERR_BAD_HANDLER_TAG 20
+#define ISE_SUBERR_NO_HANDLER_TAG 21
+#define ISE_SUBERR_LONGMETHODNAME 22
+#define ISE_SUBERR_LONGHANDLERNAME 23
+#define ISE_SUBERR_IMPERSONATIONFAILED 24
+#define ISE_SUBERR_ISAPISTARTUPFAILED 25
+#define ISE_SUBERR_SOAPNOSOAPACTION 26
+#define SUBERR_NO_PROCESS 27
+#define SUBERR_S_FALSE 28
+#define SUBERR_ASYNC 29
+#define SUBERR_ASYNC_DONE 30
+#define SUBERR_ASYNC_NOFLUSH 31
+#define SUBERR_ASYNC_NOFLUSH_DONE 32
+#define SUBERR_NO_CACHE 33
+#define DBG_SUBERR_ALREADY_DEBUGGING 34
+#define DBG_SUBERR_NOT_DEBUGGING 35
+#define DBG_SUBERR_INVALID_SESSION 36
+#define DBG_SUBERR_BAD_ID 37
+#define DBG_SUBERR_COCREATE 38
+#define DBG_SUBERR_ATTACH 39
+
+
+#define HTTP_FAIL HTTP_ERROR(500, SUBERR_NONE)
+#define HTTP_SUCCESS_NO_PROCESS HTTP_ERROR(200, SUBERR_NO_PROCESS)
+#define HTTP_S_FALSE HTTP_ERROR(HTTP_ERROR_CODE(HTTP_SUCCESS), SUBERR_S_FALSE)
+#define HTTP_SUCCESS_ASYNC HTTP_ERROR(200, SUBERR_ASYNC)
+#define HTTP_SUCCESS_ASYNC_DONE HTTP_ERROR(200, SUBERR_ASYNC_DONE)
+#define HTTP_SUCCESS_ASYNC_NOFLUSH HTTP_ERROR(200, SUBERR_ASYNC_NOFLUSH)
+#define HTTP_SUCCESS_ASYNC_NOFLUSH_DONE HTTP_ERROR(200, SUBERR_ASYNC_NOFLUSH_DONE)
+#define HTTP_SUCCESS_NO_CACHE HTTP_ERROR(200, SUBERR_NO_CACHE)
+#define HTTP_OK HTTP_ERROR(200, SUBERR_NONE)
+#define HTTP_CONTINUE HTTP_ERROR(100, SUBERR_NONE)
+
+#define HTTP_CREATED HTTP_ERROR(201, SUBERR_NONE)
+#define HTTP_ACCEPTED HTTP_ERROR(202, SUBERR_NONE)
+#define HTTP_NON_AUTHORITATIVE HTTP_ERROR(203, SUBERR_NONE)
+#define HTTP_NO_CONTENT HTTP_ERROR(204, SUBERR_NONE)
+#define HTTP_RESET_CONTENT HTTP_ERROR(205, SUBERR_NONE)
+#define HTTP_PARTIAL_CONTENT HTTP_ERROR(206, SUBERR_NONE)
+
+#define HTTP_MULTIPLE_CHOICES HTTP_ERROR(300, SUBERR_NONE)
+#define HTTP_MOVED_PERMANENTLY HTTP_ERROR(301, SUBERR_NONE)
+#define HTTP_FOUND HTTP_ERROR(302, SUBERR_NONE)
+#define HTTP_SEE_OTHER HTTP_ERROR(303, SUBERR_NONE)
+#define HTTP_NOT_MODIFIED HTTP_ERROR(304, SUBERR_NONE)
+#define HTTP_USE_PROXY HTTP_ERROR(305, SUBERR_NONE)
+#define HTTP_TEMPORARY_REDIRECT HTTP_ERROR(307, SUBERR_NONE)
+
+#define HTTP_BAD_REQUEST HTTP_ERROR(400, SUBERR_NONE)
+#define HTTP_UNAUTHORIZED HTTP_ERROR(401, SUBERR_NONE)
+#define HTTP_PAYMENT_REQUIRED HTTP_ERROR(402, SUBERR_NONE)
+#define HTTP_FORBIDDEN HTTP_ERROR(403, SUBERR_NONE)
+#define HTTP_NOT_FOUND HTTP_ERROR(404, SUBERR_NONE)
+#define HTTP_METHOD_NOT_ALLOWED HTTP_ERROR(405, SUBERR_NONE)
+#define HTTP_NOT_ACCEPTABLE HTTP_ERROR(406, SUBERR_NONE)
+#define HTTP_PROXY_AUTHENTICATION_REQUIRED HTTP_ERROR(407, SUBERR_NONE)
+#define HTTP_REQUEST_TIMEOUT HTTP_ERROR(408, SUBERR_NONE)
+#define HTTP_CONFLICT HTTP_ERROR(409, SUBERR_NONE)
+#define HTTP_GONE HTTP_ERROR(410, SUBERR_NONE)
+#define HTTP_LENGTH_REQUIRED HTTP_ERROR(411, SUBERR_NONE)
+#define HTTP_PRECONDITION_FAILED HTTP_ERROR(412, SUBERR_NONE)
+#define HTTP_REQUEST_ENTITY_TOO_LONG HTTP_ERROR(413, SUBERR_NONE)
+#define HTTP_REQUEST_URI_TOO_LONG HTTP_ERROR(414, SUBERR_NONE)
+#define HTTP_UNSUPPORTED_MEDIA_TYPE HTTP_ERROR(415, SUBERR_NONE)
+#define HTTP_RANGE_NOT_SATISFIABLE HTTP_ERROR(416, SUBERR_NONE)
+#define HTTP_EXPECTATION_FAILED HTTP_ERROR(417, SUBERR_NONE)
+
+#define HTTP_INTERNAL_SERVER_ERROR HTTP_ERROR(500, SUBERR_NONE)
+#define HTTP_NOT_IMPLEMENTED HTTP_ERROR(501, SUBERR_NONE)
+#define HTTP_BAD_GATEWAY HTTP_ERROR(502, SUBERR_NONE)
+#define HTTP_SERVICE_UNAVAILABLE HTTP_ERROR(503, SUBERR_NONE)
+#define HTTP_GATEWAY_TIMEOUT HTTP_ERROR(504, SUBERR_NONE)
+#define HTTP_VERSION_NOT_SUPPORTED HTTP_ERROR(505, SUBERR_NONE)
+
+inline bool IsAsyncStatus(HTTP_CODE hcStatus)
+{
+ return
+ hcStatus == HTTP_SUCCESS_ASYNC ||
+ hcStatus == HTTP_SUCCESS_ASYNC_DONE ||
+ hcStatus == HTTP_SUCCESS_ASYNC_NOFLUSH ||
+ hcStatus == HTTP_SUCCESS_ASYNC_NOFLUSH_DONE;
+}
+
+inline bool IsAsyncContinueStatus(HTTP_CODE hcStatus)
+{
+ return
+ hcStatus == HTTP_SUCCESS_ASYNC ||
+ hcStatus == HTTP_SUCCESS_ASYNC_NOFLUSH;
+}
+
+inline bool IsAsyncDoneStatus(HTTP_CODE hcStatus)
+{
+ return
+ hcStatus == HTTP_SUCCESS_ASYNC_DONE ||
+ hcStatus == HTTP_SUCCESS_ASYNC_NOFLUSH_DONE;
+}
+
+inline bool IsAsyncFlushStatus(HTTP_CODE hcStatus)
+{
+ return
+ hcStatus == HTTP_SUCCESS_ASYNC ||
+ hcStatus == HTTP_SUCCESS_ASYNC_DONE;
+}
+
+inline bool IsAsyncNoFlushStatus(HTTP_CODE hcStatus)
+{
+ return
+ hcStatus == HTTP_SUCCESS_ASYNC_NOFLUSH ||
+ hcStatus == HTTP_SUCCESS_ASYNC_NOFLUSH_DONE;
+}
+
+ATL_NOINLINE inline HTTP_CODE AtlsHttpError(WORD wStatus, WORD wSubErr) throw()
+{
+ return HTTP_ERROR(wStatus, wSubErr);
+}
+
+}; // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLSERR_H__
diff --git a/include/atl/atlsession.h b/include/atl/atlsession.h
new file mode 100644
index 000000000..808748b21
--- /dev/null
+++ b/include/atl/atlsession.h
@@ -0,0 +1,2490 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSESSION_H__
+#define __ATLSESSION_H__
+
+#pragma once
+#pragma warning(push)
+#pragma warning(disable: 4702) // unreachable code
+
+#include <atldbcli.h>
+#include <atlcom.h>
+#include <atlstr.h>
+#include <stdio.h>
+#include <atlcoll.h>
+#include <atltime.h>
+#include <atlcrypt.h>
+#include <atlenc.h>
+#include <atlutil.h>
+#include <atlcache.h>
+#include <atlspriv.h>
+#include <atlsiface.h>
+
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+
+#ifndef MAX_SESSION_KEY_LEN
+ #define MAX_SESSION_KEY_LEN 128
+#endif
+
+#ifndef MAX_VARIABLE_NAME_LENGTH
+ #define MAX_VARIABLE_NAME_LENGTH 50
+#endif
+
+#ifndef MAX_VARIABLE_VALUE_LENGTH
+ #define MAX_VARIABLE_VALUE_LENGTH 1024
+#endif
+
+#ifndef MAX_CONNECTION_STRING_LEN
+ #define MAX_CONNECTION_STRING_LEN 2048
+#endif
+
+#ifndef SESSION_COOKIE_NAME
+ #define SESSION_COOKIE_NAME "SESSIONID"
+#endif
+
+#ifndef ATL_SESSION_TIMEOUT
+ #define ATL_SESSION_TIMEOUT 600000 //10 min
+#endif
+
+#ifndef ATL_SESSION_SWEEPER_TIMEOUT
+ #define ATL_SESSION_SWEEPER_TIMEOUT 1000 // 1sec
+#endif
+
+#define INVALID_DB_SESSION_POS 0x0
+#define ATL_DBSESSION_ID _T("__ATL_SESSION_DB_CONNECTION")
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+// CSessionNameGenerator
+// This is a helper class that generates random data for session key
+// names. This class tries to use the CryptoApi to generate random
+// bytes for the session key name. If the CryptoApi isn't available
+// then the CRT rand() is used to generate the random bytes. This
+// class's GetNewSessionName member function is used to actually
+// generate the session name.
+class CSessionNameGenerator :
+ public CCryptProv
+{
+public:
+ bool m_bCryptNotAvailable;
+ enum {MIN_SESSION_KEY_LEN=5};
+
+ CSessionNameGenerator() throw() :
+ m_bCryptNotAvailable(false)
+ {
+ // Note that the crypto api is being
+ // initialized with no private key
+ // information
+ HRESULT hr = InitVerifyContext();
+ m_bCryptNotAvailable = FAILED(hr) ? true : false;
+ }
+
+ // This function creates a new session name and base64 encodes it.
+ // The base64 encoding algorithm used needs at least MIN_SESSION_KEY_LEN
+ // bytes to work correctly. Since we stack allocate the temporary
+ // buffer that holds the key name, the buffer must be less than or equal to
+ // the MAX_SESSION_KEY_LEN in size.
+ HRESULT GetNewSessionName(__out_ecount_part_z(*pdwSize, *pdwSize) LPSTR szNewID, __inout DWORD *pdwSize) throw()
+ {
+ HRESULT hr = E_FAIL;
+
+ if (!pdwSize)
+ return E_POINTER;
+
+ if (*pdwSize < MIN_SESSION_KEY_LEN ||
+ *pdwSize > MAX_SESSION_KEY_LEN)
+ return E_INVALIDARG;
+
+ if (!szNewID)
+ return E_POINTER;
+
+ BYTE key[MAX_SESSION_KEY_LEN] = {0x0};
+
+
+ // calculate the number of bytes that will fit in the
+ // buffer we've been passed
+ DWORD dwDataSize = CalcMaxInputSize(*pdwSize);
+
+ if (dwDataSize && *pdwSize >= (DWORD)(Base64EncodeGetRequiredLength(dwDataSize,
+ ATL_BASE64_FLAG_NOCRLF)))
+ {
+ int dwKeySize = *pdwSize;
+ hr = GenerateRandomName(key, dwDataSize);
+ if (SUCCEEDED(hr))
+ {
+ if( Base64Encode(key,
+ dwDataSize,
+ szNewID,
+ &dwKeySize,
+ ATL_BASE64_FLAG_NOCRLF) )
+ {
+ //null terminate
+ szNewID[dwKeySize]=0;
+ *pdwSize = dwKeySize+1;
+ }
+ else
+ hr = E_FAIL;
+ }
+ else
+ {
+ *pdwSize = (DWORD)(Base64EncodeGetRequiredLength(dwDataSize,
+ ATL_BASE64_FLAG_NOCRLF));
+ return E_OUTOFMEMORY;
+ }
+ }
+ return hr;
+ }
+
+ DWORD CalcMaxInputSize(DWORD nOutputSize) throw()
+ {
+ if (nOutputSize < (DWORD)MIN_SESSION_KEY_LEN)
+ return 0;
+ // subtract one from the output size to make room
+ // for the NULL terminator in the output then
+ // calculate the biggest number of input bytes that
+ // when base64 encoded will fit in a buffer of size
+ // nOutputSize (including base64 padding)
+ int nInputSize = ((nOutputSize-1)*3)/4;
+ int factor = ((nInputSize*4)/3)%4;
+ if (factor)
+ nInputSize -= factor;
+ return nInputSize;
+ }
+
+
+ HRESULT GenerateRandomName(BYTE *pBuff, DWORD dwBuffSize) throw()
+ {
+ if (!pBuff)
+ return E_POINTER;
+
+ if (!dwBuffSize)
+ return E_UNEXPECTED;
+
+ if (!m_bCryptNotAvailable && GetHandle())
+ {
+ // Use the crypto api to generate random data.
+ return GenRandom(dwBuffSize, pBuff);
+ }
+
+ // CryptoApi isn't available so we generate
+ // random data using rand. We seed the random
+ // number generator with a seed that is a combination
+ // of bytes from an arbitrary number and the system
+ // time which changes every millisecond so it will
+ // be different for every call to this function.
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ static DWORD dwVal = 0x21;
+ DWORD dwSeed = (dwVal++ << 0x18) | (ft.dwLowDateTime & 0x00ffff00) | dwVal++ & 0x000000ff;
+ srand(dwSeed);
+ BYTE *pCurr = pBuff;
+ // fill buffer with random bytes
+ for (int i=0; i < (int)dwBuffSize; i++)
+ {
+ *pCurr = (BYTE) (rand() & 0x000000ff);
+ pCurr++;
+ }
+ return S_OK;
+ }
+};
+
+
+//
+// CDefaultQueryClass
+// returns Query strings for use in SQL queries used
+// by the database persisted session service.
+class CDefaultQueryClass
+{
+public:
+ LPCTSTR GetSessionRefDelete() throw()
+ {
+ return _T("DELETE FROM SessionReferences ")
+ _T("WHERE SessionID=? AND RefCount <= 0 ")
+ _T("AND DATEDIFF(millisecond, LastAccess, getdate()) > TimeoutMs");
+ }
+
+ LPCTSTR GetSessionRefIsExpired() throw()
+ {
+ return _T("SELECT SessionID FROM SessionReferences ")
+ _T("WHERE (SessionID=?) AND (DATEDIFF(millisecond, LastAccess, getdate()) > TimeoutMs)");
+ }
+
+ LPCTSTR GetSessionRefDeleteFinal() throw()
+ {
+ return _T("DELETE FROM SessionReferences ")
+ _T("WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionRefCreate() throw()
+ {
+ return _T("INSERT INTO SessionReferences ")
+ _T("(SessionID, LastAccess, RefCount, TimeoutMs) ")
+ _T("VALUES (?, getdate(), 1, ?)");
+ }
+
+ LPCTSTR GetSessionRefUpdateTimeout() throw()
+ {
+ return _T("UPDATE SessionReferences ")
+ _T("SET TimeoutMs=? WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionRefAddRef() throw()
+ {
+ return _T("UPDATE SessionReferences ")
+ _T("SET RefCount=RefCount+1, ")
+ _T("LastAccess=getdate() ")
+ _T("WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionRefRemoveRef() throw()
+ {
+ return _T("UPDATE SessionReferences ")
+ _T("SET RefCount=RefCount-1, ")
+ _T("LastAccess=getdate() ")
+ _T("WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionRefAccess() throw()
+ {
+ return _T("UPDATE SessionReferences ")
+ _T("SET LastAccess=getdate() ")
+ _T("WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionRefSelect() throw()
+ {
+ return _T("SELECT * FROM SessionReferences ")
+ _T("WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionRefGetCount() throw()
+ {
+ return _T("SELECT COUNT(*) FROM SessionReferences");
+ }
+
+
+ LPCTSTR GetSessionVarCount() throw()
+ {
+ return _T("SELECT COUNT(*) FROM SessionVariables WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionVarInsert() throw()
+ {
+ return _T("INSERT INTO SessionVariables ")
+ _T("(VariableValue, SessionID, VariableName) ")
+ _T("VALUES (?, ?, ?)");
+ }
+
+ LPCTSTR GetSessionVarUpdate() throw()
+ {
+ return _T("UPDATE SessionVariables ")
+ _T("SET VariableValue=? ")
+ _T("WHERE SessionID=? AND VariableName=?");
+ }
+
+ LPCTSTR GetSessionVarDeleteVar() throw()
+ {
+ return _T("DELETE FROM SessionVariables ")
+ _T("WHERE SessionID=? AND VariableName=?");
+ }
+
+ LPCTSTR GetSessionVarDeleteAllVars() throw()
+ {
+ return _T("DELETE FROM SessionVariables WHERE (SessionID=?)");
+ }
+
+ LPCTSTR GetSessionVarSelectVar()throw()
+ {
+ return _T("SELECT SessionID, VariableName, VariableValue ")
+ _T("FROM SessionVariables ")
+ _T("WHERE SessionID=? AND VariableName=?");
+ }
+
+ LPCTSTR GetSessionVarSelectAllVars() throw()
+ {
+ return _T("SELECT SessionID, VariableName, VariableValue ")
+ _T("FROM SessionVariables ")
+ _T("WHERE SessionID=?");
+ }
+
+ LPCTSTR GetSessionReferencesSet() throw()
+ {
+ return _T("UPDATE SessionReferences SET TimeoutMs=?");
+ }
+};
+
+
+// Contains the data for the session variable accessors
+class CSessionDataBase
+{
+public:
+ TCHAR m_szSessionID[MAX_SESSION_KEY_LEN];
+ TCHAR m_VariableName[MAX_VARIABLE_NAME_LENGTH];
+ BYTE m_VariableValue[MAX_VARIABLE_VALUE_LENGTH];
+ DBLENGTH m_VariableLen;
+ CSessionDataBase() throw()
+ {
+ m_szSessionID[0] = '\0';
+ m_VariableName[0] = '\0';
+ m_VariableValue[0] = '\0';
+ m_VariableLen = 0;
+ }
+ HRESULT Assign(LPCTSTR szSessionID, LPCTSTR szVarName, VARIANT *pVal) throw()
+ {
+ HRESULT hr = S_OK;
+ CVariantStream stream;
+ if ( szSessionID )
+ {
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN)< MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_szSessionID, _countof(m_szSessionID), szSessionID);
+ else
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ return E_INVALIDARG;
+
+ if (hr == S_OK && szVarName)
+ if (Checked::tcsnlen(szVarName, MAX_VARIABLE_NAME_LENGTH) < MAX_VARIABLE_NAME_LENGTH)
+ Checked::tcscpy_s(m_VariableName, _countof(m_VariableName), szVarName);
+ else
+ hr = E_OUTOFMEMORY;
+
+ if (hr == S_OK && pVal)
+ {
+ hr = stream.InsertVariant(pVal);
+ if (hr == S_OK)
+ {
+ BYTE *pBytes = stream.m_stream;
+ size_t size = stream.GetVariantSize();
+ if (pBytes && size && size < MAX_VARIABLE_VALUE_LENGTH)
+ {
+ Checked::memcpy_s(m_VariableValue, MAX_VARIABLE_VALUE_LENGTH, pBytes, size);
+ m_VariableLen = static_cast<DBLENGTH>(size);
+ }
+ else
+ hr = E_INVALIDARG;
+ }
+ }
+
+ return hr;
+ }
+};
+
+// Use to select a session variable given the name
+// of a session and the name of a variable.
+class CSessionDataSelector : public CSessionDataBase
+{
+public:
+ BEGIN_COLUMN_MAP(CSessionDataSelector)
+ COLUMN_ENTRY(1, m_szSessionID)
+ COLUMN_ENTRY(2, m_VariableName)
+ COLUMN_ENTRY_LENGTH(3, m_VariableValue, m_VariableLen)
+ END_COLUMN_MAP()
+ BEGIN_PARAM_MAP(CSessionDataSelector)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_szSessionID)
+ COLUMN_ENTRY(2, m_VariableName)
+ END_PARAM_MAP()
+};
+
+// Use to select all session variables given the name of
+// of a session.
+class CAllSessionDataSelector : public CSessionDataBase
+{
+public:
+ BEGIN_COLUMN_MAP(CAllSessionDataSelector)
+ COLUMN_ENTRY(1, m_szSessionID)
+ COLUMN_ENTRY(2, m_VariableName)
+ COLUMN_ENTRY_LENGTH(3, m_VariableValue, m_VariableLen)
+ END_COLUMN_MAP()
+ BEGIN_PARAM_MAP(CAllSessionDataSelector)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_szSessionID)
+ END_PARAM_MAP()
+};
+
+// Use to update the value of a session variable
+class CSessionDataUpdator : public CSessionDataBase
+{
+public:
+ BEGIN_PARAM_MAP(CSessionDataUpdator)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY_LENGTH(1, m_VariableValue, m_VariableLen)
+ COLUMN_ENTRY(2, m_szSessionID)
+ COLUMN_ENTRY(3, m_VariableName)
+ END_PARAM_MAP()
+};
+
+// Use to delete a session variable given the
+// session name and the name of the variable
+class CSessionDataDeletor
+{
+public:
+ CSessionDataDeletor()
+ {
+ m_szSessionID[0] = '\0';
+ m_VariableName[0] = '\0';
+ }
+
+ TCHAR m_szSessionID[MAX_SESSION_KEY_LEN];
+ TCHAR m_VariableName[MAX_VARIABLE_NAME_LENGTH];
+ HRESULT Assign(LPCTSTR szSessionID, LPCTSTR szVarName) throw()
+ {
+ if (szSessionID)
+ {
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_szSessionID, _countof(m_szSessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+ }
+
+ if (szVarName)
+ {
+ if(Checked::tcsnlen(szVarName, MAX_VARIABLE_NAME_LENGTH) < MAX_VARIABLE_NAME_LENGTH)
+ Checked::tcscpy_s(m_VariableName, _countof(m_VariableName), szVarName);
+ else
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+ }
+
+ BEGIN_PARAM_MAP(CSessionDataDeletor)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_szSessionID)
+ COLUMN_ENTRY(2, m_VariableName)
+ END_PARAM_MAP()
+};
+
+class CSessionDataDeleteAll
+{
+public:
+ TCHAR m_szSessionID[MAX_SESSION_KEY_LEN];
+ HRESULT Assign(LPCTSTR szSessionID) throw()
+ {
+ if (!szSessionID)
+ return E_INVALIDARG;
+
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_szSessionID, _countof(m_szSessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+
+ return S_OK;
+ }
+
+ BEGIN_PARAM_MAP(CSessionDataDeleteAll)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_szSessionID)
+ END_PARAM_MAP()
+};
+
+// Used for retrieving the count of session variables for
+// a given session ID.
+class CCountAccessor
+{
+public:
+ LONG m_nCount;
+ TCHAR m_szSessionID[MAX_SESSION_KEY_LEN];
+ CCountAccessor() throw()
+ {
+ m_szSessionID[0] = '\0';
+ m_nCount = 0;
+ }
+
+ HRESULT Assign(LPCTSTR szSessionID) throw()
+ {
+ if (!szSessionID)
+ return E_INVALIDARG;
+
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_szSessionID, _countof(m_szSessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+
+ return S_OK;
+ }
+
+ BEGIN_COLUMN_MAP(CCountAccessor)
+ COLUMN_ENTRY(1, m_nCount)
+ END_COLUMN_MAP()
+ BEGIN_PARAM_MAP(CCountAccessor)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_szSessionID)
+ END_PARAM_MAP()
+};
+
+
+// Used for updating entries in the session
+// references table, given a session ID
+class CSessionRefUpdator
+{
+public:
+ TCHAR m_SessionID[MAX_SESSION_KEY_LEN];
+ HRESULT Assign(LPCTSTR szSessionID) throw()
+ {
+ if (!szSessionID)
+ return E_INVALIDARG;
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_SessionID, _countof(m_SessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+ return S_OK;
+ }
+ BEGIN_PARAM_MAP(CSessionRefUpdator)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_SessionID)
+ END_PARAM_MAP()
+};
+
+class CSessionRefIsExpired
+{
+public:
+ TCHAR m_SessionID[MAX_SESSION_KEY_LEN];
+ TCHAR m_SessionIDOut[MAX_SESSION_KEY_LEN];
+ HRESULT Assign(LPCTSTR szSessionID) throw()
+ {
+ m_SessionIDOut[0]=0;
+ if (!szSessionID)
+ return E_INVALIDARG;
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_SessionID, _countof(m_SessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+ return S_OK;
+ }
+ BEGIN_COLUMN_MAP(CSessionRefIsExpired)
+ COLUMN_ENTRY(1, m_SessionIDOut)
+ END_COLUMN_MAP()
+ BEGIN_PARAM_MAP(CSessionRefIsExpired)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_SessionID)
+ END_PARAM_MAP()
+};
+
+class CSetAllTimeouts
+{
+public:
+ unsigned __int64 m_dwNewTimeout;
+ HRESULT Assign(unsigned __int64 dwNewValue)
+ {
+ m_dwNewTimeout = dwNewValue;
+ return S_OK;
+ }
+ BEGIN_PARAM_MAP(CSetAllTimeouts)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_dwNewTimeout)
+ END_PARAM_MAP()
+};
+
+class CSessionRefUpdateTimeout
+{
+public:
+ TCHAR m_SessionID[MAX_SESSION_KEY_LEN];
+ unsigned __int64 m_nNewTimeout;
+ HRESULT Assign(LPCTSTR szSessionID, unsigned __int64 nNewTimeout) throw()
+ {
+ if (!szSessionID)
+ return E_INVALIDARG;
+
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_SessionID, _countof(m_SessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+
+ m_nNewTimeout = nNewTimeout;
+
+ return S_OK;
+ }
+
+ BEGIN_PARAM_MAP(CSessionRefUpdateTimeout)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_nNewTimeout)
+ COLUMN_ENTRY(2, m_SessionID)
+ END_PARAM_MAP()
+};
+
+class CSessionRefSelector
+{
+public:
+ TCHAR m_SessionID[MAX_SESSION_KEY_LEN];
+ int m_RefCount;
+ HRESULT Assign(LPCTSTR szSessionID) throw()
+ {
+ if (!szSessionID)
+ return E_INVALIDARG;
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_SessionID, _countof(m_SessionID), szSessionID);
+ else
+ return E_OUTOFMEMORY;
+ return S_OK;
+ }
+ BEGIN_COLUMN_MAP(CSessionRefSelector)
+ COLUMN_ENTRY(1, m_SessionID)
+ COLUMN_ENTRY(3, m_RefCount)
+ END_COLUMN_MAP()
+ BEGIN_PARAM_MAP(CSessionRefSelector)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_SessionID)
+ END_PARAM_MAP()
+};
+
+class CSessionRefCount
+{
+public:
+ LONG m_nCount;
+ BEGIN_COLUMN_MAP(CSessionRefCount)
+ COLUMN_ENTRY(1, m_nCount)
+ END_COLUMN_MAP()
+};
+
+// Used for creating new entries in the session
+// references table.
+class CSessionRefCreator
+{
+public:
+ TCHAR m_SessionID[MAX_SESSION_KEY_LEN];
+ unsigned __int64 m_TimeoutMs;
+ HRESULT Assign(LPCTSTR szSessionID, unsigned __int64 timeout) throw()
+ {
+ if (!szSessionID)
+ return E_INVALIDARG;
+ if (Checked::tcsnlen(szSessionID, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ {
+ Checked::tcscpy_s(m_SessionID, _countof(m_SessionID), szSessionID);
+ m_TimeoutMs = timeout;
+ }
+ else
+ return E_OUTOFMEMORY;
+ return S_OK;
+ }
+ BEGIN_PARAM_MAP(CSessionRefCreator)
+ SET_PARAM_TYPE(DBPARAMIO_INPUT)
+ COLUMN_ENTRY(1, m_SessionID)
+ COLUMN_ENTRY(2, m_TimeoutMs)
+ END_PARAM_MAP()
+};
+
+
+// CDBSession
+// This session persistance class persists session variables to
+// an OLEDB datasource. The following table gives a general description
+// of the table schema for the tables this class uses.
+//
+// TableName: SessionVariables
+// Column Name Type Description
+// 1 SessionID char[MAX_SESSION_KEY_LEN] Session Key name
+// 2 VariableName char[MAX_VARIABLE_NAME_LENGTH] Variable Name
+// 3 VariableValue varbinary[MAX_VARIABLE_VALUE_LENGTH] Variable Value
+
+//
+// TableName: SessionReferences
+// Column Name Type Description
+// 1 SessionID char[MAX_SESSION_KEY_LEN] Session Key Name.
+// 2 LastAccess datetime Date and time of last access to this session.
+// 3 RefCount int Current references on this session.
+// 4 TimeoutMS int Timeout value for the session in milli seconds
+
+typedef bool (*PFN_GETPROVIDERINFO)(DWORD_PTR, wchar_t **);
+
+template <class QueryClass=CDefaultQueryClass>
+class CDBSession:
+ public ISession,
+ public CComObjectRootEx<CComGlobalsThreadModel>
+
+{
+ typedef CCommand<CAccessor<CAllSessionDataSelector> > iterator_accessor;
+public:
+ typedef QueryClass DBQUERYCLASS_TYPE;
+ BEGIN_COM_MAP(CDBSession)
+ COM_INTERFACE_ENTRY(ISession)
+ END_COM_MAP()
+
+ CDBSession() throw():
+ m_dwTimeout(ATL_SESSION_TIMEOUT)
+ {
+ m_szSessionName[0] = '\0';
+ }
+
+ ~CDBSession() throw()
+ {
+ }
+
+ void FinalRelease()throw()
+ {
+ SessionUnlock();
+ }
+
+ STDMETHOD(SetVariable)(LPCSTR szName, VARIANT Val) throw()
+ {
+ HRESULT hr = E_FAIL;
+ if (!szName)
+ return E_INVALIDARG;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // Update the last access time for this session
+ hr = Access();
+ if (hr != S_OK)
+ return hr;
+
+ // Allocate an updator command and fill out it's input parameters.
+ CCommand<CAccessor<CSessionDataUpdator> > command;
+ _ATLTRY
+ {
+ CA2CT name(szName);
+ hr = command.Assign(m_szSessionName, name, &Val);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ if (hr != S_OK)
+ return hr;
+
+ // Try an update. Update will fail if the variable is not already there.
+ DBROWCOUNT nRows = 0;
+
+ hr = command.Open(dataconn,
+ m_QueryObj.GetSessionVarUpdate(),
+ NULL, &nRows, DBGUID_DEFAULT, false);
+ if (hr == S_OK && nRows <= 0)
+ hr = E_UNEXPECTED;
+ if (hr != S_OK)
+ {
+ // Try an insert
+ hr = command.Open(dataconn, m_QueryObj.GetSessionVarInsert(), NULL, &nRows, DBGUID_DEFAULT, false);
+ if (hr == S_OK && nRows <=0)
+ hr = E_UNEXPECTED;
+ }
+
+ return hr;
+ }
+
+ // Warning: For string data types, depending on the configuration of
+ // your database, strings might be returned with trailing white space.
+ STDMETHOD(GetVariable)(LPCSTR szName, VARIANT *pVal) throw()
+ {
+ HRESULT hr = E_FAIL;
+ if (!szName)
+ return E_INVALIDARG;
+ if (pVal)
+ VariantInit(pVal);
+ else
+ return E_POINTER;
+
+ // Get the data connection for this thread
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // Update the last access time for this session
+ hr = Access();
+ if (hr != S_OK)
+ return hr;
+
+ // Allocate a command a fill out it's input parameters.
+ CCommand<CAccessor<CSessionDataSelector> > command;
+ _ATLTRY
+ {
+ CA2CT name(szName);
+ hr = command.Assign(m_szSessionName, name, NULL);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (hr == S_OK)
+ {
+ hr = command.Open(dataconn, m_QueryObj.GetSessionVarSelectVar());
+ if (SUCCEEDED(hr))
+ {
+ if ( S_OK == (hr = command.MoveFirst()))
+ {
+ CStreamOnByteArray stream(command.m_VariableValue);
+ CComVariant vOut;
+ hr = vOut.ReadFromStream(static_cast<IStream*>(&stream));
+ if (hr == S_OK)
+ hr = vOut.Detach(pVal);
+ }
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(RemoveVariable)(LPCSTR szName) throw()
+ {
+ HRESULT hr = E_FAIL;
+ if (!szName)
+ return E_INVALIDARG;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // update the last access time for this session
+ hr = Access();
+ if (hr != S_OK)
+ return hr;
+
+ // allocate a command and set it's input parameters
+ CCommand<CAccessor<CSessionDataDeletor> > command;
+ _ATLTRY
+ {
+ CA2CT name(szName);
+ hr = command.Assign(m_szSessionName, name);
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ // execute the command
+ DBROWCOUNT nRows = 0;
+ if (hr == S_OK)
+ hr = command.Open(dataconn, m_QueryObj.GetSessionVarDeleteVar(),
+ NULL, &nRows, DBGUID_DEFAULT, false);
+ if (hr == S_OK && nRows <= 0)
+ hr = E_FAIL;
+ return hr;
+ }
+
+ // Gives the count of rows in the table for this session ID.
+ STDMETHOD(GetCount)(long *pnCount) throw()
+ {
+ HRESULT hr = S_OK;
+ if (pnCount)
+ *pnCount = 0;
+ else
+ return E_POINTER;
+
+ // Get the database connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+ hr = Access();
+ if (hr != S_OK)
+ return hr;
+ CCommand<CAccessor<CCountAccessor> > command;
+
+ hr = command.Assign(m_szSessionName);
+ if (hr == S_OK)
+ {
+ hr = command.Open(dataconn, m_QueryObj.GetSessionVarCount());
+ if (hr == S_OK)
+ {
+ if (S_OK == (hr = command.MoveFirst()))
+ {
+ *pnCount = command.m_nCount;
+ hr = S_OK;
+ }
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(RemoveAllVariables)() throw()
+ {
+ HRESULT hr = E_UNEXPECTED;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ CCommand<CAccessor<CSessionDataDeleteAll> > command;
+ hr = command.Assign(m_szSessionName);
+ if (hr != S_OK)
+ return hr;
+
+ // delete all session variables
+ hr = command.Open(dataconn, m_QueryObj.GetSessionVarDeleteAllVars(), NULL, NULL, DBGUID_DEFAULT, false);
+ return hr;
+ }
+
+ // Iteration of variables works by taking a snapshot
+ // of the sessions at the point in time BeginVariableEnum
+ // is called, and then keeping an index variable that you use to
+ // move through the snapshot rowset. It is important to know
+ // that the handle returned in phEnum is not thread safe. It
+ // should only be used by the calling thread.
+ STDMETHOD(BeginVariableEnum)(POSITION *pPOS, HSESSIONENUM *phEnum) throw()
+ {
+ HRESULT hr = E_FAIL;
+ if (!pPOS)
+ return E_POINTER;
+
+ if (phEnum)
+ *phEnum = NULL;
+ else
+ return E_POINTER;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // Update the last access time for this session.
+ hr = Access();
+ if (hr != S_OK)
+ return hr;
+
+ // Allocate a new iterator accessor and initialize it's input parameters.
+ iterator_accessor *pIteratorAccessor = NULL;
+ ATLTRYALLOC(pIteratorAccessor = new iterator_accessor);
+ if (!pIteratorAccessor)
+ return E_OUTOFMEMORY;
+
+ hr = pIteratorAccessor->Assign(m_szSessionName, NULL, NULL);
+ if (hr == S_OK)
+ {
+ // execute the command and move to the first row of the recordset.
+ hr = pIteratorAccessor->Open(dataconn,
+ m_QueryObj.GetSessionVarSelectAllVars());
+ if (hr == S_OK)
+ {
+ hr = pIteratorAccessor->MoveFirst();
+ if (hr == S_OK)
+ {
+ *pPOS = (POSITION) INVALID_DB_SESSION_POS + 1;
+ *phEnum = reinterpret_cast<HSESSIONENUM>(pIteratorAccessor);
+ }
+ }
+
+ if (hr != S_OK)
+ {
+ *pPOS = INVALID_DB_SESSION_POS;
+ *phEnum = NULL;
+ delete pIteratorAccessor;
+ }
+ }
+ return hr;
+ }
+
+ // The values for hEnum and pPos must have been initialized in a previous
+ // call to BeginVariableEnum. On success, the out variant will hold the next
+ // variable
+ STDMETHOD(GetNextVariable)(POSITION *pPOS, VARIANT *pVal, HSESSIONENUM hEnum, LPSTR szName=NULL, DWORD dwLen=0) throw()
+ {
+ if (!pPOS)
+ return E_INVALIDARG;
+
+ if (pVal)
+ VariantInit(pVal);
+ else
+ return E_POINTER;
+
+ if (!hEnum)
+ return E_UNEXPECTED;
+
+ if (*pPOS <= INVALID_DB_SESSION_POS)
+ return E_UNEXPECTED;
+
+ iterator_accessor *pIteratorAccessor = reinterpret_cast<iterator_accessor*>(hEnum);
+
+ // update the last access time.
+ HRESULT hr = Access();
+
+ POSITION posCurrent = *pPOS;
+
+ if (szName)
+ {
+ // caller wants entry name
+ _ATLTRY
+ {
+ CT2CA szVarName(pIteratorAccessor->m_VariableName);
+ if (szVarName != NULL && dwLen > Checked::strnlen(szVarName, dwLen))
+ {
+ Checked::strcpy_s(szName, dwLen, szVarName);
+ }
+ else
+ hr = E_OUTOFMEMORY; // buffer not big enough
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ }
+
+ if (hr == S_OK)
+ {
+ CStreamOnByteArray stream(pIteratorAccessor->m_VariableValue);
+ CComVariant vOut;
+ hr = vOut.ReadFromStream(static_cast<IStream*>(&stream));
+ if (hr == S_OK)
+ vOut.Detach(pVal);
+ else
+ return hr;
+ }
+ else
+ return hr;
+
+ hr = pIteratorAccessor->MoveNext();
+ *pPOS = ++posCurrent;
+
+ if (hr == DB_S_ENDOFROWSET)
+ {
+ // We're done iterating, reset everything
+ *pPOS = INVALID_DB_SESSION_POS;
+ hr = S_OK;
+ }
+
+ if (hr != S_OK)
+ {
+ VariantClear(pVal);
+ }
+ return hr;
+ }
+
+ // CloseEnum frees up any resources allocated by the iterator
+ STDMETHOD(CloseEnum)(HSESSIONENUM hEnum) throw()
+ {
+ iterator_accessor *pIteratorAccessor = reinterpret_cast<iterator_accessor*>(hEnum);
+ if (!pIteratorAccessor)
+ return E_INVALIDARG;
+ pIteratorAccessor->Close();
+ delete pIteratorAccessor;
+ return S_OK;
+ }
+
+ //
+ // Returns S_FALSE if it's not expired
+ // S_OK if it is expired and an error HRESULT
+ // if an error occurred.
+ STDMETHOD(IsExpired)() throw()
+ {
+ HRESULT hrRet = S_FALSE;
+ HRESULT hr = E_UNEXPECTED;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ CCommand<CAccessor<CSessionRefIsExpired> > command;
+ hr = command.Assign(m_szSessionName);
+ if (hr != S_OK)
+ return hr;
+
+ hr = command.Open(dataconn, m_QueryObj.GetSessionRefIsExpired(),
+ NULL, NULL, DBGUID_DEFAULT, true);
+ if (hr == S_OK)
+ {
+ if (S_OK == command.MoveFirst())
+ {
+ if (!_tcscmp(command.m_SessionIDOut, m_szSessionName))
+ hrRet = S_OK;
+ }
+ }
+
+ if (hr == S_OK)
+ return hrRet;
+ return hr;
+ }
+
+ STDMETHOD(SetTimeout)(unsigned __int64 dwNewTimeout) throw()
+ {
+ HRESULT hr = E_UNEXPECTED;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // allocate a command and set it's input parameters
+ CCommand<CAccessor<CSessionRefUpdateTimeout> > command;
+ hr = command.Assign(m_szSessionName, dwNewTimeout);
+ if (hr != S_OK)
+ return hr;
+
+ hr = command.Open(dataconn, m_QueryObj.GetSessionRefUpdateTimeout(),
+ NULL, NULL, DBGUID_DEFAULT, false);
+
+ return hr;
+ }
+
+ // SessionLock increments the session reference count for this session.
+ // If there is not a session by this name in the session references table,
+ // a new session entry is created in the the table.
+ HRESULT SessionLock() throw()
+ {
+ HRESULT hr = E_UNEXPECTED;
+ if (!m_szSessionName || m_szSessionName[0]==0)
+ return hr; // no session to lock.
+
+ // retrieve the data connection for this thread
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // first try to update a session with this name
+ DBROWCOUNT nRows = 0;
+ CCommand<CAccessor<CSessionRefUpdator> > updator;
+ if (S_OK == updator.Assign(m_szSessionName))
+ {
+ if (S_OK != (hr = updator.Open(dataconn, m_QueryObj.GetSessionRefAddRef(),
+ NULL, &nRows, DBGUID_DEFAULT, false)) ||
+ nRows == 0)
+ {
+ // No session to update. Use the creator accessor
+ // to create a new session reference.
+ CCommand<CAccessor<CSessionRefCreator> > creator;
+ hr = creator.Assign(m_szSessionName, m_dwTimeout);
+ if (hr == S_OK)
+ hr = creator.Open(dataconn, m_QueryObj.GetSessionRefCreate(),
+ NULL, &nRows, DBGUID_DEFAULT, false);
+ }
+ }
+
+ // We should have been able to create or update a session.
+ ATLASSERT(nRows > 0);
+ if (hr == S_OK && nRows <= 0)
+ hr = E_UNEXPECTED;
+
+ return hr;
+ }
+
+ // SessionUnlock decrements the session RefCount for this session.
+ // Sessions cannot be removed from the database unless the session
+ // refcount is 0
+ HRESULT SessionUnlock() throw()
+ {
+ HRESULT hr = E_UNEXPECTED;
+ if (!m_szSessionName ||
+ m_szSessionName[0]==0)
+ return hr;
+
+ // get the data connection for this thread
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // The session must exist at this point in order to unlock it
+ // so we can just use the session updator here.
+ DBROWCOUNT nRows = 0;
+ CCommand<CAccessor<CSessionRefUpdator> > updator;
+ hr = updator.Assign(m_szSessionName);
+ if (hr == S_OK)
+ {
+ hr = updator.Open( dataconn,
+ m_QueryObj.GetSessionRefRemoveRef(),
+ NULL,
+ &nRows,
+ DBGUID_DEFAULT,
+ false);
+ }
+ if (hr != S_OK)
+ return hr;
+
+ // delete the session from the database if
+ // nobody else is using it and it's expired.
+ hr = FreeSession();
+ return hr;
+ }
+
+ // Access updates the last access time for the session. The access
+ // time for sessions is updated using the SQL GETDATE function on the
+ // database server so that all clients will be using the same clock
+ // to compare access times against.
+ HRESULT Access() throw()
+ {
+ HRESULT hr = E_UNEXPECTED;
+
+ if (!m_szSessionName ||
+ m_szSessionName[0]==0)
+ return hr; // no session to access
+
+ // get the data connection for this thread
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // The session reference entry in the references table must
+ // be created prior to calling this function so we can just
+ // use an updator to update the current entry.
+ CCommand<CAccessor<CSessionRefUpdator> > updator;
+
+ DBROWCOUNT nRows = 0;
+ hr = updator.Assign(m_szSessionName);
+ if (hr == S_OK)
+ {
+ hr = updator.Open( dataconn,
+ m_QueryObj.GetSessionRefAccess(),
+ NULL,
+ &nRows,
+ DBGUID_DEFAULT,
+ false);
+ }
+
+ ATLASSERT(nRows > 0);
+ if (hr == S_OK && nRows <= 0)
+ hr = E_UNEXPECTED;
+ return hr;
+ }
+
+ // If the session is expired and it's reference is 0,
+ // it can be deleted. SessionUnlock calls this function to
+ // unlock the session and delete it after we release a session
+ // lock. Note that our SQL command will only delete the session
+ // if it is expired and it's refcount is <= 0
+ HRESULT FreeSession() throw()
+ {
+ HRESULT hr = E_UNEXPECTED;
+ if (!m_szSessionName ||
+ m_szSessionName[0]==0)
+ return hr;
+
+ // Get the data connection for this thread.
+ CDataConnection dataconn;
+ hr = GetSessionConnection(&dataconn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ CCommand<CAccessor<CSessionRefUpdator> > updator;
+
+ // The SQL for this command only deletes the
+ // session reference from the references table if it's access
+ // count is 0 and it has expired.
+ return updator.Open(dataconn,
+ m_QueryObj.GetSessionRefDelete(),
+ NULL,
+ NULL,
+ DBGUID_DEFAULT,
+ false);
+ }
+
+ // Initialize is called each time a new session is created.
+ HRESULT Initialize( LPCSTR szSessionName,
+ IServiceProvider *pServiceProvider,
+ DWORD_PTR dwCookie,
+ PFN_GETPROVIDERINFO pfnInfo) throw()
+ {
+ if (!szSessionName)
+ return E_INVALIDARG;
+
+ if (!pServiceProvider)
+ return E_INVALIDARG;
+
+ if (!pfnInfo)
+ return E_INVALIDARG;
+
+ m_pfnInfo = pfnInfo;
+ m_dwProvCookie = dwCookie;
+ m_spServiceProvider = pServiceProvider;
+
+ _ATLTRY
+ {
+ CA2CT tcsSessionName(szSessionName);
+ if (Checked::tcsnlen(tcsSessionName, MAX_SESSION_KEY_LEN) < MAX_SESSION_KEY_LEN)
+ Checked::tcscpy_s(m_szSessionName, _countof(m_szSessionName), tcsSessionName);
+ else
+ return E_OUTOFMEMORY;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+ return SessionLock();
+ }
+
+ HRESULT GetSessionConnection(CDataConnection *pConn,
+ IServiceProvider *pProv) throw()
+ {
+ if (!pProv)
+ return E_INVALIDARG;
+
+ if (!m_pfnInfo ||
+ !m_dwProvCookie)
+ return E_UNEXPECTED;
+
+ wchar_t *wszProv = NULL;
+ if (m_pfnInfo(m_dwProvCookie, &wszProv) && wszProv!=NULL)
+ {
+ return GetDataSource(pProv,
+ ATL_DBSESSION_ID,
+ wszProv,
+ pConn);
+ }
+ return E_FAIL;
+ }
+
+
+protected:
+ TCHAR m_szSessionName[MAX_SESSION_KEY_LEN];
+ unsigned __int64 m_dwTimeout;
+ CComPtr<IServiceProvider> m_spServiceProvider;
+ DWORD_PTR m_dwProvCookie;
+ PFN_GETPROVIDERINFO m_pfnInfo;
+ DBQUERYCLASS_TYPE m_QueryObj;
+}; // CDBSession
+
+
+template <class TDBSession=CDBSession<> >
+class CDBSessionServiceImplT
+{
+ wchar_t m_szConnectionString[MAX_CONNECTION_STRING_LEN];
+ CComPtr<IServiceProvider> m_spServiceProvider;
+ typename TDBSession::DBQUERYCLASS_TYPE m_QueryObj;
+public:
+ typedef const wchar_t* SERVICEIMPL_INITPARAM_TYPE;
+ CDBSessionServiceImplT() throw()
+ {
+ m_dwTimeout = ATL_SESSION_TIMEOUT;
+ m_szConnectionString[0] = '\0';
+ }
+
+ static bool GetProviderInfo(DWORD_PTR dwProvCookie, wchar_t **ppszProvInfo) throw()
+ {
+ if (dwProvCookie &&
+ ppszProvInfo)
+ {
+ CDBSessionServiceImplT<TDBSession> *pSvc =
+ reinterpret_cast<CDBSessionServiceImplT<TDBSession>*>(dwProvCookie);
+ *ppszProvInfo = pSvc->m_szConnectionString;
+ return true;
+ }
+ return false;
+ }
+
+ HRESULT GetSessionConnection(CDataConnection *pConn,
+ IServiceProvider *pProv) throw()
+ {
+ if (!pProv)
+ return E_INVALIDARG;
+
+ if(!m_szConnectionString[0])
+ return E_UNEXPECTED;
+
+ return GetDataSource(pProv,
+ ATL_DBSESSION_ID,
+ m_szConnectionString,
+ pConn);
+ }
+
+ HRESULT Initialize(SERVICEIMPL_INITPARAM_TYPE pData,
+ IServiceProvider *pProvider,
+ unsigned __int64 dwInitialTimeout) throw()
+ {
+ if (!pData || !pProvider)
+ return E_INVALIDARG;
+
+ if (Checked::wcsnlen(pData, MAX_CONNECTION_STRING_LEN) < MAX_CONNECTION_STRING_LEN)
+ {
+ Checked::wcscpy_s(m_szConnectionString, _countof(m_szConnectionString), pData);
+ }
+ else
+ return E_OUTOFMEMORY;
+
+ m_dwTimeout = dwInitialTimeout;
+ m_spServiceProvider = pProvider;
+ return S_OK;
+ }
+
+ HRESULT CreateNewSession(__out_ecount_part_z(*pdwSize, *pdwSize) LPSTR szNewID, __inout DWORD *pdwSize, __deref_out ISession** ppSession) throw()
+ {
+ HRESULT hr = E_FAIL;
+ CComObject<TDBSession> *pNewSession = NULL;
+
+ if (!pdwSize)
+ return E_POINTER;
+
+ if (ppSession)
+ *ppSession = NULL;
+ else
+ return E_POINTER;
+
+ if (szNewID)
+ *szNewID = NULL;
+ else
+ return E_INVALIDARG;
+
+
+ // Create new session
+ CComObject<TDBSession>::CreateInstance(&pNewSession);
+ if (pNewSession == NULL)
+ return E_OUTOFMEMORY;
+
+ // Create a session name and initialize the object
+ hr = m_SessionNameGenerator.GetNewSessionName(szNewID, pdwSize);
+ if (hr == S_OK)
+ {
+ hr = pNewSession->Initialize(szNewID,
+ m_spServiceProvider,
+ reinterpret_cast<DWORD_PTR>(this),
+ GetProviderInfo);
+ if (hr == S_OK)
+ {
+ // we don't hold a reference to the object
+ hr = pNewSession->QueryInterface(ppSession);
+ }
+ }
+
+ if (hr != S_OK)
+ delete pNewSession;
+ return hr;
+ }
+
+ HRESULT CreateNewSessionByName(__in_z LPSTR szNewID, __deref_out ISession** ppSession) throw()
+ {
+ HRESULT hr = E_FAIL;
+ CComObject<TDBSession> *pNewSession = NULL;
+
+ if (!szNewID || *szNewID == 0)
+ return E_INVALIDARG;
+
+ if (ppSession)
+ *ppSession = NULL;
+ else
+ return E_POINTER;
+
+ // Create new session
+ CComObject<TDBSession>::CreateInstance(&pNewSession);
+ if (pNewSession == NULL)
+ return E_OUTOFMEMORY;
+
+ hr = pNewSession->Initialize(szNewID,
+ m_spServiceProvider,
+ reinterpret_cast<DWORD_PTR>(this),
+ GetProviderInfo);
+ if (hr == S_OK)
+ {
+ // we don't hold a reference to the object
+ hr = pNewSession->QueryInterface(ppSession);
+ }
+
+
+ if (hr != S_OK)
+ delete pNewSession;
+ return hr;
+ }
+
+
+ HRESULT GetSession(LPCSTR szID, ISession **ppSession) throw()
+ {
+ HRESULT hr = E_FAIL;
+ if (!szID)
+ return E_INVALIDARG;
+
+ if (ppSession)
+ *ppSession = NULL;
+ else
+ return E_POINTER;
+
+ CComObject<TDBSession> *pNewSession = NULL;
+
+ // Check the DB to see if the session ID is a valid session
+ _ATLTRY
+ {
+ CA2CT session(szID);
+ hr = IsValidSession(session);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ if (hr == S_OK)
+ {
+ // Create new session object to represent this session
+ CComObject<TDBSession>::CreateInstance(&pNewSession);
+ if (pNewSession == NULL)
+ return E_OUTOFMEMORY;
+
+ hr = pNewSession->Initialize(szID,
+ m_spServiceProvider,
+ reinterpret_cast<DWORD_PTR>(this),
+ GetProviderInfo);
+ if (hr == S_OK)
+ {
+ // we don't hold a reference to the object
+ hr = pNewSession->QueryInterface(ppSession);
+ }
+ }
+
+ if (hr != S_OK && pNewSession)
+ delete pNewSession;
+ return hr;
+ }
+
+ HRESULT CloseSession(LPCSTR szID) throw()
+ {
+ if (!szID)
+ return E_INVALIDARG;
+
+ CDataConnection conn;
+ HRESULT hr = GetSessionConnection(&conn,
+ m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // set up accessors
+ CCommand<CAccessor<CSessionRefUpdator> > updator;
+ CCommand<CAccessor<CSessionDataDeleteAll> > command;
+ _ATLTRY
+ {
+ CA2CT session(szID);
+ hr = updator.Assign(session);
+ if (hr == S_OK)
+ hr = command.Assign(session);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (hr == S_OK)
+ {
+ // delete all session variables (may not be any!)
+ hr = command.Open(conn,
+ m_QueryObj.GetSessionVarDeleteAllVars(),
+ NULL,
+ NULL,
+ DBGUID_DEFAULT,
+ false);
+ if (hr == S_OK)
+ {
+ DBROWCOUNT nRows = 0;
+ nRows = 0;
+ // delete references in the session references table
+ hr = updator.Open(conn,
+ m_QueryObj.GetSessionRefDeleteFinal(),
+ NULL,
+ &nRows,
+ DBGUID_DEFAULT,
+ false);
+ if (nRows == 0)
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+ }
+
+ HRESULT SetSessionTimeout(unsigned __int64 nTimeout) throw()
+ {
+ // Get the data connection for this thread
+ CDataConnection conn;
+
+ HRESULT hr = GetSessionConnection(&conn, m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // all sessions get the same timeout
+ CCommand<CAccessor<CSetAllTimeouts> > command;
+ hr = command.Assign(nTimeout);
+ if (hr == S_OK)
+ {
+ hr = command.Open(conn, m_QueryObj.GetSessionReferencesSet(),
+ NULL,
+ NULL,
+ DBGUID_DEFAULT,
+ false);
+ if (hr == S_OK)
+ {
+ m_dwTimeout = nTimeout;
+ }
+ }
+ return hr;
+ }
+
+
+ HRESULT GetSessionTimeout(unsigned __int64* pnTimeout) throw()
+ {
+ if (pnTimeout)
+ *pnTimeout = m_dwTimeout;
+ else
+ return E_INVALIDARG;
+
+ return S_OK;
+ }
+
+ HRESULT GetSessionCount(DWORD *pnCount) throw()
+ {
+ if (pnCount)
+ *pnCount = 0;
+ else
+ return E_POINTER;
+
+ CCommand<CAccessor<CSessionRefCount> > command;
+ CDataConnection conn;
+ HRESULT hr = GetSessionConnection(&conn,
+ m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ hr = command.Open(conn,
+ m_QueryObj.GetSessionRefGetCount());
+ if (hr == S_OK)
+ {
+ hr = command.MoveFirst();
+ if (hr == S_OK)
+ {
+ *pnCount = (DWORD)command.m_nCount;
+ }
+ }
+
+ return hr;
+ }
+
+ void ReleaseAllSessions() throw()
+ {
+ // nothing to do
+ }
+
+ void SweepSessions() throw()
+ {
+ // nothing to do
+ }
+
+
+ // Helpers
+ HRESULT IsValidSession(LPCTSTR szID) throw()
+ {
+ if (!szID)
+ return E_INVALIDARG;
+ // Look in the sessionreferences table to see if there is an entry
+ // for this session.
+ if (m_szConnectionString[0] == 0)
+ return E_UNEXPECTED;
+
+ CDataConnection conn;
+ HRESULT hr = GetSessionConnection(&conn,
+ m_spServiceProvider);
+ if (hr != S_OK)
+ return hr;
+
+ // Check the session references table to see if
+ // this is a valid session
+ CCommand<CAccessor<CSessionRefSelector> > selector;
+ hr = selector.Assign(szID);
+ if (hr != S_OK)
+ return hr;
+
+ hr = selector.Open(conn,
+ m_QueryObj.GetSessionRefSelect(),
+ NULL,
+ NULL,
+ DBGUID_DEFAULT,
+ true);
+ if (hr == S_OK)
+ return selector.MoveFirst();
+ return hr;
+ }
+
+ CSessionNameGenerator m_SessionNameGenerator; // Object for generating session names
+ unsigned __int64 m_dwTimeout;
+}; // CDBSessionServiceImplT
+
+typedef CDBSessionServiceImplT<> CDBSessionServiceImpl;
+
+
+
+
+
+//////////////////////////////////////////////////////////////////
+//
+// In-memory persisted session
+//
+//////////////////////////////////////////////////////////////////
+
+// In-memory persisted session service keeps a pointer
+// to the session obejct around in memory. The pointer is
+// contained in a CComPtr, which is stored in a CAtlMap, so
+// we have to have a CElementTraits class for that.
+typedef CComPtr<ISession> SESSIONPTRTYPE;
+
+template<>
+class CElementTraits<SESSIONPTRTYPE> :
+ public CElementTraitsBase<SESSIONPTRTYPE>
+{
+public:
+ static ULONG Hash( INARGTYPE obj ) throw()
+ {
+ return( (ULONG)(ULONG_PTR)obj.p);
+ }
+
+ static BOOL CompareElements( OUTARGTYPE element1, OUTARGTYPE element2 ) throw()
+ {
+ return element1.IsEqualObject(element2.p) ? TRUE : FALSE;
+ }
+
+ static int CompareElementsOrdered( INARGTYPE , INARGTYPE ) throw()
+ {
+ ATLASSERT(0); // NOT IMPLEMENTED
+ return 0;
+ }
+};
+
+
+// CMemSession
+// This session persistance class persists session variables in memory.
+// Note that this type of persistance should only be used on single server
+// web sites.
+class CMemSession :
+ public ISession,
+ public CComObjectRootEx<CComGlobalsThreadModel>
+{
+public:
+ BEGIN_COM_MAP(CMemSession)
+ COM_INTERFACE_ENTRY(ISession)
+ END_COM_MAP()
+
+ CMemSession() throw(...)
+ {
+ }
+ virtual ~CMemSession()
+ {
+ }
+
+ STDMETHOD(GetVariable)(LPCSTR szName, VARIANT *pVal) throw()
+ {
+ if (!szName)
+ return E_INVALIDARG;
+
+ if (pVal)
+ VariantInit(pVal);
+ else
+ return E_POINTER;
+
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ hr = E_FAIL;
+ _ATLTRY
+ {
+ CComVariant val;
+ if (m_Variables.Lookup(szName, val))
+ {
+ hr = VariantCopy(pVal, &val);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(SetVariable)(LPCSTR szName, VARIANT vNewVal) throw()
+ {
+ if (!szName)
+ return E_INVALIDARG;
+
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ _ATLTRY
+ {
+ hr = m_Variables.SetAt(szName, vNewVal) ? S_OK : E_FAIL;
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(RemoveVariable)(LPCSTR szName) throw()
+ {
+ if (!szName)
+ return E_INVALIDARG;
+
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ _ATLTRY
+ {
+ hr = m_Variables.RemoveKey(szName) ? S_OK : E_FAIL;
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(GetCount)(long *pnCount) throw()
+ {
+ if (pnCount)
+ *pnCount = 0;
+ else
+ return E_POINTER;
+
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ *pnCount = (long) m_Variables.GetCount();
+ }
+ return hr;
+ }
+
+ STDMETHOD(RemoveAllVariables)() throw()
+ {
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ m_Variables.RemoveAll();
+ }
+
+ return hr;
+ }
+
+ STDMETHOD(BeginVariableEnum)(POSITION *pPOS, HSESSIONENUM *phEnumHandle=NULL) throw()
+ {
+ if (phEnumHandle)
+ *phEnumHandle = NULL;
+
+ if (pPOS)
+ *pPOS = NULL;
+ else
+ return E_POINTER;
+
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ *pPOS = m_Variables.GetStartPosition();
+ }
+ return hr;
+ }
+
+ STDMETHOD(GetNextVariable)(POSITION *pPOS, VARIANT *pVal,
+ HSESSIONENUM hEnum=NULL,
+ LPSTR szName=NULL,
+ DWORD dwLen=0 ) throw()
+ {
+ (hEnum); // Unused!
+ if (pVal)
+ VariantInit(pVal);
+ else
+ return E_POINTER;
+
+ if (!pPOS)
+ return E_POINTER;
+
+ CComVariant val;
+ POSITION pos = *pPOS;
+ HRESULT hr = Access();
+ if (hr == S_OK)
+ {
+ CSLockType lock(m_cs, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ hr = E_FAIL;
+ _ATLTRY
+ {
+ if (szName)
+ {
+ CStringA strName = m_Variables.GetKeyAt(pos);
+ if (strName.GetLength())
+ {
+ if (dwLen > (DWORD)strName.GetLength())
+ {
+ Checked::strcpy_s(szName, dwLen, strName);
+ hr = S_OK;
+ }
+ else
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ else
+ hr = S_OK;
+
+ if (hr == S_OK)
+ {
+ val = m_Variables.GetNextValue(pos);
+ hr = VariantCopy(pVal, &val);
+ if (hr == S_OK)
+ *pPOS = pos;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+ }
+
+ STDMETHOD(CloseEnum)(HSESSIONENUM /*hEnumHandle*/) throw()
+ {
+ return S_OK;
+ }
+
+ STDMETHOD(IsExpired)() throw()
+ {
+ CTime tmNow = CTime::GetCurrentTime();
+ CTimeSpan span = tmNow-m_tLastAccess;
+ if ((unsigned __int64)((span.GetTotalSeconds()*1000)) > m_dwTimeout)
+ return S_OK;
+ return S_FALSE;
+ }
+
+ HRESULT Access() throw()
+ {
+ // We lock here to protect against multiple threads
+ // updating the same member concurrently.
+ CSLockType lock(m_cs, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ m_tLastAccess = CTime::GetCurrentTime();
+ return S_OK;
+ }
+
+ STDMETHOD(SetTimeout)(unsigned __int64 dwNewTimeout) throw()
+ {
+ // We lock here to protect against multiple threads
+ // updating the same member concurrently
+ CSLockType lock(m_cs, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ m_dwTimeout = dwNewTimeout;
+ return S_OK;
+ }
+
+ HRESULT SessionLock() throw()
+ {
+ Access();
+ return S_OK;
+ }
+
+ HRESULT SessionUnlock() throw()
+ {
+ return S_OK;
+ }
+
+protected:
+ typedef CAtlMap<CStringA,
+ CComVariant,
+ CStringElementTraits<CStringA> > VarMapType;
+ unsigned __int64 m_dwTimeout;
+ CTime m_tLastAccess;
+ VarMapType m_Variables;
+ CComAutoCriticalSection m_cs;
+ typedef CComCritSecLock<CComAutoCriticalSection> CSLockType;
+}; // CMemSession
+
+
+//
+// CMemSessionServiceImpl
+// Implements the service part of in-memory persisted session services.
+//
+class CMemSessionServiceImpl
+{
+public:
+ typedef void* SERVICEIMPL_INITPARAM_TYPE;
+ CMemSessionServiceImpl() throw()
+ {
+ m_dwTimeout = ATL_SESSION_TIMEOUT;
+ }
+
+ ~CMemSessionServiceImpl() throw()
+ {
+ m_CritSec.Term();
+ }
+
+ HRESULT CreateNewSession(__out_ecount_part_z(*pdwSize, *pdwSize) LPSTR szNewID, __inout DWORD *pdwSize, __deref_out_opt ISession** ppSession) throw()
+ {
+ HRESULT hr = E_FAIL;
+ CComObject<CMemSession> *pNewSession = NULL;
+
+ if (!szNewID)
+ return E_INVALIDARG;
+
+ if (!pdwSize)
+ return E_POINTER;
+
+ if (ppSession)
+ *ppSession = NULL;
+ else
+ return E_POINTER;
+
+ _ATLTRY
+ {
+ // Create new session
+ CComObject<CMemSession>::CreateInstance(&pNewSession);
+ if (pNewSession == NULL)
+ return E_OUTOFMEMORY;
+
+ // Initialize and add to list of CSessionData
+ hr = m_SessionNameGenerator.GetNewSessionName(szNewID, pdwSize);
+
+ if (SUCCEEDED(hr))
+ {
+ CComPtr<ISession> spSession;
+ hr = pNewSession->QueryInterface(&spSession);
+ if (SUCCEEDED(hr))
+ {
+ pNewSession->SetTimeout(m_dwTimeout);
+ pNewSession->Access();
+ CSLockType lock(m_CritSec, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ hr = m_Sessions.SetAt(szNewID, spSession) != NULL ? S_OK : E_FAIL;
+ if (hr == S_OK)
+ *ppSession = spSession.Detach();
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+
+ return hr;
+
+ }
+
+ HRESULT CreateNewSessionByName(__in_z LPSTR szNewID, __deref_out_opt ISession** ppSession) throw()
+ {
+ HRESULT hr = E_FAIL;
+ CComObject<CMemSession> *pNewSession = NULL;
+
+ if (!szNewID || *szNewID == 0)
+ return E_INVALIDARG;
+
+ if (ppSession)
+ *ppSession = NULL;
+ else
+ return E_POINTER;
+
+ CComPtr<ISession> spSession;
+ // If the session already exists, you get a pointer to the
+ // existing session. You can't have multiple entries with the
+ // same name in CAtlMap
+ hr = GetSession(szNewID, &spSession);
+ if (hr == S_OK)
+ {
+ *ppSession = spSession.Detach();
+ return hr;
+ }
+
+ _ATLTRY
+ {
+ // Create new session
+ CComObject<CMemSession>::CreateInstance(&pNewSession);
+ if (pNewSession == NULL)
+ return E_OUTOFMEMORY;
+
+
+ hr = pNewSession->QueryInterface(&spSession);
+ if (SUCCEEDED(hr))
+ {
+ pNewSession->SetTimeout(m_dwTimeout);
+ pNewSession->Access();
+ CSLockType lock(m_CritSec, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ hr = m_Sessions.SetAt(szNewID, spSession) != NULL ? S_OK : E_FAIL;
+
+ if (hr == S_OK)
+ *ppSession = spSession.Detach();
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+
+ return hr;
+
+ }
+
+ HRESULT GetSession(LPCSTR szID, ISession **ppSession) throw()
+ {
+ HRESULT hr = E_FAIL;
+ SessMapType::CPair *pPair = NULL;
+
+ if (ppSession)
+ *ppSession = NULL;
+ else
+ return E_POINTER;
+
+ if (!szID)
+ return E_INVALIDARG;
+
+ CSLockType lock(m_CritSec, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ hr = E_FAIL;
+ _ATLTRY
+ {
+ pPair = m_Sessions.Lookup(szID);
+ if (pPair) // the session exists and is in our local map of sessions
+ {
+ hr = pPair->m_value.QueryInterface(ppSession);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_UNEXPECTED;
+ }
+
+ return hr;
+ }
+
+ HRESULT CloseSession(LPCSTR szID) throw()
+ {
+ if (!szID)
+ return E_INVALIDARG;
+
+ HRESULT hr = E_FAIL;
+ CSLockType lock(m_CritSec, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ _ATLTRY
+ {
+ hr = m_Sessions.RemoveKey(szID) ? S_OK : E_UNEXPECTED;
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_UNEXPECTED;
+ }
+ return hr;
+ }
+
+ void SweepSessions() throw()
+ {
+ POSITION posRemove = NULL;
+ const SessMapType::CPair *pPair = NULL;
+ POSITION pos = NULL;
+
+ CSLockType lock(m_CritSec, false);
+ if (FAILED(lock.Lock()))
+ return;
+ pos = m_Sessions.GetStartPosition();
+ while (pos)
+ {
+ posRemove = pos;
+ pPair = m_Sessions.GetNext(pos);
+ if (pPair)
+ {
+ if (pPair->m_value.p &&
+ S_OK == pPair->m_value->IsExpired())
+ {
+ // remove our reference on the session
+ m_Sessions.RemoveAtPos(posRemove);
+ }
+ }
+ }
+ }
+
+ HRESULT SetSessionTimeout(unsigned __int64 nTimeout) throw()
+ {
+ HRESULT hr = S_OK;
+ CComPtr<ISession> spSession;
+ m_dwTimeout = nTimeout;
+
+ CSLockType lock(m_CritSec, false);
+ hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+
+ POSITION pos = m_Sessions.GetStartPosition();
+ if (!pos)
+ return S_OK; // no sessions to set the timeout on
+
+
+ while (pos)
+ {
+ SessMapType::CPair *pPair = const_cast<SessMapType::CPair*>(m_Sessions.GetNext(pos));
+ if (pPair)
+ {
+ spSession = pPair->m_value;
+ if (spSession)
+ {
+ // if we fail on any of the sets we will return the
+ // error code immediately
+ hr = spSession->SetTimeout(nTimeout);
+ spSession.Release();
+ if (hr != S_OK)
+ break;
+ }
+ else
+ {
+ hr = E_UNEXPECTED;
+ break;
+ }
+ }
+ else
+ {
+ hr = E_UNEXPECTED;
+ break;
+ }
+ }
+
+ return hr;
+ }
+
+ HRESULT GetSessionTimeout(unsigned __int64* pnTimeout) throw()
+ {
+ if (pnTimeout)
+ *pnTimeout = m_dwTimeout;
+ else
+ return E_POINTER;
+
+ return S_OK;
+ }
+
+ HRESULT GetSessionCount(DWORD *pnCount) throw()
+ {
+ if (pnCount)
+ *pnCount = 0;
+ else
+ return E_POINTER;
+
+ CSLockType lock(m_CritSec, false);
+ HRESULT hr = lock.Lock();
+ if (FAILED(hr))
+ return hr;
+ *pnCount = (DWORD)m_Sessions.GetCount();
+
+ return S_OK;
+ }
+
+ void ReleaseAllSessions() throw()
+ {
+ CSLockType lock(m_CritSec, false);
+ if (FAILED(lock.Lock()))
+ return;
+ m_Sessions.RemoveAll();
+ }
+
+ HRESULT Initialize(SERVICEIMPL_INITPARAM_TYPE,
+ IServiceProvider*,
+ unsigned __int64 dwNewTimeout) throw()
+ {
+ m_dwTimeout = dwNewTimeout;
+ return m_CritSec.Init();
+ }
+
+ typedef CAtlMap<CStringA,
+ SESSIONPTRTYPE,
+ CStringElementTraits<CStringA>,
+ CElementTraitsBase<SESSIONPTRTYPE> > SessMapType;
+
+ SessMapType m_Sessions; // map for holding sessions in memory
+ CComCriticalSection m_CritSec; // for synchronizing access to map
+ typedef CComCritSecLock<CComCriticalSection> CSLockType;
+ CSessionNameGenerator m_SessionNameGenerator; // Object for generating session names
+ unsigned __int64 m_dwTimeout;
+}; // CMemSessionServiceImpl
+
+
+
+//
+// CSessionStateService
+// This class implements the session state service which can be
+// exposed to request handlers.
+//
+// Template Parameters:
+// MonitorClass: Provides periodic sweeping services for the session service class.
+// TServiceImplClass: The class that actually implements the methods of the
+// ISessionStateService and ISessionStateControl interfaces.
+template <class MonitorClass, class TServiceImplClass >
+class CSessionStateService :
+ public ISessionStateService,
+ public ISessionStateControl,
+ public IWorkerThreadClient,
+ public CComObjectRootEx<CComGlobalsThreadModel>
+{
+protected:
+ MonitorClass m_Monitor;
+ HANDLE m_hTimer;
+ CComPtr<IServiceProvider> m_spServiceProvider;
+ TServiceImplClass m_SessionServiceImpl;
+public:
+ // Construction/Initialization
+ CSessionStateService() throw() :
+ m_hTimer(NULL)
+ {
+
+ }
+ ~CSessionStateService() throw()
+ {
+ ATLASSUME(m_hTimer == NULL);
+ }
+ BEGIN_COM_MAP(CSessionStateService)
+ COM_INTERFACE_ENTRY(ISessionStateService)
+ COM_INTERFACE_ENTRY(ISessionStateControl)
+ END_COM_MAP()
+
+// ISessionStateServie methods
+ STDMETHOD(CreateNewSession)(LPSTR szNewID, DWORD *pdwSize, ISession** ppSession) throw()
+ {
+ return m_SessionServiceImpl.CreateNewSession(szNewID, pdwSize, ppSession);
+ }
+
+ STDMETHOD(CreateNewSessionByName)(LPSTR szNewID, ISession** ppSession) throw()
+ {
+ return m_SessionServiceImpl.CreateNewSessionByName(szNewID, ppSession);
+ }
+
+ STDMETHOD(GetSession)(LPCSTR szID, ISession **ppSession) throw()
+ {
+ return m_SessionServiceImpl.GetSession(szID, ppSession);
+ }
+
+ STDMETHOD(CloseSession)(LPCSTR szSessionID) throw()
+ {
+ return m_SessionServiceImpl.CloseSession(szSessionID);
+ }
+
+ STDMETHOD(SetSessionTimeout)(unsigned __int64 nTimeout) throw()
+ {
+ return m_SessionServiceImpl.SetSessionTimeout(nTimeout);
+ }
+
+ STDMETHOD(GetSessionTimeout)(unsigned __int64 *pnTimeout) throw()
+ {
+ return m_SessionServiceImpl.GetSessionTimeout(pnTimeout);
+ }
+
+ STDMETHOD(GetSessionCount)(DWORD *pnSessionCount) throw()
+ {
+ return m_SessionServiceImpl.GetSessionCount(pnSessionCount);
+ }
+
+ void SweepSessions() throw()
+ {
+ m_SessionServiceImpl.SweepSessions();
+ }
+
+ void ReleaseAllSessions() throw()
+ {
+ m_SessionServiceImpl.ReleaseAllSessions();
+ }
+
+ HRESULT Initialize(
+ IServiceProvider *pServiceProvider = NULL,
+ typename TServiceImplClass::SERVICEIMPL_INITPARAM_TYPE pInitData = NULL,
+ unsigned __int64 dwTimeout = ATL_SESSION_TIMEOUT) throw()
+ {
+ HRESULT hr = S_OK;
+ if (pServiceProvider)
+ m_spServiceProvider = pServiceProvider;
+
+ hr = m_SessionServiceImpl.Initialize(pInitData, pServiceProvider, dwTimeout);
+
+ return hr;
+ }
+
+
+ template <class ThreadTraits>
+ HRESULT Initialize(
+ CWorkerThread<ThreadTraits> *pWorker,
+ IServiceProvider *pServiceProvider = NULL,
+ typename TServiceImplClass::SERVICEIMPL_INITPARAM_TYPE pInitData = NULL,
+ unsigned __int64 dwTimeout = ATL_SESSION_TIMEOUT) throw()
+ {
+ if (!pWorker)
+ return E_INVALIDARG;
+
+ HRESULT hr = Initialize(pServiceProvider, pInitData, dwTimeout);
+ if (hr == S_OK)
+ {
+ hr = m_Monitor.Initialize(pWorker);
+ if (hr == S_OK)
+ {
+ //sweep every 500ms
+ hr = m_Monitor.AddTimer(ATL_SESSION_SWEEPER_TIMEOUT, this, 0, &m_hTimer);
+ }
+ }
+ return hr;
+ }
+
+ void Shutdown() throw()
+ {
+ if (m_hTimer)
+ {
+ if(FAILED(m_Monitor.RemoveHandle(m_hTimer)))
+ {
+ /* can't report from here */
+ ATLASSERT(FALSE);
+ }
+ m_hTimer = NULL;
+ }
+ ReleaseAllSessions();
+ }
+// Implementation
+ HRESULT Execute(DWORD_PTR /*dwParam*/, HANDLE /*hObject*/) throw()
+ {
+ SweepSessions();
+ return S_OK;
+ }
+
+ HRESULT CloseHandle(HANDLE hHandle) throw()
+ {
+ ::CloseHandle(hHandle);
+ m_hTimer = NULL;
+ return S_OK;
+ }
+
+}; // CSessionStateService
+
+} // namespace ATL
+#pragma pack(pop)
+
+#pragma warning(pop)
+#endif // __ATLSESSION_H__
diff --git a/include/atl/atlsharedsvc.h b/include/atl/atlsharedsvc.h
new file mode 100644
index 000000000..b309f12c6
--- /dev/null
+++ b/include/atl/atlsharedsvc.h
@@ -0,0 +1,202 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSHAREDSVC_H__
+#define __ATLSHAREDSVC_H__
+
+#pragma once
+
+#include <atltime.h>
+#include <atlsoap.h>
+#pragma pack(push,_ATL_PACKING)
+namespace ATL{
+
+#ifndef ATL_SHAREDBLOBCACHE_TIMEOUT
+ #define ATL_SHAREDBLOBCACHE_TIMEOUT 36000000000 // in 100 nano second intervals
+ // each entry will be free'd if
+ // no access in 1 hour.
+#endif
+
+// Interface used by to access the shared blob cache.
+[ uuid("AB4AF9CD-8DB1-4974-A617-CF0449578FB9"), object ]
+__interface ISharedBlobCache
+{
+ [id(0)] STDMETHOD(AddItem)([in] BSTR szItemName, [in] BSTR szData);
+ [id(1)] STDMETHOD(GetItem)([in] BSTR szItemName, [out,retval] BSTR *szData);
+};
+
+class CSharedCache:
+ public CBlobCache<CWorkerThread<>, CStdStatClass >,
+ public IMemoryCacheClient,
+ public ISharedBlobCache
+{
+ typedef CBlobCache<CWorkerThread<>, CStdStatClass > basecache;
+public:
+
+ // IMemoryCacheClient method, frees data in the memory cache.
+ STDMETHOD( Free )(const void *pvData)
+ {
+ if (pvData)
+ {
+ ::SysFreeString((BSTR)pvData);
+ }
+ return S_OK;
+ }
+
+
+ STDMETHODIMP AddItem(BSTR szItemName, BSTR szData)
+ {
+
+ HRESULT hr = E_UNEXPECTED;
+
+ // We make a copy of the BSTR and stick it in the cache.
+ // The BSTR will be freed in our IMemoryCacheClient::Free
+ // implementation above.
+ BSTR szEntry = SysAllocString(szData);
+ if(szEntry)
+ {
+ USES_CONVERSION_EX;
+ // create a time span and for the entry
+ CFileTime tm = CFileTime::GetCurrentTime();
+ CFileTimeSpan span;
+ span.SetTimeSpan(ATL_SHAREDBLOBCACHE_TIMEOUT);
+ tm += span;
+ HCACHEITEM h;
+ hr = basecache::Add(OLE2A_EX(szItemName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD), szEntry, sizeof(BSTR),
+ &tm, _AtlBaseModule.m_hInst, &h, static_cast<IMemoryCacheClient*>(this));
+
+ if (hr == S_OK)
+ {
+ // On successful add, we have to release our
+ // reference on the entry.
+ basecache::ReleaseEntry(h);
+ }
+ }
+ return hr;
+ }
+
+ STDMETHODIMP GetItem(BSTR szItemName, BSTR *szData)
+ {
+ USES_CONVERSION_EX;
+ HRESULT hr = E_UNEXPECTED;
+ HCACHEITEM hEntry = NULL;
+
+ if (!szItemName || !szData)
+ return hr;
+
+ hr = basecache::LookupEntry(OLE2A_EX(szItemName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD), &hEntry);
+ if (hr == S_OK)
+ {
+ void *pData = NULL;
+ DWORD dwSize = 0;
+ hr = basecache::GetData(hEntry, &pData, &dwSize);
+ if (hr == S_OK)
+ {
+ // make a copy of the string
+ *szData = ::SysAllocString((BSTR)pData);
+ }
+ basecache::ReleaseEntry(hEntry);
+ }
+ return hr;
+ }
+
+
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
+ {
+ HRESULT hr = E_NOINTERFACE;
+ if (InlineIsEqualGUID(__uuidof(IMemoryCacheClient), riid)||
+ InlineIsEqualGUID(__uuidof(IUnknown), riid))
+ {
+ *ppv = static_cast<void*>(static_cast<IMemoryCacheClient*>(this));
+ hr = S_OK;
+ }
+ else if( InlineIsEqualGUID(__uuidof(ISharedBlobCache), riid))
+ {
+ *ppv = static_cast<void*>(static_cast<ISharedBlobCache*>(this));
+ hr = S_OK;
+ }
+ return hr;
+ }
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return 1;
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ return 1;
+ }
+};
+
+
+// This class implements the SOAP interface for the shared blob cache.
+[
+ soap_handler(
+ name="SharedBlobCache",
+ namespace="http://www.microsoft.com/vc/atlserver/soap/SharedBlobCache",
+ protocol="soap"
+ ),
+ request_handler(
+ name="SharedBlobCache",
+ sdl="GenSharedBlobCacheWSDL"
+ )
+]
+class CSharedCacheHandler:
+ public ISharedBlobCache
+{
+public:
+ [soap_method]
+ STDMETHOD(AddItem)(BSTR szItemName, BSTR szData)
+ {
+ if (!m_spMemCache)
+ return E_UNEXPECTED;
+ return m_spMemCache->AddItem(szItemName, szData);
+ }
+
+ [soap_method]
+ STDMETHOD(GetItem)(BSTR szItemName, BSTR *szData)
+ {
+ if (!m_spMemCache)
+ return E_UNEXPECTED;
+ return m_spMemCache->GetItem(szItemName, szData);
+ }
+
+ HTTP_CODE Initialize(IServiceProvider *pProvider)
+ {
+ ATLASSERT(pProvider); // should never be NULL
+ if (!pProvider)
+ return HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+
+ if (m_spMemCache)
+ return HTTP_SUCCESS; // already initialized
+
+ pProvider->QueryService(__uuidof(ISharedBlobCache), &m_spMemCache);
+ return m_spMemCache ? HTTP_SUCCESS : HTTP_ERROR(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+ // override HandleRequest to Initialize our m_spServiceProvider
+ // and to handle authorizing the client.
+ HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
+ {
+ HTTP_CODE dwErr = Initialize(pProvider);
+ if (dwErr != HTTP_SUCCESS)
+ return dwErr;
+
+ dwErr = CSoapHandler<CSharedCacheHandler>::HandleRequest(pRequestInfo,
+ pProvider);
+ return dwErr;
+ }
+ CComPtr<ISharedBlobCache> m_spMemCache;
+};
+
+} //ATL
+
+#pragma pack(pop)
+
+#endif // __ATLSHAREDSVC_H__
diff --git a/include/atl/atlsiface.h b/include/atl/atlsiface.h
new file mode 100644
index 000000000..08480225a
--- /dev/null
+++ b/include/atl/atlsiface.h
@@ -0,0 +1,802 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSIFACE_H__
+#define __ATLSIFACE_H__
+
+#pragma once
+#include <atlcoll.h>
+#include <httpext.h>
+#include <atlserr.h>
+#include <atlcom.h>
+#include <string.h>
+#include <atlpath.h>
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL{
+
+// Forward declarations of custom data types used in
+// interfaces declared in this file.
+struct AtlServerRequest;
+class CIsapiWorker;
+__interface IAtlMemMgr;
+class CCookie;
+
+// Forward declarations of all interfaces declared in this file.
+__interface IWriteStream;
+__interface IHttpFile;
+__interface IHttpServerContext;
+__interface IHttpRequestLookup;
+__interface IRequestHandler;
+__interface ITagReplacer;
+__interface IIsapiExtension;
+__interface IPageCacheControl;
+__interface IRequestStats;
+__interface IBrowserCaps;
+__interface IBrowserCapsSvc;
+
+
+// ATLS Interface declarations.
+
+// IWriteStream
+// Interface for writing to a stream.
+__interface IWriteStream
+{
+ HRESULT WriteStream(LPCSTR szOut, int nLen, DWORD *pdwWritten);
+ HRESULT FlushStream();
+};
+
+// IHttpFile
+// This is an interface that provides for basic accessor
+// functionality for files (see CHttpRequestFile).
+__interface IHttpFile
+{
+ LPCSTR GetParamName();
+ LPCSTR GetFileName();
+ LPCSTR GetFullFileName();
+ LPCSTR GetContentType();
+ LPCSTR GetTempFileName();
+ ULONGLONG GetFileSize();
+ void Free();
+};
+
+// IHttpServerContext
+// This interface encapsulates the capabilities of the web server and provides information about
+// the current request being handled. See CServerContext for implementation.
+__interface ATL_NO_VTABLE __declspec(uuid("813F3F00-3881-11d3-977B-00C04F8EE25E"))
+ IHttpServerContext : public IUnknown
+{
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ LPCSTR GetRequestMethod();
+ LPCSTR GetQueryString();
+ LPCSTR GetPathInfo();
+ LPCSTR GetPathTranslated();
+ LPCSTR GetScriptPathTranslated();
+ DWORD GetTotalBytes();
+ DWORD GetAvailableBytes();
+ BYTE *GetAvailableData();
+ LPCSTR GetContentType();
+ BOOL GetServerVariable(__in_z LPCSTR pszVariableName,
+ __out_ecount_part_opt(*pdwSize, *pdwSize) LPSTR pvBuffer, __inout DWORD *pdwSize);
+ BOOL GetImpersonationToken(HANDLE * pToken);
+ BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes);
+ BOOL AsyncWriteClient(void *pvBuffer, DWORD *pdwBytes);
+ BOOL ReadClient(void *pvBuffer, DWORD *pdwSize);
+ BOOL AsyncReadClient(void *pvBuffer, DWORD *pdwSize);
+ BOOL SendRedirectResponse(LPCSTR pszRedirectUrl);
+ BOOL SendResponseHeader(LPCSTR pszHeader, LPCSTR pszStatusCode,
+ BOOL fKeepConn);
+ BOOL DoneWithSession(DWORD dwHttpStatusCode);
+ BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION pfn, DWORD *pdwContext);
+ BOOL TransmitFile(HANDLE hFile, PFN_HSE_IO_COMPLETION pfn, void *pContext,
+ LPCSTR szStatusCode, DWORD dwBytesToWrite, DWORD dwOffset,
+ void *pvHead, DWORD dwHeadLen, void *pvTail,
+ DWORD dwTailLen, DWORD dwFlags);
+ BOOL AppendToLog(LPCSTR szMessage, DWORD* pdwLen);
+ BOOL MapUrlToPathEx(LPCSTR szLogicalPath, DWORD dwLen, HSE_URL_MAPEX_INFO *pumInfo);
+};
+
+// IHttpRequestLookup
+// This interface is designed to allow one map to chain to another map.
+// The interface is implemented by the CHttpThunkMap and CHttpRequest classes.
+// Pointers to this interface are passed around by CRequestHandlerT and CHtmlTagReplacer.
+// dwType - the type of item being requested
+__interface ATL_NO_VTABLE __declspec(uuid("A5990B44-FF74-4bfe-B66D-F9E7E9F42D42"))
+ IHttpRequestLookup : public IUnknown
+{
+ POSITION GetFirstQueryParam(LPCSTR *ppszName, LPCSTR *ppszValue);
+ POSITION GetNextQueryParam(POSITION pos, LPCSTR *ppszName, LPCSTR *ppszValue);
+
+ POSITION GetFirstFormVar(LPCSTR *ppszName, LPCSTR *ppszValue);
+ POSITION GetNextFormVar(POSITION pos, LPCSTR *ppszName, LPCSTR *ppszValue);
+
+ POSITION GetFirstFile(LPCSTR *ppszName, IHttpFile **ppFile);
+ POSITION GetNextFile(POSITION pos, LPCSTR *ppszName, IHttpFile **ppFile);
+
+ HRESULT GetServerContext(IHttpServerContext **ppOut);
+};
+
+
+// IRequestHandler
+// This interface is impelemented by clients who want to be request handlers in an
+// atl server application. Server default implementations are provided in ATL, including
+// IRequestHandlerImpl (atlisapi.h) and CRequestHandlerT (atlstencil.h)
+__interface ATL_NO_VTABLE __declspec(uuid("D57F8D0C-751A-4223-92BC-0B29F65D2453"))
+IRequestHandler : public IUnknown
+{
+ HTTP_CODE GetFlags(DWORD *pdwStatus);
+ HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider);
+ HTTP_CODE InitializeChild(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider, IHttpRequestLookup *pLookup);
+ HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider);
+ void UninitializeHandler();
+};
+
+// ITagReplacer
+// This interface defines the methods necessary for server response file processing.
+__interface ATL_NO_VTABLE __declspec(uuid("8FF5E90C-8CE0-43aa-96C4-3BF930837512"))
+ ITagReplacer : public IUnknown
+{
+ HTTP_CODE FindReplacementOffset(LPCSTR szMethodName, DWORD *pdwMethodOffset,
+ LPCSTR szObjectName, DWORD *pdwObjOffset, DWORD *pdwMap, void **ppvParam, IAtlMemMgr *pMemMgr);
+ HTTP_CODE RenderReplacement(DWORD dwFnOffset, DWORD dwObjOffset, DWORD dwMap, void *pvParam);
+ HRESULT GetContext(REFIID riid, void** ppv);
+ IWriteStream *SetStream(IWriteStream *pStream);
+};
+
+
+struct CStencilState;
+
+// IIsapiExtension
+// Tnis is the interface to the ISAPI extension of a running ATL Server web
+// application. Provides request handler clients with access to functions of the
+// ISAPI server.
+__interface __declspec(uuid("79DD4A27-D820-4fa6-954D-E1DFC2C05978"))
+ IIsapiExtension : public IUnknown
+{
+ BOOL DispatchStencilCall(AtlServerRequest *pRequestInfo);
+ void RequestComplete(AtlServerRequest *pRequestInfo, DWORD hStatus, DWORD dwSubStatus);
+ BOOL OnThreadAttach();
+ void OnThreadTerminate();
+ BOOL QueueRequest(AtlServerRequest *pRequestInfo);
+ CIsapiWorker *GetThreadWorker();
+ BOOL SetThreadWorker(CIsapiWorker *pWorker);
+ HTTP_CODE LoadRequestHandler(LPCSTR szDllPath, LPCSTR szHandlerName, IHttpServerContext *pServerContext,
+ HINSTANCE *phInstance, IRequestHandler **ppHandler);
+ HRESULT AddService(REFGUID guidService, REFIID riid, IUnknown *punk, HINSTANCE hInstance);
+ HRESULT RemoveService(REFGUID guidService, REFIID riid);
+ HTTP_CODE LoadDispatchFile(LPCSTR szFileName, AtlServerRequest *pRequestInfo);
+
+ AtlServerRequest* CreateRequest();
+ void FreeRequest(AtlServerRequest* pRequest);
+ HTTP_CODE TransferRequest(
+ AtlServerRequest *pRequest,
+ IServiceProvider *pServiceProvider,
+ IWriteStream *pWriteStream,
+ IHttpRequestLookup *pLookup,
+ LPCSTR szNewUrl,
+ WORD nCodePage,
+ bool bContinueAfterProcess,
+ CStencilState *pState);
+};
+
+// IPageCacheControl
+// This interface controls the cacheability of the current page
+__interface ATL_NO_VTABLE __declspec(uuid("9868BFC0-D44D-4154-931C-D186EC0C45D5"))
+ IPageCacheControl : public IUnknown
+{
+ HRESULT GetExpiration(FILETIME *pftExpiration);
+ HRESULT SetExpiration(FILETIME ftExpiration);
+ BOOL IsCached();
+ BOOL Cache(BOOL bCache);
+};
+
+// IRequestStats
+// Used to query request statistics from a running ATL server ISAPI application.
+__interface ATL_NO_VTABLE __declspec(uuid("2B75C68D-0DDF-48d6-B58A-CC7C2387A6F2"))
+ IRequestStats : public IUnknown
+{
+ long GetTotalRequests();
+ long GetFailedRequests();
+ long GetAvgResponseTime();
+ long GetCurrWaiting();
+ long GetMaxWaiting();
+ long GetActiveThreads();
+};
+
+// IBrowserCaps
+// Interface that provides information about a particular web brorwser.
+// See atlutil.h and the ATL Browser Capabilities service for information
+// about this interface's implementation
+__interface __declspec(uuid("3339FCE2-99BC-4985-A702-4ABC8304A995"))
+ IBrowserCaps : public IUnknown
+{
+ HRESULT GetPropertyString(BSTR bstrProperty, BSTR * pbstrOut);
+ HRESULT GetBooleanPropertyValue(BSTR bstrProperty, BOOL* pbOut);
+ HRESULT GetBrowserName(BSTR * pbstrName);
+ HRESULT GetPlatform(BSTR * pbstrPlatform);
+ HRESULT GetVersion(BSTR * pbstrVersion);
+ HRESULT GetMajorVer(BSTR * pbstrMajorVer);
+ HRESULT GetMinorVer(BSTR * pbstrMinorVer);
+ HRESULT SupportsFrames(BOOL* pbFrames);
+ HRESULT SupportsTables(BOOL* pbTables);
+ HRESULT SupportsCookies(BOOL* pbCookies);
+ HRESULT SupportsBackgroundSounds(BOOL* pbBackgroundSounds);
+ HRESULT SupportsVBScript(BOOL* pbVBScript);
+ HRESULT SupportsJavaScript(BOOL* pbJavaScript);
+ HRESULT SupportsJavaApplets(BOOL* pbJavaApplets);
+ HRESULT SupportsActiveXControls(BOOL* pbActiveXControls);
+ HRESULT SupportsCDF(BOOL* pbCDF);
+ HRESULT SupportsAuthenticodeUpdate(BOOL* pbAuthenticodeUpdate);
+ HRESULT IsBeta(BOOL* pbIsBeta);
+ HRESULT IsCrawler(BOOL* pbIsCrawler);
+ HRESULT IsAOL(BOOL* pbIsAOL);
+ HRESULT IsWin16(BOOL* pbIsWin16);
+ HRESULT IsAK(BOOL* pbIsAK);
+ HRESULT IsSK(BOOL* pbIsSK);
+ HRESULT IsUpdate(BOOL* pbIsUpdate);
+};
+
+// IBrowserCapsSvc.
+// Interface on the browser caps service. Used by clients to query a running
+// instance of the browser capabilities service for information about a user's web
+// browser. See atlutil.h for implementation of the browser capabilities services.
+__interface __declspec(uuid("391E7418-863B-430e-81BB-1312ED2FF3E9"))
+ IBrowserCapsSvc : public IUnknown
+{
+ HRESULT GetCaps(IHttpServerContext * pContext, IBrowserCaps ** ppOut);
+ HRESULT GetCapsUserAgent(BSTR bstrAgent, IBrowserCaps ** ppOut);
+};
+
+class CBrowserCapsSvc : public IBrowserCapsSvc,
+ public CComObjectRootEx<CComSingleThreadModel>
+{
+public:
+ virtual ~CBrowserCapsSvc()
+ {
+ }
+
+ BEGIN_COM_MAP(CBrowserCapsSvc)
+ COM_INTERFACE_ENTRY(IBrowserCapsSvc)
+ END_COM_MAP()
+
+ __success(SUCCEEDED(return)) __checkReturn HRESULT GetCaps(__in IHttpServerContext * pContext, __deref_out_opt IBrowserCaps ** ppOut)
+ {
+ if (!pContext)
+ return E_POINTER;
+
+ if (!ppOut)
+ return E_POINTER;
+
+ *ppOut = NULL;
+
+ char szUserAgent[256];
+ DWORD dwSize = sizeof(szUserAgent);
+ if (!pContext->GetServerVariable("HTTP_USER_AGENT", szUserAgent, &dwSize))
+ return E_FAIL;
+
+ return GetCapsUserAgent(CComBSTR(szUserAgent), ppOut);
+ }
+
+ __success(SUCCEEDED(return)) __checkReturn HRESULT GetCapsUserAgent(__in BSTR bstrAgent, __deref_out IBrowserCaps ** ppOut)
+ {
+ if (::SysStringLen(bstrAgent) == 0 || ppOut == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppOut = NULL;
+
+ BrowserCaps* pCaps = NULL;
+
+ _ATLTRY
+ {
+ CW2CT szUserAgent(bstrAgent);
+
+ if (!m_mapAgent.Lookup(szUserAgent, pCaps))
+ {
+ pCaps = NULL;
+ for (size_t i=0; i<m_caps.GetCount(); i++)
+ {
+ BrowserCaps& caps = m_caps[i];
+ if (IsEqualAgentString(caps.m_strUserAgent, szUserAgent))
+ {
+ pCaps = &caps;
+ break;
+ }
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+
+#pragma warning(push)
+#pragma warning(disable: 6014)
+ if (pCaps != NULL)
+ {
+ CComObjectNoLock<CBrowserCaps> *pRet = NULL;
+
+ ATLTRY(pRet = new CComObjectNoLock<CBrowserCaps>);
+
+ if (!pRet)
+ return E_OUTOFMEMORY;
+ pRet->AddRef();
+
+ HRESULT hr = pRet->Initialize(pCaps);
+ if (FAILED(hr))
+ {
+ pRet->Release();
+ return hr;
+ }
+
+ *ppOut = pRet;
+ return S_OK;
+ }
+#pragma warning(pop)
+
+ return E_FAIL;
+ }
+
+ __checkReturn HRESULT Initialize(__in HINSTANCE hInstance) throw()
+ {
+ // tries loading browscap.ini from the same directory as the module
+ if (hInstance != NULL)
+ {
+ _ATLTRY
+ {
+ CPath strBrowscapPath;
+
+ LPTSTR sz = strBrowscapPath.m_strPath.GetBuffer(MAX_PATH);
+ UINT nChars = ::GetModuleFileName(hInstance, sz, MAX_PATH);
+ strBrowscapPath.m_strPath.ReleaseBuffer(nChars);
+ if (nChars != 0 &&
+ nChars != MAX_PATH &&
+ strBrowscapPath.RemoveFileSpec())
+ {
+ strBrowscapPath += _T("\\browscap.ini");
+ if (SUCCEEDED(Load(strBrowscapPath)))
+ return S_OK;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+ }
+
+ // falls back to the system browscap.ini if previous Load failed
+ return Load();
+ }
+
+ HRESULT Uninitialize()
+ {
+ Clear();
+ return S_OK;
+ }
+
+private:
+ static bool IsEqualAgentString(__in LPCTSTR szPattern, __in LPCTSTR szInput)
+ {
+ while (*szPattern && *szInput && (*szPattern == *szInput || *szPattern == '?'))
+ {
+ szPattern++;
+ szInput++;
+ }
+
+ if (*szPattern == *szInput)
+ {
+ return true;
+ }
+
+ if (*szPattern == '*')
+ {
+ szPattern++;
+ if (!*szPattern)
+ {
+ return true;
+ }
+ while(*szInput)
+ {
+ if (IsEqualAgentString(szPattern, szInput))
+ {
+ return true;
+ }
+
+ szInput++;
+ }
+ }
+
+ return false;
+ }
+
+ __checkReturn HRESULT Load(__in_opt LPCTSTR szPath = NULL)
+ {
+ _ATLTRY
+ {
+ Clear();
+
+ CString strBrowscapPath(szPath);
+
+ // use default load path if a path isn't specified
+ if (strBrowscapPath.IsEmpty())
+ {
+ LPTSTR sz = strBrowscapPath.GetBuffer(MAX_PATH);
+ UINT nChars = ::GetSystemDirectory(sz, MAX_PATH);
+ strBrowscapPath.ReleaseBuffer(nChars);
+ if (nChars == 0 || nChars == MAX_PATH)
+ return E_FAIL;
+
+ strBrowscapPath += _T("\\inetsrv\\browscap.ini");
+ }
+
+ size_t nCurrent = 16384;
+ CHeapPtr<TCHAR> data;
+
+ if (!data.Allocate(nCurrent))
+ return E_OUTOFMEMORY;
+
+ // load the list of all the user agents
+ bool bRetrieved = false;
+
+ do
+ {
+ DWORD dwRetrieved = ::GetPrivateProfileSectionNames(data, (DWORD) nCurrent, strBrowscapPath);
+ if (dwRetrieved == 0)
+ {
+ return AtlHresultFromWin32(ERROR_FILE_NOT_FOUND);
+ }
+ else if (dwRetrieved < nCurrent-2)
+ {
+ bRetrieved = true;
+ }
+ else if(SIZE_MAX/2<nCurrent)
+ {
+ return E_OUTOFMEMORY;
+ }
+ else if (!data.Reallocate(nCurrent *= 2))
+ {
+ return E_OUTOFMEMORY;
+ }
+ } while (!bRetrieved);
+
+ // figure out how many user agents there are
+ // and set them in the structure
+ LPTSTR sz = data;
+ int nSections = 0;
+ while (*sz)
+ {
+ nSections++;
+ sz += (lstrlen(sz)+1);
+ }
+
+ if (!m_caps.SetCount(nSections))
+ return E_OUTOFMEMORY;
+
+ sz = data;
+ nSections = 0;
+ while (*sz)
+ {
+ BrowserCaps& caps = m_caps[nSections++];
+ caps.m_strUserAgent = sz;
+ m_mapAgent[caps.m_strUserAgent] = &caps;
+ sz += (caps.m_strUserAgent.GetLength()+1);
+ }
+
+ // for each user agent, load the properties
+ for (size_t i=0; i<m_caps.GetCount(); i++)
+ {
+ bRetrieved = false;
+ BrowserCaps& caps = m_caps[i];
+ caps.m_pParent = NULL;
+
+ do
+ {
+ DWORD dwRetrieved = ::GetPrivateProfileSection(caps.m_strUserAgent, data, (DWORD) nCurrent, strBrowscapPath);
+ if (dwRetrieved == 0)
+ {
+ return AtlHresultFromWin32(ERROR_FILE_NOT_FOUND);
+ }
+ else if (dwRetrieved < nCurrent-2)
+ {
+ bRetrieved = true;
+ }
+ else if(SIZE_MAX/2<nCurrent)
+ {
+ return E_OUTOFMEMORY;
+ }
+ else if (!data.Reallocate(nCurrent *= 2))
+ {
+ return E_OUTOFMEMORY;
+ }
+ } while (!bRetrieved);
+
+ sz = data;
+ while (*sz)
+ {
+ CString str = sz;
+ int nChar = str.Find('=');
+ if (nChar != -1)
+ {
+ CString strPropName = str.Left(nChar);
+ CString strPropVal = str.Mid(nChar+1);
+ strPropName.Trim();
+ strPropVal.Trim();
+ caps.m_props.SetAt(strPropName, strPropVal);
+
+ // if it's the parent property, set up the parent pointer
+ if (strPropName.CompareNoCase(_T("parent")) == 0)
+ {
+ BrowserCaps* pParent = NULL;
+ if (m_mapAgent.Lookup(strPropVal, pParent))
+ caps.m_pParent = pParent;
+ }
+ }
+ sz += (str.GetLength()+1);
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+ }
+
+ void Clear()
+ {
+ m_caps.RemoveAll();
+ m_mapAgent.RemoveAll();
+ }
+
+ friend class CBrowserCaps;
+
+ struct BrowserCaps
+ {
+ CString m_strUserAgent; // user agent string to match against (with wildcards)
+ BrowserCaps* m_pParent;
+ CAtlMap<CString, CString, CStringElementTraitsI<CString>, CStringElementTraits<CString> > m_props;
+ };
+
+ // map from UserAgent string to caps
+ // used for non-wildcard lookup and parent lookup
+ CAtlMap<CString, BrowserCaps*, CStringElementTraits<CString> > m_mapAgent;
+
+ // all of the caps
+ CAtlArray<BrowserCaps> m_caps;
+
+ class CBrowserCaps : public IBrowserCaps, public CComObjectRootEx<CComSingleThreadModel>
+ {
+ public:
+
+ BEGIN_COM_MAP(CBrowserCaps)
+ COM_INTERFACE_ENTRY(IBrowserCaps)
+ END_COM_MAP()
+
+ CBrowserCaps()
+ {
+ }
+
+ HRESULT Initialize(__in CBrowserCapsSvc::BrowserCaps * pCaps)
+ {
+ m_pCaps = pCaps;
+ return S_OK;
+ }
+
+ __checkReturn HRESULT GetPropertyString(__in BSTR bstrProperty, __out BSTR * pbstrOut)
+ {
+ _ATLTRY
+ {
+ ATLASSUME(m_pCaps);
+ if (!m_pCaps)
+ return E_UNEXPECTED;
+
+ if (!pbstrOut)
+ return E_POINTER;
+
+ *pbstrOut = NULL;
+
+ CString strName(bstrProperty);
+ CString strVal;
+
+ CBrowserCapsSvc::BrowserCaps * pCaps = m_pCaps;
+ while (pCaps)
+ {
+ if (pCaps->m_props.Lookup(strName, strVal))
+ {
+ CComBSTR bstrVal(strVal);
+ *pbstrOut = bstrVal.Detach();
+ return S_OK;
+ }
+
+ pCaps = pCaps->m_pParent;
+ }
+
+ return S_FALSE;
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+ }
+
+ __checkReturn HRESULT GetBooleanPropertyValue(__in BSTR bstrProperty, __out BOOL* pbOut)
+ {
+ if (!pbOut)
+ return E_POINTER;
+
+ CComBSTR bstrOut;
+ HRESULT hr = GetPropertyString(bstrProperty, &bstrOut);
+ if (FAILED(hr) || S_FALSE == hr)
+ return hr;
+
+ if (_wcsicmp(bstrOut, L"true") == 0)
+ *pbOut = TRUE;
+ else
+ *pbOut = FALSE;
+
+ return S_OK;
+ }
+
+ __checkReturn HRESULT GetBrowserName(__out BSTR * pbstrName)
+ {
+ return GetPropertyString(CComBSTR(L"browser"), pbstrName);
+ }
+
+ __checkReturn HRESULT GetPlatform(__out BSTR * pbstrPlatform)
+ {
+ return GetPropertyString(CComBSTR(L"platform"), pbstrPlatform);
+ }
+
+ __checkReturn HRESULT GetVersion(__out BSTR * pbstrVersion)
+ {
+ return GetPropertyString(CComBSTR(L"version"), pbstrVersion);
+ }
+
+ __checkReturn HRESULT GetMajorVer(__out BSTR * pbstrMajorVer)
+ {
+ return GetPropertyString(CComBSTR(L"majorver"), pbstrMajorVer);
+ }
+
+ __checkReturn HRESULT GetMinorVer(__out BSTR * pbstrMinorVer)
+ {
+ return GetPropertyString(CComBSTR(L"minorver"), pbstrMinorVer);
+ }
+
+ __checkReturn HRESULT SupportsFrames(__out BOOL* pbFrames)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"frames"), pbFrames);
+ }
+
+ __checkReturn HRESULT SupportsTables(__out BOOL* pbTables)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"tables"), pbTables);
+ }
+ __checkReturn HRESULT SupportsCookies(__out BOOL* pbCookies)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"cookies"), pbCookies);
+ }
+ __checkReturn HRESULT SupportsBackgroundSounds(__out BOOL* pbBackgroundSounds)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"backgroundsounds"), pbBackgroundSounds);
+ }
+ __checkReturn HRESULT SupportsVBScript(__out BOOL* pbVBScript)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"vbscript"), pbVBScript);
+ }
+ __checkReturn HRESULT SupportsJavaScript(__out BOOL* pbJavaScript)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"javascript"), pbJavaScript);
+ }
+ __checkReturn HRESULT SupportsJavaApplets(__out BOOL* pbJavaApplets)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"javaapplets"), pbJavaApplets);
+ }
+ __checkReturn HRESULT SupportsActiveXControls(__out BOOL* pbActiveXControls)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"ActiveXControls"), pbActiveXControls);
+ }
+ __checkReturn HRESULT SupportsCDF(__out BOOL* pbCDF)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"CDF"), pbCDF);
+ }
+ __checkReturn HRESULT SupportsAuthenticodeUpdate(__out BOOL* pbAuthenticodeUpdate)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"AuthenticodeUpdate"), pbAuthenticodeUpdate);
+ }
+ __checkReturn HRESULT IsBeta(__out BOOL* pbIsBeta)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"beta"), pbIsBeta);
+ }
+ __checkReturn HRESULT IsCrawler(__out BOOL* pbIsCrawler)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"Crawler"), pbIsCrawler);
+ }
+ __checkReturn HRESULT IsAOL(__out BOOL* pbIsAOL)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"AOL"), pbIsAOL);
+ }
+ __checkReturn HRESULT IsWin16(__out BOOL* pbIsWin16)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"Win16"), pbIsWin16);
+ }
+ __checkReturn HRESULT IsAK(__out BOOL* pbIsAK)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"AK"), pbIsAK);
+ }
+ __checkReturn HRESULT IsSK(__out BOOL* pbIsSK)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"SK"), pbIsSK);
+ }
+ __checkReturn HRESULT IsUpdate(__out BOOL* pbIsUpdate)
+ {
+ return GetBooleanPropertyValue(CComBSTR(L"Update"), pbIsUpdate);
+ }
+
+ private:
+ CBrowserCapsSvc::BrowserCaps * m_pCaps;
+ };
+};
+
+typedef DWORD_PTR HSESSIONENUM;
+
+// ISession
+// Interface on a single client session. Used to access variables in the client
+// session in the session state services. See atlsession.h for implementation of
+// this interface.
+__interface __declspec(uuid("DEB69BE3-7AC9-4a13-9519-266C1EA3AB39"))
+ ISession : public IUnknown
+{
+ STDMETHOD(SetVariable)(LPCSTR szName, VARIANT NewVal);
+ STDMETHOD(GetVariable)(LPCSTR szName, VARIANT *pVal);
+ STDMETHOD(GetCount)(long *pnCount);
+ STDMETHOD(RemoveVariable)(LPCSTR szName);
+ STDMETHOD(RemoveAllVariables)();
+ STDMETHOD(BeginVariableEnum)(POSITION *pPOS, HSESSIONENUM *phEnumHandle);
+ STDMETHOD(GetNextVariable)(POSITION *pPOS, VARIANT *pVal, HSESSIONENUM hEnum, LPSTR szName, DWORD dwLen);
+ STDMETHOD(CloseEnum)(HSESSIONENUM hEnumHandle);
+ STDMETHOD(IsExpired)();
+ STDMETHOD(SetTimeout)(unsigned __int64 dwNewTimeout);
+}; //ISession
+
+
+// ISessionStateService
+// Interface on the session state service for an ISAPI application. Request
+// handler objects will use this interface to access user sessions. See
+// atlsession.h for implementation of this interface.
+__interface __declspec(uuid("C5740C4F-0C6D-4b43-92C4-2AF778F35DDE"))
+ ISessionStateService : public IUnknown
+{
+ STDMETHOD(CreateNewSession)(LPSTR szNewID, DWORD *pdwSize, ISession** ppSession);
+ STDMETHOD(CreateNewSessionByName)(LPSTR szNewID, ISession** ppSession);
+ STDMETHOD(GetSession)(LPCSTR szID, ISession **ppSession);
+ STDMETHOD(CloseSession)(LPCSTR szID);
+};
+
+// ISessionStateControl
+// Interface used by session state service to get information about the service.
+// Currently you can get the count of active sessions and the current default
+// timeout for a session.
+__interface __declspec(uuid("6C7F5F56-6CBD-49ee-9797-4C837D4C527A"))
+ ISessionStateControl : public IUnknown
+{
+ STDMETHOD(SetSessionTimeout)(unsigned __int64 nTimeout);
+ STDMETHOD(GetSessionTimeout)(unsigned __int64 *pnTimeout);
+ STDMETHOD(GetSessionCount)(DWORD *pnSessionCount);
+};
+
+}; // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLSIFACE_H__
diff --git a/include/atl/atlsmtpconnection.h b/include/atl/atlsmtpconnection.h
new file mode 100644
index 000000000..5cda41d1d
--- /dev/null
+++ b/include/atl/atlsmtpconnection.h
@@ -0,0 +1,926 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSMTPCONNECTION_H__
+#define __ATLSMTPCONNECTION_H__
+
+#pragma once
+
+#ifndef _ATL_NO_DEFAULT_LIBS
+#pragma comment(lib, "ws2_32.lib")
+#endif // !_ATL_NO_DEFAULT_LIBS
+
+#include <winsock2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <tchar.h>
+#include <atlstr.h>
+#include <atlcoll.h>
+#include <atlfile.h>
+#include <atlmime.h>
+#include <atlspriv.h>
+#include <atlsmtputil.h>
+#include <atlsocket.h>
+
+// SMTP Return Codes
+#define ATLSMTP_MAIL_SUCCESS 250
+#define ATLSMTP_RCPT_SUCCESS 250
+#define ATLSMTP_RCPT_NOT_LOCAL 251
+#define ATLSMTP_DATA_INTERMEDIATE 354
+
+#define ATLSMTP_CONN_SUCC "220"
+#define ATLSMTP_HELO_SUCC "250"
+#define ATLSMTP_MAIL_SUCC "250"
+#define ATLSMTP_RCPT_SUCC "250"
+#define ATLSMTP_RCPT_NLOC "251"
+#define ATLSMTP_DATA_INTM "354"
+#define ATLSMTP_DATA_SUCC "250"
+#define ATLSMTP_RSET_SUCC "250"
+
+// SMTP flags
+#define ATLSMTP_DUMP_SENDER 1
+#define ATLSMTP_DUMP_RECIPS 2
+#define ATLSMTP_FOR_SEND 4
+
+
+struct CSMTPWSAStartup
+{
+private:
+ bool m_bInit;
+
+public:
+ CSMTPWSAStartup() throw()
+ :m_bInit(false)
+ {
+ Init();
+ }
+
+ ~CSMTPWSAStartup() throw()
+ {
+ Uninit();
+ }
+
+ bool Init() throw()
+ {
+ if (m_bInit)
+ return true;
+
+ WSADATA wsadata;
+ if (WSAStartup(ATLSMTP_WSA_VERSION, &wsadata))
+ return false;
+ m_bInit = true;
+ ATLASSERT(wsadata.wHighVersion >= 2);
+ return true;
+ }
+
+ bool Uninit() throw()
+ {
+ if (m_bInit)
+ if (WSACleanup())
+ return false;
+ m_bInit = false;
+ return true;
+ }
+};
+
+__declspec(selectany) CSMTPWSAStartup _g_smtp_init;
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+class CSMTPConnection
+{
+protected:
+
+ // the socket
+ SOCKET m_hSocket;
+
+ // the OVERLAPPED struct
+ OVERLAPPED m_Overlapped;
+
+public:
+
+ CSMTPConnection() throw()
+ :m_hSocket(INVALID_SOCKET)
+ {
+ // initialize the OVERLAPPED struct
+ memset(&m_Overlapped, 0, sizeof(OVERLAPPED));
+ }
+
+ ~CSMTPConnection() throw()
+ {
+ Disconnect();
+ }
+
+ // Attempt to connect to the socket
+ // lpszHostName - the host name to connect to
+ BOOL Connect(LPCTSTR lpszHostName, DWORD dwTimeout = 10000) throw()
+ {
+ ATLASSERT(lpszHostName != NULL);
+
+ // If we're already connected
+ if (Connected())
+ {
+ return FALSE;
+ }
+
+ if (!_g_smtp_init.Init())
+ {
+ return FALSE;
+ }
+
+ CSocketAddr address;
+ if (address.FindAddr(lpszHostName, IPPORT_SMTP, 0, PF_UNSPEC, SOCK_STREAM, 0))
+ {
+ return FALSE;
+ }
+
+ ADDRINFOT *pAI;
+
+ BOOL bRet = FALSE;
+ int nIndex = 0;
+ while ((pAI = address.GetAddrInfo(nIndex++)) != NULL)
+ {
+ // create the socket
+ m_hSocket = WSASocket(pAI->ai_family, pAI->ai_socktype, pAI->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
+
+ if (m_hSocket == INVALID_SOCKET)
+ {
+ return FALSE;
+ }
+
+ bRet = FALSE;
+ WSAEVENT hEventConnect = WSACreateEvent();
+ if (hEventConnect != NULL)
+ {
+ if (SOCKET_ERROR != WSAEventSelect(m_hSocket, hEventConnect, FD_CONNECT))
+ {
+ if (WSAConnect(m_hSocket, pAI->ai_addr, (int)pAI->ai_addrlen,
+ NULL, NULL, NULL, NULL))
+ {
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ DWORD dwWait = WaitForSingleObject((HANDLE) hEventConnect, dwTimeout);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ // make sure there were no connection errors.
+ WSANETWORKEVENTS wse;
+ ZeroMemory(&wse, sizeof(wse));
+ WSAEnumNetworkEvents(m_hSocket, NULL, &wse);
+ if (wse.iErrorCode[FD_CONNECT_BIT]==0)
+ {
+ bRet = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ // we're done with the event
+ WSACloseEvent(hEventConnect);
+ }
+ if (bRet)
+ {
+ break;
+ }
+
+ shutdown(m_hSocket, SD_BOTH);
+ closesocket(m_hSocket);
+ m_hSocket = INVALID_SOCKET;
+ }
+
+
+ // Create an event for asynchronous I/O
+ if (bRet)
+ {
+ ATLASSUME(m_Overlapped.hEvent == NULL);
+ m_Overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (m_Overlapped.hEvent == NULL)
+ {
+ bRet = FALSE;
+ }
+ }
+
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+ int nBufLen = ATLSMTP_MAX_LINE_LENGTH;
+ if (bRet)
+ {
+ // See if the connect returns success
+ bRet = AtlSmtpReadData((HANDLE)m_hSocket, szBuf, &nBufLen, &m_Overlapped);
+ if (bRet)
+ {
+ if (strncmp(szBuf, ATLSMTP_CONN_SUCC, ATLSMTP_RETCODE_LEN))
+ {
+ bRet = FALSE;
+ }
+ }
+ }
+
+ char szLocalHost[ATLSMTP_MAX_SERVER_NAME_LENGTH+1];
+
+ // gethostname should return 0 on success
+ if (bRet && gethostname(szLocalHost, ATLSMTP_MAX_SERVER_NAME_LENGTH))
+ {
+ bRet = FALSE;
+ }
+
+ // Send HELO command and get reply
+ if (bRet)
+ {
+ nBufLen = sprintf_s(szBuf, ATLSMTP_MAX_LINE_LENGTH+1, "HELO %s\r\n", szLocalHost);
+ if (nBufLen > 0)
+ {
+ bRet = AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen, szBuf, &nBufLen,
+ ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_HELO_SUCC, &m_Overlapped);
+ }
+ else
+ {
+ bRet = FALSE;
+ }
+ }
+
+ if (!bRet)
+ {
+ if (m_Overlapped.hEvent != NULL)
+ CloseHandle(m_Overlapped.hEvent);
+ shutdown(m_hSocket, SD_BOTH);
+ closesocket(m_hSocket);
+ m_hSocket = INVALID_SOCKET;
+ }
+
+ return bRet;
+ }
+
+ // Disconnect the socket
+ inline BOOL Disconnect() throw()
+ {
+ if (!Connected())
+ {
+ return FALSE;
+ }
+
+ // shutdown should return 0 on success
+ if (shutdown(m_hSocket, SD_BOTH))
+ {
+ return FALSE;
+ }
+
+ // closesocket should return 0 on success
+ if (closesocket(m_hSocket))
+ {
+ return FALSE;
+ }
+
+ // close the handle to the overlapped event
+ CloseHandle(m_Overlapped.hEvent);
+ m_hSocket = INVALID_SOCKET;
+ memset((void*)&m_Overlapped, 0, sizeof(OVERLAPPED));
+ return TRUE;
+ }
+
+ // Are we connected?
+ inline BOOL Connected() throw()
+ {
+ return (m_hSocket != INVALID_SOCKET ? TRUE : FALSE);
+ }
+
+ // Send a message from a file
+ // lpszFileName - the file containing the message
+ // lpszRecipients - the recipients to send to (optional - if not specified, the recipients specified
+ // in the file will be used
+ // lpszSender - the sender (optional - if not specified, the recipients specified in the file
+ // will be used
+ BOOL SendMessage(LPCTSTR lpszFileName, LPCTSTR lpszRecipients = NULL, LPCTSTR lpszSender = NULL) throw()
+ {
+ if (!Connected())
+ {
+ return FALSE;
+ }
+
+ //Try to open the file
+ CAtlFile readFile;
+ if (FAILED(readFile.Create(lpszFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)))
+ {
+ return FALSE;
+ }
+
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+ int nBufLen = ATLSMTP_MAX_LINE_LENGTH;
+ BOOL bDumpedSender = FALSE;
+
+ //If the caller specifies the sender, rather than having an existing one in the file...
+ if (lpszSender)
+ {
+ nBufLen = sprintf_s(szBuf, ATLSMTP_MAX_LINE_LENGTH+1,
+ "MAIL FROM:<%s>\r\n", (LPCSTR) CT2CA(lpszSender));
+ if ((nBufLen < 0) ||
+ (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen, szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH,
+ ATLSMTP_MAIL_SUCC, &m_Overlapped)))
+ {
+ return FALSE;
+ }
+ bDumpedSender = TRUE;
+ }
+ nBufLen = ATLSMTP_MAX_LINE_LENGTH;
+
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ char buffer1[ATLSMTP_READBUFFER_SIZE];
+ char buffer2[ATLSMTP_READBUFFER_SIZE];
+ char* currBuffer = buffer1;
+ char* prevBuffer = NULL;
+
+ int nCurrBuffer = 0;
+ DWORD dwPrevLength = 0;
+ DWORD dwWritten = 0;
+#else
+ char bakBuffer[ATLSMTP_READBUFFER_SIZE];
+ char* currBuffer = bakBuffer;
+
+#endif // ATLSMTP_DOUBLE_BUFFERED
+ DWORD dwRead = 0;
+ DWORD dwBytesInBuffer = 0;
+ DWORD dwBufPos = 0;
+
+ //first handle the MAIL FROM and RCPT TO commands
+ BOOL bDumpedRecipients = FALSE;
+ BOOL bRet = TRUE;
+ while (bRet)
+ {
+ int nRetCode = 0;
+
+ //if we have dumped the sender, and we have extra recipients to send,
+ //and we haven't alredy done so, do it
+ if (lpszRecipients && !bDumpedRecipients && bDumpedSender)
+ {
+ bRet = DumpRecipients((HANDLE)m_hSocket, CT2A(lpszRecipients), &m_Overlapped, ATLSMTP_FOR_SEND);
+ }
+
+ if (bRet)
+ {
+ dwRead = 0;
+ BOOL bFullLine = FALSE;
+ bRet = ReadLine(readFile, currBuffer, szBuf, &dwBytesInBuffer, &dwBufPos,
+ ATLSMTP_READBUFFER_SIZE, ATLSMTP_MAX_LINE_LENGTH, &dwRead, &bFullLine);
+ if (dwRead == 0 || bFullLine == FALSE)
+ bRet = FALSE;
+ }
+
+ if (bRet)
+ {
+ bRet = AtlSmtpSendAndWait((HANDLE)m_hSocket, szBuf, (int)(dwRead), &m_Overlapped);
+ }
+
+ if (bRet)
+ {
+ nBufLen = ATLSMTP_MAX_LINE_LENGTH;
+ bRet = AtlSmtpReadData((HANDLE)m_hSocket, szBuf, &nBufLen, &m_Overlapped);
+ }
+
+ if (bRet)
+ {
+ nRetCode = atoi(szBuf);
+ //if the command is equal to ATLSMTP_MAIL_SUCC (or RCPT_SUCC: they are equivalent)
+ if (nRetCode == ATLSMTP_MAIL_SUCCESS || nRetCode == ATLSMTP_RCPT_NOT_LOCAL || nRetCode == ATLSMTP_RCPT_SUCCESS)
+ {
+ bDumpedSender = TRUE;
+ continue;
+ }
+
+ //If the command is equal to the data intermediate success code,
+ //break out of the loop
+ if (nRetCode == ATLSMTP_DATA_INTERMEDIATE)
+ break;
+ }
+
+ //otherwise, we got an error code
+ CancelMessage();
+ return FALSE;
+ }
+
+ dwRead = dwBytesInBuffer;
+ currBuffer+= dwBufPos;
+ DWORD dwErr = 0;
+ do
+ {
+ dwErr = 0;
+
+ //Try to send the data
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ if (!AtlSmtpSendOverlapped((HANDLE)m_hSocket, currBuffer, dwRead, prevBuffer, dwPrevLength, &m_Overlapped))
+ {
+ bRet = FALSE;
+ break;
+ }
+#else
+ if (!AtlSmtpSendAndWait((HANDLE)m_hSocket, currBuffer, dwRead, &m_Overlapped))
+ {
+ bRet = FALSE;
+ break;
+ }
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ //swap the current and previous buffer
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ prevBuffer = currBuffer;
+ currBuffer = (nCurrBuffer == 0 ? buffer2 : buffer1);
+ nCurrBuffer = (nCurrBuffer == 0 ? 1 : 0);
+ dwPrevLength = dwBytesInBuffer;
+#else
+ currBuffer = bakBuffer;
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+ if (FAILED(readFile.Read(currBuffer, ATLSMTP_READBUFFER_SIZE, dwRead)))
+ {
+ bRet = FALSE;
+ break;
+ }
+ } while (dwRead != 0);
+
+ //ensure that the last of the data is sent
+#ifdef ATLSMTP_DOUBLE_BUFFERED
+ if (!GetOverlappedResult((HANDLE)m_hSocket, &m_Overlapped, &dwWritten, TRUE))
+ {
+ if ((dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
+ bRet = FALSE;
+ else if (dwWritten < dwPrevLength)
+ bRet = AtlSmtpSendAndWait((HANDLE)m_hSocket, prevBuffer+dwWritten, dwPrevLength-dwWritten, &m_Overlapped);
+ }
+#endif // ATLSMTP_DOUBLE_BUFFERED
+
+
+ if (bRet)
+ {
+ // End the message with a CRLF.CRLF
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "\r\n.\r\n");
+ if (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_DATA_SUCC, &m_Overlapped))
+ {
+ bRet = FALSE;
+ }
+ }
+
+ return bRet;
+ }
+
+ // Send the message
+ // msg - the CMimeMessage to send
+ // lpszSender - the sender
+ inline BOOL SendMessage(CMimeMessage& msg, LPCTSTR lpszRecipients = NULL, LPCTSTR lpszSender = NULL) throw()
+ {
+ if (!Connected())
+ {
+ return FALSE;
+ }
+
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+
+ //Send MAIL FROM command and get reply
+ int nBufLen = sprintf_s(szBuf, ATLSMTP_MAX_LINE_LENGTH+1, "MAIL FROM:<%s>\r\n",
+ (lpszSender ? (LPCSTR) CT2CA(lpszSender) : msg.GetSender()));
+ if ((nBufLen < 0) ||
+ (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_MAIL_SUCC, &m_Overlapped)))
+ {
+ return FALSE;
+ }
+
+ BOOL bRet = TRUE;
+ if (!lpszRecipients)
+ {
+ LPSTR lpszRecipientsA = NULL;
+ DWORD dwLen = msg.GetRequiredRecipientsStringLength();
+ lpszRecipientsA = static_cast<LPSTR>(calloc(sizeof(char),dwLen));
+ if (!lpszRecipientsA || msg.GetRecipientsString(lpszRecipientsA, &dwLen) == FALSE)
+ {
+ bRet = FALSE;
+ }
+ if (bRet)
+ bRet = DumpRecipients((HANDLE)m_hSocket, lpszRecipientsA, &m_Overlapped, ATLSMTP_FOR_SEND);
+ free(lpszRecipientsA);
+ }
+ else
+ {
+ bRet = DumpRecipients((HANDLE)m_hSocket, CT2CA(lpszRecipients),
+ &m_Overlapped, ATLSMTP_FOR_SEND);
+ }
+
+ //Begin the data output
+ if (bRet)
+ {
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "DATA\r\n");
+ bRet = AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_DATA_INTM, &m_Overlapped);
+ }
+
+ if (!bRet)
+ CancelMessage();
+
+ //Attempt to write the data to the socket
+ if (bRet)
+ {
+ bRet = msg.WriteData((HANDLE)m_hSocket, &m_Overlapped, NULL, ATLSMTP_FORMAT_SMTP);
+ }
+
+ if (bRet)
+ {
+ //End the message with a <CRLF>.<CRLF>
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "\r\n.\r\n");
+ if (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_DATA_SUCC, &m_Overlapped))
+ {
+ return FALSE;
+ }
+ }
+
+ return bRet;
+ }
+
+ // Send a chunk of raw data
+ inline BOOL SendRaw(LPCTSTR lpszRawData, DWORD dwLen, LPCTSTR lpszRecipients, LPCTSTR lpszSender) throw()
+ {
+ ATLASSERT(lpszRawData != NULL);
+ ATLASSERT(lpszRecipients != NULL);
+ ATLASSERT(lpszSender != NULL);
+
+ if (!Connected())
+ return FALSE;
+
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+
+ //Send MAIL FROM command and get reply
+ int nBufLen = sprintf_s(szBuf, ATLSMTP_MAX_LINE_LENGTH+1,
+ "MAIL FROM:<%s>\r\n", (LPCSTR) CT2CA(lpszSender));
+ if ((nBufLen < 0) ||
+ (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_MAIL_SUCC, &m_Overlapped)))
+ {
+ return FALSE;
+ }
+
+ BOOL bRet = DumpRecipients((HANDLE)m_hSocket, CT2CA(lpszRecipients),
+ &m_Overlapped, ATLSMTP_FOR_SEND);
+
+ // Begin the data output
+ if (bRet)
+ {
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "DATA\r\n");
+ bRet = AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_DATA_INTM, &m_Overlapped);
+ }
+
+ if (!bRet)
+ CancelMessage();
+
+ if (bRet)
+ {
+ bRet = AtlSmtpSendAndWait((HANDLE)m_hSocket, (LPSTR)(lpszRawData), dwLen, &m_Overlapped);
+ }
+
+ if (bRet)
+ {
+ //End the message with a <CRLF>.<CRLF>
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "\r\n.\r\n");
+ if (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen,
+ szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH, ATLSMTP_DATA_SUCC, &m_Overlapped))
+ {
+ return FALSE;
+ }
+ }
+
+ return bRet;
+ }
+
+ inline BOOL SendSimple(LPCTSTR lpszRecipients, LPCTSTR lpszSender, LPCTSTR lpszSubject, LPCTSTR lpszBody, int nTextLen = -1) throw()
+ {
+ CMimeMessage msg;
+ BOOL bRet = msg.SetSubject(lpszSubject);
+ if (bRet)
+ bRet = msg.AddText(lpszBody, nTextLen);
+
+ CFixedStringT<CString, MAX_PATH> strRecip;
+ LPCTSTR szTmp = lpszRecipients;
+ LPCTSTR szTmp2 = lpszRecipients;
+ while (*szTmp && bRet)
+ {
+ if (AtlSmtpIsRecipientDelimiter((char) *szTmp2))
+ {
+ _ATLTRY
+ {
+ strRecip.SetString(szTmp, (int)(szTmp2-szTmp));
+ bRet = msg.AddRecipient((LPCTSTR) strRecip);
+
+ if (*szTmp2)
+ {
+ while (*szTmp2 && AtlSmtpIsRecipientDelimiter((char) *szTmp2))
+ {
+ szTmp2++;
+ }
+ }
+ szTmp = szTmp2;
+ }
+ _ATLCATCHALL()
+ {
+ bRet = FALSE;
+ }
+ }
+ else
+ {
+ szTmp2++;
+ }
+ }
+
+ if (bRet)
+ bRet = SendMessage(msg, lpszRecipients, lpszSender);
+
+ return bRet;
+ }
+
+ // Save a MIME message to a file
+ // lpszFileName - the file name
+ // lpszRecipients - the recipients string (optional)
+ // lpszSender - the sender (optional)
+ // dwFlags - the flags (optional)
+ inline BOOL WriteToFile(LPCTSTR lpszFileName, CMimeMessage& msg, LPCTSTR lpszRecipients = NULL,
+ LPCTSTR lpszSender = NULL, DWORD dwFlags = 0) throw()
+ {
+ //Try to create/open the file
+ HANDLE hFile = CreateFile(lpszFileName, GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ return FALSE;
+ }
+
+ // Use CHandle to close the file handle
+ // (CAtlFile does not allow for overlapped I/O)
+ CHandle hdlFile(hFile);
+
+ //Create and initialize the OVERLAPPED struct
+ OVERLAPPED writeOverlapped;
+ memset((void*)&writeOverlapped, 0, sizeof(OVERLAPPED));
+ writeOverlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (writeOverlapped.hEvent == NULL)
+ {
+ return FALSE;
+ }
+
+ // Use CHandle to close the event handle
+ CHandle hdlEvent(writeOverlapped.hEvent);
+
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+ BOOL bRet = TRUE;
+
+ int nBufLen = 0;
+
+ //if writing to file for purposes of sending, write out the
+ //commands as well
+ if (lpszSender || (dwFlags & ATLSMTP_DUMP_SENDER))
+ {
+ nBufLen = sprintf_s(szBuf, ATLSMTP_MAX_LINE_LENGTH+1, "MAIL FROM:<%s>\r\n",
+ (lpszSender ? (LPCSTR) CT2CA(lpszSender) : msg.GetSender()));
+ if (nBufLen > 0)
+ {
+ bRet = AtlSmtpSendAndWait(hFile, szBuf, nBufLen, &writeOverlapped);
+ }
+ else
+ {
+ bRet = FALSE;
+ }
+ }
+
+ if (bRet && (lpszRecipients || (dwFlags & ATLSMTP_DUMP_RECIPS)))
+ {
+ if (!lpszRecipients)
+ {
+ LPSTR lpszRecipientsA = NULL;
+ DWORD dwLen = msg.GetRequiredRecipientsStringLength();
+ lpszRecipientsA = static_cast<LPSTR>(calloc(sizeof(char),dwLen));
+ if (!lpszRecipientsA || msg.GetRecipientsString(lpszRecipientsA, &dwLen) == FALSE)
+ {
+ bRet = FALSE;
+ }
+ if (bRet)
+ bRet = DumpRecipients(hFile, lpszRecipientsA, &writeOverlapped);
+ free(lpszRecipientsA);
+ }
+ else
+ {
+ bRet = DumpRecipients(hFile, CT2CA(lpszRecipients), &writeOverlapped);
+ }
+ }
+
+ if (bRet)
+ {
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "DATA\r\n");
+ bRet = AtlSmtpSendAndWait(hFile, szBuf, nBufLen, &writeOverlapped);
+ }
+
+ if (bRet)
+ {
+ bRet = msg.WriteData(hFile, &writeOverlapped, NULL, ATLSMTP_FORMAT_SMTP);
+ }
+
+ return bRet;
+ }
+
+protected:
+
+ // disallow copy construction
+ CSMTPConnection(const CSMTPConnection&) throw()
+ {
+ ATLASSERT(FALSE);
+ }
+
+ // disallow assignment
+ const CSMTPConnection& operator=(const CSMTPConnection&) throw()
+ {
+ ATLASSERT(FALSE);
+ return *this;
+ }
+
+ // Tell the server we are aborting the message
+ inline BOOL CancelMessage() throw()
+ {
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+ int nBufLen = 0;
+ nBufLen = sprintf_s(szBuf, _countof(szBuf), "RSET\r\n");
+ if (!AtlSmtpSendAndCheck((HANDLE)m_hSocket, szBuf, nBufLen, szBuf, &nBufLen, ATLSMTP_MAX_LINE_LENGTH,
+ ATLSMTP_RSET_SUCC, &m_Overlapped))
+ {
+ Disconnect();
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ // Dump the recipients to hFile
+ // lpszRecipients - the recipients string
+ // pOverlapped - the OVERALAPPED struct
+ // dwFlags - the flags
+ inline BOOL DumpRecipients(HANDLE hFile, LPCSTR lpszRecipients, LPOVERLAPPED pOverlapped, DWORD dwFlags = 0)
+ {
+ ATLENSURE(lpszRecipients != NULL);
+ ATLASSERT(pOverlapped != NULL);
+
+ char rcptBuf[ATLSMTP_MAX_LINE_LENGTH-12+1];
+ char szBuf[ATLSMTP_MAX_LINE_LENGTH+1];
+ LPSTR tmpBuf = rcptBuf;
+ char ch;
+ BOOL bRet = TRUE;
+ int nMaxLength = ATLSMTP_MAX_LINE_LENGTH;
+ int nRetCode = 0;
+ size_t nCnt = 0;
+ do
+ {
+ ch = *lpszRecipients;
+ if (ch)
+ lpszRecipients++;
+ if (AtlSmtpIsRecipientDelimiter(ch))
+ {
+ *tmpBuf = 0;
+ int nBufLen = sprintf_s(szBuf, ATLSMTP_MAX_LINE_LENGTH,
+ "RCPT TO:<%s>\r\n", rcptBuf);
+ if (nBufLen > 0)
+ {
+ bRet = AtlSmtpSendAndWait(hFile, szBuf, nBufLen, pOverlapped);
+ }
+ else
+ {
+ bRet = FALSE;
+ }
+
+ if (bRet && (dwFlags & ATLSMTP_FOR_SEND))
+ {
+ bRet = AtlSmtpReadData(hFile, szBuf, &nMaxLength, pOverlapped);
+ nRetCode = atoi(szBuf);
+ if (!bRet || (nRetCode != ATLSMTP_RCPT_SUCCESS && nRetCode != ATLSMTP_RCPT_NOT_LOCAL))
+ {
+ bRet = FALSE;
+ break;
+ }
+ }
+ tmpBuf = rcptBuf;
+ nCnt = 0;
+ nMaxLength = ATLSMTP_MAX_LINE_LENGTH;
+ while (isspace(static_cast<unsigned char>(*lpszRecipients)))
+ lpszRecipients++;
+ continue;
+ }
+
+ if (nCnt >= sizeof(rcptBuf)-1)
+ {
+ // recipient string too long
+ bRet = FALSE;
+ break;
+ }
+
+ *tmpBuf++ = ch;
+ nCnt++;
+ } while (ch != '\0');
+
+ return bRet;
+ }
+
+ // Implementation - used from ReadLine
+ // fills pBuf with up to dwMaxLen bytes
+ BOOL FillBuffer(__in HANDLE hFile, __out_ecount_part(dwMaxLen, *pdwLen) LPSTR pBuf, __in DWORD dwMaxLen, __out LPDWORD pdwLen) throw()
+ {
+ ATLASSERT(hFile != INVALID_HANDLE_VALUE);
+ ATLASSERT(pdwLen != NULL);
+
+ DWORD dwRead = 0;
+ DWORD dwTotalRead = 0;
+ int nRet = 0;
+
+ do
+ {
+ nRet = ReadFile(hFile, pBuf, dwMaxLen-dwTotalRead, &dwRead, NULL);
+ if (!nRet && GetLastError() != ERROR_HANDLE_EOF)
+ {
+ return FALSE;
+ }
+
+ if (dwRead == 0)
+ break;
+
+ dwTotalRead+= dwRead;
+ } while (dwTotalRead < dwMaxLen);
+
+ *pdwLen = dwTotalRead;
+
+ return TRUE;
+ }
+
+ // Implementation
+ // Read a line (terminated by LF) from hFile
+ BOOL ReadLine(__in HANDLE hFile, __out_ecount_part_z(dwMaxSrcLen, *pdwSrcLen) LPSTR pSrc, __out_ecount_part_z(dwMaxDestLen, *pdwRead) LPSTR pDest, __inout LPDWORD pdwSrcLen, __inout LPDWORD pdwBufPos, __in DWORD dwMaxSrcLen,
+ __in DWORD dwMaxDestLen, __out_opt LPDWORD pdwRead=NULL, __out_opt LPBOOL pbFullLine=NULL)
+ {
+ ATLENSURE(hFile != INVALID_HANDLE_VALUE);
+ ATLENSURE(pSrc != NULL);
+ ATLENSURE(pDest != NULL);
+ ATLENSURE(pdwSrcLen != NULL);
+ ATLENSURE(pdwBufPos != NULL);
+
+ BOOL bRet = TRUE;
+ DWORD dwLen = 0;
+ DWORD dwBufPos = 0;
+ DWORD dwSrcLen = *pdwSrcLen;
+ LPSTR pSrcCurrent = pSrc + *pdwBufPos;
+
+ while (bRet && dwLen < dwMaxDestLen)
+ {
+ if (dwSrcLen == 0)
+ {
+ if (!FillBuffer(hFile, pSrc, dwMaxSrcLen, pdwSrcLen) || *pdwSrcLen == 0)
+ break;
+
+ dwBufPos = 0;
+ *pdwBufPos = 0;
+ dwSrcLen = *pdwSrcLen;
+ pSrcCurrent = pSrc;
+ }
+
+ --dwSrcLen;
+ *pDest = *pSrcCurrent++;
+ dwLen++;
+ dwBufPos++;
+ if (*pDest == '\n')
+ {
+ break;
+ }
+ pDest++;
+ }
+
+ *pdwSrcLen = dwSrcLen;
+
+ if (pbFullLine)
+ {
+ if (*pDest != '\n')
+ *pbFullLine = FALSE;
+ else
+ *pbFullLine = TRUE;
+ }
+
+ if (pdwRead)
+ *pdwRead = dwLen;
+
+ *pdwBufPos += dwBufPos;
+
+ return bRet;
+ }
+
+}; // class CSMTPConnection
+
+} // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLSMTPCONNECTION_H__
diff --git a/include/atl/atlsmtputil.h b/include/atl/atlsmtputil.h
new file mode 100644
index 000000000..6ce443861
--- /dev/null
+++ b/include/atl/atlsmtputil.h
@@ -0,0 +1,220 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSMTPUTIL_H__
+#define __ATLSMTPUTIL_H__
+
+#pragma once
+
+#if (defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_))
+#error <atlsmtputil.h> requires <winsock2.h> -- include <winsock2.h> before you include <windows.h> or <winsock.h>
+#endif
+#include <winsock2.h>
+#include <windows.h>
+#include <string.h>
+#include <stdlib.h>
+#include <tchar.h>
+#include <atlstr.h>
+#include <winnls.h>
+#include <atlspriv.h>
+
+//=======================================================================
+//defines for SMTPMail module
+//=======================================================================
+
+//If overlapped I/O is desired, need 2.0 or greater
+#define ATLSMTP_WSA_VERSION ATL_WINSOCK_VER
+
+//The maximum number of characters on a SMTP line
+#define ATLSMTP_MAX_LINE_LENGTH 1000
+
+#define ATLSMTP_MAX_SERVER_NAME_LENGTH 256
+
+//Encoding schemes
+#define ATLSMTP_BASE64_ENCODE 0
+#define ATLSMTP_UUENCODE 1
+#define ATLSMTP_QP_ENCODE 2
+
+//I/O Defines
+#define ATLSMTP_READBUFFER_SIZE 4096
+#define ATLSMTP_GET_LINES 100
+
+
+//Miscellaneous defines
+#define ATLSMTP_SEND_FILE 1
+#define ATLSMTP_FORMAT_SMTP 8
+
+#define ATLSMTP_RETCODE_LEN 3
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL
+{
+
+//=======================================================================
+// Miscellaneous Utility Functions
+//=======================================================================
+//A list of recipients in a string must by separated by one
+//of the following characters
+inline BOOL AtlSmtpIsRecipientDelimiter(char ch) throw()
+{
+ return (ch == ',' || ch == ';' || ch == ' ' || ch == '\0');
+}
+
+//Send data to hFile and wait for it to finish sending
+inline BOOL AtlSmtpSendAndWait(HANDLE hFile, LPCSTR lpData, int nDataLength, LPOVERLAPPED pOverlapped) throw()
+{
+ ATLASSERT(lpData != NULL);
+ ATLENSURE_RETURN_VAL(pOverlapped != NULL, FALSE);
+
+ DWORD dwWritten = 0, dwErr = 0;
+ int nRet = 0, nBufPos = 0;
+
+ //write all the data
+ do
+ {
+ //Write a chunk of data, offsetting the buffer and amount to write by what's already
+ //been written
+ nRet = WriteFile(hFile, (void*)(lpData+nBufPos), nDataLength-nBufPos, &dwWritten, pOverlapped);
+ if (!nRet && (dwErr = GetLastError()) != ERROR_IO_INCOMPLETE && dwErr != ERROR_IO_PENDING)
+ return FALSE;
+
+ //Get the result of the write operation (wait for it)
+ nRet = GetOverlappedResult(hFile, pOverlapped, &dwWritten, TRUE);
+ if (!nRet)
+ return FALSE;
+
+ //Need to update offsets when writing to a file
+ pOverlapped->Offset += dwWritten;
+ nBufPos += dwWritten;
+
+ } while (nBufPos < nDataLength);
+ return TRUE;
+}
+
+
+//Read up to nDestLen bytes from hFile, keep reading while there's more data and there's
+//room in the buffer
+inline BOOL AtlSmtpReadData(__in HANDLE hFile, __out_ecount_part_z(*pnDestLen, *pnDestLen) LPSTR lpData, __inout int* pnDestLen, __in LPOVERLAPPED pOverlapped)
+{
+ ATLASSERT(lpData != NULL);
+ ATLASSERT(pnDestLen != NULL);
+ ATLENSURE(pOverlapped != NULL);
+
+ DWORD dwRead = 0, dwErr = 0;
+ int nBufPos = 0;
+ do
+ {
+ //REad a chunk of data, offsetting the buffer and amount to read by what's already been read
+ int nRet = ReadFile(hFile, (void*)(lpData+nBufPos), (*pnDestLen)-nBufPos, &dwRead, pOverlapped);
+ if (!nRet && (dwErr = GetLastError()) != ERROR_MORE_DATA && dwErr != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
+ return FALSE;
+
+ //Get the result of the read operation (wait for it)
+ nRet = GetOverlappedResult(hFile, pOverlapped, &dwRead, TRUE);
+ if (!nRet)
+ return FALSE;
+
+ //Handle offsets when reading from a file
+ pOverlapped->Offset += dwRead;
+ nBufPos += dwRead;
+ } while (nBufPos < *pnDestLen && dwErr == ERROR_MORE_DATA);
+ *pnDestLen = nBufPos;
+ return TRUE;
+}
+
+
+//Used in sending encoded data
+//lpData is the data to be sent now
+//lpPrev is a pointer to the buffer that the previous call was made on
+//This allows the new buffer (lpData) to be filled while lpPrev is being sent
+//If all the data in lpPrev had not finished sending, we complete the send and wait
+inline BOOL AtlSmtpSendOverlapped(HANDLE hFile, LPCSTR lpData, int nDataLength, LPCSTR lpPrev, DWORD dwPrevLength, LPOVERLAPPED pOverlapped)
+{
+ ATLASSERT(lpData != NULL);
+ ATLENSURE(pOverlapped != NULL);
+
+ DWORD dwWritten = 0, dwErr = 0, dwBufPos = 0;
+ int nRet = 0;
+
+ //Get the results of the previous call (if any)
+ if (lpPrev && (!GetOverlappedResult(hFile, pOverlapped, &dwWritten, FALSE) || dwWritten < dwPrevLength))
+ {
+ //If any error but IO_INCOMPLETE, return failure
+ if ((dwErr = GetLastError()) != ERROR_SUCCESS && dwErr != ERROR_IO_INCOMPLETE && dwErr != ERROR_IO_PENDING)
+ {
+ return FALSE;
+ }
+ //Finish writing lpPrev if we need to
+ while (dwBufPos < dwPrevLength)
+ {
+ //Get the result of the previous write (wait for it)
+ nRet = GetOverlappedResult(hFile, pOverlapped, &dwWritten, TRUE);
+ if (!nRet || (dwBufPos += dwWritten) == dwPrevLength)
+ {
+ if ((dwErr = GetLastError()) != ERROR_IO_INCOMPLETE && dwErr != ERROR_IO_PENDING)
+ break;
+ }
+
+ //If we are writing to a file, we need to update the offsets
+ pOverlapped->Offset += dwWritten;
+ if(dwBufPos>dwPrevLength)
+ {
+ /* shouldn't happen */
+ ATLASSERT(false);
+ break;
+ }
+ nRet = WriteFile(hFile, (void*)(lpPrev+dwBufPos), dwPrevLength-dwBufPos, &dwWritten, pOverlapped);
+
+ //If any error but IO_PENDING and IO_INCOMPLETE, break
+ if (!nRet && (dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
+ break;
+ }
+ if (dwBufPos < dwPrevLength)
+ return FALSE;
+ }
+
+ //Now that all the previous data has been sent, start sending the current data
+ nRet = WriteFile(hFile, (void*)lpData, nDataLength, &dwWritten, pOverlapped);
+ GetOverlappedResult(hFile, pOverlapped, &dwWritten, FALSE);
+
+ pOverlapped->Offset += dwWritten;
+
+ //If any error but IO_PENDING
+ if (!nRet && (dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
+ return FALSE;
+ return TRUE;
+}
+
+
+//Send a SMTP command and read the response
+//return TRUE if it matches szResponse, FALSE otherwise
+inline BOOL AtlSmtpSendAndCheck(__in HANDLE hFile, __in LPCSTR lpData, __in int nDataLength, __out_ecount_part(nMaxResponseLength, *pnResponseLength) LPSTR lpResponse, __out int* pnResponseLength, __in int nMaxResponseLength,
+ __in_z LPCSTR szResponse, __in LPOVERLAPPED pOverlapped) throw()
+{
+ ATLASSERT(lpData != NULL);
+ ATLASSERT(lpResponse != NULL);
+ ATLASSERT(pnResponseLength != NULL);
+
+ BOOL bRet = AtlSmtpSendAndWait(hFile, lpData, nDataLength, pOverlapped);
+ if (bRet)
+ {
+ *pnResponseLength = nMaxResponseLength;
+ bRet = AtlSmtpReadData(hFile, lpResponse, pnResponseLength, pOverlapped);
+ }
+ if (!bRet || strncmp((char*)lpResponse, szResponse, ATLSMTP_RETCODE_LEN))
+ return FALSE;
+ return TRUE;
+}
+
+} // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLSMTPUTIL_H__
diff --git a/include/atl/atlsoap.h b/include/atl/atlsoap.h
new file mode 100644
index 000000000..b286e7c61
--- /dev/null
+++ b/include/atl/atlsoap.h
@@ -0,0 +1,8174 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSOAP_H__
+#define __ATLSOAP_H__
+
+#pragma once
+
+#if (defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_))
+ #error require winsock2.h -- include <winsock2.h> before you include <windows.h>
+#endif
+
+#if ((_WIN32_WINNT < 0x0400) && (_WIN32_WINDOWS <= 0x0400))
+ #error require _WIN32_WINNT >= 0x0400 or _WIN32_WINDOWS > 0x0400
+#endif
+
+#ifndef ATLSOAP_TRACE
+ #ifdef _ATLSOAP_TRACE_XML
+ #define ATLSOAP_TRACE(__data, __len) AtlSoapTraceXML(__data, __len)
+ #else
+ #define ATLSOAP_TRACE(__data, __len) __noop
+ #endif
+#endif // ATLSOAP_TRACE
+
+// override this macro to ATL_BASE64_FLAG_NOCRLF if you do
+// not want Base64-encoded binary data to contain CRLFs
+#ifndef ATLSOAP_BASE64_FLAGS
+ #define ATLSOAP_BASE64_FLAGS ATL_BASE64_FLAG_NONE
+#endif // ATLSOAP_BASE64_FLAGS
+
+[ emitidl(restricted) ];
+#include <winsock2.h>
+#include <atlstr.h>
+#include <atlcoll.h>
+#include <atlbase.h>
+#include <msxml2.h>
+#include <atlenc.h>
+#include <fcntl.h>
+#include <float.h>
+#include <math.h>
+#include <limits>
+#include <atlisapi.h>
+#include <atlstencil.h>
+#include <atlhttp.h>
+#include <atlhttp.inl>
+
+#pragma warning(push)
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+#pragma warning(disable: 4061) // enumerate 'enum value' in switch of enum 'enum type' is not explicitly handled by a case label
+
+#ifndef _CPPUNWIND
+#pragma warning(disable: 4702) // unreachable code
+#endif // _CPPUNWIND
+
+#ifndef ATLSOAP_NOWININET
+ #include <wininet.h>
+ #ifndef ATLSOAPINET_CLIENT
+ #define ATLSOAPINET_CLIENT _T("VCSoapClient")
+ #endif
+#endif
+
+#ifndef _ATL_NO_DEFAULT_LIBS
+#pragma comment(lib, "msxml2.lib")
+ #ifndef ATLSOAP_NOWININET
+ #pragma comment(lib, "wininet.lib")
+ #endif
+#endif
+
+#define _ATLSOAP_MAKEWIDESTR( str ) L ## str
+#define ATLSOAP_MAKEWIDESTR( str ) _ATLSOAP_MAKEWIDESTR( str )
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL
+{
+
+ATL_NOINLINE inline void AtlSoapTraceXML(LPBYTE pdwData, DWORD dwLen)
+{
+ HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hStdOut != INVALID_HANDLE_VALUE)
+ {
+ DWORD dwWritten;
+ WriteFile(hStdOut,
+ "\n-----------------------------------------------------------------\n",
+ sizeof("\n-----------------------------------------------------------------\n")-1,
+ &dwWritten, NULL);
+
+ WriteFile(hStdOut, pdwData, dwLen, &dwWritten, NULL);
+
+ WriteFile(hStdOut,
+ "\n-----------------------------------------------------------------\n",
+ sizeof("\n-----------------------------------------------------------------\n")-1,
+ &dwWritten, NULL);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// IStreamImpl - stub IStream implementation class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class IStreamImpl : public IStream
+{
+public:
+
+ HRESULT __stdcall Read(void * /*pDest*/, ULONG /*nMaxLen*/, ULONG * /*pnRead*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Write(const void * /*pv*/, ULONG /*cb*/, ULONG * /*pcbWritten*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Seek(LARGE_INTEGER /*dlibMove*/, DWORD /*dwOrigin*/,
+ ULARGE_INTEGER * /*pLibNewPosition*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall SetSize(ULARGE_INTEGER /*libNewSize*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall CopyTo(IStream * /*pStream*/, ULARGE_INTEGER /*cb*/,
+ ULARGE_INTEGER * /*pcbRead*/, ULARGE_INTEGER * /*pcbWritten*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Commit(DWORD /*grfCommitFlags*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Revert()
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall LockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall UnlockRegion(ULARGE_INTEGER /*libOffset*/, ULARGE_INTEGER /*cb*/, DWORD /*dwLockType*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Stat(STATSTG * /*pstatstg*/, DWORD /*grfStatFlag*/)
+ {
+ return E_NOTIMPL;
+ }
+
+ HRESULT __stdcall Clone(IStream ** /*ppstm*/)
+ {
+ return E_NOTIMPL;
+ }
+}; // class IStreamImpl
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CStreamOnServerContext
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class CStreamOnServerContext : public IStreamImpl
+{
+public:
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (ppv == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppv = NULL;
+
+ if (InlineIsEqualGUID(riid, IID_IUnknown) ||
+ InlineIsEqualGUID(riid, IID_IStream) ||
+ InlineIsEqualGUID(riid, IID_ISequentialStream))
+ {
+ *ppv = static_cast<IStream *>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+private:
+
+ IHttpServerContext * m_pServerContext;
+ DWORD m_dwBytesRead;
+
+public:
+
+ CStreamOnServerContext(IHttpServerContext *pServerContext = NULL)
+ : m_pServerContext(pServerContext), m_dwBytesRead(0)
+ {
+ }
+
+ void SetServerContext(IHttpServerContext *pServerContext)
+ {
+ ATLASSUME( m_pServerContext == NULL );
+
+ m_pServerContext = pServerContext;
+ }
+
+ HRESULT __stdcall Read(void *pDest, ULONG nMaxLen, ULONG *pnRead)
+ {
+ ATLENSURE( pDest != NULL );
+ ATLASSUME( m_pServerContext != NULL );
+
+ DWORD dwToRead = __min(m_pServerContext->GetTotalBytes()-m_dwBytesRead, nMaxLen);
+ if (ReadClientData(m_pServerContext, (LPSTR) pDest, &dwToRead, m_dwBytesRead) != FALSE)
+ {
+ m_dwBytesRead+= dwToRead;
+
+ if (pnRead != NULL)
+ {
+ *pnRead = dwToRead;
+ }
+
+ return S_OK;
+ }
+
+ ATLTRACE( _T("ATLSOAP: CStreamOnServerContext::Read -- ReadClientData failed.\r\n") );
+
+ return E_FAIL;
+ }
+}; // class CStreamOnServerContext
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CReadStreamOnSocket
+//
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename TSocketClass>
+class CReadStreamOnSocket : public IStreamImpl
+{
+public:
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (ppv == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppv = NULL;
+
+ if (InlineIsEqualGUID(riid, IID_IUnknown) ||
+ InlineIsEqualGUID(riid, IID_IStream) ||
+ InlineIsEqualGUID(riid, IID_ISequentialStream))
+ {
+ *ppv = static_cast<IStream *>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+private:
+
+ CAtlHttpClientT<TSocketClass> * m_pSocket;
+ LPCSTR m_szBuffer;
+ LPCSTR m_szCurr;
+ long m_nBodyLen;
+
+public:
+
+ CReadStreamOnSocket()
+ : m_pSocket(NULL), m_szBuffer(NULL), m_szCurr(NULL), m_nBodyLen(0)
+ {
+ }
+
+ BOOL Init(CAtlHttpClientT<TSocketClass> *pSocket)
+ {
+ ATLENSURE( pSocket != NULL );
+
+ m_pSocket = pSocket;
+ m_szBuffer = (LPCSTR) pSocket->GetBody();
+
+ ATLSOAP_TRACE( (LPBYTE) pSocket->GetBody(), pSocket->GetBodyLength() );
+
+ if (m_szBuffer != NULL)
+ {
+ m_szCurr = m_szBuffer;
+ m_nBodyLen = pSocket->GetBodyLength();
+ if (m_nBodyLen != 0)
+ {
+ return TRUE;
+ }
+ }
+
+ ATLTRACE( _T("ATLSOAP: CReadStreamOnSocket::Init failed.\r\n") );
+
+ return FALSE;
+ }
+
+ HRESULT __stdcall Read(void *pDest, ULONG nMaxLen, ULONG *pnRead)
+ {
+ ATLASSERT( pDest != NULL );
+ ATLASSUME( m_pSocket != NULL );
+ ATLASSUME( m_szBuffer != NULL );
+
+ if (pnRead != NULL)
+ {
+ *pnRead = 0;
+ }
+
+ long nRead = (int) (m_szCurr-m_szBuffer);
+ if (nRead < m_nBodyLen)
+ {
+ long nLength = __min((int)(m_nBodyLen-nRead), (LONG) nMaxLen);
+ Checked::memcpy_s(pDest, nMaxLen, m_szCurr, nLength);
+ m_szCurr+= nLength;
+
+ if (pnRead != NULL)
+ {
+ *pnRead = (ULONG) nLength;
+ }
+ }
+
+ return S_OK;
+ }
+}; // class CReadStreamOnSocket
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CWriteStreamOnCString
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class CWriteStreamOnCString : public IWriteStream
+{
+
+public:
+ CStringA m_str;
+
+ virtual ~CWriteStreamOnCString()
+ {
+ }
+
+ HRESULT WriteStream(LPCSTR szOut, int nLen, LPDWORD pdwWritten)
+ {
+ ATLENSURE_RETURN( szOut != NULL );
+
+ if (nLen < 0)
+ {
+ nLen = (int) strlen(szOut);
+ }
+
+ _ATLTRY
+ {
+ m_str.Append(szOut, nLen);
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (pdwWritten != NULL)
+ {
+ *pdwWritten = (DWORD) nLen;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT FlushStream()
+ {
+ return S_OK;
+ }
+
+ void Cleanup()
+ {
+ m_str.Empty();
+ }
+}; // class CWriteStreamOnCString
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Namespaces
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#define SOAPENV_NAMESPACEA "http://schemas.xmlsoap.org/soap/envelope/"
+#define SOAPENV_NAMESPACEW ATLSOAP_MAKEWIDESTR( SOAPENV_NAMESPACEA )
+
+#define SOAPENC_NAMESPACEA "http://schemas.xmlsoap.org/soap/encoding/"
+#define SOAPENC_NAMESPACEW ATLSOAP_MAKEWIDESTR( SOAPENC_NAMESPACEA )
+
+#define XSI_NAMESPACEA "http://www.w3.org/2001/XMLSchema-instance"
+#define XSI_NAMESPACEW ATLSOAP_MAKEWIDESTR( XSI_NAMESPACEA )
+
+#define XSD_NAMESPACEA "http://www.w3.org/2001/XMLSchema"
+#define XSD_NAMESPACEW ATLSOAP_MAKEWIDESTR( XSD_NAMESPACEA )
+
+#ifndef ATLSOAP_GENERIC_NAMESPACE
+#define ATLSOAP_GENERIC_NAMESPACE L"http://www.tempuri.org"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Helpers
+//
+////////////////////////////////////////////////////////////////////////////////
+
+inline HRESULT GetAttribute(
+ __in ISAXAttributes *pAttributes,
+ __in_ecount(cchName) const wchar_t *wszAttrName, __in int cchName,
+ __out_ecount_part(*pcchValue, *pcchValue) const wchar_t **pwszValue, __inout int *pcchValue,
+ __in_ecount_opt(cchNamespace) wchar_t *wszNamespace = NULL, __in int cchNamespace = 0)
+{
+ if (!pAttributes || !wszAttrName || !pwszValue || !pcchValue)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pwszValue = NULL;
+ *pcchValue = 0;
+ if (!wszNamespace)
+ {
+ return (pAttributes->getValueFromQName(wszAttrName, cchName, pwszValue, pcchValue) == S_OK ? S_OK : E_FAIL);
+ }
+ return (pAttributes->getValueFromName(wszNamespace, cchNamespace,
+ wszAttrName, cchName, pwszValue, pcchValue) == S_OK ? S_OK : E_FAIL);
+}
+
+inline HRESULT GetAttribute(
+ __in ISAXAttributes *pAttributes,
+ __in_ecount(cchName) const wchar_t *wszAttrName, __in int cchName,
+ __inout CStringW &strValue,
+ __in_ecount_opt(cchNamespace) wchar_t *wszNamespace = NULL, __in int cchNamespace = 0)
+{
+ const wchar_t *wszValue = NULL;
+ int cchValue = 0;
+
+ if (!pAttributes || !wszAttrName)
+ {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr;
+ if (!wszNamespace)
+ {
+ hr = (pAttributes->getValueFromQName(wszAttrName, cchName, &wszValue, &cchValue) == S_OK ? S_OK : E_FAIL);
+ }
+ else
+ {
+ hr = (pAttributes->getValueFromName(wszNamespace, cchNamespace,
+ wszAttrName, cchName, &wszValue, &cchValue) == S_OK ? S_OK : E_FAIL);
+ }
+
+ if (hr == S_OK)
+ {
+ _ATLTRY
+ {
+ strValue.SetString(wszValue, cchValue);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: GetAttribute -- out of memory.\r\n") );
+
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ return hr;
+}
+
+inline const wchar_t *SkipWhitespace(const wchar_t *wsz)
+{
+ while (*wsz && iswspace(*wsz))
+ ++wsz;
+ return wsz;
+}
+
+} // namespace ATL
+#pragma pack(pop)
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BLOB data type - use this struct when you want to send BLOB data
+// the attribute provider and proxy generator will only properly special
+// case blob data when using this struct.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+[ export ]
+typedef struct _tagATLSOAP_BLOB
+{
+ unsigned long size;
+ unsigned char *data;
+} ATLSOAP_BLOB;
+
+#ifndef _ATL_SOAP_NO_PARAMETER_VALIDATIONS
+#define _ATL_VALIDATE_PARAMETER_END(p)\
+ do \
+ { \
+ if(*(p) !='\0') \
+ return E_FAIL; \
+ } while(0)
+#else
+#define _ATL_VALIDATE_PARAMETER_END(p)
+#endif
+
+// All non-integral types have specializations which
+// will be called. The following function will be called
+// only for integral types
+
+#pragma push_macro("max")
+#pragma push_macro("min")
+#undef max
+#undef min
+template <typename T>
+inline HRESULT AtlGetSAXValue(T * pVal , const wchar_t * wsz , int cch )
+{
+ __int64 nVal = *pVal;
+ if (FAILED(AtlGetSAXValue(&nVal, wsz, cch)))
+ return E_FAIL;
+
+#ifndef _ATL_SOAP_NO_PARAMETER_VALIDATIONS
+ if(nVal < std::numeric_limits<T>::min() || nVal > std::numeric_limits<T>::max())
+ return E_FAIL;
+#endif
+
+ *pVal = T(nVal);
+ return S_OK;
+
+
+}
+#pragma pop_macro("max")
+#pragma pop_macro("min")
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AtlGetXMLValue (for IXMLDOMDocument) - get the real type from the XML data
+//
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// generic IXMLDOMNode template function
+// delegates to AtlGetSAXValue
+//
+template <typename T>
+inline HRESULT AtlGetXMLValue(IXMLDOMNode *pParam, T *pVal)
+{
+ CComBSTR bstrVal;
+ HRESULT hr = AtlGetXMLValue(pParam, &bstrVal);
+ if (SUCCEEDED(hr))
+ {
+ hr = AtlGetSAXValue(pVal, bstrVal, bstrVal.Length());
+ }
+
+ return hr;
+}
+
+// specialization for BSTR
+template <>
+inline HRESULT AtlGetXMLValue<BSTR>(IXMLDOMNode *pParam, BSTR *pbstrVal)
+{
+ if (pParam == NULL)
+ {
+ return E_INVALIDARG;
+ }
+ if (pbstrVal == NULL)
+ {
+ return E_POINTER;
+ }
+
+ CComPtr<IXMLDOMNode> spChild;
+ if (pParam->get_firstChild(&spChild) == S_OK)
+ {
+ CComPtr<IXMLDOMNode> spXmlChild;
+ if (spChild->get_firstChild(&spXmlChild) == S_OK)
+ {
+ return (pParam->get_xml(pbstrVal) == S_OK ? S_OK : E_FAIL);
+ }
+ }
+
+ return (pParam->get_text(pbstrVal) == S_OK) ? S_OK : E_FAIL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AtlGetSAXValue - (for SAX or generic) get the real type from the XML data
+//
+////////////////////////////////////////////////////////////////////////////////
+
+template <>
+inline HRESULT AtlGetSAXValue<bool>(bool *pVal, __in_z const wchar_t *wsz, int cch)
+{
+ ATLENSURE( wsz != NULL );
+
+ if (!pVal)
+ {
+ return E_POINTER;
+ }
+
+ *pVal = false;
+
+ HRESULT hr = E_FAIL;
+ switch (wsz[0])
+ {
+ case L'1':
+ {
+ if (cch==1)
+ {
+ *pVal = true;
+ hr = S_OK;
+ }
+ break;
+ }
+ case L'0':
+ {
+ if (cch==1)
+ {
+ *pVal = false;
+ hr = S_OK;
+ }
+ break;
+ }
+ case L't':
+ {
+ if (cch==sizeof("true")-1 && !wcsncmp(wsz, L"true", cch))
+ {
+ *pVal = true;
+ hr = S_OK;
+ }
+ break;
+ }
+ case L'f':
+ {
+ if (cch==sizeof("false")-1 && !wcsncmp(wsz, L"false", cch))
+ {
+ *pVal = false;
+ hr = S_OK;
+ }
+ break;
+ }
+ }
+
+ return hr;
+}
+
+template <>
+inline HRESULT AtlGetSAXValue<__int64>(__int64 *pVal, __in_z const wchar_t *wsz, int cch)
+{
+ ATLENSURE_RETURN( wsz != NULL );
+
+ if (!pVal)
+ {
+ return E_POINTER;
+ }
+
+ _ATLTRY
+ {
+ CFixedStringT<CStringW, 1024> wstr(wsz, cch);
+ const wchar_t *pStart = ATL::SkipWhitespace(static_cast<LPCWSTR>(wstr));
+ const wchar_t *pEnd;
+
+ __int64 i = 0;
+ errno_t errnoValue = AtlStrToNum(&i, pStart, const_cast<wchar_t **>(&pEnd), 10);
+ if (errnoValue == ERANGE)
+ {
+ return E_FAIL;//overflow or underflow case
+ }
+ pEnd = ATL::SkipWhitespace(pEnd);
+ _ATL_VALIDATE_PARAMETER_END(pEnd);
+ *pVal = i;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+template <>
+inline HRESULT AtlGetSAXValue<unsigned __int64>(unsigned __int64 *pVal, __in_z const wchar_t *wsz, int cch)
+{
+ ATLENSURE_RETURN( wsz != NULL );
+
+ if (!pVal)
+ {
+ return E_POINTER;
+ }
+
+ _ATLTRY
+ {
+ CFixedStringT<CStringW, 1024> wstr(wsz, cch);
+ const wchar_t *pStart = ATL::SkipWhitespace(static_cast<LPCWSTR>(wstr));
+ const wchar_t *pEnd;
+
+ unsigned __int64 i = 0;
+ errno_t errnoValue = AtlStrToNum(&i, pStart, const_cast<wchar_t **>(&pEnd), 10);
+ if (errnoValue == ERANGE)
+ {
+ return E_FAIL;//overflow or underflow case
+ }
+ pEnd = ATL::SkipWhitespace(pEnd);
+ _ATL_VALIDATE_PARAMETER_END(pEnd);
+ *pVal = i;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+template <>
+inline HRESULT AtlGetSAXValue<double>(double *pVal, __in_z const wchar_t *wsz, int cch)
+{
+ ATLENSURE_RETURN( wsz != NULL );
+
+ if (!pVal)
+ {
+ return E_POINTER;
+ }
+
+ if ((cch == 3) && (wsz[0]==L'I') && (!wcsncmp(wsz, L"INF", cch)))
+ {
+ *(((int *) pVal)+0) = 0x0000000;
+ *(((int *) pVal)+1) = 0x7FF00000;
+ }
+ else if ((cch == 3) && (wsz[0]==L'N') && (!wcsncmp(wsz, L"NaN", cch)))
+ {
+ *(((int *) pVal)+0) = 0x0000000;
+ *(((int *) pVal)+1) = 0xFFF80000;
+ }
+ else if ((cch == 4) && (wsz[1]==L'I') && (!wcsncmp(wsz, L"-INF", cch)))
+ {
+ *(((int *) pVal)+0) = 0x0000000;
+ *(((int *) pVal)+1) = 0xFFF00000;
+ }
+ else
+ {
+ errno_t errnoValue = 0;
+
+ _ATLTRY
+ {
+ CFixedStringT<CStringW, 1024> wstr(wsz, cch);
+ const wchar_t *pStart = ATL::SkipWhitespace(static_cast<LPCWSTR>(wstr));
+ const wchar_t *pEnd;
+ double d = 0.0;
+ errnoValue = AtlStrToNum(&d, pStart, const_cast<wchar_t **>(&pEnd));
+ pEnd = ATL::SkipWhitespace(pEnd);
+ _ATL_VALIDATE_PARAMETER_END(pEnd);
+ *pVal = d;
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if ((*pVal == -HUGE_VAL) || (*pVal == HUGE_VAL) || (errnoValue == ERANGE))
+ {
+ return E_FAIL;
+ }
+ }
+
+ return S_OK;
+}
+
+template <>
+inline HRESULT AtlGetSAXValue<float>(float *pVal, __in_z const wchar_t *wsz, int cch)
+{
+ ATLASSERT( wsz != NULL );
+
+ if (!pVal)
+ {
+ return E_POINTER;
+ }
+
+ double d = *pVal;
+ if (SUCCEEDED(AtlGetSAXValue(&d, wsz, cch)))
+ {
+#ifdef _ATL_SOAP_PARAMETER_VALIDATIONS
+ if(d > FLT_MAX || d < -FLT_MAX)
+ return E_FAIL;
+#endif
+ *pVal = (float) d;
+ return S_OK;
+ }
+
+ return E_FAIL;
+}
+
+template <>
+inline HRESULT AtlGetSAXValue<BSTR>(BSTR *pVal, __in_z const wchar_t *wsz, int cch)
+{
+ ATLASSERT( wsz != NULL );
+
+ if (pVal == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *pVal = SysAllocStringLen(wsz, cch);
+
+ return ((*pVal != NULL) ? S_OK : E_OUTOFMEMORY);
+}
+
+inline HRESULT AtlGetSAXBlobValue(
+ ATLSOAP_BLOB *pVal,
+ const wchar_t *wsz,
+ int cch,
+ IAtlMemMgr *pMemMgr,
+ bool bHex = false)
+{
+ ATLENSURE_RETURN( wsz != NULL );
+ ATLENSURE_RETURN( pMemMgr != NULL );
+
+ if (pVal == NULL)
+ {
+ return E_POINTER;
+ }
+
+ if (pVal->data != NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ pVal->data = NULL;
+ pVal->size = 0;
+
+ int nLength = AtlUnicodeToUTF8(wsz, cch, NULL, 0);
+
+ if (nLength != 0)
+ {
+ char * pSrc = (char *) pMemMgr->Allocate(nLength);
+ if (pSrc != NULL)
+ {
+ nLength = AtlUnicodeToUTF8(wsz, cch, pSrc, nLength);
+ if (nLength != 0)
+ {
+ pVal->data = (unsigned char *) pMemMgr->Allocate(nLength);
+ if (pVal->data != NULL)
+ {
+ BOOL bRet;
+ int nDataLength = nLength;
+ if (!bHex)
+ {
+ bRet = Base64Decode(pSrc, nLength, pVal->data, &nDataLength);
+ }
+ else
+ {
+ bRet = AtlHexDecode(pSrc, nLength, pVal->data, &nDataLength);
+ }
+ if (bRet)
+ {
+ pVal->size = nDataLength;
+ }
+ }
+ }
+
+ pMemMgr->Free(pSrc);
+ }
+ }
+
+ if (pVal->size == 0)
+ {
+ if (pVal->data != NULL)
+ {
+ pMemMgr->Free(pVal->data);
+ pVal->data = NULL;
+ }
+ }
+
+ return S_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AtlGenXMLValue template and specializations
+//
+////////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+inline HRESULT AtlGenXMLValue(__in IWriteStream *pStream, __in T *pVal)
+{
+ if ((pStream == NULL) || (pVal == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ //
+ // delegate to CWriteStreamHelper
+ //
+ CWriteStreamHelper s(pStream);
+
+ return (s.Write(*pVal) == TRUE ? S_OK : E_FAIL);
+}
+
+#ifdef _NATIVE_WCHAR_T_DEFINED
+template <>
+inline HRESULT AtlGenXMLValue<wchar_t>(__in IWriteStream *pStream, __in wchar_t *pVal)
+{
+ return AtlGenXMLValue(pStream, (unsigned short *)pVal);
+}
+#endif
+
+template <>
+inline HRESULT AtlGenXMLValue<wchar_t *>(__in IWriteStream *pStream, __deref_inout_z wchar_t **pVal)
+{
+ if ((pStream == NULL) || (*pVal == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ wchar_t *wszWrite = *pVal;
+ int nSrcLen = (int)wcslen(*pVal);
+ int nCnt = EscapeXML(*pVal, nSrcLen, NULL, 0);
+ if (nCnt > nSrcLen)
+ {
+ nCnt++;
+ wszWrite = (wchar_t *)calloc((nCnt),sizeof(wchar_t));
+ if (wszWrite == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ nCnt = EscapeXML(*pVal, nSrcLen, wszWrite, nCnt);
+ if (nCnt == 0)
+ {
+ free(wszWrite);
+ return E_FAIL;
+ }
+ wszWrite[nCnt] = L'\0';
+ nSrcLen = nCnt;
+ }
+
+ nCnt = AtlUnicodeToUTF8(wszWrite, nSrcLen, NULL, 0);
+ HRESULT hr = E_FAIL;
+ if ((nCnt == 0) || (nCnt == nSrcLen))
+ {
+ CWriteStreamHelper s(pStream);
+
+ hr = (s.Write(wszWrite) == TRUE ? S_OK : E_FAIL);
+ }
+ else
+ {
+ nCnt++;
+ CHeapPtr<char> szWrite;
+ szWrite.AllocateBytes((size_t)(nCnt));
+ if (szWrite != NULL)
+ {
+ nCnt = AtlUnicodeToUTF8(wszWrite, nSrcLen, szWrite, nCnt);
+ if (nCnt != 0)
+ {
+ hr = pStream->WriteStream(szWrite, nCnt, NULL);
+ }
+ }
+ else
+ {
+ ATLTRACE( _T("ATLSOAP: AtlGenXMLValue<wchar_t *> -- out of memory.\r\n") );
+
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ if (wszWrite != *pVal)
+ {
+ free(wszWrite);
+ }
+
+ return hr;
+}
+
+template <>
+inline HRESULT AtlGenXMLValue<double>(IWriteStream *pStream, double *pVal)
+{
+ if ((pStream == NULL) || (pVal == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr;
+ switch (_fpclass(*pVal))
+ {
+ case _FPCLASS_SNAN:
+ case _FPCLASS_QNAN:
+ {
+ hr = pStream->WriteStream("NaN", 3, NULL);
+ break;
+ }
+ case _FPCLASS_NINF:
+ {
+ hr = pStream->WriteStream("-INF", 4, NULL);
+ break;
+ }
+ case _FPCLASS_PINF:
+ {
+ hr = pStream->WriteStream("INF", 3, NULL);
+ break;
+ }
+ case _FPCLASS_NZ:
+ {
+ hr = pStream->WriteStream("-0", 2, NULL);
+ break;
+ }
+ default:
+ {
+ /***
+ * 2 = sign + decimal point
+ * ndec = decimal digits
+ * 5 = exponent letter (e or E), exponent sign, three digits exponent
+ * 1 = extra space for rounding
+ * 1 = string terminator '\0'
+ ***/
+ const int ndec = 512;
+ CHAR szBuf[ndec+9];
+ szBuf[0] = '\0';
+ Checked::gcvt_s(szBuf, _countof(szBuf), *pVal, ndec);
+ size_t nLen = strlen(szBuf);
+ if (nLen && szBuf[nLen-1] == '.')
+ {
+ szBuf[--nLen] = '\0';
+ }
+
+ hr = pStream->WriteStream(szBuf, (int)nLen, NULL);
+ break;
+ }
+ }
+
+ return hr;
+}
+
+template <>
+inline HRESULT AtlGenXMLValue<float>(IWriteStream *pStream, float *pVal)
+{
+ if ((pStream == NULL) || (pVal == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ double d = *pVal;
+
+ return AtlGenXMLValue(pStream, &d);
+}
+
+template <>
+inline HRESULT AtlGenXMLValue<bool>(IWriteStream *pStream, bool *pVal)
+{
+ if ((pStream == NULL) || (pVal == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ if (*pVal == true)
+ {
+ return pStream->WriteStream("true", sizeof("true")-1, NULL);
+ }
+
+ return pStream->WriteStream("false", sizeof("false")-1, NULL);
+}
+
+inline HRESULT AtlGenXMLBlobValue(
+ IWriteStream *pStream,
+ ATLSOAP_BLOB *pVal,
+ IAtlMemMgr *pMemMgr,
+ bool bHex = false)
+{
+ if ((pStream == NULL) || (pVal == NULL) || (pMemMgr == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr = E_FAIL;
+ int nLength;
+ if (!bHex)
+ {
+ nLength = Base64EncodeGetRequiredLength(pVal->size, ATLSOAP_BASE64_FLAGS);
+ }
+ else
+ {
+ nLength = AtlHexEncodeGetRequiredLength(pVal->size);
+ }
+
+ char *pEnc = (char *) pMemMgr->Allocate(nLength);
+ if (pEnc != NULL)
+ {
+ BOOL bRet;
+ if (!bHex)
+ {
+ bRet = Base64Encode(pVal->data, pVal->size, pEnc, &nLength, ATLSOAP_BASE64_FLAGS);
+ }
+ else
+ {
+ bRet = AtlHexEncode(pVal->data, pVal->size, pEnc, &nLength);
+ }
+ if (bRet)
+ {
+ hr = pStream->WriteStream(pEnc, nLength, NULL);
+ }
+
+ pMemMgr->Free(pEnc);
+ }
+
+ return hr;
+}
+
+template <typename T>
+inline HRESULT AtlCleanupValue(T * /*pVal*/)
+{
+ return S_OK;
+}
+
+inline HRESULT AtlCleanupBlobValue(ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr)
+{
+ if ((pVal == NULL) || (pMemMgr == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ if (pVal->data != NULL)
+ {
+ pMemMgr->Free(pVal->data);
+ pVal->data = NULL;
+ pVal->size = 0;
+ }
+
+ return S_OK;
+}
+
+template <>
+inline HRESULT AtlCleanupValue<ATLSOAP_BLOB>(ATLSOAP_BLOB *pVal)
+{
+ ATLTRACE( _T("Warning: AtlCleanupValue<ATLSOAP_BLOB> was called -- assuming CRT allocator.\r\n") );
+
+ if (pVal == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (pVal->data != NULL)
+ {
+ free(pVal->data);
+ pVal->data = NULL;
+ pVal->size = 0;
+ }
+
+ return S_OK;
+}
+
+template <>
+inline HRESULT AtlCleanupValue<BSTR>(BSTR *pVal)
+{
+ if (pVal == NULL)
+ {
+ // should never happen
+ ATLASSERT( FALSE );
+ return E_INVALIDARG;
+ }
+
+ if ((*pVal) != NULL)
+ {
+ // null strings are okay
+ SysFreeString(*pVal);
+ *pVal = NULL;
+ }
+
+ return S_OK;
+}
+
+template <typename T>
+inline HRESULT AtlCleanupValueEx(T *pVal, IAtlMemMgr *pMemMgr)
+{
+ pMemMgr;
+
+ return AtlCleanupValue(pVal);
+}
+
+template <>
+inline HRESULT AtlCleanupValueEx<ATLSOAP_BLOB>(ATLSOAP_BLOB *pVal, IAtlMemMgr *pMemMgr)
+{
+ return AtlCleanupBlobValue(pVal, pMemMgr);
+}
+
+// single dimensional arrays
+template <typename T>
+inline HRESULT AtlCleanupArray(T *pArray, int nCnt)
+{
+ if (pArray == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ for (int i=0; i<nCnt; i++)
+ {
+ AtlCleanupValue(&pArray[i]);
+ }
+
+ return S_OK;
+}
+
+
+template <typename T>
+inline HRESULT AtlCleanupArrayEx(T *pArray, int nCnt, IAtlMemMgr *pMemMgr)
+{
+ if (pArray == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ for (int i=0; i<nCnt; i++)
+ {
+ AtlCleanupValueEx(&pArray[i], pMemMgr);
+ }
+
+ return S_OK;
+}
+
+
+// multi-dimensional arrays
+template <typename T>
+inline HRESULT AtlCleanupArrayMD(T *pArray, const int *pDims)
+{
+ if ((pArray == NULL) || (pDims == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ // calculate size
+ int nCnt = 1;
+ for (int i=1; i<=pDims[0]; i++)
+ {
+ nCnt*= pDims[i];
+ }
+
+ return AtlCleanupArray(pArray, nCnt);
+}
+
+template <typename T>
+inline HRESULT AtlCleanupArrayMDEx(T *pArray, const int *pDims, IAtlMemMgr *pMemMgr)
+{
+ if ((pArray == NULL) || (pDims == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ // calculate size
+ int nCnt = 1;
+ for (int i=1; i<=pDims[0]; i++)
+ {
+ nCnt*= pDims[i];
+ }
+
+ return AtlCleanupArrayEx(pArray, nCnt, pMemMgr);
+}
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL
+{
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CSAXSoapErrorHandler
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class CSAXSoapErrorHandler : public ISAXErrorHandler
+{
+private:
+
+ CFixedStringT<CStringW, 256> m_strParseError;
+
+public:
+ virtual ~CSAXSoapErrorHandler()
+ {
+ }
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (!ppv)
+ {
+ return E_POINTER;
+ }
+
+ if (InlineIsEqualGUID(riid, __uuidof(ISAXErrorHandler)) ||
+ InlineIsEqualGUID(riid, __uuidof(IUnknown)))
+ {
+ *ppv = static_cast<ISAXErrorHandler*>(this);
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+ const CStringW& GetParseError()
+ {
+ return m_strParseError;
+ }
+
+ HRESULT __stdcall error(
+ ISAXLocator *pLocator,
+ const wchar_t *wszErrorMessage,
+ HRESULT hrErrorCode)
+ {
+ (pLocator);
+ (wszErrorMessage);
+ (hrErrorCode);
+
+ ATLTRACE( _T("ATLSOAP: parse error: %ws\r\n"), wszErrorMessage );
+
+ _ATLTRY
+ {
+ m_strParseError = wszErrorMessage;
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+
+ return hrErrorCode;
+ }
+
+ HRESULT __stdcall fatalError(
+ ISAXLocator *pLocator,
+ const wchar_t *wszErrorMessage,
+ HRESULT hrErrorCode)
+ {
+ (pLocator);
+ (wszErrorMessage);
+ (hrErrorCode);
+
+ ATLTRACE( _T("ATLSOAP: fatal parse error: %ws\r\n"), wszErrorMessage );
+
+ _ATLTRY
+ {
+ m_strParseError = wszErrorMessage;
+ }
+ _ATLCATCHALL()
+ {
+ return E_FAIL;
+ }
+
+ return hrErrorCode;
+ }
+
+ HRESULT __stdcall ignorableWarning(
+ ISAXLocator *pLocator,
+ const wchar_t *wszErrorMessage,
+ HRESULT hrErrorCode)
+ {
+ (pLocator);
+ (wszErrorMessage);
+ (hrErrorCode);
+
+ ATLTRACE( _T("ATLSOAP: ignorable warning: %ws\r\n"), wszErrorMessage );
+
+ return hrErrorCode;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ISAXContentHandlerImpl
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class ISAXContentHandlerImpl :
+ public ISAXContentHandler
+{
+public:
+
+ //
+ // ISAXContentHandler interface
+ //
+
+ HRESULT __stdcall putDocumentLocator(ISAXLocator * /*pLocator*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall startDocument()
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall endDocument()
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall startPrefixMapping(
+ const wchar_t * /*wszPrefix*/,
+ int /*cchPrefix*/,
+ const wchar_t * /*wszUri*/,
+ int /*cchUri*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall endPrefixMapping(
+ const wchar_t * /*wszPrefix*/,
+ int /*cchPrefix*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall startElement(
+ const wchar_t * /*wszNamespaceUri*/,
+ int /*cchNamespaceUri*/,
+ const wchar_t * /*wszLocalName*/,
+ int /*cchLocalName*/,
+ const wchar_t * /*wszQName*/,
+ int /*cchQName*/,
+ ISAXAttributes * /*pAttributes*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall endElement(
+ const wchar_t * /*wszNamespaceUri*/,
+ int /*cchNamespaceUri*/,
+ const wchar_t * /*wszLocalName*/,
+ int /*cchLocalName*/,
+ const wchar_t * /*wszQName*/,
+ int /*cchQName*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall characters(
+ const wchar_t * /*wszChars*/,
+ int /*cchChars*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall ignorableWhitespace(
+ const wchar_t * /*wszChars*/,
+ int /*cchChars*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall processingInstruction(
+ const wchar_t * /*wszTarget*/,
+ int /*cchTarget*/,
+ const wchar_t * /*wszData*/,
+ int /*cchData*/)
+ {
+ return S_OK;
+ }
+
+ HRESULT __stdcall skippedEntity(
+ const wchar_t * /*wszName*/,
+ int /*cchName*/)
+ {
+ return S_OK;
+ }
+}; // class ISAXContentHandlerImpl
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SAX skip element handler utility class
+// (skip an element and all its child elements)
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class CSkipHandler : public ISAXContentHandlerImpl
+{
+public:
+ virtual ~CSkipHandler()
+ {
+ }
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (ppv == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppv = NULL;
+
+ if (InlineIsEqualGUID(riid, IID_IUnknown) ||
+ InlineIsEqualGUID(riid, IID_ISAXContentHandler))
+ {
+ *ppv = static_cast<ISAXContentHandler *>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+private:
+
+ DWORD m_dwReset;
+ CComPtr<ISAXXMLReader> m_spReader;
+ CComPtr<ISAXContentHandler> m_spParent;
+
+ DWORD DisableReset(DWORD dwCnt = 1)
+ {
+ m_dwReset += dwCnt;
+
+ return m_dwReset;
+ }
+
+ DWORD EnableReset()
+ {
+ if (m_dwReset > 0)
+ {
+ --m_dwReset;
+ }
+
+ return m_dwReset;
+ }
+
+public:
+
+ CSkipHandler(ISAXContentHandler *pParent = NULL, ISAXXMLReader *pReader = NULL)
+ : m_spParent(pParent), m_spReader(pReader), m_dwReset(1)
+ {
+ }
+
+ void SetParent(ISAXContentHandler *pParent)
+ {
+ m_spParent = pParent;
+ }
+ void DetachParent()
+ {
+ m_spParent.Detach();
+ }
+
+ void SetReader(ISAXXMLReader *pReader)
+ {
+ m_spReader = pReader;
+ }
+
+ HRESULT __stdcall startElement(
+ const wchar_t * /*wszNamespaceUri*/,
+ int /*cchNamespaceUri*/,
+ const wchar_t * /*wszLocalName*/,
+ int /*cchLocalName*/,
+ const wchar_t * /*wszQName*/,
+ int /*cchQName*/,
+ ISAXAttributes * /*pAttributes*/)
+ {
+ DisableReset();
+ return S_OK;
+ }
+
+ HRESULT __stdcall endElement(
+ const wchar_t * /*wszNamespaceUri*/,
+ int /*cchNamespaceUri*/,
+ const wchar_t * /*wszLocalName*/,
+ int /*cchLocalName*/,
+ const wchar_t * /*wszQName*/,
+ int /*cchQName*/)
+ {
+ if (EnableReset() == 0)
+ {
+ m_spReader->putContentHandler(m_spParent);
+ }
+
+ return S_OK;
+ }
+}; // class CSkipHandler
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SAX string builder class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class CSAXStringBuilder : public ISAXContentHandlerImpl
+{
+public:
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (ppv == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppv = NULL;
+
+ if (InlineIsEqualGUID(riid, IID_IUnknown) ||
+ InlineIsEqualGUID(riid, IID_ISAXContentHandler))
+ {
+ *ppv = static_cast<ISAXContentHandler *>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+private:
+
+ ISAXContentHandler * m_pParent;
+ ISAXXMLReader * m_pReader;
+ DWORD m_dwReset;
+ CFixedStringT<CStringW, 64> m_str;
+
+ DWORD DisableReset(DWORD dwReset = 1)
+ {
+ m_dwReset+= dwReset;
+
+ return m_dwReset;
+ }
+
+ DWORD EnableReset()
+ {
+ if (m_dwReset > 0)
+ {
+ --m_dwReset;
+ }
+
+ return m_dwReset;
+ }
+
+public:
+
+ CSAXStringBuilder(ISAXXMLReader *pReader = NULL, ISAXContentHandler *pParent = NULL)
+ :m_pReader(pReader), m_pParent(pParent), m_dwReset(0)
+ {
+ }
+
+ virtual ~CSAXStringBuilder()
+ {
+ }
+
+ void SetReader(ISAXXMLReader *pReader)
+ {
+ m_pReader = pReader;
+ }
+
+ void SetParent(ISAXContentHandler *pParent)
+ {
+ m_pParent = pParent;
+ }
+
+ const CStringW& GetString()
+ {
+ return m_str;
+ }
+
+ void Clear()
+ {
+ m_str.Empty();
+ m_dwReset = 0;
+ }
+
+ HRESULT __stdcall startElement(
+ const wchar_t * /*wszNamespaceUri*/,
+ int /*cchNamespaceUri*/,
+ const wchar_t * /*wszLocalName*/,
+ int /*cchLocalName*/,
+ const wchar_t *wszQName,
+ int cchQName,
+ ISAXAttributes *pAttributes)
+ {
+ if (m_dwReset == 0)
+ {
+ // if there is unescaped, nested XML, must disable
+ // an additional time for the first element
+ DisableReset();
+ }
+ DisableReset();
+
+ int nAttrs = 0;
+ HRESULT hr = pAttributes->getLength(&nAttrs);
+
+ _ATLTRY
+ {
+ if (SUCCEEDED(hr))
+ {
+ m_str.Append(L"<", 1);
+ m_str.Append(wszQName, cchQName);
+
+ const wchar_t *wszAttrNamespaceUri = NULL;
+ const wchar_t *wszAttrLocalName = NULL;
+ const wchar_t *wszAttrQName = NULL;
+ const wchar_t *wszAttrValue = NULL;
+ int cchAttrUri = 0;
+ int cchAttrLocalName = 0;
+ int cchAttrQName = 0;
+ int cchAttrValue = 0;
+
+ for (int i=0; i<nAttrs; i++)
+ {
+ hr = pAttributes->getName(i, &wszAttrNamespaceUri, &cchAttrUri,
+ &wszAttrLocalName, &cchAttrLocalName, &wszAttrQName, &cchAttrQName);
+
+ if (FAILED(hr))
+ {
+ ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- MSXML error.\r\n") );
+
+ break;
+ }
+
+ m_str.Append(L" ", 1);
+ m_str.Append(wszAttrQName, cchAttrQName);
+
+ hr = pAttributes->getValue(i, &wszAttrValue, &cchAttrValue);
+
+ if (FAILED(hr))
+ {
+ ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- MSXML error.\r\n") );
+
+ break;
+ }
+
+ m_str.Append(L"=\"", sizeof("=\"")-1);
+ if (cchAttrValue != 0)
+ {
+ m_str.Append(wszAttrValue, cchAttrValue);
+ }
+ m_str.Append(L"\"", 1);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ m_str.Append(L">", 1);
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::startElement -- out of memory.\r\n") );
+
+ hr = E_OUTOFMEMORY;
+ }
+
+ return hr;
+ }
+
+ HRESULT __stdcall endElement(
+ const wchar_t * wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t * wszLocalName,
+ int cchLocalName,
+ const wchar_t *wszQName,
+ int cchQName)
+ {
+ HRESULT hr = S_OK;
+ _ATLTRY
+ {
+ if (EnableReset() == 0)
+ {
+ hr = m_pParent->characters((LPCWSTR) m_str, m_str.GetLength());
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pParent->endElement(wszNamespaceUri, cchNamespaceUri,
+ wszLocalName, cchLocalName, wszQName, cchQName);
+ }
+
+ m_pReader->putContentHandler(m_pParent);
+ }
+
+ if (m_dwReset > 0)
+ {
+ m_str.Append(L"</", 2);
+ m_str.Append(wszQName, cchQName);
+ m_str.Append(L">", 1);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::endElement -- out of memory.\r\n") );
+
+ hr = E_OUTOFMEMORY;
+ }
+
+ return hr;
+ }
+
+ HRESULT __stdcall characters(
+ const wchar_t *wszChars,
+ int cchChars)
+ {
+ _ATLTRY
+ {
+ m_str.Append(wszChars, cchChars);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::characters -- out of memory.\r\n") );
+
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT __stdcall ignorableWhitespace(
+ const wchar_t *wszChars,
+ int cchChars)
+ {
+ _ATLTRY
+ {
+ m_str.Append(wszChars, cchChars);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSAXStringBuilder::ignorableWhitespace -- out of memory.\r\n") );
+
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+}; // class CSAXStringBuilder
+
+} // namespace ATL
+#pragma pack(pop)
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SOAP data structure definitions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//
+// ***************************** WARNING *****************************
+// THESE STRUCTURES ARE INTERNAL ONLY, FOR USE WITH THE ATL SERVER SOAP
+// ATTRIBUTES. USERS SHOULD NOT USE THESE TYPES DIRECTLY. ABSOLUTELY NO
+// GUARANTEES ARE MADE ABOUT BACKWARD COMPATIBILITY FOR DIRECT USE OF
+// THESE TYPES.
+//
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// BEGIN PRIVATE DEFINITIONS
+//
+////////////////////////////////////////////////////////////////////////////////
+
+inline HRESULT AtlSoapGetArraySize(ISAXAttributes *pAttributes, size_t *pnSize,
+ const wchar_t **pwszTypeStart = NULL, const wchar_t **pwszTypeEnd = NULL)
+{
+ if (pnSize == NULL)
+ {
+ return E_POINTER;
+ }
+
+ if (pAttributes == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pnSize = 0;
+
+ HRESULT hr = S_OK;
+
+ _ATLTRY
+ {
+ const wchar_t *wszTmp;
+ int cch;
+
+ hr = GetAttribute(pAttributes, L"arrayType", sizeof("arrayType")-1,
+ &wszTmp, &cch, SOAPENC_NAMESPACEW, sizeof(SOAPENC_NAMESPACEA)-1);
+
+ if ((SUCCEEDED(hr)) && (wszTmp != NULL))
+ {
+ hr = E_FAIL;
+
+ CFixedStringT<CStringW, 1024> wstrArrayType(wszTmp, cch);
+ const wchar_t *wsz = static_cast<LPCWSTR>(wstrArrayType);
+
+ const wchar_t *wszTypeStart = NULL;
+ const wchar_t *wszTypeEnd = NULL;
+
+ // skip spaces
+ while (iswspace(*wsz) != 0)
+ {
+ wsz++;
+ }
+
+ // no need to walk the string if the caller is not interested
+ if ((pwszTypeStart != NULL) && (pwszTypeEnd != NULL))
+ {
+ wszTypeStart = wsz;
+ wszTypeEnd = wcschr(wszTypeStart, L':');
+ if (wszTypeEnd != NULL)
+ {
+ wszTypeStart = wszTypeEnd+1;
+ }
+ }
+
+ // SOAP Section 5 encodings are of the form:
+ // <soap_enc namespace>:arrayType="<type_qname>[dim1(,dim_i)*]
+ // for example: SOAP-ENC:arrayType="xsd:string[2,4]"
+
+ wsz = wcschr(wsz, L'[');
+ if (wsz != NULL)
+ {
+ wszTypeEnd = wsz-1;
+ if (wsz[1] == ']')
+ {
+ return S_FALSE;
+ }
+
+ *pnSize = 1;
+
+ // get the size of each dimension
+ while (wsz != NULL)
+ {
+ wsz++;
+ int nDim = _wtoi(wsz);
+ if (nDim < 0)
+ {
+ hr = E_FAIL;
+ break;
+ }
+ *pnSize *= (size_t) nDim;
+ if (!nDim)
+ {
+ break;
+ }
+
+ wsz = wcschr(wsz, L',');
+ }
+
+ if ((pwszTypeStart != NULL) && (pwszTypeEnd != NULL))
+ {
+ *pwszTypeStart = wszTypeStart;
+ *pwszTypeEnd = wszTypeEnd;
+ }
+
+ hr = S_OK;
+ }
+ }
+ else
+ {
+ // not a section-5 encoding
+ hr = S_FALSE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ return hr;
+}
+
+inline size_t AtlSoapGetArrayDims(const int *pDims)
+{
+ if (pDims == NULL)
+ {
+ return 0;
+ }
+
+ size_t nRet = 1;
+ for (int i=1; i<=pDims[0]; i++)
+ {
+ nRet *= pDims[i];
+ }
+
+ return nRet;
+}
+
+enum SOAPFLAGS
+{
+ SOAPFLAG_NONE = 0x00000000,
+ SOAPFLAG_IN = 0x00000001,
+ SOAPFLAG_OUT = 0x00000002,
+ SOAPFLAG_RETVAL = 0x00000004,
+ SOAPFLAG_DYNARR = 0x00000008,
+ SOAPFLAG_FIXEDARR = 0x00000010,
+ SOAPFLAG_MUSTUNDERSTAND = 0x00000020,
+ SOAPFLAG_UNKSIZE = 0x00000040,
+ SOAPFLAG_READYSTATE = 0x00000080,
+ SOAPFLAG_FIELD = 0x00000100,
+ SOAPFLAG_NOMARSHAL = 0x00000200,
+ SOAPFLAG_NULLABLE = 0x00000400,
+ SOAPFLAG_DOCUMENT = 0x00000800,
+ SOAPFLAG_RPC = 0x00001000,
+ SOAPFLAG_LITERAL = 0x00002000,
+ SOAPFLAG_ENCODED = 0x00004000,
+ SOAPFLAG_PID = 0x00008000,
+ SOAPFLAG_PAD = 0x00010000,
+ SOAPFLAG_CHAIN = 0x00020000,
+ SOAPFLAG_SIZEIS = 0x00040000,
+ SOAPFLAG_DYNARRWRAPPER = 0x00080000
+};
+
+enum SOAPMAPTYPE
+{
+ SOAPMAP_ERR = 0,
+ SOAPMAP_ENUM,
+ SOAPMAP_FUNC,
+ SOAPMAP_STRUCT,
+ SOAPMAP_UNION,
+ SOAPMAP_HEADER,
+ SOAPMAP_PARAM
+};
+
+struct _soapmap;
+
+struct _soapmapentry
+{
+ ULONG nHash;
+ const char * szField;
+ const WCHAR * wszField;
+ int cchField;
+ int nVal;
+ DWORD dwFlags;
+
+ size_t nOffset;
+ const int * pDims;
+
+ const _soapmap * pChain;
+
+ int nSizeIs;
+
+ ULONG nNamespaceHash;
+ const char *szNamespace;
+ const wchar_t *wszNamespace;
+ int cchNamespace;
+};
+
+struct _soapmap
+{
+ ULONG nHash;
+ const char * szName;
+ const wchar_t * wszName;
+ int cchName;
+ int cchWName;
+ SOAPMAPTYPE mapType;
+ const _soapmapentry * pEntries;
+ size_t nElementSize;
+ size_t nElements;
+ int nRetvalIndex;
+
+ DWORD dwCallFlags;
+
+ ULONG nNamespaceHash;
+ const char *szNamespace;
+ const wchar_t *wszNamespace;
+ int cchNamespace;
+};
+
+enum SOAPTYPES
+{
+ SOAPTYPE_ERR = -2,
+ SOAPTYPE_UNK = -1,
+ SOAPTYPE_STRING = 0,
+ SOAPTYPE_BOOLEAN,
+ SOAPTYPE_FLOAT,
+ SOAPTYPE_DOUBLE,
+ SOAPTYPE_DECIMAL,
+ SOAPTYPE_DURATION,
+ SOAPTYPE_HEXBINARY,
+ SOAPTYPE_BASE64BINARY,
+ SOAPTYPE_ANYURI,
+ SOAPTYPE_ID,
+ SOAPTYPE_IDREF,
+ SOAPTYPE_ENTITY,
+ SOAPTYPE_NOTATION,
+ SOAPTYPE_QNAME,
+ SOAPTYPE_NORMALIZEDSTRING,
+ SOAPTYPE_TOKEN,
+ SOAPTYPE_LANGUAGE,
+ SOAPTYPE_IDREFS,
+ SOAPTYPE_ENTITIES,
+ SOAPTYPE_NMTOKEN,
+ SOAPTYPE_NMTOKENS,
+ SOAPTYPE_NAME,
+ SOAPTYPE_NCNAME,
+ SOAPTYPE_INTEGER,
+ SOAPTYPE_NONPOSITIVEINTEGER,
+ SOAPTYPE_NEGATIVEINTEGER,
+ SOAPTYPE_LONG,
+ SOAPTYPE_INT,
+ SOAPTYPE_SHORT,
+ SOAPTYPE_BYTE,
+ SOAPTYPE_NONNEGATIVEINTEGER,
+ SOAPTYPE_UNSIGNEDLONG,
+ SOAPTYPE_UNSIGNEDINT,
+ SOAPTYPE_UNSIGNEDSHORT,
+ SOAPTYPE_UNSIGNEDBYTE,
+ SOAPTYPE_POSITIVEINTEGER,
+ SOAPTYPE_DATETIME,
+ SOAPTYPE_TIME,
+ SOAPTYPE_DATE,
+ SOAPTYPE_GMONTH,
+ SOAPTYPE_GYEARMONTH,
+ SOAPTYPE_GYEAR,
+ SOAPTYPE_GMONTHDAY,
+ SOAPTYPE_GDAY,
+
+ SOAPTYPE_USERBASE = 0x00001000
+};
+
+inline ULONG AtlSoapHashStr(const char * sz)
+{
+ ULONG nHash = 0;
+ while (*sz != 0)
+ {
+ nHash = (nHash<<5)+nHash+(*sz);
+ sz++;
+ }
+
+ return nHash;
+}
+
+inline ULONG AtlSoapHashStr(const wchar_t * sz)
+{
+ ULONG nHash = 0;
+ while (*sz != 0)
+ {
+ nHash = (nHash<<5)+nHash+(*sz);
+ sz++;
+ }
+
+ return nHash;
+}
+
+inline ULONG AtlSoapHashStr(const char * sz, int cch)
+{
+ ULONG nHash = 0;
+ for (int i=0; i<cch; i++)
+ {
+ nHash = (nHash<<5)+nHash+(*sz);
+ sz++;
+ }
+
+ return nHash;
+}
+
+inline ULONG AtlSoapHashStr(const wchar_t * sz, int cch)
+{
+ ULONG nHash = 0;
+ for (int i=0; i<cch; i++)
+ {
+ nHash = (nHash<<5)+nHash+(*sz);
+ sz++;
+ }
+
+ return nHash;
+}
+
+inline size_t AtlSoapGetElementSize(SOAPTYPES type)
+{
+ size_t nRet;
+ switch (type)
+ {
+ case SOAPTYPE_BOOLEAN:
+ nRet = sizeof(bool);
+ break;
+ case SOAPTYPE_FLOAT:
+ nRet = sizeof(float);
+ break;
+ case SOAPTYPE_DOUBLE:
+ case SOAPTYPE_DECIMAL:
+ nRet = sizeof(double);
+ break;
+ case SOAPTYPE_HEXBINARY:
+ case SOAPTYPE_BASE64BINARY:
+ nRet = sizeof(ATLSOAP_BLOB);
+ break;
+ case SOAPTYPE_INTEGER:
+ case SOAPTYPE_NONPOSITIVEINTEGER:
+ case SOAPTYPE_NEGATIVEINTEGER:
+ case SOAPTYPE_LONG:
+ nRet = sizeof(__int64);
+ break;
+ case SOAPTYPE_INT:
+ nRet = sizeof(int);
+ break;
+ case SOAPTYPE_SHORT:
+ nRet = sizeof(short);
+ break;
+ case SOAPTYPE_BYTE:
+ nRet = sizeof(char);
+ break;
+ case SOAPTYPE_POSITIVEINTEGER:
+ case SOAPTYPE_NONNEGATIVEINTEGER:
+ case SOAPTYPE_UNSIGNEDLONG:
+ nRet = sizeof(unsigned __int64);
+ break;
+ case SOAPTYPE_UNSIGNEDINT:
+ nRet = sizeof(unsigned int);
+ break;
+ case SOAPTYPE_UNSIGNEDSHORT:
+ nRet = sizeof(unsigned short);
+ break;
+ case SOAPTYPE_UNSIGNEDBYTE:
+ nRet = sizeof(unsigned char);
+ break;
+ default:
+ if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
+ {
+ // treat as string
+ nRet = sizeof(BSTR);
+ }
+ else
+ {
+ ATLTRACE( _T("ATLSOAP: AtlSoapGetElementSize -- internal error.\r\n") );
+ // should never get here
+ ATLASSERT( FALSE );
+ nRet = 0;
+ }
+ break;
+ }
+
+ return nRet;
+}
+
+inline HRESULT AtlSoapGetElementValue(const wchar_t *wsz, int cch,
+ void *pVal, SOAPTYPES type, IAtlMemMgr *pMemMgr)
+{
+ HRESULT hr = E_FAIL;
+
+ switch (type)
+ {
+ case SOAPTYPE_BOOLEAN:
+ hr = AtlGetSAXValue((bool *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_FLOAT:
+ hr = AtlGetSAXValue((float *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_DOUBLE:
+ case SOAPTYPE_DECIMAL:
+ hr = AtlGetSAXValue((double *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_HEXBINARY:
+ hr = AtlGetSAXBlobValue((ATLSOAP_BLOB *)pVal, wsz, cch, pMemMgr, true);
+ break;
+ case SOAPTYPE_BASE64BINARY:
+ hr = AtlGetSAXBlobValue((ATLSOAP_BLOB *)pVal, wsz, cch, pMemMgr, false);
+ break;
+
+ case SOAPTYPE_INTEGER:
+ case SOAPTYPE_NONPOSITIVEINTEGER:
+ case SOAPTYPE_NEGATIVEINTEGER:
+ case SOAPTYPE_LONG:
+ hr = AtlGetSAXValue((__int64 *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_INT:
+ hr = AtlGetSAXValue((int *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_SHORT:
+ hr = AtlGetSAXValue((short *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_BYTE:
+ hr = AtlGetSAXValue((char *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_POSITIVEINTEGER:
+ case SOAPTYPE_NONNEGATIVEINTEGER:
+ case SOAPTYPE_UNSIGNEDLONG:
+ hr = AtlGetSAXValue((unsigned __int64 *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_UNSIGNEDINT:
+ hr = AtlGetSAXValue((unsigned int *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_UNSIGNEDSHORT:
+ hr = AtlGetSAXValue((unsigned short *)pVal, wsz, cch);
+ break;
+ case SOAPTYPE_UNSIGNEDBYTE:
+ hr = AtlGetSAXValue((unsigned char *)pVal, wsz, cch);
+ break;
+ default:
+ if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
+ {
+ hr = AtlGetSAXValue((BSTR *)pVal, wsz, cch);
+ }
+#ifdef _DEBUG
+ else
+ {
+ ATLTRACE( _T("ATLSOAP: AtlSoapGetElementValue -- internal error.\r\n") );
+
+ // should never get here
+ ATLASSERT( FALSE );
+ }
+#endif
+ break;
+ }
+
+ return hr;
+}
+
+inline HRESULT AtlSoapGenElementValue(void *pVal, IWriteStream *pStream, SOAPTYPES type, IAtlMemMgr *pMemMgr)
+{
+ HRESULT hr = E_FAIL;
+
+ switch (type)
+ {
+ case SOAPTYPE_BOOLEAN:
+ hr = AtlGenXMLValue(pStream, (bool *)pVal);
+ break;
+ case SOAPTYPE_FLOAT:
+ hr = AtlGenXMLValue(pStream, (float *)pVal);
+ break;
+ case SOAPTYPE_DOUBLE:
+ case SOAPTYPE_DECIMAL:
+ hr = AtlGenXMLValue(pStream, (double *)pVal);
+ break;
+ case SOAPTYPE_HEXBINARY:
+ hr = AtlGenXMLBlobValue(pStream, (ATLSOAP_BLOB *)pVal, pMemMgr, true);
+ break;
+ case SOAPTYPE_BASE64BINARY:
+ hr = AtlGenXMLBlobValue(pStream, (ATLSOAP_BLOB *)pVal, pMemMgr, false);
+ break;
+
+ case SOAPTYPE_INTEGER:
+ case SOAPTYPE_NONPOSITIVEINTEGER:
+ case SOAPTYPE_NEGATIVEINTEGER:
+ case SOAPTYPE_LONG:
+ hr = AtlGenXMLValue(pStream, (__int64 *)pVal);
+ break;
+ case SOAPTYPE_INT:
+ hr = AtlGenXMLValue(pStream, (int *)pVal);
+ break;
+ case SOAPTYPE_SHORT:
+ hr = AtlGenXMLValue(pStream, (short *)pVal);
+ break;
+ case SOAPTYPE_BYTE:
+ hr = AtlGenXMLValue(pStream, (char *)pVal);
+ break;
+ case SOAPTYPE_POSITIVEINTEGER:
+ case SOAPTYPE_NONNEGATIVEINTEGER:
+ case SOAPTYPE_UNSIGNEDLONG:
+ hr = AtlGenXMLValue(pStream, (unsigned __int64 *)pVal);
+ break;
+ case SOAPTYPE_UNSIGNEDINT:
+ hr = AtlGenXMLValue(pStream, (unsigned int *)pVal);
+ break;
+ case SOAPTYPE_UNSIGNEDSHORT:
+ hr = AtlGenXMLValue(pStream, (unsigned short *)pVal);
+ break;
+ case SOAPTYPE_UNSIGNEDBYTE:
+ hr = AtlGenXMLValue(pStream, (unsigned char *)pVal);
+ break;
+ default:
+ if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
+ {
+ hr = AtlGenXMLValue(pStream, (BSTR *)pVal);
+ }
+#ifdef _DEBUG
+ else
+ {
+ ATLTRACE( _T("ATLSOAP: AtlSoapGenElementValue -- internal error.\r\n" ) );
+
+ // should never get here
+ ATLASSERT( FALSE );
+ }
+#endif
+ break;
+ }
+ return hr;
+}
+
+inline HRESULT AtlSoapCleanupElement(void *pVal, SOAPTYPES type, IAtlMemMgr *pMemMgr)
+{
+ HRESULT hr = S_OK;
+
+ switch (type)
+ {
+ case SOAPTYPE_BOOLEAN:
+ case SOAPTYPE_FLOAT:
+ case SOAPTYPE_DOUBLE:
+ case SOAPTYPE_DECIMAL:
+ case SOAPTYPE_INT:
+ case SOAPTYPE_INTEGER:
+ case SOAPTYPE_NONPOSITIVEINTEGER:
+ case SOAPTYPE_NEGATIVEINTEGER:
+ case SOAPTYPE_LONG:
+ case SOAPTYPE_SHORT:
+ case SOAPTYPE_BYTE:
+ case SOAPTYPE_POSITIVEINTEGER:
+ case SOAPTYPE_NONNEGATIVEINTEGER:
+ case SOAPTYPE_UNSIGNEDLONG:
+ case SOAPTYPE_UNSIGNEDINT:
+ case SOAPTYPE_UNSIGNEDSHORT:
+ case SOAPTYPE_UNSIGNEDBYTE:
+ break;
+
+ case SOAPTYPE_HEXBINARY:
+ case SOAPTYPE_BASE64BINARY:
+ hr = AtlCleanupBlobValue((ATLSOAP_BLOB *)pVal, pMemMgr);
+ break;
+
+ default:
+ if ((type != SOAPTYPE_ERR) && (type != SOAPTYPE_UNK) && (type != SOAPTYPE_USERBASE))
+ {
+ // treat as string
+ hr = AtlCleanupValue((BSTR *)pVal);
+ }
+#ifdef _DEBUG
+ else
+ {
+ ATLTRACE( _T("ATLSOAP: AtlSoapCleanupElement -- internal error.\r\n" ) );
+
+ // should never get here
+ ATLASSERT( FALSE );
+ }
+#endif
+ break;
+ }
+
+ return hr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// END PRIVATE DEFINITIONS
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#define SOAP_ENVELOPEA "Envelope"
+#define SOAP_ENVELOPEW ATLSOAP_MAKEWIDESTR( SOAP_ENVELOPEA )
+
+#define SOAP_HEADERA "Header"
+#define SOAP_HEADERW ATLSOAP_MAKEWIDESTR( SOAP_HEADERA )
+
+#define SOAP_BODYA "Body"
+#define SOAP_BODYW ATLSOAP_MAKEWIDESTR( SOAP_BODYA )
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL
+{
+
+//
+// SOAP fault helpers
+//
+
+enum SOAP_ERROR_CODE
+{
+ SOAP_E_UNK=0,
+ SOAP_E_VERSION_MISMATCH=100,
+ SOAP_E_MUST_UNDERSTAND=200,
+ SOAP_E_CLIENT=300,
+ SOAP_E_SERVER=400
+};
+
+// forward declaration of CSoapFault
+class CSoapFault;
+
+class CSoapFaultParser : public ISAXContentHandlerImpl
+{
+private:
+
+ CSoapFault *m_pFault;
+
+ DWORD m_dwState;
+
+ const static DWORD STATE_ERROR = 0;
+ const static DWORD STATE_ENVELOPE = 1;
+ const static DWORD STATE_BODY = 2;
+ const static DWORD STATE_START = 4;
+ const static DWORD STATE_FAULTCODE = 8;
+ const static DWORD STATE_FAULTSTRING = 16;
+ const static DWORD STATE_FAULTACTOR = 32;
+ const static DWORD STATE_DETAIL = 64;
+ const static DWORD STATE_RESET = 128;
+ const static DWORD STATE_SKIP = 256;
+
+
+ CComPtr<ISAXXMLReader> m_spReader;
+ CSAXStringBuilder m_stringBuilder;
+ CSkipHandler m_skipHandler;
+
+ const wchar_t *m_wszSoapPrefix;
+ int m_cchSoapPrefix;
+
+public:
+ virtual ~CSoapFaultParser()
+ {
+ m_skipHandler.DetachParent();
+ }
+
+ // IUnknown interface
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (ppv == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppv = NULL;
+
+ if (InlineIsEqualGUID(riid, IID_IUnknown) ||
+ InlineIsEqualGUID(riid, IID_ISAXContentHandler))
+ {
+ *ppv = static_cast<ISAXContentHandler *>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+ // constructor
+
+ CSoapFaultParser(CSoapFault *pFault, ISAXXMLReader *pReader)
+ :m_pFault(pFault), m_dwState(STATE_ERROR), m_spReader(pReader)
+ {
+ ATLASSERT( pFault != NULL );
+ ATLASSERT( pReader != NULL );
+ }
+
+ // ISAXContentHandler interface
+ HRESULT __stdcall startElement(
+ const wchar_t * wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t * wszLocalName,
+ int cchLocalName,
+ const wchar_t * /*wszQName*/,
+ int /*cchQName*/,
+ ISAXAttributes * /*pAttributes*/)
+ {
+ struct _faultmap
+ {
+ const wchar_t *wszTag;
+ int cchTag;
+ DWORD dwState;
+ };
+
+ const static _faultmap s_faultParseMap[] =
+ {
+ { L"Envelope", sizeof("Envelope")-1, CSoapFaultParser::STATE_ENVELOPE },
+ { L"Body", sizeof("Body")-1, CSoapFaultParser::STATE_BODY },
+ { L"Header", sizeof("Header")-1, CSoapFaultParser::STATE_BODY },
+ { L"Fault", sizeof("Fault")-1, CSoapFaultParser::STATE_START },
+ { L"faultcode", sizeof("faultcode")-1, CSoapFaultParser::STATE_FAULTCODE },
+ { L"faultstring", sizeof("faultstring")-1, CSoapFaultParser::STATE_FAULTSTRING },
+ { L"faultactor", sizeof("faultactor")-1, CSoapFaultParser::STATE_FAULTACTOR },
+ { L"detail", sizeof("detail")-1, CSoapFaultParser::STATE_DETAIL }
+ };
+
+ if (m_spReader.p == NULL)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- ISAXXMLReader is NULL.\r\n" ) );
+
+ return E_INVALIDARG;
+ }
+
+ m_dwState &= ~STATE_RESET;
+ for (int i=0; i<(sizeof(s_faultParseMap)/sizeof(s_faultParseMap[0])); i++)
+ {
+ if ((cchLocalName == s_faultParseMap[i].cchTag) &&
+ (!wcsncmp(wszLocalName, s_faultParseMap[i].wszTag, cchLocalName)))
+ {
+ DWORD dwState = s_faultParseMap[i].dwState;
+ if ((dwState & (STATE_START | STATE_ENVELOPE | STATE_BODY)) == 0)
+ {
+ m_stringBuilder.SetReader(m_spReader);
+ m_stringBuilder.SetParent(this);
+
+ m_stringBuilder.Clear();
+ m_spReader->putContentHandler( &m_stringBuilder );
+ }
+ else
+ {
+ if ((dwState <= m_dwState) ||
+ (cchNamespaceUri != sizeof(SOAPENV_NAMESPACEA)-1) ||
+ (wcsncmp(wszNamespaceUri, SOAPENV_NAMESPACEW, cchNamespaceUri)))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- malformed SOAP fault.\r\n" ) );
+
+ return E_FAIL;
+ }
+ }
+
+ m_dwState = dwState;
+ return S_OK;
+ }
+ }
+ if (m_dwState > STATE_START)
+ {
+ m_dwState = STATE_SKIP;
+ m_skipHandler.SetReader(m_spReader);
+ m_skipHandler.SetParent(this);
+
+ m_spReader->putContentHandler( &m_skipHandler );
+ return S_OK;
+ }
+
+ ATLTRACE( _T("ATLSOAP: CSoapFaultParser::startElement -- malformed SOAP fault.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+ HRESULT __stdcall startPrefixMapping(
+ const wchar_t * wszPrefix,
+ int cchPrefix,
+ const wchar_t * wszUri,
+ int cchUri)
+ {
+ if ((cchUri == sizeof(SOAPENV_NAMESPACEA)-1) &&
+ (!wcsncmp(wszUri, SOAPENV_NAMESPACEW, cchUri)))
+ {
+ m_wszSoapPrefix = wszPrefix;
+ m_cchSoapPrefix = cchPrefix;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT __stdcall characters(
+ const wchar_t * wszChars,
+ int cchChars);
+};
+
+extern __declspec(selectany) const int ATLS_SOAPFAULT_CNT = 4;
+
+class CSoapFault
+{
+private:
+
+ struct _faultcode
+ {
+ const wchar_t *wsz;
+ int cch;
+ const wchar_t *wszFaultString;
+ int cchFaultString;
+ SOAP_ERROR_CODE errCode;
+ };
+
+ static const _faultcode s_faultCodes[];
+
+public:
+
+ // members
+ SOAP_ERROR_CODE m_soapErrCode;
+ CStringW m_strFaultCode;
+ CStringW m_strFaultString;
+ CStringW m_strFaultActor;
+ CStringW m_strDetail;
+
+ CSoapFault()
+ : m_soapErrCode(SOAP_E_UNK)
+ {
+ }
+
+ HRESULT SetErrorCode(
+ const wchar_t *wsz,
+ const wchar_t *wszSoapPrefix,
+ int cch = -1,
+ int cchSoapPrefix = -1,
+ bool bSetFaultString = true)
+ {
+ if ((wsz == NULL) || (wszSoapPrefix == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ if (cch == -1)
+ {
+ cch = (int) wcslen(wsz);
+ }
+
+ while (*wsz && iswspace(*wsz))
+ {
+ ++wsz;
+ --cch;
+ }
+
+ if (cchSoapPrefix == -1)
+ {
+ cchSoapPrefix = (int) wcslen(wszSoapPrefix);
+ }
+
+ const wchar_t *wszLocalName = wcschr(wsz, L':');
+ if (wszLocalName == NULL)
+ {
+ // faultCode must be QName
+
+ ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- faultCode is not a QName.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+ // make sure the namespace of the fault is the
+ // SOAPENV namespace
+ if ((cchSoapPrefix != (int)(wszLocalName-wsz)) ||
+ (wcsncmp(wsz, wszSoapPrefix, cchSoapPrefix)))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- fault namespace is incorrect.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+ wszLocalName++;
+ cch -= (int) (wszLocalName-wsz);
+
+ _ATLTRY
+ {
+ for (int i=0; i<ATLS_SOAPFAULT_CNT; i++)
+ {
+ if ((cch == s_faultCodes[i].cch) &&
+ (!wcsncmp(wszLocalName, s_faultCodes[i].wsz, cch)))
+ {
+ m_soapErrCode = s_faultCodes[i].errCode;
+ if (bSetFaultString != false)
+ {
+ m_strFaultString.SetString(s_faultCodes[i].wszFaultString, s_faultCodes[i].cchFaultString);
+ break;
+ }
+ }
+ }
+ if (m_strFaultString.GetLength() == 0)
+ {
+ m_strFaultCode.SetString(wszLocalName, cch);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFault::SetErrorCode -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT ParseFault(IStream *pStream, ISAXXMLReader *pReader = NULL)
+ {
+ if (pStream == NULL)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFault::ParseFault -- NULL IStream was passed.\r\n" ) );
+
+ return E_INVALIDARG;
+ }
+
+ CComPtr<ISAXXMLReader> spReader;
+ if (pReader != NULL)
+ {
+ spReader = pReader;
+ }
+ else
+ {
+ if (FAILED(spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER)))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFault::ParseFault -- CoCreateInstance of SAXXMLReader failed.\r\n" ) );
+
+ return E_FAIL;
+ }
+ }
+
+ Clear();
+ CSoapFaultParser parser(const_cast<CSoapFault *>(this), spReader);
+ spReader->putContentHandler(&parser);
+
+ CComVariant varStream;
+ varStream = static_cast<IUnknown*>(pStream);
+
+ HRESULT hr = spReader->parse(varStream);
+ spReader->putContentHandler(NULL);
+ return hr;
+ }
+
+ HRESULT GenerateFault(IWriteStream *pWriteStream)
+ {
+ if ((pWriteStream == NULL) || (m_soapErrCode == SOAP_E_UNK))
+ {
+ return E_INVALIDARG;
+ }
+
+ ATLASSERT( (m_soapErrCode == SOAP_E_UNK) ||
+ (m_soapErrCode == SOAP_E_VERSION_MISMATCH) ||
+ (m_soapErrCode == SOAP_E_MUST_UNDERSTAND) ||
+ (m_soapErrCode == SOAP_E_CLIENT) ||
+ (m_soapErrCode == SOAP_E_SERVER) );
+
+ HRESULT hr = S_OK;
+ _ATLTRY
+ {
+ const wchar_t *wszFaultCode = NULL;
+ if (m_strFaultCode.GetLength() == 0)
+ {
+ for (int i=0; i<4; i++)
+ {
+ if (s_faultCodes[i].errCode == m_soapErrCode)
+ {
+ if (m_strFaultString.GetLength() == 0)
+ {
+ m_strFaultString.SetString(s_faultCodes[i].wszFaultString,
+ s_faultCodes[i].cchFaultString);
+ }
+
+ wszFaultCode = s_faultCodes[i].wsz;
+ break;
+ }
+ }
+ }
+
+ if (wszFaultCode == NULL)
+ {
+ if (m_strFaultCode.GetLength() != 0)
+ {
+ wszFaultCode = m_strFaultCode;
+ }
+ else
+ {
+ ATLTRACE( _T("CSoapFault::GenerateFault -- missing/invalid fault code.\r\n") );
+ return E_FAIL;
+ }
+ }
+
+ const LPCSTR s_szErrorFormat =
+ "<SOAP:Envelope xmlns:SOAP=\"" SOAPENV_NAMESPACEA "\">"
+ "<SOAP:Body>"
+ "<SOAP:Fault>"
+ "<faultcode>SOAP:%ws</faultcode>"
+ "<faultstring>%ws</faultstring>"
+ "%s%ws%s"
+ "<detail>%ws</detail>"
+ "</SOAP:Fault>"
+ "</SOAP:Body>"
+ "</SOAP:Envelope>";
+
+ CStringA strFault;
+ strFault.Format(s_szErrorFormat, wszFaultCode, m_strFaultString,
+ m_strFaultActor.GetLength() ? "<faultactor>" : "", m_strFaultActor,
+ m_strFaultActor.GetLength() ? "</faultactor>" : "",
+ m_strDetail);
+
+ hr = pWriteStream->WriteStream(strFault, strFault.GetLength(), NULL);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFault::GenerateFault -- out of memory.\r\n" ) );
+ hr = E_OUTOFMEMORY;
+ }
+
+ return hr;
+ }
+
+ void Clear()
+ {
+ m_soapErrCode = SOAP_E_UNK;
+ m_strFaultCode.Empty();
+ m_strFaultString.Empty();
+ m_strFaultActor.Empty();
+ m_strDetail.Empty();
+ }
+}; // class CSoapFault
+
+#define DECLARE_SOAP_FAULT(__name, __faultstring, __errcode) \
+ { L ## __name, sizeof(__name)-1, L ## __faultstring, sizeof(__faultstring), __errcode },
+
+__declspec(selectany) const CSoapFault::_faultcode CSoapFault::s_faultCodes[] =
+{
+ DECLARE_SOAP_FAULT("VersionMismatch", "SOAP Version Mismatch Error", SOAP_E_VERSION_MISMATCH)
+ DECLARE_SOAP_FAULT("MustUnderstand", "SOAP Must Understand Error", SOAP_E_MUST_UNDERSTAND)
+ DECLARE_SOAP_FAULT("Client", "SOAP Invalid Request", SOAP_E_CLIENT)
+ DECLARE_SOAP_FAULT("Server", "SOAP Server Application Faulted", SOAP_E_SERVER)
+};
+
+ATL_NOINLINE inline HRESULT __stdcall CSoapFaultParser::characters(
+ const wchar_t * wszChars,
+ int cchChars)
+{
+ if (m_pFault == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ if (m_dwState & STATE_RESET)
+ {
+ return S_OK;
+ }
+
+ HRESULT hr = E_FAIL;
+ _ATLTRY
+ {
+ switch (m_dwState)
+ {
+ case STATE_FAULTCODE:
+ if (m_pFault->m_soapErrCode == SOAP_E_UNK)
+ {
+ hr = m_pFault->SetErrorCode(wszChars, m_wszSoapPrefix,
+ cchChars, m_cchSoapPrefix, false);
+ }
+ break;
+ case STATE_FAULTSTRING:
+ if (m_pFault->m_strFaultString.GetLength() == 0)
+ {
+ m_pFault->m_strFaultString.SetString(wszChars, cchChars);
+ hr = S_OK;
+ }
+ break;
+ case STATE_FAULTACTOR:
+ if (m_pFault->m_strFaultActor.GetLength() == 0)
+ {
+ m_pFault->m_strFaultActor.SetString(wszChars, cchChars);
+ hr = S_OK;
+ }
+ break;
+ case STATE_DETAIL:
+ if (m_pFault->m_strDetail.GetLength() == 0)
+ {
+ m_pFault->m_strDetail.SetString(wszChars, cchChars);
+ hr = S_OK;
+ }
+ break;
+ case STATE_START: case STATE_ENVELOPE : case STATE_BODY : case STATE_SKIP:
+ hr = S_OK;
+ break;
+ default:
+ // should never get here
+ ATLASSERT( FALSE );
+ break;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapFaultParser::characters -- out of memory.\r\n" ) );
+
+ hr = E_OUTOFMEMORY;
+ }
+
+ m_dwState |= STATE_RESET;
+
+ return hr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CSoapRootHandler - the class that does most of the work
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef ATLSOAP_STACKSIZE
+ // 16 will be plenty for the 99% case
+ #define ATLSOAP_STACKSIZE 16
+#endif
+
+#ifndef ATLSOAP_GROWARRAY
+ #define ATLSOAP_GROWARRAY 10
+#endif
+
+class CSoapRootHandler : public ISAXContentHandlerImpl
+{
+private:
+
+ friend class _CSDLGenerator;
+
+ //
+ // state constants
+ //
+ const static DWORD SOAP_START = 0;
+ const static DWORD SOAP_ENVELOPE = 1;
+ const static DWORD SOAP_HEADERS = 2;
+ const static DWORD SOAP_BODY = 3;
+ const static DWORD SOAP_PARAMS = 4;
+ const static DWORD SOAP_CALLED = 5;
+ const static DWORD SOAP_RESPONSE = 6;
+ const static DWORD SOAP_HEADERS_DONE = 7;
+
+ //
+ // hash values for SOAP namespaces and elements
+ //
+ const static ULONG SOAP_ENV = 0x5D3574E2;
+ const static ULONG SOAP_ENC = 0xBD62724B;
+ const static ULONG ENVELOPE = 0xDBE6009E;
+ const static ULONG HEADER = 0xAF4DFFC9;
+ const static ULONG BODY = 0x0026168E;
+
+ //
+ // XSD Names
+ //
+ struct XSDEntry
+ {
+ wchar_t * wszName;
+ char * szName;
+ int cchName;
+ };
+
+ const static XSDEntry s_xsdNames[];
+
+ //
+ // CBitVector - a dynamically sized bit vector class
+ //
+ class CBitVector
+ {
+ private:
+
+ // 64 bits will handle the 99% case
+ unsigned __int64 m_nBits;
+
+ // when we need to grow
+ unsigned __int64 * m_pBits;
+
+ size_t m_nSize;
+
+ bool Grow(size_t nIndex)
+ {
+ // Think carefully
+ // In our current implementation, CHAR_BIT==8, and sizeof(m_nBits)==8. Easy to confuse the two.
+
+ // We do math in bits, so this is our max size
+ ATLENSURE(nIndex<SIZE_MAX/((sizeof(m_nBits)*CHAR_BIT)));
+
+ // round up to nearest 64 bits
+ size_t nAllocSizeBits = nIndex+((sizeof(m_nBits)*CHAR_BIT)-(nIndex%(sizeof(m_nBits)*CHAR_BIT)));
+ size_t nAllocSizeBytes = nAllocSizeBits/CHAR_BIT;
+
+ if (m_pBits != &m_nBits)
+ {
+ unsigned __int64 * pNewBits=NULL;
+ pNewBits = (unsigned __int64 *) realloc(m_pBits, nAllocSizeBytes );
+ if(!pNewBits)
+ {
+ return false;
+ }
+ m_pBits=pNewBits;
+ }
+ else
+ {
+ m_pBits = (unsigned __int64 *) malloc(nAllocSizeBytes );
+ if (m_pBits != NULL)
+ {
+ Checked::memcpy_s(m_pBits, nAllocSizeBytes, &m_nBits, sizeof(m_nBits));
+ }
+ }
+
+ if (m_pBits != NULL)
+ {
+ // set new bits to 0
+ memset(m_pBits+(m_nSize/(CHAR_BIT*sizeof(m_nBits))), 0x00, (nAllocSizeBits-m_nSize)/CHAR_BIT);
+ m_nSize = nAllocSizeBits;
+ return true;
+ }
+
+ ATLTRACE( _T("ATLSOAP: CBitVector::Grow -- out of memory.\r\n" ) );
+
+ return false;
+ }
+
+ public:
+
+ CBitVector()
+ : m_nBits(0), m_nSize(sizeof(m_nBits)*CHAR_BIT)
+ {
+ m_pBits = &m_nBits;
+ }
+
+ CBitVector(const CBitVector&)
+ {
+ m_pBits = &m_nBits;
+ }
+
+ const CBitVector& operator=(const CBitVector& that)
+ {
+ if (this != &that)
+ {
+ m_pBits = &m_nBits;
+ }
+
+ return *this;
+ }
+
+ bool GetBit(size_t nIndex) const
+ {
+ if (nIndex >= m_nSize)
+ {
+ return false;
+ }
+
+ size_t i = nIndex/(sizeof(m_nBits)*CHAR_BIT);
+ size_t nBits = nIndex-i*(sizeof(m_nBits)*CHAR_BIT);
+ return ((m_pBits[i] >> nBits) & 0x01);
+ }
+
+ bool SetBit(size_t nIndex)
+ {
+ if (nIndex >= m_nSize)
+ {
+ if (!Grow(nIndex))
+ {
+ return false;
+ }
+ }
+
+ size_t i = nIndex/(sizeof(m_nBits)*CHAR_BIT);
+ size_t nBits = nIndex-i*(sizeof(m_nBits)*CHAR_BIT);
+ m_pBits[i] |= (((unsigned __int64) 1) << nBits);
+
+ return true;
+ }
+
+ void Clear()
+ {
+ if (m_pBits == &m_nBits)
+ {
+ m_nBits = 0;
+ }
+ else
+ {
+ memset(m_pBits, 0x00, (m_nSize/CHAR_BIT));
+ }
+ }
+
+ ~CBitVector()
+ {
+ if (m_pBits != &m_nBits)
+ {
+ free(m_pBits);
+ }
+
+ m_pBits = &m_nBits;
+ m_nSize = sizeof(m_nBits)*CHAR_BIT;
+ }
+
+ void RelocateFixup()
+ {
+ if (m_nSize <= sizeof(m_nBits)*CHAR_BIT)
+ {
+ m_pBits = &m_nBits;
+ }
+ }
+ }; // class CBitVector
+
+ //
+ // Parsing State
+ //
+ struct ParseState
+ {
+ void *pvElement;
+ DWORD dwFlags;
+ size_t nAllocSize;
+ size_t nExpectedElements;
+ size_t nElement;
+ const _soapmap *pMap;
+ const _soapmapentry *pEntry;
+
+ // mark when we get an item
+ CBitVector vec;
+
+ size_t nDepth;
+
+ ParseState(void *pvElement_ = NULL, DWORD dwFlags_ = 0,
+ size_t nAllocSize_ = 0, size_t nExpectedElements_ = 0,
+ size_t nElement_ = 0, const _soapmap *pMap_ = NULL,
+ const _soapmapentry *pEntry_ = NULL)
+ : pvElement(pvElement_), dwFlags(dwFlags_), nAllocSize(nAllocSize_),
+ nExpectedElements(nExpectedElements_), nElement(nElement_), pMap(pMap_),
+ pEntry(pEntry_), nDepth(0)
+ {
+ vec.Clear();
+ }
+
+ ParseState(const ParseState& that)
+ {
+ pvElement = that.pvElement;
+ dwFlags = that.dwFlags;
+ nAllocSize = that.nAllocSize;
+ nExpectedElements = that.nExpectedElements;
+ nElement = that.nElement;
+ pMap = that.pMap;
+ pEntry = that.pEntry;
+ nDepth = that.nDepth;
+ vec.Clear();
+ }
+
+ ~ParseState()
+ {
+ pvElement = NULL;
+ dwFlags = 0;
+ nAllocSize = 0;
+ nExpectedElements = 0;
+ nElement = 0;
+ pMap = NULL;
+ pEntry = NULL;
+ nDepth = 0;
+ vec.Clear();
+ }
+
+ void RelocateFixup()
+ {
+ vec.RelocateFixup();
+ }
+ }; // struct ParseState
+
+ class CParseStateElementTraits : public CDefaultElementTraits<ParseState>
+ {
+ public:
+ // CBitVector relocate fixup
+ static void RelocateElements( ParseState* pDest, ParseState* pSrc, size_t nElements )
+ {
+ CDefaultElementTraits<ParseState>::RelocateElements(pDest, pSrc, nElements);
+
+ // fixup CBitVector
+ for (size_t i=0; i<nElements; i++)
+ {
+ pDest[i].RelocateFixup();
+ }
+ }
+ };
+
+ class CResponseGenerator
+ {
+ public:
+ HRESULT StartEnvelope(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ return pStream->WriteStream("<soap:Envelope "
+ "xmlns:soap=\"" SOAPENV_NAMESPACEA "\" "
+ "xmlns:xsi=\"" XSI_NAMESPACEA "\" "
+ "xmlns:xsd=\"" XSD_NAMESPACEA "\" "
+ "xmlns:soapenc=\"" SOAPENC_NAMESPACEA "\">",
+
+ sizeof("<soap:Envelope "
+ "xmlns:soap=\"" SOAPENV_NAMESPACEA "\" "
+ "xmlns:xsi=\"" XSI_NAMESPACEA "\" "
+ "xmlns:xsd=\"" XSD_NAMESPACEA "\" "
+ "xmlns:soapenc=\"" SOAPENC_NAMESPACEA "\">")-1,
+
+ NULL);
+ }
+
+ HRESULT StartHeaders(IWriteStream *pStream, const _soapmap *pMap)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ HRESULT hr = pStream->WriteStream("<soap:Header", sizeof("<soap:Header")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if ((pMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) !=
+ (SOAPFLAG_RPC | SOAPFLAG_ENCODED))
+ {
+ // qualify document/literal by default
+ // For this version, ATL Server will not respect
+ // the elementForm* attributes in an XSD schema
+
+ hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL);
+ }
+ }
+ }
+ else
+ {
+ // rpc/encoded
+ hr = pStream->WriteStream(">", sizeof(">")-1, NULL);
+ }
+ }
+ return hr;
+ }
+
+ HRESULT EndHeaders(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ return pStream->WriteStream("</soap:Header>", sizeof("</soap:Header>")-1, NULL);
+ }
+
+ virtual HRESULT StartBody(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ return pStream->WriteStream(
+ "<soap:Body>", sizeof("<soap:Body>")-1, NULL);
+ }
+
+ HRESULT EndBody(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ return pStream->WriteStream("</soap:Body>", sizeof("</soap:Body>")-1, NULL);
+ }
+
+ HRESULT EndEnvelope(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ return pStream->WriteStream("</soap:Envelope>", sizeof("</soap:Envelope>")-1, NULL);
+ }
+
+ virtual HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) = 0;
+ virtual HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient) = 0;
+
+ virtual HRESULT StartEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pEntry != NULL );
+
+ // output name
+ HRESULT hr = pStream->WriteStream("<", 1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ const char *szHeaderNamespace = NULL;
+ int cchHeaderNamespace = 0;
+
+ if ((pMap != NULL) && (pMap->mapType == SOAPMAP_HEADER) &&
+ ((pEntry->pChain != NULL) &&
+ (pEntry->pChain->szNamespace !=NULL)) ||
+ (pEntry->szNamespace != NULL))
+ {
+ hr = pStream->WriteStream("snp:", sizeof("snp:")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ szHeaderNamespace = pEntry->pChain ?
+ pEntry->pChain->szNamespace : pEntry->szNamespace;
+
+ cchHeaderNamespace = pEntry->pChain ?
+ pEntry->pChain->cchNamespace : pEntry->cchNamespace;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if ((pEntry->dwFlags & SOAPFLAG_RETVAL)==0)
+ {
+ hr = pStream->WriteStream(pEntry->szField, pEntry->cchField, NULL);
+ }
+ else
+ {
+ hr = pStream->WriteStream("return", sizeof("return")-1, NULL);
+ }
+ if (SUCCEEDED(hr))
+ {
+ if (szHeaderNamespace != NULL)
+ {
+ ATLASSERT( cchHeaderNamespace != 0 );
+
+ hr = pStream->WriteStream(" xmlns:snp=\"", sizeof(" xmlns:snp=\"")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(szHeaderNamespace, cchHeaderNamespace, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream("\"", sizeof("\"")-1, NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (SUCCEEDED(hr))
+ {
+ if (pEntry->dwFlags & SOAPFLAG_MUSTUNDERSTAND)
+ {
+ // output mustUnderstand
+ hr = pStream->WriteStream(" soap:mustUnderstand=\"1\"", sizeof(" soap:mustUnderstand=\"1\"")-1, NULL);
+ }
+ }
+ return hr;
+ }
+
+ HRESULT EndEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pEntry != NULL );
+
+ HRESULT hr = pStream->WriteStream("</", 2, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if ((pMap != NULL) &&
+ (pMap->mapType == SOAPMAP_HEADER) &&
+ ((pEntry->pChain != NULL) &&
+ (pEntry->pChain->szNamespace !=NULL)) ||
+ (pEntry->szNamespace != NULL))
+ {
+ hr = pStream->WriteStream("snp:", sizeof("snp:")-1, NULL);
+ }
+ if ((pEntry->dwFlags & SOAPFLAG_RETVAL)==0)
+ {
+ hr = pStream->WriteStream(pEntry->szField, pEntry->cchField, NULL);
+ }
+ else
+ {
+ hr = pStream->WriteStream("return", sizeof("return")-1, NULL);
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+ return hr;
+ }
+ }; // class CResponseGenerator
+
+ class CDocLiteralGenerator : public CResponseGenerator
+ {
+ public:
+
+ HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ HRESULT hr = S_OK;
+ // output type name
+ hr = pStream->WriteStream("<", 1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if ((pMap->mapType == SOAPMAP_FUNC) &&
+ (bClient == false) &&
+ (pMap->dwCallFlags & SOAPFLAG_PID))
+ {
+ hr = pStream->WriteStream("Response", sizeof("Response")-1, NULL);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ if (pMap->mapType == SOAPMAP_FUNC)
+ {
+ hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL);
+ }
+ }
+ }
+ else
+ {
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+ }
+ return hr;
+ }
+
+ HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ HRESULT hr = pStream->WriteStream("</", sizeof("</")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if ((pMap->mapType == SOAPMAP_FUNC) &&
+ (bClient == false) &&
+ (pMap->dwCallFlags & SOAPFLAG_PID))
+ {
+ hr = pStream->WriteStream("Response", sizeof("Response")-1, NULL);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+
+ return hr;
+ }
+
+ }; // class CDocLiteralGenerator
+
+ class CPIDGenerator : public CDocLiteralGenerator
+ {
+ };
+
+ class CPADGenerator : public CDocLiteralGenerator
+ {
+ public:
+
+ virtual HRESULT StartEntry(IWriteStream *pStream, const _soapmap *pMap, const _soapmapentry *pEntry)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pEntry != NULL );
+
+ HRESULT hr = __super::StartEntry(pStream, pMap, pEntry);
+ if (SUCCEEDED(hr) && (pMap->dwCallFlags & SOAPFLAG_PAD))
+ {
+ hr = pStream->WriteStream(" xmlns=\"", sizeof(" xmlns=\"")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream("\"", sizeof("\"")-1, NULL);
+ }
+ }
+ }
+
+ return hr;
+ }
+ }; // class CPADGenerator
+
+ class CRpcEncodedGenerator : public CResponseGenerator
+ {
+ public:
+
+ HRESULT StartBody(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ return pStream->WriteStream(
+ "<soap:Body soap:encodingStyle=\"" SOAPENC_NAMESPACEA "\">",
+ sizeof("<soap:Body soap:encodingStyle=\"" SOAPENC_NAMESPACEA "\">")-1, NULL);
+ }
+
+ HRESULT StartMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ (bClient); // unused for rpc/encoded
+
+ HRESULT hr = pStream->WriteStream("<snp:", sizeof("<snp:")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if (pMap->mapType == SOAPMAP_FUNC)
+ {
+ hr = pStream->WriteStream(" xmlns:snp=\"", sizeof(" xmlns:snp=\"")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ ATLASSERT( pMap->szNamespace != NULL );
+ hr = pStream->WriteStream(pMap->szNamespace, pMap->cchNamespace, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream("\">", sizeof("\">")-1, NULL);
+ }
+ }
+ }
+ else
+ {
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+ }
+ return hr;
+ }
+
+ HRESULT EndMap(IWriteStream *pStream, const _soapmap *pMap, bool bClient)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ (bClient); // unused for rpc/encoded
+
+ HRESULT hr = pStream->WriteStream("</snp:", sizeof("</snp:")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+
+ return hr;
+ }
+ }; // class CRpcEncodedGenerator
+
+ //
+ // members
+ //
+ CAtlArray<ParseState, CParseStateElementTraits> m_stateStack;
+ size_t m_nState;
+
+ DWORD m_dwState;
+
+ CComPtr<ISAXXMLReader> m_spReader;
+
+ CSAXStringBuilder m_stringBuilder;
+ CSkipHandler m_skipHandler;
+
+ IAtlMemMgr * m_pMemMgr;
+
+ static CCRTHeap m_crtHeap;
+
+ bool m_bClient;
+
+ void *m_pvParam;
+
+ bool m_bNullCheck;
+ bool m_bChildCheck;
+ bool m_bCharacters;
+ size_t m_nDepth;
+
+ typedef CFixedStringT<CStringW, 16> REFSTRING;
+
+ // used for rpc/encoded messages with href's
+ typedef CAtlMap<REFSTRING, ParseState, CStringRefElementTraits<REFSTRING> > REFMAP;
+ REFMAP m_refMap;
+
+ //
+ // Implementation helpers
+ //
+
+ HRESULT PushState(void *pvElement = NULL, const _soapmap *pMap = NULL,
+ const _soapmapentry *pEntry = NULL, DWORD dwFlags = 0, size_t nAllocSize = 0,
+ size_t nExpectedElements = 0, size_t nElement = 0)
+ {
+ if (m_stateStack.IsEmpty())
+ {
+ // 16 will be plenty for the 99% case
+ if (!m_stateStack.SetCount(0, 16))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::PushState -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ size_t nCnt = m_stateStack.GetCount();
+ m_nState = m_stateStack.Add();
+ if (m_stateStack.GetCount() <= nCnt)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::PushState -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+
+ ParseState &state = m_stateStack[m_nState];
+
+ state.pvElement = pvElement;
+ state.dwFlags = dwFlags;
+ state.nAllocSize = nAllocSize;
+ state.nExpectedElements = nExpectedElements;
+ state.nElement = nElement;
+ state.pMap = pMap;
+ state.pEntry = pEntry;
+ state.nDepth = m_nDepth;
+
+ return S_OK;
+ }
+
+ ParseState& GetState()
+ {
+ return m_stateStack[m_nState];
+ }
+
+ void PopState(bool bForce = false)
+ {
+ if ((m_nState != 0) || (bForce != false))
+ {
+ m_stateStack.RemoveAt(m_nState);
+ --m_nState;
+ }
+ }
+
+ BOOL IsEqualElement(int cchLocalNameCheck, const wchar_t *wszLocalNameCheck,
+ int cchNamespaceUriCheck, const wchar_t *wszNamespaceUriCheck,
+ int cchLocalName, const wchar_t *wszLocalName,
+ int cchNamespaceUri, const wchar_t *wszNamespaceUri)
+ {
+ ATLENSURE(wszLocalName);
+ ATLENSURE(wszLocalNameCheck);
+ ATLENSURE(wszNamespaceUri);
+ ATLENSURE(wszNamespaceUriCheck);
+
+ if (cchLocalName == cchLocalNameCheck &&
+ cchNamespaceUri == cchNamespaceUriCheck &&
+ !wcsncmp(wszLocalName, wszLocalNameCheck, cchLocalName) &&
+ !wcsncmp(wszNamespaceUri, wszNamespaceUriCheck, cchNamespaceUri))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ ATL_FORCEINLINE BOOL IsEqualString(const wchar_t *wszStr1, int cchStr1, const wchar_t *wszStr2, int cchStr2)
+ {
+ ATLENSURE( wszStr1 != NULL );
+ ATLENSURE( wszStr2 != NULL );
+ ATLENSURE( cchStr1 >= 0 );
+ ATLENSURE( cchStr2 >= 0 );
+
+ if (cchStr1 == cchStr2)
+ {
+ return !wcsncmp(wszStr1, wszStr2, cchStr2);
+ }
+ return FALSE;
+ }
+
+ ATL_FORCEINLINE BOOL IsEqualStringHash(const wchar_t *wszStr1, int cchStr1, ULONG nHash1,
+ const wchar_t *wszStr2, int cchStr2, ULONG nHash2)
+ {
+ ATLENSURE( wszStr1 != NULL );
+ ATLENSURE( wszStr2 != NULL );
+ ATLENSURE( cchStr1 >= 0 );
+ ATLENSURE( cchStr2 >= 0 );
+
+ if (nHash1 == nHash2)
+ {
+ return IsEqualString(wszStr1, cchStr1, wszStr2, cchStr2);
+ }
+
+ return FALSE;
+ }
+
+ BOOL IsEqualElement(int cchLocalNameCheck, const wchar_t *wszLocalNameCheck,
+ int cchLocalName, const wchar_t *wszLocalName)
+ {
+ if (cchLocalName == cchLocalNameCheck &&
+ !wcsncmp(wszLocalName, wszLocalNameCheck, cchLocalName))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ void SetOffsetValue(void *pBase, void *pSrc, size_t nOffset)
+ {
+ void **ppDest = (void **)(((unsigned char *)pBase)+nOffset);
+ *ppDest = pSrc;
+ }
+
+ bool IsRpcEncoded()
+ {
+ if ((m_stateStack[0].pMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) ==
+ (SOAPFLAG_RPC | SOAPFLAG_ENCODED))
+ {
+ return true;
+ }
+ return false;
+ }
+
+
+ HRESULT ValidateArrayEntry(
+ ParseState& state,
+ const wchar_t *wszLocalName,
+ int cchLocalName)
+ {
+ (cchLocalName);
+ (wszLocalName);
+
+ ATLASSERT( state.pEntry != NULL );
+
+ // SOAP Section 5.4.2
+
+ // check number of elements
+ if (state.nElement == state.nExpectedElements)
+ {
+ // too many elements
+ if ((state.dwFlags & SOAPFLAG_UNKSIZE)==0)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::ValidateArrayEntry -- too many elements.\r\n" ) );
+ return E_FAIL;
+ }
+
+ ATLASSERT( IsRpcEncoded() == false );
+
+ // see if we need to allocate more
+ if (state.nElement == state.nAllocSize)
+ {
+ unsigned char **ppArr = (unsigned char **)state.pvElement;
+ size_t nNewElement=0;
+ HRESULT hr=E_FAIL;
+ if(FAILED(hr=::ATL::AtlMultiply(&nNewElement, state.nElement, static_cast<size_t>(2))))
+ {
+ return hr;
+ }
+ hr = AllocateArray(state.pEntry, (void **)ppArr, __max(nNewElement, ATLSOAP_GROWARRAY), state.nElement);
+
+ if (SUCCEEDED(hr))
+ {
+ state.nAllocSize = __max((state.nElement)*2, ATLSOAP_GROWARRAY);
+ }
+
+ return hr;
+ }
+ }
+
+ return S_OK;
+ }
+
+ HRESULT CheckID(
+ const wchar_t *wszNamespaceUri,
+ const wchar_t *wszLocalName,
+ int cchLocalName,
+ ISAXAttributes *pAttributes)
+ {
+ (cchLocalName);
+ (wszLocalName);
+ (wszNamespaceUri);
+ ATLASSERT( pAttributes != NULL );
+
+ const wchar_t *wsz = NULL;
+ int cch = 0;
+
+ HRESULT hr = GetAttribute(pAttributes, L"id", sizeof("id")-1, &wsz, &cch);
+ if ((hr == S_OK) && (wsz != NULL))
+ {
+ const REFMAP::CPair *p = NULL;
+ _ATLTRY
+ {
+ REFSTRING strRef(wsz, cch);
+ p = m_refMap.Lookup(strRef);
+ if (p == NULL)
+ {
+ return S_FALSE;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckID -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+
+ ATLASSERT( IsRpcEncoded() == true );
+
+ const ParseState& state = p->m_value;
+
+ // disallow href-chaining
+ hr = CheckHref(state.pEntry, state.pvElement, pAttributes);
+ if (hr != S_FALSE)
+ {
+ return E_FAIL;
+ }
+
+ hr = S_OK;
+
+ // do array stuff
+ if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
+ {
+ hr = GetSection5Info(state, state.pEntry, pAttributes);
+ }
+ else
+ {
+ // only structs and arrays are allowed for hrefs
+ ATLASSERT( state.pEntry->pChain != NULL );
+ ATLASSERT( state.pEntry->pChain->mapType == SOAPMAP_STRUCT );
+
+ // structs must have child entries
+ m_bChildCheck = state.pEntry->pChain->nElements != 0;
+
+ if (S_OK != PushState(state.pvElement, state.pEntry->pChain, state.pEntry,
+ state.dwFlags, 0, state.pEntry->pChain->nElements))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckID -- out of memory.\n" ) );
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ m_refMap.DisableAutoRehash();
+ m_refMap.RemoveAtPos(const_cast<REFMAP::CPair*>(p));
+ m_refMap.EnableAutoRehash();
+
+ return hr;
+ }
+
+ return S_FALSE;
+ }
+
+ HRESULT GetElementEntry(
+ ParseState& state,
+ const wchar_t *wszNamespaceUri,
+ const wchar_t *wszLocalName,
+ int cchLocalName,
+ ISAXAttributes *pAttributes,
+ const _soapmapentry **ppEntry)
+ {
+ ATLENSURE_RETURN( state.pMap != NULL );
+ ATLENSURE_RETURN( ppEntry != NULL );
+
+ *ppEntry = NULL;
+ const _soapmapentry *pEntries = state.pMap->pEntries;
+ DWORD dwIncludeFlags;
+ DWORD dwExcludeFlags;
+
+ HRESULT hr = CheckID(wszNamespaceUri, wszLocalName, cchLocalName, pAttributes);
+ if (hr != S_FALSE)
+ {
+ if (hr == S_OK)
+ {
+ hr = S_FALSE;
+ }
+ return hr;
+ }
+
+ if (m_bClient != false)
+ {
+ dwIncludeFlags = SOAPFLAG_OUT;
+ dwExcludeFlags = SOAPFLAG_IN;
+ }
+ else
+ {
+ dwIncludeFlags = SOAPFLAG_IN;
+ dwExcludeFlags = SOAPFLAG_OUT;
+ }
+
+ ULONG nHash = AtlSoapHashStr(wszLocalName, cchLocalName);
+
+ for (size_t i=0; pEntries[i].nHash != 0; i++)
+ {
+ if (nHash == pEntries[i].nHash &&
+ ((pEntries[i].dwFlags & dwIncludeFlags) ||
+ ((pEntries[i].dwFlags & dwExcludeFlags) == 0)) &&
+ IsEqualElement(pEntries[i].cchField, pEntries[i].wszField,
+ cchLocalName, wszLocalName)/* &&
+ !wcscmp(wszNamespaceUri, wszNamespace)*/)
+ {
+ // check bit vector
+
+ if (state.vec.GetBit(i) == false)
+ {
+ if (state.vec.SetBit(i) == false)
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ else
+ {
+ // already received this element
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::GetElementEntry -- duplicate element was sent.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+ state.nElement++;
+ *ppEntry = &pEntries[i];
+
+ return S_OK;
+ }
+ }
+
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::GetElementEntry -- element not found: %.*ws.\r\n" ), cchLocalName, wszLocalName );
+
+ return E_FAIL;
+ }
+
+ HRESULT CheckMustUnderstandHeader(ISAXAttributes *pAttributes)
+ {
+ ATLASSERT( pAttributes != NULL );
+
+ const wchar_t* wszMustUnderstand;
+ int cchMustUnderstand;
+ bool bMustUnderstand= false;
+
+ if (SUCCEEDED(GetAttribute(pAttributes, L"mustUnderstand", sizeof("mustUnderstand")-1,
+ &wszMustUnderstand, &cchMustUnderstand,
+ SOAPENV_NAMESPACEW, sizeof(SOAPENV_NAMESPACEA)-1)) &&
+ (wszMustUnderstand != NULL))
+ {
+ if (FAILED(AtlGetSAXValue(&bMustUnderstand, wszMustUnderstand, cchMustUnderstand)))
+ {
+ bMustUnderstand = true;
+ }
+ }
+
+ if (bMustUnderstand == false)
+ {
+ ATLASSERT( GetReader() != NULL );
+
+ m_skipHandler.SetReader(GetReader());
+ m_skipHandler.SetParent(this);
+
+ return GetReader()->putContentHandler( &m_skipHandler );
+ }
+ else
+ {
+ SoapFault(SOAP_E_MUST_UNDERSTAND, NULL, 0);
+ }
+
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckMustUnderstandHeader -- unknown \"mustUnderstand\" SOAP Header was received.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+ HRESULT AllocateArray(
+ const _soapmapentry *pEntry,
+ void **ppArr, size_t nElements,
+ size_t nCurrElements = 0)
+ {
+ ATLENSURE_RETURN( ppArr != NULL );
+ ATLENSURE_RETURN( pEntry != NULL );
+
+ size_t nElementSize;
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal);
+ }
+ else // UDT
+ {
+ ATLENSURE_RETURN( pEntry->pChain != NULL );
+ nElementSize = pEntry->pChain->nElementSize;
+ }
+ if (nElementSize != 0)
+ {
+ if (*ppArr == NULL)
+ {
+ ATLASSERT( nCurrElements == 0 );
+ size_t nBytes=0;
+ HRESULT hr=S_OK;
+ if( FAILED(hr=::ATL::AtlMultiply(&nBytes, nElementSize, nElements)))
+ {
+ return hr;
+ }
+ *ppArr = m_pMemMgr->Allocate(nBytes);
+ }
+ else // *ppArr != NULL
+ {
+ ATLASSERT( nCurrElements != 0 );
+ size_t nBytes=0;
+ HRESULT hr=S_OK;
+ if( FAILED(hr=::ATL::AtlAdd(&nBytes, nElements, nCurrElements)) ||
+ FAILED(hr=::ATL::AtlMultiply(&nBytes, nElementSize, nBytes)))
+ {
+ return hr;
+ }
+ *ppArr = m_pMemMgr->Reallocate(*ppArr, nBytes);
+ }
+ }
+ else
+ {
+ // internal error
+ ATLASSERT( FALSE );
+ return E_FAIL;
+ }
+
+ if (*ppArr == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ memset(((unsigned char *)(*ppArr))+(nCurrElements*nElementSize), 0x00, nElements*nElementSize);
+
+ return S_OK;
+ }
+
+ HRESULT GetSection5Info(
+ const ParseState& state,
+ const _soapmapentry *pEntry,
+ ISAXAttributes *pAttributes)
+ {
+ ATLENSURE_RETURN( pEntry != NULL );
+ ATLENSURE_RETURN( pAttributes != NULL );
+
+ HRESULT hr;
+ if (IsRpcEncoded() != false)
+ {
+ // check for href
+ // we ONLY do this for rpc/encoded (required for interop)
+ // NOTE: ATL Server does not support object graphs, so
+ // only single-reference elements are allowed
+ hr = CheckHref(pEntry, state.pvElement, pAttributes,
+ pEntry->dwFlags, SOAPFLAG_READYSTATE);
+ if (hr != S_FALSE)
+ {
+ return hr;
+ }
+ }
+
+ size_t nElements;
+ DWORD dwFlags = 0;
+ hr = AtlSoapGetArraySize(pAttributes, &nElements);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ size_t nAllocSize = 0;
+ size_t nElementsPush = 0;
+
+ if (pEntry->dwFlags & SOAPFLAG_DYNARR)
+ {
+ // set size_is value
+ ATLENSURE_RETURN( state.pMap != NULL );
+ int *pnSizeIs = (int *)(((unsigned char *)state.pvElement)+
+ (state.pMap->pEntries[pEntry->nSizeIs].nOffset));
+
+ if (hr != S_OK)
+ {
+ if (IsRpcEncoded())
+ {
+ // rpc/encoded requires soapenc:arrayType attribute
+ return E_FAIL;
+ }
+
+ nElements = ATLSOAP_GROWARRAY;
+ nAllocSize = ATLSOAP_GROWARRAY;
+ dwFlags |= SOAPFLAG_UNKSIZE;
+ *pnSizeIs = 0;
+ }
+ else
+ {
+ *pnSizeIs = (int)nElements;
+ if (nElements == 0)
+ {
+ // soapenc:arrayType="type[0]"
+ // treat as null array
+
+ m_bNullCheck = true;
+
+ // push an emtpy state
+ return PushState();
+ }
+
+ nElementsPush = nElements;
+ }
+ void *p = NULL;
+ hr = AllocateArray(pEntry, &p, nElements);
+ if (hr != S_OK)
+ {
+ return hr;
+ }
+
+ SetOffsetValue(state.pvElement, p, pEntry->nOffset);
+ }
+ else
+ {
+ // for fixed-size arrays, we know the number of elements
+ ATLASSERT( pEntry->dwFlags & SOAPFLAG_FIXEDARR );
+ if (hr == S_OK)
+ {
+ if (nElements != AtlSoapGetArrayDims(pEntry->pDims))
+ {
+ return E_FAIL;
+ }
+ }
+ else
+ {
+ hr = S_OK;
+ nElements = AtlSoapGetArrayDims(pEntry->pDims);
+ }
+ nElementsPush = nElements;
+ }
+
+ dwFlags |= pEntry->dwFlags;
+
+ // push element with array flag
+
+ if (S_OK != PushState(((unsigned char *)state.pvElement)+pEntry->nOffset,
+ state.pMap, pEntry, dwFlags & ~SOAPFLAG_READYSTATE, nAllocSize, nElementsPush))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ m_bChildCheck = true;
+
+ return S_OK;
+ }
+
+ void * UpdateArray(ParseState& state, const _soapmapentry *pEntry)
+ {
+ ATLENSURE(pEntry);
+
+ size_t nSize;
+ void *pVal = NULL;
+
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ nSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal);
+ }
+ else
+ {
+ ATLENSURE( pEntry->pChain != NULL );
+
+ nSize = pEntry->pChain->nElementSize;
+ }
+
+ if (state.dwFlags & SOAPFLAG_FIXEDARR)
+ {
+ unsigned char *ppArr = (unsigned char *)state.pvElement;
+ pVal = ppArr+(state.nElement*nSize);
+ }
+ else
+ {
+ ATLASSERT( state.dwFlags & SOAPFLAG_DYNARR );
+
+ unsigned char **ppArr = (unsigned char **)state.pvElement;
+ pVal = (*ppArr)+(state.nElement*nSize);
+ if (state.dwFlags & SOAPFLAG_UNKSIZE)
+ {
+ ATLASSERT( IsRpcEncoded() == false );
+
+ // need to use the previous state's pvElement to update the size_is value
+ ATLASSUME( m_nState > 0 );
+ int *pnSizeIs = (int *)(((unsigned char *)m_stateStack[m_nState-1].pvElement)+
+ (state.pMap->pEntries[pEntry->nSizeIs].nOffset));
+
+ // update size_is parameter
+ *pnSizeIs = (int)(state.nElement+1);
+ state.nExpectedElements++;
+ }
+ }
+ state.nElement++;
+
+ return pVal;
+ }
+
+ HRESULT ProcessString(const _soapmapentry *pEntry, void *pVal)
+ {
+ ATLENSURE_RETURN( pEntry != NULL );
+
+ // set to the string builder class
+
+ ATLASSERT( GetReader() != NULL );
+
+ m_stringBuilder.SetReader(GetReader());
+ m_stringBuilder.SetParent(this);
+
+ m_stringBuilder.Clear();
+ GetReader()->putContentHandler( &m_stringBuilder );
+
+ if (S_OK != PushState(pVal, NULL, pEntry, SOAPFLAG_READYSTATE | pEntry->dwFlags))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+
+ HRESULT CheckHref(
+ const _soapmapentry *pEntry,
+ void *pVal,
+ ISAXAttributes *pAttributes,
+ DWORD dwIncludeFlags = 0,
+ DWORD dwExcludeFlags = 0)
+ {
+ ATLASSERT( pEntry != NULL );
+ ATLASSERT( pVal != NULL );
+ ATLASSERT( pAttributes != NULL );
+
+ const wchar_t *wsz = NULL;
+ int cch = 0;
+
+ HRESULT hr = GetAttribute(pAttributes, L"href", sizeof("href")-1, &wsz, &cch);
+ if ((hr == S_OK) && (wsz != NULL))
+ {
+ // only allow hrefs on structs and arrays
+ if (((pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))==0) &&
+ (pEntry->pChain == NULL || pEntry->pChain->mapType != SOAPMAP_STRUCT))
+ {
+ ATLTRACE( _T("ATL Server only allows href's on arrays and structs.\r\n") );
+
+ return E_FAIL;
+ }
+
+ ATLASSERT( IsRpcEncoded() == true );
+
+ _ATLTRY
+ {
+ if (*wsz == L'#')
+ {
+ wsz++;
+ cch--;
+ }
+
+ REFSTRING strRef(wsz, cch);
+ if (m_refMap.Lookup(strRef) != NULL)
+ {
+ // ATL Server does not support multi-reference objects
+ ATLASSERT( FALSE );
+ return E_FAIL;
+ }
+
+ ParseState& currState = GetState();
+ if ((currState.pEntry != NULL) && (currState.pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)))
+ {
+ // it is an array item
+ ATLASSERT( currState.nElement != 0 );
+
+ // exclude array flags for href'd array elements
+ dwExcludeFlags |= SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR;
+ }
+
+ ParseState state;
+ state.pvElement = pVal;
+ state.dwFlags = (pEntry->dwFlags | dwIncludeFlags) & ~dwExcludeFlags;
+ state.nExpectedElements = 0;
+
+ state.nElement = 0;
+ state.pMap = GetState().pMap;
+ state.pEntry = pEntry;
+
+ if (!m_refMap.SetAt(strRef, state))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckHref -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+
+ // make sure there are no child elements
+ m_bNullCheck = true;
+
+ // push an emtpy state
+ return PushState();
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckHref -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ return S_FALSE;
+ }
+
+ HRESULT ProcessUDT(
+ const _soapmapentry *pEntry,
+ void *pVal)
+ {
+ ATLENSURE_RETURN( pEntry != NULL );
+ ATLENSURE_RETURN( pVal != NULL );
+ ATLENSURE_RETURN( pEntry->nVal != SOAPTYPE_ERR );
+ ATLENSURE_RETURN( pEntry->nVal != SOAPTYPE_USERBASE );
+
+ // if it is a complex type, get the chain entry
+ // and push the new state on the stack
+
+ DWORD dwFlags = pEntry->dwFlags;
+ if (pEntry->pChain->mapType != SOAPMAP_ENUM)
+ {
+ // struct
+ dwFlags &= ~(SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR);
+ m_bChildCheck = pEntry->pChain->nElements != 0;
+ }
+ else
+ {
+ // enum
+ dwFlags |= SOAPFLAG_READYSTATE;
+
+ // enums must not have child elements
+ m_bNullCheck = true;
+
+ // enums must be specified
+ m_bCharacters = true;
+ }
+
+ if (S_OK != PushState(pVal, pEntry->pChain, pEntry, dwFlags, 0, pEntry->pChain->nElements))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT ChainEntry(
+ const ParseState& state,
+ const wchar_t *wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t *wszLocalName,
+ int cchLocalName,
+ ISAXAttributes *pAttributes)
+ {
+ ATLENSURE_RETURN( state.pMap != NULL );
+
+ // PAD is only supported on the client
+ const _soapmap *pMap = state.pMap;
+ if ((pMap->dwCallFlags & SOAPFLAG_CHAIN)==0)
+ {
+ return S_FALSE;
+ }
+
+ ATLENSURE_RETURN( pMap->dwCallFlags & SOAPFLAG_PAD );
+ ATLASSUME( m_bClient == true );
+ ATLENSURE_RETURN( pMap->nElements == 1 );
+ const _soapmapentry *pEntries = pMap->pEntries;
+ ATLENSURE_RETURN( pEntries != NULL );
+
+ int nIndex;
+ if (pEntries[0].dwFlags & SOAPFLAG_OUT)
+ {
+ nIndex = 0;
+ }
+ else
+ {
+ nIndex = 1;
+ }
+
+ const _soapmapentry *pEntry = &pEntries[nIndex];
+ ATLENSURE_RETURN( pEntry->nHash != 0 );
+ ATLENSURE_RETURN( pEntry->pChain != NULL );
+
+ if (S_OK != PushState(state.pvElement, pEntry->pChain, pEntry, pEntry->dwFlags, 0, pEntry->pChain->nElements))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return ProcessParams(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName, pAttributes);
+ }
+
+ HRESULT IsNullEntry(const _soapmapentry *pEntry, ISAXAttributes *pAttributes)
+ {
+ ATLASSERT( pEntry != NULL );
+ ATLASSERT( pAttributes != NULL );
+
+ HRESULT hr = E_FAIL;
+ bool bNull = false;
+ const wchar_t *wszNull = NULL;
+ int cchNull = 0;
+ hr = GetAttribute(pAttributes, L"nil", sizeof("nil")-1, &wszNull, &cchNull,
+ XSI_NAMESPACEW, sizeof(XSI_NAMESPACEA)-1);
+ if ((hr == S_OK) && (wszNull != NULL))
+ {
+ hr = AtlGetSAXValue(&bNull, wszNull, cchNull);
+ if (hr == S_OK)
+ {
+ if (bNull != false)
+ {
+ if (pEntry->dwFlags & SOAPFLAG_NULLABLE)
+ {
+ m_bNullCheck = true;
+
+ // push an emtpy state
+ return PushState();
+ }
+
+ // non-nullable element
+ return E_FAIL;
+ }
+ }
+ }
+
+ return S_FALSE;
+ }
+
+ HRESULT ProcessParams(
+ const wchar_t *wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t *wszLocalName,
+ int cchLocalName,
+ ISAXAttributes *pAttributes)
+ {
+ (wszNamespaceUri);
+ (cchNamespaceUri);
+
+ if (m_stateStack.IsEmpty())
+ {
+ if (m_dwState == SOAP_HEADERS)
+ {
+ return CheckMustUnderstandHeader(pAttributes);
+ }
+
+ return E_FAIL;
+ }
+
+ ParseState &state = GetState();
+
+ ATLASSERT( state.pvElement != NULL );
+ HRESULT hr = E_FAIL;
+ const _soapmapentry *pEntry = NULL;
+
+ // if array element
+ if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
+ {
+ hr = ValidateArrayEntry(state, wszLocalName, cchLocalName);
+
+ if (SUCCEEDED(hr))
+ {
+ pEntry = state.pEntry;
+ }
+ else
+ {
+ return hr;
+ }
+ }
+ else // not an array element
+ {
+ // special-case for PAD with type=
+ hr = ChainEntry(state, wszNamespaceUri, cchNamespaceUri,
+ wszLocalName, cchLocalName, pAttributes);
+
+ if (hr == S_FALSE)
+ {
+ hr = GetElementEntry(state, wszNamespaceUri, wszLocalName, cchLocalName, pAttributes, &pEntry);
+ if (hr != S_OK)
+ {
+ if (hr == S_FALSE)
+ {
+ hr = S_OK;
+ }
+ else if (m_dwState == SOAP_HEADERS)
+ {
+ hr = CheckMustUnderstandHeader(pAttributes);
+ }
+ return hr;
+ }
+
+ ATLASSERT( pEntry != NULL );
+ }
+ else
+ {
+ return hr;
+ }
+ }
+
+ hr = IsNullEntry(pEntry, pAttributes);
+ if (hr != S_FALSE)
+ {
+ return hr;
+ }
+ hr = S_OK;
+ ATLENSURE_RETURN(pEntry);
+ // if is array
+ if (((pEntry->pDims != NULL) || (pEntry->dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))) &&
+ ((state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) == 0))
+ {
+ // get SOAP section-5 info (if it is there)
+ return GetSection5Info(state, pEntry, pAttributes);
+ }
+ else
+ {
+ // if it is a simple type, push a new (ready) state on the stack
+ void *pVal;
+ if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
+ {
+ pVal = UpdateArray(state, pEntry);
+ ATLASSERT( pVal != NULL );
+ }
+ else
+ {
+ pVal = (((unsigned char *)state.pvElement)+pEntry->nOffset);
+ }
+
+ if (IsRpcEncoded() != false)
+ {
+ // check for href
+ // we ONLY do this for rpc/encoded (required for interop)
+ // NOTE: ATL Server does not support object graphs, so
+ // only single-reference elements are allowed
+ hr = CheckHref(pEntry, pVal, pAttributes);
+ if (hr != S_FALSE)
+ {
+ return hr;
+ }
+ hr = S_OK;
+ }
+
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ // simple types should not have child elements
+ m_bNullCheck = true;
+
+ // if it is a string
+ if ((pEntry->nVal == SOAPTYPE_STRING) || (pEntry->nVal == SOAPTYPE_BASE64BINARY))
+ {
+ hr = ProcessString(pEntry, pVal);
+ }
+ else
+ {
+ // expect characters for all non-string simple types
+ m_bCharacters = true;
+
+ // basic simple type
+ if (S_OK != PushState(pVal, NULL, pEntry, SOAPFLAG_READYSTATE | pEntry->dwFlags))
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ }
+ else
+ {
+ hr = ProcessUDT(pEntry, pVal);
+ if (pEntry->dwFlags & (SOAPFLAG_DYNARRWRAPPER))
+ {
+ // We're moving to the **first** entry in the dynamic array wrapper.
+ // We know it is the first entry because the dynamic array wrapper is created
+ // by sproxy and it guarantees this layouts.
+ ++m_nDepth;
+ ProcessParams (wszNamespaceUri, cchNamespaceUri, pEntry->pChain->pEntries[0].wszField,
+ pEntry->pChain->pEntries[0].cchField, pAttributes);
+ }
+ }
+ }
+
+ return hr;
+ }
+
+ size_t GetSizeIsValue(void *pvParam, const _soapmap *pMap, const _soapmapentry *pEntry)
+ {
+ ATLENSURE( pvParam != NULL );
+ ATLENSURE( pMap != NULL );
+ ATLENSURE( pEntry != NULL );
+
+ int nSizeIs = pEntry->nSizeIs;
+ size_t nOffset = pMap->pEntries[nSizeIs].nOffset;
+ void *pVal = ((unsigned char *)pvParam)+nOffset;
+
+ __int64 nVal = 0;
+ switch(pMap->pEntries[nSizeIs].nVal)
+ {
+ case SOAPTYPE_INTEGER:
+ case SOAPTYPE_NONPOSITIVEINTEGER:
+ case SOAPTYPE_NEGATIVEINTEGER:
+ case SOAPTYPE_LONG:
+ nVal = *((__int64 *)pVal);
+ break;
+ case SOAPTYPE_INT:
+ nVal = *((int *)pVal);
+ break;
+ case SOAPTYPE_SHORT:
+ nVal = *((short *)pVal);
+ break;
+ case SOAPTYPE_BYTE:
+ nVal = *((char *)pVal);
+ break;
+ case SOAPTYPE_POSITIVEINTEGER:
+ case SOAPTYPE_NONNEGATIVEINTEGER:
+ case SOAPTYPE_UNSIGNEDLONG:
+ unsigned __int64 n;
+ n = *((unsigned __int64 *)pVal);
+ if (n > _I64_MAX)
+ {
+ // come on ...
+ nVal = 0;
+ }
+ else
+ {
+ nVal = (__int64)n;
+ }
+ break;
+ case SOAPTYPE_UNSIGNEDINT:
+ nVal = *((unsigned int *)pVal);
+ break;
+ case SOAPTYPE_UNSIGNEDSHORT:
+ nVal = *((unsigned short *)pVal);
+ break;
+ case SOAPTYPE_UNSIGNEDBYTE:
+ nVal = *((unsigned char *)pVal);
+ break;
+ default:
+ nVal = 0;
+ }
+
+ if (nVal < 0)
+ {
+ nVal = 0;
+ }
+
+ return (size_t) nVal;
+ }
+
+ HRESULT GenerateArrayInfo(const _soapmapentry *pEntry, const int *pDims, IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pEntry != NULL );
+ ATLENSURE_RETURN( pStream != NULL );
+
+ HRESULT hr = S_OK;
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ // xsd type
+ hr = pStream->WriteStream(" soapenc:arrayType=\"xsd:",
+ sizeof(" soapenc:arrayType=\"xsd:")-1, NULL);
+ }
+ else
+ {
+ ATLENSURE_RETURN( pEntry->pChain != NULL );
+
+ hr = pStream->WriteStream(" xmlns:q1=\"", sizeof(" xmlns:q1=\"")-1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ if (pEntry->pChain->szNamespace != NULL)
+ {
+ hr = pStream->WriteStream(pEntry->pChain->szNamespace, pEntry->pChain->cchNamespace, NULL);
+ }
+ else
+ {
+ hr = pStream->WriteStream(GetNamespaceUriA(), -1, NULL);
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream("\"", 1, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(" soapenc:arrayType=\"q1:",
+ sizeof(" soapenc:arrayType=\"q1:")-1, NULL);
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ hr = pStream->WriteStream(s_xsdNames[pEntry->nVal].szName ,
+ s_xsdNames[pEntry->nVal].cchName, NULL);
+ }
+ else
+ {
+ ATLASSERT( pEntry->pChain != NULL );
+
+ hr = pStream->WriteStream(pEntry->pChain->szName, pEntry->pChain->cchName, NULL);
+ }
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ hr = pStream->WriteStream("[", 1, NULL);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ CWriteStreamHelper s( pStream );
+ for (int i=1; i<=pDims[0]; i++)
+ {
+ if (!s.Write(pDims[i]) ||
+ ((i < pDims[0]) && (S_OK != pStream->WriteStream(", ", 2, NULL))))
+ {
+ return E_FAIL;
+ }
+ }
+
+ hr = pStream->WriteStream("]\"", 2, NULL);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ return S_OK;
+ }
+
+ HRESULT GenerateXSDWrapper(bool bStart, int nVal, bool bNull, IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+
+ HRESULT hr = pStream->WriteStream((bStart != false) ? "<" : "</",
+ (bStart != false) ? 1 : 2, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(s_xsdNames[nVal].szName,
+ s_xsdNames[nVal].cchName, NULL);
+ if ((bNull != false) && (SUCCEEDED(hr)))
+ {
+ hr = pStream->WriteStream(" xsi:nil=\"1\"", sizeof(" xsi:nil=\"1\"")-1, NULL);
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+
+ return hr;
+ }
+
+ HRESULT GenerateGenericWrapper(bool bStart, const _soapmap *pMap, IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ HRESULT hr = pStream->WriteStream((bStart != false) ? "<" : "</",
+ (bStart != false) ? 1 : 2, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(pMap->szName, pMap->cchName, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->WriteStream(">", 1, NULL);
+ }
+ }
+
+ return hr;
+ }
+
+ HRESULT GetArrayInformation(
+ IWriteStream *pStream,
+ const _soapmap *pMap,
+ const _soapmapentry *pEntry,
+ void *pvParam,
+ size_t &nCnt,
+ size_t &nElementSize)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+ ATLENSURE_RETURN( pEntry != NULL );
+ ATLENSURE_RETURN( pvParam != NULL );
+
+ const int *pDims = NULL;
+ int arrDims[2] = { 0 };
+
+ if (pEntry->dwFlags & SOAPFLAG_FIXEDARR)
+ {
+ pDims = pEntry->pDims;
+ }
+ else
+ {
+ ATLASSERT( pEntry->dwFlags & SOAPFLAG_DYNARR );
+ nCnt = GetSizeIsValue(pvParam, pMap, pEntry);
+
+ if (nCnt == 0)
+ {
+ // array size should only be zero if array is NULL
+ // did you forget to set the array size?
+ ATLASSERT( FALSE );
+ return E_FAIL;
+ }
+
+ arrDims[0] = 1;
+ arrDims[1] = (int) nCnt;
+
+ pDims = arrDims;
+ }
+
+ // output array information
+ HRESULT hr = GenerateArrayInfo(pEntry, pDims, pStream);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ if (SUCCEEDED(hr))
+ {
+ nCnt = AtlSoapGetArrayDims(pDims);
+
+ // did you forget to set the size_is value?
+ ATLASSERT( nCnt != 0 );
+
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntry->nVal);
+ }
+ else
+ {
+ ATLENSURE_RETURN( pEntry->pChain != NULL );
+
+ nElementSize = pEntry->pChain->nElementSize;
+ }
+ }
+
+ return hr;
+ }
+
+ HRESULT GenerateEnum(IWriteStream *pStream, void *pVal, const _soapmapentry *pEntry, bool bArray)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pVal != NULL );
+ ATLENSURE_RETURN( pEntry != NULL );
+
+ int nVal = *((int *)pVal);
+ const _soapmapentry *pEnumEntries = pEntry->pChain->pEntries;
+
+ ATLENSURE_RETURN( pEnumEntries != NULL );
+ size_t j;
+ HRESULT hr = E_FAIL;
+ for (j=0; pEnumEntries[j].nHash != 0; j++)
+ {
+ if (nVal == pEnumEntries[j].nVal)
+ {
+ hr = pStream->WriteStream(pEnumEntries[j].szField, pEnumEntries[j].cchField, NULL);
+ if ((bArray != false) && (SUCCEEDED(hr)))
+ {
+ hr = GenerateGenericWrapper(false, pEntry->pChain, pStream);
+ }
+ break;
+ }
+ }
+
+ return hr;
+ }
+
+ HRESULT GenerateHeaders(CResponseGenerator *pGenerator, const _soapmap *pMap, IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+
+ ATLENSURE_RETURN( pGenerator != NULL );
+
+ DWORD dwIncludeFlags = SOAPFLAG_OUT;
+ if (m_bClient != false)
+ {
+ dwIncludeFlags = SOAPFLAG_IN;
+ }
+
+ size_t nCnt = 0;
+ for (size_t i=0; pMap->pEntries[i].nHash != 0; i++)
+ {
+ if (pMap->pEntries[i].dwFlags & dwIncludeFlags)
+ {
+ nCnt++;
+ }
+ }
+
+ // no headers to be sent
+ if (nCnt == 0)
+ {
+ return S_OK;
+ }
+
+ HRESULT hr = pGenerator->StartHeaders(pStream, pMap);
+ if (SUCCEEDED(hr))
+ {
+ hr = GenerateResponseHelper(pGenerator, pMap, GetHeaderValue(), pStream);
+ if (SUCCEEDED(hr))
+ {
+ hr = pGenerator->EndHeaders(pStream);
+ }
+ }
+
+ return hr;
+ }
+
+ bool IsNullElement(const _soapmapentry *pEntry, void *pVal, DWORD dwExcludeFlags=0)
+ {
+ ATLENSURE( pEntry != NULL );
+ ATLENSURE( pVal != NULL );
+
+ bool bNull = false;
+
+ DWORD dwFlags = pEntry->dwFlags & ~dwExcludeFlags;
+
+ if (dwFlags & SOAPFLAG_DYNARR)
+ {
+ unsigned char **ppArr = (unsigned char **)pVal;
+ if (*ppArr == NULL)
+ {
+ bNull = true;
+ }
+ }
+ else if (pEntry->nVal == SOAPTYPE_STRING)
+ {
+ BSTR *pBSTR = (BSTR *)pVal;
+ if (*pBSTR == NULL)
+ {
+ bNull = true;
+ }
+ }
+ else if ((pEntry->nVal == SOAPTYPE_BASE64BINARY) || (pEntry->nVal == SOAPTYPE_HEXBINARY))
+ {
+ if (((ATLSOAP_BLOB *)pVal)->data == NULL)
+ {
+ bNull = true;
+ }
+ }
+
+ return bNull;
+ }
+
+ HRESULT GenerateNull(IWriteStream *pStream)
+ {
+ ATLENSURE_RETURN( pStream != NULL );
+ return pStream->WriteStream(" xsi:nil=\"1\"/>", sizeof(" xsi:nil=\"1\"/>")-1, NULL);
+ }
+
+ HRESULT GenerateResponseHelper(CResponseGenerator *pGenerator, const _soapmap *pMap, void *pvParam, IWriteStream *pStream,
+ bool bArrayElement = false)
+ {
+ ATLENSURE_RETURN( pGenerator != NULL );
+ ATLENSURE_RETURN( pMap != NULL );
+ ATLENSURE_RETURN( pStream != NULL );
+
+ HRESULT hr = S_OK;
+
+ if ((bArrayElement != false) &&
+ ((pMap->dwCallFlags & SOAPFLAG_PAD)==0))
+ {
+ hr = pGenerator->StartMap(pStream, pMap, m_bClient);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ ATLENSURE_RETURN( pMap->pEntries != NULL );
+
+ const _soapmapentry *pEntries = pMap->pEntries;
+ size_t i;
+
+ DWORD dwIncludeFlags;
+ DWORD dwExcludeFlags;
+ if (m_bClient != false)
+ {
+ dwIncludeFlags = SOAPFLAG_IN;
+ dwExcludeFlags = SOAPFLAG_OUT;
+ }
+ else
+ {
+ dwIncludeFlags = SOAPFLAG_OUT;
+ dwExcludeFlags = SOAPFLAG_IN;
+ }
+
+ for (i=0; pEntries[i].nHash != 0; i++)
+ {
+ if (((pEntries[i].dwFlags & dwIncludeFlags) ||
+ ((pEntries[i].dwFlags & dwExcludeFlags)==0)) &&
+ ((pEntries[i].dwFlags & SOAPFLAG_NOMARSHAL)==0))
+ {
+ hr = pGenerator->StartEntry(pStream, pMap, &pEntries[i]);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ size_t nElementSize = 0;
+ size_t nCnt = 1;
+
+ ATLASSERT( pvParam != NULL );
+
+ void *pvCurrent = ((unsigned char *)pvParam)+pEntries[i].nOffset;
+
+ if (IsNullElement(&pEntries[i], pvCurrent))
+ {
+ hr = GenerateNull(pStream);
+ if (SUCCEEDED(hr))
+ {
+ continue;
+ }
+ return hr;
+ }
+
+ bool bArray = (pEntries[i].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) != 0;
+ if (bArray != false)
+ {
+ hr = GetArrayInformation(pStream, pMap, &pEntries[i], pvParam, nCnt, nElementSize);
+ }
+
+ hr = pStream->WriteStream(">", 1, NULL);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ for (size_t nElement=0; nElement<nCnt; nElement++)
+ {
+ void *pVal;
+
+ // get updated value
+ if (bArray != false)
+ {
+ if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR)
+ {
+ unsigned char *ppArr = (unsigned char *)pvCurrent;
+ pVal = ppArr+(nElement*nElementSize);
+ }
+ else
+ {
+ ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR );
+
+ unsigned char **ppArr = (unsigned char **)pvCurrent;
+ pVal = (*ppArr)+(nElement*nElementSize);
+ }
+ }
+ else
+ {
+ pVal = pvCurrent;
+ }
+
+ if (pEntries[i].nVal != SOAPTYPE_UNK)
+ {
+ bool bNull = false;
+ if (bArray != false)
+ {
+ bNull = IsNullElement(&pEntries[i], pVal, SOAPFLAG_DYNARR | SOAPFLAG_FIXEDARR);
+ hr = GenerateXSDWrapper(true, pEntries[i].nVal, bNull, pStream);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+ if (bNull == false)
+ {
+ hr = AtlSoapGenElementValue(pVal, pStream, (SOAPTYPES) pEntries[i].nVal, GetMemMgr());
+ }
+ if ((SUCCEEDED(hr)) && (bArray != false))
+ {
+ hr = GenerateXSDWrapper(false, pEntries[i].nVal, false, pStream);
+ }
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+ else
+ {
+ ATLASSERT( pEntries[i].pChain != NULL );
+
+ if (pEntries[i].pChain->mapType != SOAPMAP_ENUM)
+ {
+ // struct
+ hr = GenerateResponseHelper(pGenerator, pEntries[i].pChain, pVal, pStream, bArray);
+ }
+ else
+ {
+ if (bArray != false)
+ {
+ hr = GenerateGenericWrapper(true, pEntries[i].pChain, pStream);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ hr = GenerateEnum(pStream, pVal, &pEntries[i], bArray);
+ }
+ }
+ }
+
+ // output element close
+ if (SUCCEEDED(hr))
+ {
+ hr = pGenerator->EndEntry(pStream, pMap, &pEntries[i]);
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ if ((bArrayElement != false) &&
+ ((pMap->dwCallFlags & SOAPFLAG_PAD)==0))
+ {
+ // output type name
+ hr = pGenerator->EndMap(pStream, pMap, m_bClient);
+ }
+
+ return hr;
+ }
+
+ void CleanupHelper(const _soapmap *pMap, void *pvParam)
+ {
+ ATLENSURE( pMap != NULL );
+ ATLENSURE( pMap->pEntries != NULL );
+
+ if (pvParam == NULL)
+ {
+ return;
+ }
+
+ const _soapmapentry *pEntries = pMap->pEntries;
+ size_t i;
+
+ for (i=0; pEntries[i].nHash != 0; i++)
+ {
+ if ((m_bClient != false) && ((pEntries[i].dwFlags & SOAPFLAG_OUT)==0))
+ {
+ // skip in-only headers on the client
+ continue;
+ }
+
+ void *pvCheck = ((unsigned char *)pvParam)+pEntries[i].nOffset;
+ if (IsNullElement(&pEntries[i], pvCheck))
+ {
+ continue;
+ }
+
+ size_t nElementSize = 0;
+ size_t nCnt = 1;
+
+ const int *pDims = NULL;
+ int arrDims[2] = { 0 };
+
+ bool bArray = (pEntries[i].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) != 0;
+
+ if (bArray != false)
+ {
+ if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR)
+ {
+ pDims = pEntries[i].pDims;
+ }
+ else
+ {
+ ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR );
+ nCnt = GetSizeIsValue(pvParam, pMap, &pEntries[i]);
+
+ arrDims[0] = 1;
+ arrDims[1] = (int) nCnt;
+
+ pDims = arrDims;
+ }
+
+ nCnt = AtlSoapGetArrayDims(pDims);
+
+ if (pEntries[i].nVal != SOAPTYPE_UNK)
+ {
+ nElementSize = AtlSoapGetElementSize((SOAPTYPES) pEntries[i].nVal);
+ }
+ else
+ {
+ ATLENSURE( pEntries[i].pChain != NULL );
+
+ nElementSize = pEntries[i].pChain->nElementSize;
+ }
+ }
+
+ void *pvCurrent = ((unsigned char *)pvParam)+pEntries[i].nOffset;
+
+ for (size_t nElement=0; nElement<nCnt; nElement++)
+ {
+ void *pVal;
+
+ // get updated value
+ if (bArray != false)
+ {
+ if (pEntries[i].dwFlags & SOAPFLAG_FIXEDARR)
+ {
+ unsigned char *ppArr = (unsigned char *)pvCurrent;
+ pVal = ppArr+(nElement*nElementSize);
+ }
+ else
+ {
+ ATLASSERT( pEntries[i].dwFlags & SOAPFLAG_DYNARR );
+
+ unsigned char **ppArr = (unsigned char **)pvCurrent;
+ if (*ppArr == NULL)
+ {
+ break;
+ }
+ pVal = (*ppArr)+(nElement*nElementSize);
+ }
+ }
+ else
+ {
+ pVal = pvCurrent;
+ }
+
+ if (pEntries[i].nVal != SOAPTYPE_UNK)
+ {
+ AtlSoapCleanupElement(pVal, (SOAPTYPES) pEntries[i].nVal, GetMemMgr());
+ }
+ else
+ {
+ ATLENSURE( pEntries[i].pChain != NULL );
+
+ if (pEntries[i].pChain->mapType != SOAPMAP_ENUM)
+ {
+ CleanupHelper(pEntries[i].pChain, pVal);
+ }
+ }
+ }
+
+ if (pEntries[i].dwFlags & SOAPFLAG_DYNARR)
+ {
+ // free it
+ unsigned char **ppArr = (unsigned char **)pvCurrent;
+
+ ATLENSURE( ppArr != NULL );
+
+ if (*ppArr != NULL)
+ {
+ m_pMemMgr->Free(*ppArr);
+ *ppArr = NULL;
+ }
+ }
+ }
+ }
+
+ const _soapmap * GetSoapMapFromName(
+ const wchar_t * wszName,
+ int cchName = -1,
+ const wchar_t * wszNamespaceUri = NULL,
+ int cchNamespaceUri = -1,
+ int *pnVal = NULL,
+ bool bHeader = false)
+ {
+ (cchNamespaceUri);
+
+ const _soapmap ** pEntry = NULL;
+
+ if (bHeader == false)
+ {
+ pEntry = GetFunctionMap();
+ }
+ else
+ {
+ pEntry = GetHeaderMap();
+ }
+
+ if (pEntry == NULL)
+ {
+ return NULL;
+ }
+
+ if (cchName < 0)
+ {
+ cchName = (int)wcslen(wszName);
+ }
+ if ((cchNamespaceUri < 0) && (wszNamespaceUri != NULL))
+ {
+ cchNamespaceUri = (int)wcslen(wszNamespaceUri);
+ }
+
+ ULONG nFunctionHash = AtlSoapHashStr(wszName, cchName);
+ ULONG nNamespaceHash = wszNamespaceUri ? AtlSoapHashStr(wszNamespaceUri, cchNamespaceUri) : 0;
+
+ int i;
+ for (i=0; pEntry[i] != NULL; i++)
+ {
+ if ((IsEqualStringHash(wszName, cchName, nFunctionHash,
+ pEntry[i]->wszName, pEntry[i]->cchWName, pEntry[i]->nHash) != FALSE) &&
+ (!wszNamespaceUri ||
+ IsEqualStringHash(wszNamespaceUri, cchNamespaceUri, nNamespaceHash,
+ pEntry[i]->wszNamespace, pEntry[i]->cchNamespace, pEntry[i]->nNamespaceHash) != FALSE))
+ {
+ break;
+ }
+ }
+
+ if (pnVal != NULL)
+ {
+ *pnVal = i;
+ }
+ return pEntry[i];
+ }
+
+ HRESULT CheckEndElement(const ParseState& state)
+ {
+ // check for all elements
+ if (state.nElement == state.nExpectedElements)
+ {
+ return S_OK;
+ }
+
+ // error check for fixed arrays
+ if (state.dwFlags & SOAPFLAG_FIXEDARR)
+ {
+ return E_FAIL;
+ }
+
+ // check for dynamic arrays
+ if (state.dwFlags & SOAPFLAG_DYNARR)
+ {
+ // check for dynamic arrays with known size
+ // (from soap:arrayType attribute)
+ if ((state.dwFlags & SOAPFLAG_UNKSIZE)==0)
+ {
+ return E_FAIL;
+ }
+ }
+
+ DWORD dwIncludeFlags;
+ DWORD dwExcludeFlags;
+
+ if (m_bClient != false)
+ {
+ dwIncludeFlags = SOAPFLAG_OUT;
+ dwExcludeFlags = SOAPFLAG_IN;
+ }
+ else
+ {
+ dwIncludeFlags = SOAPFLAG_IN;
+ dwExcludeFlags = SOAPFLAG_OUT;
+ }
+
+ if (state.pMap != NULL)
+ {
+ // ensure all omitted elements were nullable elements or nomarshal elements
+ const _soapmapentry *pEntries = state.pMap->pEntries;
+ for (size_t i=0; pEntries[i].nHash != 0; i++)
+ {
+ if ((pEntries[i].dwFlags & dwIncludeFlags) ||
+ ((pEntries[i].dwFlags & dwExcludeFlags)==0))
+ {
+ if (state.vec.GetBit(i) == false)
+ {
+ if (((pEntries[i].dwFlags & (SOAPFLAG_NULLABLE | SOAPFLAG_NOMARSHAL))==0) && (pEntries[i].nVal != SOAPTYPE_UNK))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckEndElement -- invalid number of elements for parameter/field\r\n") );
+ return E_FAIL;
+ }
+ }
+ }
+ }
+ }
+
+ return S_OK;
+ }
+
+ HRESULT CheckSoapHeaders(const ParseState &state)
+ {
+ DWORD dwIncludeFlags;
+ DWORD dwExcludeFlags;
+
+ if (m_bClient != false)
+ {
+ dwIncludeFlags = SOAPFLAG_OUT;
+ dwExcludeFlags = SOAPFLAG_IN;
+ }
+ else
+ {
+ dwIncludeFlags = SOAPFLAG_IN;
+ dwExcludeFlags = SOAPFLAG_OUT;
+ }
+
+ if (state.pMap != NULL)
+ {
+ ATLASSERT( state.pMap->mapType == SOAPMAP_HEADER );
+
+ // ensure all omitted elements were nullable elements, nomarshal elements, or non-required elements
+ const _soapmapentry *pEntries = state.pMap->pEntries;
+ for (size_t i=0; pEntries[i].nHash != 0; i++)
+ {
+ if ((pEntries[i].dwFlags & dwIncludeFlags) ||
+ ((pEntries[i].dwFlags & dwExcludeFlags)==0))
+ {
+ if (state.vec.GetBit(i) == false)
+ {
+ bool bNoOmit = (pEntries[i].dwFlags & (SOAPFLAG_NULLABLE | SOAPFLAG_NOMARSHAL))==0;
+
+ if ((bNoOmit != false) ||
+ ((bNoOmit != false) && (pEntries[i].dwFlags & SOAPFLAG_MUSTUNDERSTAND)))
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::CheckSoapHeaders -- missing header\r\n") );
+ return E_FAIL;
+ }
+ }
+ }
+ }
+ }
+
+ return S_OK;
+ }
+
+ HRESULT CheckEndHeaders(
+ const wchar_t * wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t * wszLocalName,
+ int cchLocalName)
+ {
+ if (IsEqualElement(sizeof(SOAP_HEADERA)-1, SOAP_HEADERW,
+ sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
+ cchLocalName, wszLocalName,
+ cchNamespaceUri, wszNamespaceUri))
+ {
+ m_dwState = SOAP_HEADERS_DONE;
+ return S_OK;
+ }
+
+ // some sort of error
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::endElement -- invalid SOAP message format while processing headers.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+protected:
+
+ ISAXXMLReader * SetReader(ISAXXMLReader *pReader)
+ {
+ ISAXXMLReader *pPrevRdr = m_spReader;
+ m_spReader = pReader;
+
+ return pPrevRdr;
+ }
+
+ ISAXXMLReader * GetReader()
+ {
+ return m_spReader;
+ }
+
+ HRESULT SetSoapMapFromName(
+ const wchar_t * wszName,
+ int cchName = -1,
+ const wchar_t * wszNamespaceUri = NULL,
+ int cchNamespaceUri = -1,
+ bool bHeader = false)
+ {
+ ATLENSURE_RETURN( wszName != NULL );
+
+ int nVal;
+ const _soapmap *pMap = NULL;
+ if (m_stateStack.GetCount() != 0)
+ {
+ ATLASSUME( m_stateStack[0].pMap != NULL );
+ nVal = (int) m_stateStack[0].nAllocSize;
+ ATLASSERT( GetFunctionMap() != NULL );
+ pMap = GetFunctionMap()[nVal];
+ }
+ else
+ {
+ pMap = GetSoapMapFromName(wszName, cchName,
+ wszNamespaceUri, cchNamespaceUri, &nVal, bHeader);
+ }
+
+ if (pMap == NULL)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::SetSoapMapFromName -- _soapmap not found for: %.*ws, with namespace %.*ws\r\n"),
+ (int)wcslen(wszName), wszName, wszNamespaceUri ? (int)wcslen(wszNamespaceUri) : 0, wszNamespaceUri ? wszNamespaceUri : L"");
+
+ return E_FAIL;
+ }
+
+ HRESULT hr = E_OUTOFMEMORY;
+
+ // allocate the parameter struct
+
+ void *pvParam = NULL;
+ if (bHeader != false)
+ {
+ pvParam = GetHeaderValue();
+ }
+ else
+ {
+ if (m_bClient == false)
+ {
+ m_pvParam = m_pMemMgr->Allocate(pMap->nElementSize);
+ }
+ pvParam = m_pvParam;
+ }
+
+ if (pvParam != NULL)
+ {
+ if (bHeader == false)
+ {
+ memset(pvParam, 0x00, pMap->nElementSize);
+ }
+
+ // push initial state
+
+ if (m_stateStack.GetCount() != 0)
+ {
+ m_stateStack.RemoveAll();
+ }
+
+ hr = PushState(pvParam, pMap, NULL, 0, nVal, pMap->nElements);
+
+ if (FAILED(hr))
+ {
+ if ((m_bClient == false) && (bHeader == false))
+ {
+ m_pMemMgr->Free(pvParam);
+ }
+ }
+ }
+
+#ifdef _DEBUG
+ if (hr == E_OUTOFMEMORY)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::SetSoapMapFromName -- out of memory.\r\n" ) );
+ }
+#endif // _DEBUG
+
+ return hr;
+ }
+
+ // implementation
+ virtual const _soapmap ** GetFunctionMap() = 0;
+ virtual const _soapmap ** GetHeaderMap() = 0;
+ virtual const wchar_t * GetNamespaceUri() = 0;
+ virtual const char * GetServiceName() = 0;
+ virtual const char * GetNamespaceUriA() = 0;
+ virtual HRESULT CallFunction(
+ void *pvParam,
+ const wchar_t *wszLocalName, int cchLocalName,
+ size_t nItem) = 0;
+ virtual void * GetHeaderValue() = 0;
+
+public:
+
+ CSoapRootHandler(ISAXXMLReader *pReader = NULL)
+ : m_pMemMgr(&m_crtHeap), m_spReader(pReader), m_bClient(false),
+ m_nState(0), m_pvParam(NULL), m_nDepth(0)
+ {
+ InitHandlerState();
+ }
+ virtual ~CSoapRootHandler()
+ {
+ m_skipHandler.DetachParent();
+ }
+
+ IAtlMemMgr * SetMemMgr(IAtlMemMgr *pMemMgr)
+ {
+ IAtlMemMgr *pPrevMgr = m_pMemMgr;
+ m_pMemMgr = pMemMgr;
+
+ return pPrevMgr;
+ }
+
+ IAtlMemMgr * GetMemMgr()
+ {
+ return m_pMemMgr;
+ }
+
+ // override this function to do SOAP Fault handling
+ virtual HRESULT SoapFault(
+ SOAP_ERROR_CODE /*errCode*/,
+ const wchar_t * /*wszDetail*/,
+ int /*cchDetail*/)
+ {
+ if (m_bClient != false)
+ {
+ return S_OK;
+ }
+
+ // SOAP servers must implement this function
+ ATLASSERT( FALSE );
+ return E_FAIL;
+ }
+
+ //
+ // implementation
+ //
+
+ void InitHandlerState()
+ {
+ m_bNullCheck = false;
+ m_bCharacters = false;
+ m_bChildCheck = false;
+ m_dwState = SOAP_START;
+ }
+ HRESULT __stdcall startDocument()
+ {
+ InitHandlerState();
+ return S_OK;
+ }
+
+ HRESULT __stdcall startElement(
+ const wchar_t *wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t *wszLocalName,
+ int cchLocalName,
+ const wchar_t * wszQName,
+ int cchQName,
+ ISAXAttributes *pAttributes)
+ {
+ if (m_bNullCheck || m_bCharacters)
+ {
+ // make sure elements that aren't supposed to have child elements
+ // do not have child elements, and where we were expecting
+ // characters, we got them
+ return E_FAIL;
+ }
+
+ m_bChildCheck = false;
+ ++m_nDepth;
+
+ HRESULT hr = S_OK;
+ switch (m_dwState)
+ {
+ case SOAP_PARAMS: case SOAP_HEADERS:
+ {
+ hr = ProcessParams(wszNamespaceUri, cchNamespaceUri, wszLocalName,
+ cchLocalName, pAttributes);
+
+ break;
+ }
+ case SOAP_START: case SOAP_ENVELOPE: case SOAP_HEADERS_DONE:
+ {
+ ULONG nNamespaceHash = AtlSoapHashStr(wszNamespaceUri,
+ cchNamespaceUri);
+ if (nNamespaceHash != SOAP_ENV)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- incorrect SOAP-ENV namespace.\r\n" ) );
+
+ return E_FAIL;
+ }
+
+ ULONG nElementHash = AtlSoapHashStr(wszLocalName, cchLocalName);
+
+ if (nElementHash == ENVELOPE &&
+ IsEqualElement(
+ sizeof(SOAP_ENVELOPEA)-1, SOAP_ENVELOPEW,
+ sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
+ cchLocalName, wszLocalName,
+ cchNamespaceUri, wszNamespaceUri))
+ {
+ // Envelope must be first element in package
+
+ if (m_dwState != SOAP_START)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Envelope\" in unexpected location.\r\n" ) );
+
+ hr = E_FAIL;
+ }
+ m_dwState = SOAP_ENVELOPE;
+ }
+ else if (nElementHash == HEADER &&
+ IsEqualElement(sizeof(SOAP_HEADERA)-1, SOAP_HEADERW,
+ sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
+ cchLocalName, wszLocalName,
+ cchNamespaceUri, wszNamespaceUri))
+ {
+ if (m_dwState != SOAP_ENVELOPE)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Headers\" in unexpected location.\r\n" ) );
+
+ hr = E_FAIL;
+ }
+
+ m_dwState = SOAP_HEADERS;
+ }
+ else if (nElementHash == BODY &&
+ IsEqualElement(sizeof(SOAP_BODYA)-1, SOAP_BODYW,
+ sizeof(SOAPENV_NAMESPACEA)-1, SOAPENV_NAMESPACEW,
+ cchLocalName, wszLocalName,
+ cchNamespaceUri, wszNamespaceUri))
+ {
+ if (m_dwState == SOAP_START)
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::startElement -- invalid SOAP message format: \"Body\" in unexpected location.\r\n" ) );
+
+ hr = E_FAIL;
+ }
+ m_dwState = SOAP_BODY;
+ }
+
+ break;
+ }
+ case SOAP_BODY:
+ {
+ hr = DispatchSoapCall(wszNamespaceUri, cchNamespaceUri,
+ wszLocalName, cchLocalName);
+
+ m_dwState = SOAP_PARAMS;
+
+ if (SUCCEEDED(hr))
+ {
+ if (GetState().pMap->dwCallFlags & SOAPFLAG_PAD)
+ {
+ hr = startElement(wszNamespaceUri, cchNamespaceUri,
+ wszLocalName, cchLocalName, wszQName, cchQName,
+ pAttributes);
+ }
+ }
+
+ break;
+ }
+
+#ifdef _DEBUG
+
+ default:
+ {
+ // should never get here -- internal error
+ ATLASSERT( FALSE );
+ }
+
+#endif // _DEBUG
+ }
+
+ return hr;
+ }
+
+ HRESULT __stdcall characters(
+ const wchar_t *wszChars,
+ int cchChars)
+ {
+ m_bCharacters = false;
+
+ // if it is a ready state, get the value
+ if (m_stateStack.IsEmpty() == false)
+ {
+ ParseState& state = GetState();
+ if ((state.dwFlags & SOAPFLAG_READYSTATE) &&
+ ((state.dwFlags & SOAPFLAG_SIZEIS)==0)) // don't marshal struct size_is elements -- should be filled in by array marshaling code
+ {
+ if ((state.pMap == NULL) || (state.pMap->mapType != SOAPMAP_ENUM))
+ {
+ return AtlSoapGetElementValue(wszChars, cchChars,
+ state.pvElement, (SOAPTYPES)state.pEntry->nVal, GetMemMgr());
+ }
+ else
+ {
+ // enum
+
+ ATLASSERT( state.pMap != NULL );
+ ATLASSERT( state.pMap->pEntries != NULL );
+
+ ULONG nHash = AtlSoapHashStr(wszChars, cchChars);
+ const _soapmapentry *pEntries = state.pMap->pEntries;
+
+ size_t i;
+ for (i=0; pEntries[i].nHash != 0; i++)
+ {
+ if ((nHash == pEntries[i].nHash) &&
+ (cchChars == pEntries[i].cchField) &&
+ (!wcsncmp(wszChars, pEntries[i].wszField, cchChars)))
+ {
+ break;
+ }
+ }
+
+ if (pEntries[i].nHash != 0)
+ {
+ *((int *)state.pvElement) = pEntries[i].nVal;
+ state.nElement++;
+ return S_OK;
+ }
+
+ // no matching enum entry found
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::characters -- no matching enum entry found for: %.*ws.\r\n" ), cchChars, wszChars );
+
+ return E_FAIL;
+ }
+ }
+ }
+
+ // otherwise, ignore
+
+ return S_OK;
+ }
+
+ HRESULT __stdcall endElement(
+ const wchar_t * wszNamespaceUri,
+ int cchNamespaceUri,
+ const wchar_t * wszLocalName,
+ int cchLocalName,
+ const wchar_t * /*wszQName*/,
+ int /*cchQName*/)
+ {
+ static bool bDynArrWrapper = false;
+ if (m_bCharacters)
+ {
+ return E_FAIL;
+ }
+
+ m_bNullCheck = false;
+
+ if (m_stateStack.IsEmpty() != false)
+ {
+ return S_OK;
+ }
+
+ if (!bDynArrWrapper && (m_nState > 1))
+ {
+ ParseState prevState = m_stateStack.GetAt(m_nState - 1);
+ ParseState curState = m_stateStack.GetAt(m_nState);
+ if (prevState.dwFlags & SOAPFLAG_DYNARRWRAPPER)
+ {
+ bDynArrWrapper = true;
+ endElement (wszNamespaceUri, cchNamespaceUri, curState.pEntry->wszField,
+ curState.pEntry->cchField, NULL, 0);
+ }
+ }
+ else
+ {
+ bDynArrWrapper = false;
+ }
+
+ --m_nDepth;
+
+ const ParseState& state = GetState();
+
+ if ((m_dwState == SOAP_HEADERS) && (m_stateStack.GetCount() == 1))
+ {
+ return CheckEndHeaders(wszNamespaceUri, cchNamespaceUri, wszLocalName, cchLocalName);
+ }
+
+ if (state.dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
+ {
+ if (state.dwFlags & SOAPFLAG_READYSTATE)
+ {
+ PopState();
+ }
+
+ const ParseState& currstate = GetState();
+ ATLENSURE_RETURN( currstate.pEntry != NULL );
+
+ if (m_nDepth == (currstate.nDepth-1))
+ {
+ if (S_OK != CheckEndElement(currstate))
+ {
+ // invalid number of elements
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::endElement -- invalid number of array elements for array parameter %.*ws.\r\n"),
+ currstate.pEntry->cchField, currstate.pEntry->wszField );
+
+ return E_FAIL;
+ }
+
+ PopState();
+ }
+ }
+ else
+ {
+ if (S_OK != CheckEndElement(state))
+ {
+ return E_FAIL;
+ }
+
+ PopState();
+ }
+
+ return S_OK;
+ }
+
+ HRESULT SetClientStruct(void *pvParam, int nMapIndex)
+ {
+ ATLENSURE_RETURN( pvParam != NULL );
+ ATLENSURE_RETURN( nMapIndex >= 0 );
+
+ // this is the params struct
+ // store for later use
+ m_pvParam = pvParam;
+
+ const _soapmap ** pEntries = GetHeaderMap();
+ ATLENSURE_RETURN( pEntries != NULL );
+
+ // push header value
+ return PushState(GetHeaderValue(), pEntries[nMapIndex], NULL, 0, nMapIndex, pEntries[nMapIndex]->nElements);
+ }
+
+ void ResetClientState(bool bFull = false)
+ {
+ m_stateStack.RemoveAll();
+ m_nState = 0;
+ if (bFull != false)
+ {
+ m_dwState = SOAP_START;
+ m_pvParam = NULL;
+ }
+ }
+
+ HRESULT CreateReader()
+ {
+ return m_spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
+ }
+
+ HRESULT InitializeSOAP(IServiceProvider *pProvider)
+ {
+ HRESULT hr = S_OK;
+
+ if (m_spReader.p == NULL)
+ {
+ hr = E_FAIL;
+ if (pProvider != NULL)
+ {
+ IAtlMemMgr *pMemMgr = NULL;
+ hr = pProvider->QueryService(__uuidof(IAtlMemMgr),
+ __uuidof(IAtlMemMgr), (void **)&pMemMgr);
+ if ((SUCCEEDED(hr)) && (pMemMgr != NULL))
+ {
+ SetMemMgr(pMemMgr);
+ }
+
+ hr = pProvider->QueryService(__uuidof(ISAXXMLReader),
+ __uuidof(ISAXXMLReader), (void **)&m_spReader);
+ }
+
+ if (FAILED(hr))
+ {
+ hr = CreateReader();
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_spReader->putContentHandler(this);
+ }
+
+#ifdef _DEBUG
+ else
+ {
+ ATLTRACE( _T("ATLSOAP: CSoapRootHandler::InitializeSOAP -- failed to get SAXXMLReader.\r\n" ) );
+ }
+#endif // _DEBUG
+
+ return hr;
+ }
+
+ void UninitializeSOAP()
+ {
+ if (m_spReader.p != NULL)
+ {
+ m_spReader->putContentHandler(NULL);
+ m_spReader.Release();
+ }
+ }
+
+ virtual HRESULT DispatchSoapCall(const wchar_t *wszNamespaceUri,
+ int cchNamespaceUri, const wchar_t *wszLocalName,
+ int cchLocalName)
+ {
+ HRESULT hr = S_OK;
+
+ if (m_stateStack.IsEmpty() == false)
+ {
+ ATLASSUME( m_stateStack[0].pMap != NULL );
+
+ // check to see if all required and non-nullable SOAP headers were sent
+ if (m_stateStack[0].pMap->mapType == SOAPMAP_HEADER)
+ {
+ hr = CheckSoapHeaders(m_stateStack[0]);
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = SetSoapMapFromName(wszLocalName, cchLocalName,
+ wszNamespaceUri, cchNamespaceUri);
+ }
+ }
+ else
+ {
+ // get the appropriate function map
+ hr = SetSoapMapFromName(wszLocalName, cchLocalName,
+ wszNamespaceUri, cchNamespaceUri);
+
+ if (SUCCEEDED(hr))
+ {
+ // set the SOAP Header map for the function
+ ATLASSUME( m_stateStack.IsEmpty() == false );
+
+ const _soapmap **ppHeaderMap = GetHeaderMap();
+ ATLENSURE_RETURN( ppHeaderMap != NULL );
+
+ // create a temporary parse state for checking headers
+ ParseState state;
+ state.pMap = ppHeaderMap[m_stateStack[0].nAllocSize];
+ ATLENSURE_RETURN( state.pMap != NULL );
+
+ // check to see if all required and non-nullable SOAP headers were sent
+ hr = CheckSoapHeaders(state);
+ }
+ }
+
+ return hr;
+ }
+
+ virtual HRESULT BeginParse(IStream *pStream)
+ {
+ ATLASSERT( pStream != NULL );
+
+ CComVariant varStream;
+ varStream = static_cast<IUnknown*>(pStream);
+
+ HRESULT hr = m_spReader->parse(varStream);
+ if (SUCCEEDED(hr))
+ {
+ if (m_refMap.GetCount() != 0)
+ {
+ hr = E_FAIL;
+ }
+ }
+ return hr;
+ }
+
+ HRESULT CallFunctionInternal()
+ {
+ HRESULT hr = E_FAIL;
+ const ParseState& state = m_stateStack[0];
+ hr = CallFunction(
+ state.pvElement,
+ state.pMap->wszName,
+ state.pMap->cchWName,
+ state.nAllocSize);
+
+ return hr;
+ }
+
+ virtual HRESULT GenerateResponse(IWriteStream *pStream)
+ {
+ ATLASSUME( m_stateStack.IsEmpty() == false );
+ ATLASSUME( m_stateStack[0].pMap != NULL );
+ ATLASSUME( m_stateStack[0].pvElement != NULL );
+
+ const ParseState& state = m_stateStack[0];
+
+ const _soapmap *pHeaderMap = NULL;
+ if (m_bClient == false)
+ {
+ const _soapmap **ppHeaderMap = GetHeaderMap();
+ if (ppHeaderMap != NULL)
+ {
+ pHeaderMap = ppHeaderMap[state.nAllocSize];
+ }
+ }
+ else
+ {
+ pHeaderMap = state.pMap;
+ }
+
+ const _soapmap *pFuncMap = NULL;
+ if (m_bClient == false)
+ {
+ pFuncMap = state.pMap;
+ }
+ else
+ {
+ const _soapmap **ppFuncMap = GetFunctionMap();
+ ATLENSURE_RETURN( ppFuncMap != NULL );
+ pFuncMap = ppFuncMap[state.nAllocSize];
+ }
+
+ ATLENSURE_RETURN( pFuncMap != NULL );
+
+ CRpcEncodedGenerator rpcGen;
+ CPADGenerator padGen;
+ CPIDGenerator pidGen;
+
+ CResponseGenerator *pGenerator = NULL;
+
+ if ((pFuncMap->dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) == (SOAPFLAG_RPC | SOAPFLAG_ENCODED))
+ {
+ pGenerator = &rpcGen;
+ }
+ else if (pFuncMap->dwCallFlags & SOAPFLAG_PID)
+ {
+ ATLASSERT( (pFuncMap->dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL) );
+ pGenerator = &pidGen;
+ }
+ else
+ {
+ ATLASSERT( (pFuncMap->dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) == (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL) );
+ ATLASSERT( pFuncMap->dwCallFlags & SOAPFLAG_PAD );
+ pGenerator = &padGen;
+ }
+
+ HRESULT hr = pGenerator->StartEnvelope(pStream);
+ if (SUCCEEDED(hr))
+ {
+ // generate headers if necessary
+ hr = GenerateHeaders(pGenerator, pHeaderMap, pStream);
+ if (SUCCEEDED(hr))
+ {
+ hr = pGenerator->StartBody(pStream);
+ if (SUCCEEDED(hr))
+ {
+ hr = GenerateResponseHelper(pGenerator, pFuncMap, m_pvParam, pStream, true);
+ if (SUCCEEDED(hr))
+ {
+ hr = pGenerator->EndBody(pStream);
+ if (SUCCEEDED(hr))
+ {
+ hr = pGenerator->EndEnvelope(pStream);
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+ }
+
+ virtual void Cleanup()
+ {
+ // cleanup headers
+ CleanupHeaders();
+
+ if ((m_stateStack.IsEmpty() == false) && (m_pvParam != NULL))
+ {
+ const _soapmap **ppFuncMap = GetFunctionMap();
+ ATLENSURE( ppFuncMap != NULL );
+
+ const _soapmap *pFuncMap = ppFuncMap[m_stateStack[0].nAllocSize];
+ ATLENSURE( pFuncMap != NULL );
+
+ CleanupHelper(pFuncMap, m_pvParam);
+ if (m_bClient == false)
+ {
+ m_pMemMgr->Free(m_pvParam);
+ m_stateStack.RemoveAll();
+ }
+ }
+ }
+
+ virtual void CleanupHeaders()
+ {
+ if (m_stateStack.IsEmpty() == false)
+ {
+ const _soapmap **ppHeaderMap = GetHeaderMap();
+ ATLENSURE( ppHeaderMap != NULL );
+
+ const _soapmap *pHeaderMap = ppHeaderMap[m_stateStack[0].nAllocSize];
+ ATLENSURE( pHeaderMap != NULL );
+
+ CleanupHelper(pHeaderMap, GetHeaderValue());
+ }
+ }
+
+ void SetClient(bool bClient)
+ {
+ m_bClient = bClient;
+ }
+
+}; // class CSoapRootHandler
+
+#define DECLARE_XSD_ENTRY( __name ) \
+ { L ## __name, __name, sizeof(__name)-1 },
+
+__declspec(selectany) const CSoapRootHandler::XSDEntry CSoapRootHandler::s_xsdNames[] =
+{
+ DECLARE_XSD_ENTRY("string")
+ DECLARE_XSD_ENTRY("boolean")
+ DECLARE_XSD_ENTRY("float")
+ DECLARE_XSD_ENTRY("double")
+ DECLARE_XSD_ENTRY("decimal")
+ DECLARE_XSD_ENTRY("duration")
+ DECLARE_XSD_ENTRY("hexBinary")
+ DECLARE_XSD_ENTRY("base64Binary")
+ DECLARE_XSD_ENTRY("anyURI")
+ DECLARE_XSD_ENTRY("ID")
+ DECLARE_XSD_ENTRY("IDREF")
+ DECLARE_XSD_ENTRY("ENTITY")
+ DECLARE_XSD_ENTRY("NOTATION")
+ DECLARE_XSD_ENTRY("QName")
+ DECLARE_XSD_ENTRY("normalizedString")
+ DECLARE_XSD_ENTRY("token")
+ DECLARE_XSD_ENTRY("language")
+ DECLARE_XSD_ENTRY("IDREFS")
+ DECLARE_XSD_ENTRY("ENTITIES")
+ DECLARE_XSD_ENTRY("NMTOKEN")
+ DECLARE_XSD_ENTRY("NMTOKENS")
+ DECLARE_XSD_ENTRY("Name")
+ DECLARE_XSD_ENTRY("NCName")
+ DECLARE_XSD_ENTRY("integer")
+ DECLARE_XSD_ENTRY("nonPositiveInteger")
+ DECLARE_XSD_ENTRY("negativeInteger")
+ DECLARE_XSD_ENTRY("long")
+ DECLARE_XSD_ENTRY("int")
+ DECLARE_XSD_ENTRY("short")
+ DECLARE_XSD_ENTRY("byte")
+ DECLARE_XSD_ENTRY("nonNegativeInteger")
+ DECLARE_XSD_ENTRY("unsignedLong")
+ DECLARE_XSD_ENTRY("unsignedInt")
+ DECLARE_XSD_ENTRY("unsignedShort")
+ DECLARE_XSD_ENTRY("unsignedByte")
+ DECLARE_XSD_ENTRY("positiveInteger")
+ DECLARE_XSD_ENTRY("dateTime")
+ DECLARE_XSD_ENTRY("time")
+ DECLARE_XSD_ENTRY("date")
+ DECLARE_XSD_ENTRY("gMonth")
+ DECLARE_XSD_ENTRY("gYearMonth")
+ DECLARE_XSD_ENTRY("gYear")
+ DECLARE_XSD_ENTRY("gMonthDay")
+ DECLARE_XSD_ENTRY("gDay")
+};
+
+__declspec(selectany) CCRTHeap CSoapRootHandler::m_crtHeap;
+
+template <typename THandler>
+class CSoapHandler :
+ public CSoapRootHandler,
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IRequestHandlerImpl<THandler>
+{
+protected:
+
+ HTTP_CODE m_hcErr;
+ CHttpResponse *m_pHttpResponse;
+
+ // heap for SOAP requests
+ CWin32Heap m_heap;
+
+ // default heap is COM heap (SOAP Servers can double as COM objects)
+ CComHeap m_comHeap;
+
+public:
+
+ BEGIN_COM_MAP(CSoapHandler<THandler>)
+ COM_INTERFACE_ENTRY(ISAXContentHandler)
+ COM_INTERFACE_ENTRY(IRequestHandler)
+ END_COM_MAP()
+
+ CSoapHandler()
+ :m_pHttpResponse(NULL), m_hcErr(HTTP_SUCCESS)
+ {
+ SetMemMgr(&m_comHeap);
+ }
+
+ void SetHttpError(HTTP_CODE hcErr)
+ {
+ m_hcErr = hcErr;
+ }
+
+ HRESULT SoapFault(
+ SOAP_ERROR_CODE errCode,
+ const wchar_t *wszDetail,
+ int cchDetail)
+ {
+ ATLASSUME( m_pHttpResponse != NULL );
+
+ SetHttpError(AtlsHttpError(500, SUBERR_NO_PROCESS));
+
+ m_pHttpResponse->ClearHeaders();
+ m_pHttpResponse->ClearContent();
+ m_pHttpResponse->SetContentType("text/xml");
+ m_pHttpResponse->SetStatusCode(500);
+
+ CSoapFault fault;
+ if (wszDetail != NULL)
+ {
+ if (cchDetail < 0)
+ {
+ cchDetail = (int) wcslen(wszDetail);
+ }
+
+ _ATLTRY
+ {
+ fault.m_strDetail.SetString(wszDetail, cchDetail);
+ }
+ _ATLCATCHALL()
+ {
+ ATLTRACE( _T("CSoapHandler::SoapFault -- out of memory.\r\n" ) );
+
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ fault.m_soapErrCode = errCode;
+ fault.GenerateFault(m_pHttpResponse);
+ return S_OK;
+ }
+
+ HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
+ {
+ m_hcErr = IRequestHandlerImpl<THandler>::InitializeHandler(pRequestInfo, pProvider);
+ if (m_hcErr == HTTP_SUCCESS)
+ {
+ HRESULT hr = InitializeSOAP(m_spServiceProvider);
+ if (SUCCEEDED(hr))
+ {
+ // try to use the per-thread heap
+ CIsapiWorker *pWorker = pRequestInfo->pExtension->GetThreadWorker();
+ if (pWorker != NULL)
+ {
+ m_heap.Attach(pWorker->m_hHeap, false);
+ SetMemMgr(&m_heap);
+ }
+
+ return m_hcErr;
+ }
+ }
+
+ // some initialization failure
+ CHttpResponse HttpResponse(pRequestInfo->pServerContext);
+ m_pHttpResponse = &HttpResponse;
+
+ SoapFault(SOAP_E_SERVER, NULL, 0);
+
+ m_pHttpResponse = NULL;
+
+ return m_hcErr;
+ }
+
+ HTTP_CODE HandleRequest(AtlServerRequest *pRequestInfo, IServiceProvider * /*pProvider*/)
+ {
+ // SOAPACTION header is required per the SOAP 1.1
+ // mainly so firewalls can filter on it.
+ char szBuf[ATL_URL_MAX_URL_LENGTH+1];
+ szBuf[0] = '\0';
+ DWORD dwLen = ATL_URL_MAX_URL_LENGTH;
+ if ( m_spServerContext->GetServerVariable("HTTP_SOAPACTION", szBuf, &dwLen) != FALSE )
+ {
+ if ( dwLen >= 2 )
+ {
+ // drop the last "
+ szBuf[dwLen-2] = '\0';
+ char *szMethod = strrchr(szBuf, '#');
+ if (szMethod != NULL)
+ {
+ _ATLTRY
+ {
+ // ignore return code here
+ SetSoapMapFromName(CA2W( szMethod+1 ), -1, GetNamespaceUri(), -1, true);
+ }
+ _ATLCATCHALL()
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ }
+ }
+ }
+ else
+ {
+ // SOAP requestion that use the HTTP transport
+ // must have a SOAPACTION header.
+ return HTTP_ERROR(500, ISE_SUBERR_SOAPNOSOAPACTION);
+ }
+
+ // set the header map
+ CHttpResponse HttpResponse(pRequestInfo->pServerContext);
+ m_pHttpResponse = &HttpResponse;
+
+ CStreamOnServerContext s(pRequestInfo->pServerContext);
+
+#ifdef _DEBUG
+
+ CSAXSoapErrorHandler err;
+ GetReader()->putErrorHandler(&err);
+
+#endif // _DEBUG
+
+ HRESULT hr = BeginParse(&s);
+
+#ifdef _DEBUG
+ // release the error handler
+ GetReader()->putErrorHandler(NULL);
+#endif // _DEBUG
+
+ if (FAILED(hr))
+ {
+ Cleanup();
+ if (m_hcErr == HTTP_SUCCESS)
+ {
+ SoapFault(SOAP_E_CLIENT, NULL, NULL);
+ }
+
+ return m_hcErr;
+ }
+
+ _ATLTRY
+ {
+ hr = CallFunctionInternal();
+ }
+ _ATLCATCHALL()
+ {
+ // cleanup before propagating user exception
+ Cleanup();
+ HttpResponse.Detach();
+ _ATLRETHROW;
+ }
+
+ if (FAILED(hr))
+ {
+ Cleanup();
+ HttpResponse.ClearHeaders();
+ HttpResponse.ClearContent();
+ if (m_hcErr != HTTP_SUCCESS)
+ {
+ HttpResponse.SetStatusCode(HTTP_ERROR_CODE(m_hcErr));
+ return HTTP_SUCCESS_NO_PROCESS;
+ }
+ HttpResponse.SetStatusCode(500);
+ GenerateAppError(&HttpResponse, hr);
+ return AtlsHttpError(500, SUBERR_NO_PROCESS);
+ }
+
+ HttpResponse.SetContentType("text/xml");
+ hr = GenerateResponse(&HttpResponse);
+ Cleanup();
+ if (FAILED(hr))
+ {
+ SoapFault(SOAP_E_SERVER, NULL, 0);
+ return m_hcErr;
+ }
+
+ return HTTP_SUCCESS;
+ }
+
+ virtual ATL_NOINLINE HRESULT GenerateAppError(IWriteStream *pStream, HRESULT hr)
+ {
+ if (pStream == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ LPWSTR pwszMessage = NULL;
+ DWORD dwLen = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, hr, 0, (LPWSTR) &pwszMessage, 0, NULL);
+
+ if (dwLen == 0)
+ {
+ pwszMessage = L"Application Error";
+ }
+
+ hr = SoapFault(SOAP_E_SERVER, pwszMessage, dwLen ? dwLen : -1);
+ if (dwLen != 0)
+ {
+ ::LocalFree(pwszMessage);
+ }
+
+ return hr;
+ }
+
+ void UninitializeHandler()
+ {
+ UninitializeSOAP();
+ }
+};
+
+
+// client error states
+enum SOAPCLIENT_ERROR
+{
+ SOAPCLIENT_SUCCESS=0, // everything succeeded
+ SOAPCLIENT_INITIALIZE_ERROR, // initialization failed -- most likely an MSXML installation problem
+ SOAPCLIENT_OUTOFMEMORY, // out of memory
+ SOAPCLIENT_GENERATE_ERROR, // failed in generating the response
+ SOAPCLIENT_CONNECT_ERROR, // failed connecting to server
+ SOAPCLIENT_SEND_ERROR, // failed in sending message
+ SOAPCLIENT_SERVER_ERROR, // server error
+ SOAPCLIENT_SOAPFAULT, // a SOAP Fault was returned by the server
+ SOAPCLIENT_PARSEFAULT_ERROR, // failed in parsing SOAP fault
+ SOAPCLIENT_READ_ERROR, // failed in reading response
+ SOAPCLIENT_PARSE_ERROR // failed in parsing response
+};
+
+template <typename TSocketClass = ZEvtSyncSocket>
+class CSoapSocketClientT
+{
+private:
+
+ CUrl m_url;
+ CWriteStreamOnCString m_writeStream;
+ CReadStreamOnSocket<TSocketClass> m_readStream;
+ DWORD m_dwTimeout;
+
+ SOAPCLIENT_ERROR m_errorState;
+
+protected:
+
+ virtual HRESULT GetClientReader(ISAXXMLReader **pReader)
+ {
+ if (pReader == NULL)
+ {
+ return E_POINTER;
+ }
+ *pReader = NULL;
+
+ CComPtr<ISAXXMLReader> spReader;
+ HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr))
+ {
+ *pReader = spReader.Detach();
+ }
+ return hr;
+ }
+
+public:
+
+ // note : not shared across stock client implementations
+ CAtlHttpClientT<TSocketClass> m_socket;
+
+ CSoapFault m_fault;
+
+ // constructor
+ CSoapSocketClientT(LPCTSTR szUrl)
+ : m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
+ {
+ TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
+ if(AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
+ m_url.CrackUrl(szTmp);
+ }
+
+ CSoapSocketClientT(LPCTSTR szServer, LPCTSTR szUri, ATL_URL_PORT nPort=80)
+ : m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
+ {
+ ATLASSERT( szServer != NULL );
+ ATLASSERT( szUri != NULL );
+
+ m_url.SetUrlPath(szUri);
+ m_url.SetHostName(szServer);
+ m_url.SetPortNumber(nPort);
+ }
+
+ ~CSoapSocketClientT()
+ {
+ CleanupClient();
+ }
+
+ SOAPCLIENT_ERROR GetClientError()
+ {
+ return m_errorState;
+ }
+
+ void SetClientError(SOAPCLIENT_ERROR errorState)
+ {
+ m_errorState = errorState;
+ }
+
+ IWriteStream * GetWriteStream()
+ {
+ return &m_writeStream;
+ }
+
+ HRESULT GetReadStream(IStream **ppStream)
+ {
+ if (ppStream == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppStream = &m_readStream;
+ return S_OK;
+ }
+
+ void CleanupClient()
+ {
+ m_writeStream.Cleanup();
+ m_fault.Clear();
+ SetClientError(SOAPCLIENT_SUCCESS);
+ }
+
+ HRESULT SendRequest(LPCTSTR szAction)
+ {
+ HRESULT hr = E_FAIL;
+ _ATLTRY
+ {
+ // create extra headers to send with request
+ CFixedStringT<CString, 256> strExtraHeaders(szAction);
+ strExtraHeaders.Append(_T("Accept: text/xml\r\n"), sizeof("Accept: text/xml\r\n")-1);
+ CAtlNavigateData navData;
+ navData.SetMethod(ATL_HTTP_METHOD_POST);
+ navData.SetPort(m_url.GetPortNumber());
+ navData.SetExtraHeaders(strExtraHeaders);
+ navData.SetPostData((LPBYTE)(LPCSTR) m_writeStream.m_str, m_writeStream.m_str.GetLength(), _T("text/xml; charset=utf-8"));
+
+ ATLSOAP_TRACE( (LPBYTE)(LPCSTR)m_writeStream.m_str, m_writeStream.m_str.GetLength() );
+
+ if (m_dwTimeout != 0)
+ {
+ navData.SetSocketTimeout(m_dwTimeout);
+ }
+
+ if (m_socket.Navigate(&m_url, &navData) != false)
+ {
+ if (GetStatusCode() == 200)
+ {
+ hr = (m_readStream.Init(&m_socket) != FALSE ? S_OK : E_FAIL);
+ if (hr != S_OK)
+ {
+ SetClientError(SOAPCLIENT_READ_ERROR);
+ }
+ }
+ else if (GetStatusCode() == 202)
+ {
+ // for one-way methods
+ hr = S_OK;
+ }
+ else
+ {
+ SetClientError(SOAPCLIENT_SERVER_ERROR);
+ }
+ }
+ else if (GetStatusCode() == 500)
+ {
+ SetClientError(SOAPCLIENT_SOAPFAULT);
+
+ // if returned 500, get the SOAP fault
+ if (m_readStream.Init(&m_socket) != FALSE)
+ {
+ CComPtr<ISAXXMLReader> spReader;
+ if (SUCCEEDED(GetClientReader(&spReader)))
+ {
+ CComPtr<IStream> spReadStream;
+ if (SUCCEEDED(GetReadStream(&spReadStream)))
+ {
+ if (FAILED(m_fault.ParseFault(spReadStream, spReader)))
+ {
+ SetClientError(SOAPCLIENT_PARSEFAULT_ERROR);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ SetClientError(SOAPCLIENT_SEND_ERROR);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_FAIL;
+ }
+
+ return hr;
+ }
+
+ HRESULT SetUrl(LPCTSTR szUrl)
+ {
+ TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
+ if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
+ {
+ return E_FAIL;
+ }
+
+ return (m_url.CrackUrl(szTmp) != FALSE) ? S_OK : E_FAIL;
+ }
+
+ HRESULT GetUrl(__out_ecount_part_z(*pdwLen, *pdwLen) LPTSTR szUrl, __inout LPDWORD pdwLen)
+ {
+ if ((szUrl == NULL) || (pdwLen == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL;
+ }
+
+ HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80)
+ {
+ BOOL bRet = m_socket.SetProxy(szProxy, nProxyPort);
+ return (bRet != FALSE) ? S_OK : E_FAIL;
+ }
+
+ void SetTimeout(DWORD dwTimeout)
+ {
+ m_dwTimeout = dwTimeout;
+ }
+
+ int GetStatusCode()
+ {
+ return m_socket.GetStatus();
+ }
+
+}; // CSoapSocketClientT
+
+#ifndef ATLSOAP_NOWININET
+
+class CReadStreamOnInet : public IStreamImpl
+{
+public:
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
+ {
+ if (ppv == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppv = NULL;
+
+ if (InlineIsEqualGUID(riid, IID_IUnknown) ||
+ InlineIsEqualGUID(riid, IID_IStream) ||
+ InlineIsEqualGUID(riid, IID_ISequentialStream))
+ {
+ *ppv = static_cast<IStream *>(this);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ ULONG __stdcall AddRef()
+ {
+ return 1;
+ }
+
+ ULONG __stdcall Release()
+ {
+ return 1;
+ }
+
+private:
+
+ HINTERNET m_hFile;
+
+public:
+
+ CReadStreamOnInet()
+ :m_hFile(NULL)
+ {
+ }
+
+ void Init(HINTERNET hFile)
+ {
+ m_hFile = hFile;
+ }
+
+ HRESULT STDMETHODCALLTYPE Read(void *pDest, ULONG dwMaxLen, ULONG *pdwRead)
+ {
+ BOOL bRet = InternetReadFile(m_hFile, pDest, dwMaxLen, pdwRead);
+ return (bRet != FALSE) ? S_OK : E_FAIL;
+ }
+
+}; // CStreamOnInet
+
+class CSoapWininetClient
+{
+private:
+
+ CUrl m_url;
+ CWriteStreamOnCString m_writeStream;
+ CReadStreamOnInet m_readStream;
+ CString m_strProxy;
+ DWORD m_dwTimeout;
+ CFixedStringT<CString, ATL_URL_MAX_URL_LENGTH+1> m_strUrl;
+ SOAPCLIENT_ERROR m_errorState;
+
+ void CloseAll()
+ {
+ if (m_hRequest != NULL)
+ {
+ InternetCloseHandle(m_hRequest);
+ m_hRequest = NULL;
+ }
+ if (m_hConnection != NULL)
+ {
+ InternetCloseHandle(m_hConnection);
+ m_hConnection = NULL;
+ }
+ if (m_hInternet != NULL)
+ {
+ InternetCloseHandle(m_hInternet);
+ m_hInternet = NULL;
+ }
+ }
+
+ HRESULT ConnectToServer()
+ {
+ if (m_hConnection != NULL)
+ {
+ return S_OK;
+ }
+
+ m_hInternet = InternetOpen(
+ ATLSOAPINET_CLIENT,
+ m_strProxy.GetLength() ? (INTERNET_OPEN_TYPE_PRECONFIG | INTERNET_OPEN_TYPE_PROXY) : INTERNET_OPEN_TYPE_PRECONFIG,
+ m_strProxy.GetLength() ? (LPCTSTR) m_strProxy : NULL,
+ NULL, 0);
+
+ if (m_hInternet != NULL)
+ {
+ if (m_dwTimeout != 0)
+ {
+ InternetSetOption(m_hInternet, INTERNET_OPTION_CONNECT_TIMEOUT,
+ &m_dwTimeout, sizeof(m_dwTimeout));
+ InternetSetOption(m_hInternet, INTERNET_OPTION_RECEIVE_TIMEOUT,
+ &m_dwTimeout, sizeof(m_dwTimeout));
+ InternetSetOption(m_hInternet, INTERNET_OPTION_SEND_TIMEOUT,
+ &m_dwTimeout, sizeof(m_dwTimeout));
+ }
+ m_hConnection = InternetConnect(m_hInternet, m_url.GetHostName(),
+ (INTERNET_PORT) m_url.GetPortNumber(), NULL, NULL,
+ INTERNET_SERVICE_HTTP, 0, NULL);
+
+ if (m_hConnection != NULL)
+ {
+ return S_OK;
+ }
+ }
+ CloseAll();
+ return E_FAIL;
+ }
+
+protected:
+
+ virtual HRESULT GetClientReader(ISAXXMLReader **pReader)
+ {
+ if (pReader == NULL)
+ {
+ return E_POINTER;
+ }
+ *pReader = NULL;
+
+ CComPtr<ISAXXMLReader> spReader;
+ HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr))
+ {
+ *pReader = spReader.Detach();
+ }
+ return hr;
+ }
+
+public:
+
+ // note : not shared across stock client implementations
+ HINTERNET m_hInternet;
+ HINTERNET m_hConnection;
+ HINTERNET m_hRequest;
+
+ CSoapFault m_fault;
+
+ CSoapWininetClient(LPCTSTR szUrl)
+ :m_hInternet(NULL), m_hConnection(NULL), m_hRequest(NULL), m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
+ {
+ TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
+ if(AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
+ {
+ if (m_url.CrackUrl(szTmp) != FALSE)
+ {
+ SetProxy();
+ _ATLTRY
+ {
+ m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength());
+ m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength());
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+ }
+ }
+
+ CSoapWininetClient(LPCTSTR szServer, LPCTSTR szUri, short nPort=80)
+ :m_hInternet(NULL), m_hConnection(NULL), m_hRequest(NULL), m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
+ {
+ if (m_url.SetHostName(szServer) != FALSE)
+ {
+ if (m_url.SetUrlPath(szUri) != FALSE)
+ {
+ if (m_url.SetPortNumber((ATL_URL_PORT) nPort) != FALSE)
+ {
+ _ATLTRY
+ {
+ m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength());
+ m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength());
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+ }
+ }
+ }
+
+ virtual ~CSoapWininetClient()
+ {
+ CleanupClient();
+ CloseAll();
+ }
+
+ SOAPCLIENT_ERROR GetClientError()
+ {
+ return m_errorState;
+ }
+
+ void SetClientError(SOAPCLIENT_ERROR errorState)
+ {
+ m_errorState = errorState;
+ }
+
+ IWriteStream * GetWriteStream()
+ {
+ return &m_writeStream;
+ }
+
+ HRESULT GetReadStream(IStream **ppStream)
+ {
+ if (ppStream == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppStream = &m_readStream;
+ return S_OK;
+ }
+
+ void CleanupClient()
+ {
+ m_writeStream.Cleanup();
+ if (m_hRequest != NULL)
+ {
+ InternetCloseHandle(m_hRequest);
+ m_hRequest = NULL;
+ }
+ m_fault.Clear();
+ SetClientError(SOAPCLIENT_SUCCESS);
+ }
+
+ HRESULT SendRequest(LPCTSTR szAction)
+ {
+ if (ConnectToServer() != S_OK)
+ {
+ SetClientError(SOAPCLIENT_CONNECT_ERROR);
+ return E_FAIL;
+ }
+
+ CString strHeaders;
+ _ATLTRY
+ {
+ strHeaders.Append(szAction);
+ strHeaders.Append(_T("Content-Type: text/xml; charset=utf-8\r\n"));
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ static LPCTSTR s_szAcceptTypes[] = { _T("text/*"), NULL };
+ m_hRequest = HttpOpenRequest(m_hConnection, _T("POST"),
+ m_strUrl, _T("HTTP/1.0"), NULL,
+ s_szAcceptTypes,
+ INTERNET_FLAG_NO_UI | INTERNET_FLAG_KEEP_CONNECTION | ((m_url.GetScheme() == ATL_URL_SCHEME_HTTPS) ? INTERNET_FLAG_SECURE : 0)
+ , NULL);
+
+ if (m_hRequest != NULL)
+ {
+ if (FALSE != HttpSendRequest(m_hRequest, strHeaders, (DWORD) strHeaders.GetLength(),
+ (void *)(LPCSTR)m_writeStream.m_str, m_writeStream.m_str.GetLength()))
+ {
+ m_readStream.Init(m_hRequest);
+ if (GetStatusCode() != HTTP_STATUS_SERVER_ERROR)
+ {
+ return S_OK;
+ }
+ else
+ {
+ SetClientError(SOAPCLIENT_SOAPFAULT);
+
+ CComPtr<ISAXXMLReader> spReader;
+ if (SUCCEEDED(GetClientReader(&spReader)))
+ {
+ CComPtr<IStream> spReadStream;
+ if (SUCCEEDED(GetReadStream(&spReadStream)))
+ {
+ if (FAILED(m_fault.ParseFault(spReadStream, spReader)))
+ {
+ SetClientError(SOAPCLIENT_PARSEFAULT_ERROR);
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ SetClientError(SOAPCLIENT_SEND_ERROR);
+ }
+
+ return E_FAIL;
+ }
+
+ HRESULT SetUrl(LPCTSTR szUrl)
+ {
+ CloseAll();
+ TCHAR szTmp[ATL_URL_MAX_URL_LENGTH];
+ if(!AtlEscapeUrl(szUrl,szTmp,0,ATL_URL_MAX_URL_LENGTH-1,ATL_URL_BROWSER_MODE))
+ {
+ return E_FAIL;
+ }
+
+ if (m_url.CrackUrl(szTmp) != FALSE)
+ {
+ _ATLTRY
+ {
+ m_strUrl.SetString(m_url.GetUrlPath(), m_url.GetUrlPathLength());
+ m_strUrl.Append(m_url.GetExtraInfo(), m_url.GetExtraInfoLength());
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+ }
+ return E_FAIL;
+ }
+
+ HRESULT GetUrl(LPTSTR szUrl, LPDWORD pdwLen)
+ {
+ if ((szUrl == NULL) || (pdwLen == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL;
+ }
+
+ HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80)
+ {
+ _ATLTRY
+ {
+ if (szProxy && szProxy[0])
+ {
+ m_strProxy.Format(_T("http=http://%s:%d https=http://%s:%d"), szProxy, nProxyPort, szProxy, nProxyPort);
+ }
+ else
+ {
+ m_strProxy.Empty();
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+ }
+
+ void SetTimeout(DWORD dwTimeout)
+ {
+ m_dwTimeout = dwTimeout;
+ }
+
+ int GetStatusCode()
+ {
+ DWORD dwLen = 255;
+ TCHAR szBuf[256];
+ if (HttpQueryInfo(m_hRequest, HTTP_QUERY_STATUS_CODE, szBuf, &dwLen, NULL))
+ {
+ szBuf[dwLen] = '\0';
+ return _ttoi(szBuf);
+ }
+ return 0;
+ }
+}; // CSoapWininetClient
+#endif
+
+#ifndef ATLSOAP_NOMSXML_INET
+class CSoapMSXMLInetClient
+{
+private:
+
+ CUrl m_url;
+ CWriteStreamOnCString m_writeStream;
+ DWORD m_dwTimeout;
+ SOAPCLIENT_ERROR m_errorState;
+
+ HRESULT ConnectToServer()
+ {
+ TCHAR szURL[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwLen = ATL_URL_MAX_URL_LENGTH;
+ HRESULT hr = E_FAIL;
+
+ if (m_spHttpRequest)
+ return S_OK;
+
+ if (!m_url.CreateUrl(szURL, &dwLen))
+ return E_FAIL;
+
+
+ hr = m_spHttpRequest.CoCreateInstance(__uuidof(ServerXMLHTTP30));
+ if (hr != S_OK)
+ return hr;
+
+ CComVariant vEmpty;
+ hr = m_spHttpRequest->open( CComBSTR(L"POST"),
+ CComBSTR(szURL),
+ CComVariant(VARIANT_BOOL(VARIANT_FALSE)),
+ vEmpty,
+ vEmpty );
+ if (hr != S_OK)
+ {
+ m_spHttpRequest.Release();
+ return hr;
+ }
+
+ return S_OK;
+ }
+
+protected:
+
+ virtual HRESULT GetClientReader(ISAXXMLReader **pReader)
+ {
+ if (pReader == NULL)
+ {
+ return E_POINTER;
+ }
+ *pReader = NULL;
+
+ CComPtr<ISAXXMLReader> spReader;
+ HRESULT hr = spReader.CoCreateInstance(ATLS_SAXXMLREADER_CLSID, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr))
+ {
+ *pReader = spReader.Detach();
+ }
+ return hr;
+ }
+
+public:
+
+ // note : not shared across stock client implementations
+ CComPtr<IServerXMLHTTPRequest> m_spHttpRequest;
+
+ CSoapFault m_fault;
+
+ CSoapMSXMLInetClient(LPCTSTR szUrl)
+ :m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
+ {
+ m_url.CrackUrl(szUrl);
+ }
+
+ CSoapMSXMLInetClient(LPCTSTR szServer, LPCTSTR szUri, short nPort=80)
+ : m_dwTimeout(0), m_errorState(SOAPCLIENT_SUCCESS)
+ {
+ m_url.SetHostName(szServer);
+ m_url.SetUrlPath(szUri);
+ m_url.SetPortNumber((ATL_URL_PORT) nPort);
+ }
+
+ virtual ~CSoapMSXMLInetClient()
+ {
+ CleanupClient();
+ }
+
+ SOAPCLIENT_ERROR GetClientError()
+ {
+ return m_errorState;
+ }
+
+ void SetClientError(SOAPCLIENT_ERROR errorState)
+ {
+ m_errorState = errorState;
+ }
+
+ IWriteStream * GetWriteStream()
+ {
+ return &m_writeStream;
+ }
+
+ HRESULT GetReadStream(IStream **ppStream)
+ {
+ if (ppStream == NULL)
+ {
+ return E_POINTER;
+ }
+
+ *ppStream = NULL;
+ HRESULT hr = E_FAIL;
+
+ if (m_spHttpRequest)
+ {
+ VARIANT vResponseStream;
+ VariantInit(&vResponseStream);
+ hr = m_spHttpRequest->get_responseStream(&vResponseStream);
+ if (S_OK == hr)
+ {
+ hr = E_FAIL;
+ if ((vResponseStream.vt == VT_UNKNOWN) && (vResponseStream.punkVal != NULL))
+ {
+ // we return the refcount with the pointer!
+ hr = vResponseStream.punkVal->QueryInterface(__uuidof(IStream), (void **)ppStream);
+ }
+ else
+ {
+ SetClientError(SOAPCLIENT_READ_ERROR);
+ }
+ }
+ VariantClear(&vResponseStream);
+ }
+ return hr;
+ }
+
+ void CleanupClient()
+ {
+ m_writeStream.Cleanup();
+ m_spHttpRequest.Release();
+ m_fault.Clear();
+ SetClientError(SOAPCLIENT_SUCCESS);
+ }
+
+ HRESULT SendRequest(LPCTSTR szAction)
+ {
+ if (ConnectToServer() != S_OK)
+ {
+ SetClientError(SOAPCLIENT_CONNECT_ERROR);
+ return E_FAIL;
+ }
+
+ // set the action header
+ LPCTSTR szColon = _tcschr(szAction, _T(':'));
+ if (szColon != NULL)
+ {
+ do
+ {
+ szColon++;
+ } while (_istspace(static_cast<unsigned char>(*szColon)));
+
+ if (FAILED(m_spHttpRequest->setRequestHeader(
+ CComBSTR( L"SOAPAction" ), CComBSTR( szColon ))))
+ {
+ SetClientError(SOAPCLIENT_SEND_ERROR);
+ return E_FAIL;
+ }
+ } // if SOAPAction header not properly formed, attempt to send anyway
+
+ if (FAILED(m_spHttpRequest->setRequestHeader(CComBSTR( L"Content-Type" ), CComBSTR(L"text/xml; charset=utf-8"))))
+ {
+ SetClientError(SOAPCLIENT_SEND_ERROR);
+ return E_FAIL;
+ }
+
+ // set timeout
+ if (m_dwTimeout != 0)
+ {
+ long nTimeout = (long) m_dwTimeout;
+ m_spHttpRequest->setTimeouts(nTimeout, nTimeout, nTimeout, nTimeout);
+ // reset timeout
+ m_dwTimeout = 0;
+ }
+
+ CComVariant vBody(m_writeStream.m_str);
+ HRESULT hr = m_spHttpRequest->send(vBody);
+ if ((SUCCEEDED(hr)) && (GetStatusCode() == 500))
+ {
+ hr = E_FAIL;
+ CComPtr<ISAXXMLReader> spReader;
+ if (SUCCEEDED(GetClientReader(&spReader)))
+ {
+ SetClientError(SOAPCLIENT_SOAPFAULT);
+
+ CComPtr<IStream> spReadStream;
+ if (SUCCEEDED(GetReadStream(&spReadStream)))
+ {
+ if (FAILED(m_fault.ParseFault(spReadStream, spReader)))
+ {
+ SetClientError(SOAPCLIENT_PARSEFAULT_ERROR);
+ }
+ }
+ }
+ }
+ else if (FAILED(hr))
+ {
+ SetClientError(SOAPCLIENT_SEND_ERROR);
+ }
+
+ return hr;
+ }
+
+ HRESULT SetUrl(LPCTSTR szUrl)
+ {
+ CleanupClient();
+ return (m_url.CrackUrl(szUrl) != FALSE ? S_OK : E_FAIL);
+ }
+
+ HRESULT GetUrl(LPTSTR szUrl, LPDWORD pdwLen)
+ {
+ if ((szUrl == NULL) || (pdwLen == NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ return (m_url.CreateUrl(szUrl, pdwLen) != FALSE) ? S_OK : E_FAIL;
+ }
+
+ void SetTimeout(DWORD dwTimeout)
+ {
+ m_dwTimeout = dwTimeout;
+ }
+
+ int GetStatusCode()
+ {
+ long lStatus;
+ if (m_spHttpRequest->get_status(&lStatus) == S_OK)
+ {
+ return (int) lStatus;
+ }
+ return 0;
+ }
+
+ HRESULT SetProxy(LPCTSTR szProxy = NULL, short nProxyPort = 80)
+ {
+ (szProxy);
+ (nProxyPort);
+
+ ATLTRACE( _T("CSoapMSXMLInetClient does not support SetProxy") );
+
+ return S_OK;
+ }
+}; // CSoapMSXMLInetClient
+#endif
+
+
+class _CSDLGenerator : public ITagReplacerImpl<_CSDLGenerator>
+{
+private:
+
+ typedef CAtlMap<CStringA, const _soapmap *, CStringElementTraits<CStringA> > WSDLMAP;
+ typedef CAtlMap<CStringA, const _soapmapentry *, CStringElementTraits<CStringA> > HEADERMAP;
+
+ HRESULT GenerateWSDLHelper(const _soapmap *pMap, WSDLMAP& structMap, WSDLMAP& enumMap)
+ {
+ ATLENSURE_RETURN( pMap != NULL );
+
+ const _soapmapentry *pEntries = pMap->pEntries;
+ ATLENSURE_RETURN( pEntries != NULL );
+
+ HRESULT hr = S_OK;
+
+ for (int i=0; pEntries[i].nHash != 0; i++)
+ {
+ if (pEntries[i].nVal == SOAPTYPE_UNK)
+ {
+ ATLENSURE_RETURN( pEntries[i].pChain != NULL );
+
+ _ATLTRY
+ {
+ POSITION pos = NULL;
+ CStringA strName(pEntries[i].pChain->szName, pEntries[i].pChain->cchName);
+ if (pEntries[i].pChain->mapType == SOAPMAP_STRUCT)
+ {
+ pos = structMap.SetAt(strName, pEntries[i].pChain);
+ }
+ else if (pEntries[i].pChain->mapType == SOAPMAP_ENUM)
+ {
+ pos = enumMap.SetAt(strName, pEntries[i].pChain);
+ }
+ if (pos == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+
+ hr = GenerateWSDLHelper(pEntries[i].pChain, structMap, enumMap);
+ if (FAILED(hr))
+ {
+ break;
+ }
+ }
+ }
+
+ return hr;
+ }
+
+ HTTP_CODE IsUDT(const _soapmapentry *pEntry)
+ {
+ ATLENSURE( pEntry != NULL );
+ return (pEntry->nVal != SOAPTYPE_UNK) ? HTTP_S_FALSE : HTTP_SUCCESS;
+ }
+
+ HTTP_CODE GetSoapDims(const _soapmapentry *pEntry)
+ {
+ ATLENSURE( pEntry != NULL );
+ if (pEntry->pDims[0] != 0)
+ {
+ if (SUCCEEDED(m_pWriteStream->WriteStream("[", 1, NULL)))
+ {
+ for (int i=1; i<=pEntry->pDims[0]; i++)
+ {
+ if (m_writeHelper.Write(pEntry->pDims[i]) != FALSE)
+ {
+ if (i < pEntry->pDims[0])
+ {
+ if (FAILED(m_pWriteStream->WriteStream(", ", 2, NULL)))
+ {
+ return HTTP_FAIL;
+ }
+ }
+ }
+ }
+ if (SUCCEEDED(m_pWriteStream->WriteStream("]", 1, NULL)))
+ {
+ return HTTP_SUCCESS;
+ }
+ }
+ }
+ return HTTP_FAIL;
+ }
+
+ const _soapmap **m_pFuncs;
+ const _soapmap **m_pHeaders;
+ int m_nFunc;
+ int m_nParam;
+ int m_nHeader;
+ WSDLMAP m_structMap;
+ WSDLMAP m_enumMap;
+ POSITION m_currUDTPos;
+ int m_nCurrUDTField;
+
+ HEADERMAP m_headerMap;
+ POSITION m_currHeaderPos;
+
+ CWriteStreamHelper m_writeHelper;
+
+ CStringA m_strServiceName;
+ CStringA m_strNamespaceUri;
+
+ IWriteStream *m_pWriteStream;
+
+ CComPtr<IHttpServerContext> m_spHttpServerContext;
+
+ DWORD m_dwCallFlags;
+
+protected:
+
+ void SetWriteStream(IWriteStream *pStream)
+ {
+ m_pWriteStream = pStream;
+ m_writeHelper.Attach(m_pWriteStream);
+ }
+
+ void SetHttpServerContext(IHttpServerContext *pServerContext)
+ {
+ m_spHttpServerContext = pServerContext;
+ }
+
+ static HTTP_CODE GetSoapType(int nVal, IWriteStream *pStream)
+ {
+ return (pStream->WriteStream(CSoapRootHandler::s_xsdNames[nVal].szName,
+ CSoapRootHandler::s_xsdNames[nVal].cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+
+ HRESULT InitializeSDL(CSoapRootHandler *pHdlr)
+ {
+ m_pFuncs = pHdlr->GetFunctionMap();
+
+ if (m_pFuncs == NULL)
+ {
+ return E_FAIL;
+ }
+
+ ATLASSUME( m_pFuncs[0] != NULL );
+
+ m_dwCallFlags = m_pFuncs[0]->dwCallFlags;
+
+ size_t i;
+ for (i=0; m_pFuncs[i] != NULL; i++)
+ {
+ const _soapmap *pMap = m_pFuncs[i];
+ HRESULT hr = GenerateWSDLHelper(pMap, m_structMap, m_enumMap);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ m_pHeaders = pHdlr->GetHeaderMap();
+ if (m_pHeaders != NULL)
+ {
+ for (i=0; m_pHeaders[i] != NULL; i++)
+ {
+ const _soapmap *pMap = m_pHeaders[i];
+ HRESULT hr = GenerateWSDLHelper(pMap, m_structMap, m_enumMap);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+
+ for (i=0; m_pHeaders[i] != NULL; i++)
+ {
+ const _soapmap *pMap = m_pHeaders[i];
+ for (size_t j=0; pMap->pEntries[j].nHash != 0; j++)
+ {
+ HRESULT hr = S_OK;
+ _ATLTRY
+ {
+ if (m_headerMap.SetAt(pMap->pEntries[j].szField, &pMap->pEntries[j]) == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ }
+ }
+ }
+
+ _ATLTRY
+ {
+ m_strServiceName = pHdlr->GetServiceName();
+ m_strNamespaceUri = pHdlr->GetNamespaceUriA();
+ }
+ _ATLCATCHALL()
+ {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+ }
+
+ virtual const char * GetHandlerName() = 0;
+
+public:
+
+ _CSDLGenerator()
+ :m_pFuncs(NULL), m_nFunc(-1), m_nParam(-1),
+ m_currUDTPos(NULL), m_nCurrUDTField(-1),
+ m_pWriteStream(NULL), m_nHeader(-1), m_currHeaderPos(NULL)
+ {
+ }
+ virtual ~_CSDLGenerator()
+ {
+ }
+
+ HTTP_CODE OnGetURL()
+ {
+ char szURL[ATL_URL_MAX_URL_LENGTH];
+ DWORD dwUrlSize = sizeof(szURL);
+ char szServer[ATL_URL_MAX_HOST_NAME_LENGTH];
+ DWORD dwServerSize = sizeof(szServer);
+ char szHttps[16];
+ DWORD dwHttpsLen = sizeof(szHttps);
+ char szPort[ATL_URL_MAX_PORT_NUMBER_LENGTH+1];
+ DWORD dwPortLen = sizeof(szPort);
+
+ if (m_spHttpServerContext->GetServerVariable("URL", szURL, &dwUrlSize) != FALSE)
+ {
+ if (m_spHttpServerContext->GetServerVariable("SERVER_NAME", szServer, &dwServerSize) != FALSE)
+ {
+ bool bHttps = false;
+ if ((m_spHttpServerContext->GetServerVariable("HTTPS", szHttps, &dwHttpsLen) != FALSE) &&
+ (!_stricmp(szHttps, "ON")))
+ {
+ bHttps = true;
+ }
+
+ if (m_spHttpServerContext->GetServerVariable("SERVER_PORT", szPort, &dwPortLen) != FALSE)
+ {
+ _ATLTRY
+ {
+ CStringA strUrl;
+ strUrl.Format("http%s://%s:%s%s?Handler=%s", bHttps ? "s" : "", szServer, szPort, szURL, GetHandlerName());
+
+ CA2W wszUrl(strUrl);
+ wchar_t *pwszUrl = wszUrl;
+ HRESULT hr = AtlGenXMLValue(m_pWriteStream, &pwszUrl);
+ return SUCCEEDED(hr) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+ }
+ }
+ }
+ return HTTP_FAIL;
+ }
+
+ HTTP_CODE OnGetNamespace()
+ {
+ return (m_pWriteStream->WriteStream(m_strNamespaceUri,
+ m_strNamespaceUri.GetLength(), NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ HTTP_CODE OnGetNextFunction()
+ {
+ m_nFunc++;
+ if (m_pFuncs[m_nFunc] == NULL)
+ {
+ m_nFunc = -1;
+ return HTTP_S_FALSE;
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnGetFunctionName()
+ {
+ return (m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->szName,
+ m_pFuncs[m_nFunc]->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetNextParameter()
+ {
+ ++m_nParam;
+ if (m_pFuncs[m_nFunc]->pEntries[m_nParam].nHash != 0)
+ {
+ if (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_NOMARSHAL)
+ {
+ return OnGetNextParameter();
+ }
+ return HTTP_SUCCESS;
+ }
+ m_nParam = -1;
+ return HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsInParameter()
+ {
+ return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_IN) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetParameterName()
+ {
+ HRESULT hr = S_OK;
+ if (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_RETVAL)
+ {
+ hr = m_pWriteStream->WriteStream("return", sizeof("return")-1, NULL);
+ }
+ else
+ {
+ hr = m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->pEntries[m_nParam].szField,
+ m_pFuncs[m_nFunc]->pEntries[m_nParam].cchField, NULL);
+ }
+
+ return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ HTTP_CODE OnNotIsArrayParameter()
+ {
+ return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR))
+ ? HTTP_S_FALSE: HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnIsParameterUDT()
+ {
+ return IsUDT(&m_pFuncs[m_nFunc]->pEntries[m_nParam]);
+ }
+
+ HTTP_CODE OnGetParameterSoapType()
+ {
+ if (m_pFuncs[m_nFunc]->pEntries[m_nParam].nVal != SOAPTYPE_UNK)
+ {
+ return GetSoapType(m_pFuncs[m_nFunc]->pEntries[m_nParam].nVal, m_pWriteStream);
+ }
+ ATLASSUME( m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain != NULL );
+ return (m_pWriteStream->WriteStream(m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain->szName,
+ m_pFuncs[m_nFunc]->pEntries[m_nParam].pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsParameterDynamicArray()
+ {
+ return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_DYNARR) ? HTTP_SUCCESS: HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsArrayParameter()
+ {
+ return (OnNotIsArrayParameter() != HTTP_SUCCESS) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsParameterOneDimensional()
+ {
+ return (m_pFuncs[m_nFunc]->pEntries[m_nParam].pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetParameterArraySize()
+ {
+ return (m_writeHelper.Write(m_pFuncs[m_nFunc]->pEntries[m_nParam].pDims[1]) != FALSE)
+ ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ HTTP_CODE OnGetParameterArraySoapDims()
+ {
+ return GetSoapDims(&m_pFuncs[m_nFunc]->pEntries[m_nParam]);
+ }
+
+ HTTP_CODE OnIsOutParameter()
+ {
+ return (m_pFuncs[m_nFunc]->pEntries[m_nParam].dwFlags & SOAPFLAG_OUT) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetNextEnum()
+ {
+ if (m_currUDTPos == NULL)
+ {
+ m_currUDTPos = m_enumMap.GetStartPosition();
+ }
+ else
+ {
+ m_enumMap.GetNext(m_currUDTPos);
+ }
+
+ return (m_currUDTPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetEnumName()
+ {
+ const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
+ return (m_pWriteStream->WriteStream(pMap->szName, pMap->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetNextEnumElement()
+ {
+ const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
+ ++m_nCurrUDTField;
+ if (pMap->pEntries[m_nCurrUDTField].nHash != 0)
+ {
+ return HTTP_SUCCESS;
+ }
+ m_nCurrUDTField = -1;
+ return HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetEnumElementName()
+ {
+ const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
+ return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].szField,
+ pMap->pEntries[m_nCurrUDTField].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetNextStruct()
+ {
+ if (m_currUDTPos == NULL)
+ {
+ m_currUDTPos = m_structMap.GetStartPosition();
+ }
+ else
+ {
+ m_structMap.GetNext(m_currUDTPos);
+ }
+
+ return (m_currUDTPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetStructName()
+ {
+ const _soapmap *pMap = m_enumMap.GetValueAt(m_currUDTPos);
+ return (m_pWriteStream->WriteStream(pMap->szName, pMap->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetNextStructField()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ ++m_nCurrUDTField;
+ if (pMap->pEntries[m_nCurrUDTField].nHash != 0)
+ {
+ return HTTP_SUCCESS;
+ }
+ m_nCurrUDTField = -1;
+ return HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetStructFieldName()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].szField,
+ pMap->pEntries[m_nCurrUDTField].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnNotIsArrayField()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return (pMap->pEntries[m_nCurrUDTField].dwFlags & (SOAPFLAG_FIXEDARR | SOAPFLAG_DYNARR)) ? HTTP_S_FALSE : HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnIsFieldUDT()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return IsUDT(&pMap->pEntries[m_nCurrUDTField]);
+ }
+
+ HTTP_CODE OnGetStructFieldSoapType()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ if (pMap->pEntries[m_nCurrUDTField].nVal != SOAPTYPE_UNK)
+ {
+ return GetSoapType(pMap->pEntries[m_nCurrUDTField].nVal, m_pWriteStream);
+ }
+ ATLASSERT( pMap->pEntries[m_nCurrUDTField].pChain != NULL );
+ return (m_pWriteStream->WriteStream(pMap->pEntries[m_nCurrUDTField].pChain->szName,
+ pMap->pEntries[m_nCurrUDTField].pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsArrayField()
+ {
+ return (OnNotIsArrayField() != HTTP_SUCCESS) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsFieldDynamicArray()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return (pMap->pEntries[m_nCurrUDTField].dwFlags & SOAPFLAG_DYNARR) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetFieldSizeIsName()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ int nIndex = pMap->pEntries[m_nCurrUDTField].nSizeIs;
+ ATLASSERT( nIndex >= 0 );
+ return (m_pStream->WriteStream(pMap->pEntries[nIndex].szField,
+ pMap->pEntries[nIndex].cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsFieldOneDimensional()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return (pMap->pEntries[m_nCurrUDTField].pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetFieldArraySize()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return (m_writeHelper.Write(pMap->pEntries[m_nCurrUDTField].pDims[1]) != FALSE) ?
+ HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetFieldArraySoapDims()
+ {
+ const _soapmap *pMap = m_structMap.GetValueAt(m_currUDTPos);
+ return GetSoapDims(&pMap->pEntries[m_nCurrUDTField]);
+ }
+
+ HTTP_CODE OnGetServiceName()
+ {
+ return (m_pWriteStream->WriteStream(m_strServiceName,
+ m_strServiceName.GetLength(), NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ HTTP_CODE OnGetNextHeader()
+ {
+ if (m_currHeaderPos == NULL)
+ {
+ m_currHeaderPos = m_headerMap.GetStartPosition();
+ }
+ else
+ {
+ m_headerMap.GetNext(m_currHeaderPos);
+ }
+
+ return (m_currHeaderPos != NULL) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsInHeader()
+ {
+ return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_IN)
+ ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsOutHeader()
+ {
+ return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_OUT)
+ ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsRequiredHeader()
+ {
+ return (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_MUSTUNDERSTAND)
+ ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetHeaderName()
+ {
+ const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
+ return (m_pWriteStream->WriteStream(pEntry->szField,
+ pEntry->cchField, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ HTTP_CODE OnNotIsArrayHeader()
+ {
+ const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
+ return (pEntry->dwFlags & SOAPFLAG_FIXEDARR) ? HTTP_S_FALSE : HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnIsHeaderUDT()
+ {
+ return IsUDT(m_headerMap.GetValueAt(m_currHeaderPos));
+ }
+
+ HTTP_CODE OnGetHeaderSoapType()
+ {
+ const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
+ if (pEntry->nVal != SOAPTYPE_UNK)
+ {
+ return GetSoapType(pEntry->nVal, m_pWriteStream);
+ }
+ ATLENSURE( pEntry->pChain != NULL );
+ return (m_pWriteStream->WriteStream(pEntry->pChain->szName,
+ pEntry->pChain->cchName, NULL) == S_OK) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsHeaderOneDimensional()
+ {
+ const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
+ return (pEntry->pDims[0] == 1) ? HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetHeaderArraySize()
+ {
+ const _soapmapentry *pEntry = m_headerMap.GetValueAt(m_currHeaderPos);
+ return (m_writeHelper.Write(pEntry->pDims[1]) != FALSE) ?
+ HTTP_SUCCESS : HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetHeaderArraySoapDims()
+ {
+ return GetSoapDims(m_headerMap.GetValueAt(m_currHeaderPos));
+ }
+
+ HTTP_CODE OnGetNextFunctionHeader()
+ {
+ ++m_nHeader;
+ if (m_pHeaders[m_nFunc]->pEntries[m_nHeader].nHash != 0)
+ {
+ if (m_pHeaders[m_nFunc]->pEntries[m_nHeader].dwFlags & SOAPFLAG_NOMARSHAL)
+ {
+ return OnGetNextHeader();
+ }
+ return HTTP_SUCCESS;
+ }
+ m_nHeader = -1;
+ return HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnGetFunctionHeaderName()
+ {
+ return (m_pWriteStream->WriteStream(
+ m_pHeaders[m_nFunc]->pEntries[m_nHeader].szField,
+ m_pHeaders[m_nFunc]->pEntries[m_nHeader].cchField,
+ NULL) == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ HTTP_CODE OnIsArrayHeader()
+ {
+ return (OnNotIsArrayHeader() == HTTP_SUCCESS) ? HTTP_S_FALSE : HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnIsDocumentLiteral()
+ {
+ if ((m_dwCallFlags & (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL)) ==
+ (SOAPFLAG_DOCUMENT | SOAPFLAG_LITERAL))
+ {
+ return HTTP_SUCCESS;
+ }
+ return HTTP_S_FALSE;
+ }
+
+ HTTP_CODE OnIsRpcEncoded()
+ {
+ if ((m_dwCallFlags & (SOAPFLAG_RPC | SOAPFLAG_ENCODED)) ==
+ (SOAPFLAG_RPC | SOAPFLAG_ENCODED))
+ {
+ return HTTP_SUCCESS;
+ }
+ return HTTP_S_FALSE;
+ }
+
+#pragma warning (push)
+#pragma warning (disable : 4640) // construction of local static object is not thread-safe
+
+ BEGIN_REPLACEMENT_METHOD_MAP(_CSDLGenerator)
+ REPLACEMENT_METHOD_ENTRY("GetNamespace", OnGetNamespace)
+ REPLACEMENT_METHOD_ENTRY("GetNextFunction", OnGetNextFunction)
+ REPLACEMENT_METHOD_ENTRY("GetFunctionName", OnGetFunctionName)
+ REPLACEMENT_METHOD_ENTRY("GetNextParameter", OnGetNextParameter)
+ REPLACEMENT_METHOD_ENTRY("IsInParameter", OnIsInParameter)
+ REPLACEMENT_METHOD_ENTRY("GetParameterName", OnGetParameterName)
+ REPLACEMENT_METHOD_ENTRY("NotIsArrayParameter", OnNotIsArrayParameter)
+ REPLACEMENT_METHOD_ENTRY("IsParameterUDT", OnIsParameterUDT)
+ REPLACEMENT_METHOD_ENTRY("GetParameterSoapType", OnGetParameterSoapType)
+ REPLACEMENT_METHOD_ENTRY("IsParameterDynamicArray", OnIsParameterDynamicArray)
+ REPLACEMENT_METHOD_ENTRY("IsArrayParameter", OnIsArrayParameter)
+ REPLACEMENT_METHOD_ENTRY("IsParameterOneDimensional", OnIsParameterOneDimensional)
+ REPLACEMENT_METHOD_ENTRY("GetParameterArraySize", OnGetParameterArraySize)
+ REPLACEMENT_METHOD_ENTRY("GetParameterArraySoapDims", OnGetParameterArraySoapDims)
+ REPLACEMENT_METHOD_ENTRY("IsOutParameter", OnIsOutParameter)
+ REPLACEMENT_METHOD_ENTRY("GetNextEnum", OnGetNextEnum)
+ REPLACEMENT_METHOD_ENTRY("GetEnumName", OnGetEnumName)
+ REPLACEMENT_METHOD_ENTRY("GetNextEnumElement", OnGetNextEnumElement)
+ REPLACEMENT_METHOD_ENTRY("GetEnumElementName", OnGetEnumElementName)
+ REPLACEMENT_METHOD_ENTRY("GetNextStruct", OnGetNextStruct)
+ REPLACEMENT_METHOD_ENTRY("GetStructName", OnGetStructName)
+ REPLACEMENT_METHOD_ENTRY("GetNextStructField", OnGetNextStructField)
+ REPLACEMENT_METHOD_ENTRY("GetStructFieldName", OnGetStructFieldName)
+ REPLACEMENT_METHOD_ENTRY("NotIsArrayField", OnNotIsArrayField)
+ REPLACEMENT_METHOD_ENTRY("IsFieldUDT", OnIsFieldUDT)
+ REPLACEMENT_METHOD_ENTRY("GetStructFieldSoapType", OnGetStructFieldSoapType)
+ REPLACEMENT_METHOD_ENTRY("IsArrayField", OnIsArrayField)
+ REPLACEMENT_METHOD_ENTRY("IsFieldOneDimensional", OnIsFieldOneDimensional)
+ REPLACEMENT_METHOD_ENTRY("GetFieldArraySize", OnGetFieldArraySize)
+ REPLACEMENT_METHOD_ENTRY("GetFieldArraySoapDims", OnGetFieldArraySoapDims)
+ REPLACEMENT_METHOD_ENTRY("GetServiceName", OnGetServiceName)
+ REPLACEMENT_METHOD_ENTRY("GetURL", OnGetURL)
+
+ REPLACEMENT_METHOD_ENTRY("GetNextHeader", OnGetNextHeader)
+ REPLACEMENT_METHOD_ENTRY("GetHeaderName", OnGetHeaderName)
+ REPLACEMENT_METHOD_ENTRY("NotIsArrayHeader", OnNotIsArrayHeader)
+ REPLACEMENT_METHOD_ENTRY("IsArrayHeader", OnIsArrayHeader)
+ REPLACEMENT_METHOD_ENTRY("IsHeaderUDT", OnIsHeaderUDT)
+ REPLACEMENT_METHOD_ENTRY("GetHeaderSoapType", OnGetHeaderSoapType)
+ REPLACEMENT_METHOD_ENTRY("IsHeaderOneDimensional", OnIsHeaderOneDimensional)
+ REPLACEMENT_METHOD_ENTRY("GetHeaderArraySize", OnGetHeaderArraySize)
+ REPLACEMENT_METHOD_ENTRY("GetHeaderArraySoapDims", OnGetHeaderArraySoapDims)
+ REPLACEMENT_METHOD_ENTRY("GetNextFunctionHeader", OnGetNextFunctionHeader)
+ REPLACEMENT_METHOD_ENTRY("GetFunctionHeaderName", OnGetFunctionHeaderName)
+ REPLACEMENT_METHOD_ENTRY("IsInHeader", OnIsInHeader)
+ REPLACEMENT_METHOD_ENTRY("IsOutHeader", OnIsOutHeader)
+ REPLACEMENT_METHOD_ENTRY("IsRequiredHeader", OnIsRequiredHeader)
+
+ REPLACEMENT_METHOD_ENTRY("IsDocumentLiteral", OnIsDocumentLiteral)
+ REPLACEMENT_METHOD_ENTRY("IsRpcEncoded", OnIsRpcEncoded)
+ REPLACEMENT_METHOD_ENTRY("IsFieldDynamicArray", OnIsFieldDynamicArray)
+ REPLACEMENT_METHOD_ENTRY("GetFieldSizeIsName", OnGetFieldSizeIsName)
+ END_REPLACEMENT_METHOD_MAP()
+
+#pragma warning (pop)
+
+}; // class _CSDLGenerator
+
+template <class THandler, const char *szHandlerName>
+class CSDLGenerator :
+ public _CSDLGenerator,
+ public IRequestHandlerImpl< CSDLGenerator<THandler,szHandlerName> >,
+ public CComObjectRootEx<CComSingleThreadModel>
+{
+private:
+
+public:
+ typedef CSDLGenerator<THandler, szHandlerName> _sdlGenerator;
+
+ BEGIN_COM_MAP(_sdlGenerator)
+ COM_INTERFACE_ENTRY(IRequestHandler)
+ COM_INTERFACE_ENTRY(ITagReplacer)
+ END_COM_MAP()
+
+ HTTP_CODE InitializeHandler(AtlServerRequest *pRequestInfo, IServiceProvider *pServiceProvider)
+ {
+ IRequestHandlerImpl<CSDLGenerator>::InitializeHandler(pRequestInfo, pServiceProvider);
+
+ CComObjectStack<THandler> handler;
+ if (FAILED(InitializeSDL(&handler)))
+ {
+ return HTTP_FAIL;
+ }
+
+ CStencil s;
+ HTTP_CODE hcErr = s.LoadFromString(s_szAtlsWSDLSrf, (DWORD) strlen(s_szAtlsWSDLSrf));
+ if (hcErr == HTTP_SUCCESS)
+ {
+ hcErr = HTTP_FAIL;
+ CHttpResponse HttpResponse(pRequestInfo->pServerContext);
+ HttpResponse.SetContentType("text/xml");
+ if (s.ParseReplacements(this) != false)
+ {
+ s.FinishParseReplacements();
+
+ SetStream(&HttpResponse);
+ SetWriteStream(&HttpResponse);
+ SetHttpServerContext(m_spServerContext);
+
+ ATLASSERT( s.ParseSuccessful() != false );
+
+ hcErr = s.Render(this, &HttpResponse);
+ }
+ }
+
+ return hcErr;
+ }
+
+ const char * GetHandlerName()
+ {
+ return szHandlerName;
+ }
+}; // class CSDLGenerator
+
+} // namespace ATL
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif // __ATLSOAP_H__
diff --git a/include/atl/atlspriv.h b/include/atl/atlspriv.h
new file mode 100644
index 000000000..f278e4761
--- /dev/null
+++ b/include/atl/atlspriv.h
@@ -0,0 +1,1017 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSPRIV_H__
+#define __ATLSPRIV_H__
+
+#pragma once
+#include <atlsocket.h>
+
+#ifndef _WINSOCK2API_
+#error Winsock2.h has to be included before including windows.h or use atlbase.h instead of windows.h
+#endif
+
+#ifndef _ATL_NO_DEFAULT_LIBS
+#pragma comment(lib, "ws2_32.lib")
+#endif // !_ATL_NO_DEFAULT_LIBS
+
+#include <svcguid.h>
+#include <atlcoll.h>
+#include <mlang.h>
+#include <atlutil.h>
+
+// ATL_SOCK_TIMEOUT defines the amount of time
+// this socket will block the calling thread waiting
+// for the socket before the call times out.
+#ifndef ATL_SOCK_TIMEOUT
+ #define ATL_SOCK_TIMEOUT 10000
+#endif
+
+#define ATL_WINSOCK_VER MAKELONG(2,0)
+
+// This file contains unsupported code used in ATL implementation files. Most of
+// this code is support code for various ATL Server functions.
+#pragma pack(push,_ATL_PACKING)
+namespace ATL{
+
+ // One of these objects can be created globally to turn
+// on the socket stuff at CRT startup and shut it down
+// on CRT term.
+class _AtlWSAInit
+{
+public:
+ _AtlWSAInit() throw()
+ {
+ m_dwErr = WSAEFAULT;
+ }
+
+ bool Init()
+ {
+ if (!IsStarted())
+ m_dwErr = WSAStartup(ATL_WINSOCK_VER, &m_stData);
+
+ return m_dwErr == 0;
+ }
+
+ bool IsStarted(){ return m_dwErr == 0; }
+
+ ~_AtlWSAInit() throw()
+ {
+ if (!m_dwErr)
+ WSACleanup();
+ }
+
+ WSADATA m_stData;
+ DWORD m_dwErr;
+};
+
+#ifndef _ATL_NO_GLOBAL_SOCKET_STARTUP
+ __declspec(selectany)_AtlWSAInit g_HttpInit;
+#endif
+
+
+class ZEvtSyncSocket
+{
+public:
+ ZEvtSyncSocket() throw();
+ ~ZEvtSyncSocket() throw();
+ operator SOCKET() throw();
+ void Close() throw();
+ void Term() throw();
+ bool Create(const ADDRINFOT* pAI, WORD wFlags=0) throw();
+ bool Create(int af, int st, int proto, WORD wFlags=0) throw();
+ bool Connect(LPCTSTR szAddr, unsigned short nPort) throw();
+ bool Connect(const SOCKADDR* psa, int len) throw();
+ bool Connect(const ADDRINFOT *pAI) throw();
+ bool Write(WSABUF *pBuffers, int nCount, DWORD *pdwSize) throw();
+ bool Write(const unsigned char *pBuffIn, DWORD *pdwSize) throw();
+ bool Read(const unsigned char *pBuff, DWORD *pdwSize) throw();
+ bool Init(SOCKET hSocket, void * /*pData=NULL*/) throw();
+ DWORD GetSocketTimeout() throw();
+ DWORD SetSocketTimeout(DWORD dwNewTimeout) throw();
+ bool SupportsScheme(ATL_URL_SCHEME scheme) throw();
+
+protected:
+ DWORD m_dwCreateFlags;
+ WSAEVENT m_hEventRead;
+ WSAEVENT m_hEventWrite;
+ WSAEVENT m_hEventConnect;
+
+ CComAutoCriticalSection m_csRead;
+ CComAutoCriticalSection m_csWrite;
+ SOCKET m_socket;
+ bool m_bConnected;
+ DWORD m_dwLastError;
+ DWORD m_dwSocketTimeout;
+};
+inline bool _AtlIsHttpSpace(TCHAR c)
+{
+ return (c == 0x09 ||
+ c == 0x0A ||
+ c == 0x0D ||
+ c == 0x20);
+}
+
+// MIME helper functions
+
+extern __declspec(selectany) const DWORD ATL_MIME_DEFAULT_CP = 28591;
+
+// This function is used to create an CSMTPConnection-compatible recipient string
+// from a recipient string that is in a CMimeMessage object.
+inline BOOL AtlMimeMakeRecipientsString(_In_ LPCSTR szNames, _Out_z_cap_post_count_(*pdwLen, *pdwLen) LPSTR szRecipients, _Inout_ LPDWORD pdwLen)
+{
+ ATLENSURE(szNames != NULL);
+ ATLENSURE(szRecipients != NULL);
+ ATLENSURE(pdwLen != NULL);
+
+ char ch;
+ DWORD dwLen = 0;
+ while ((ch = *szNames++) != '\0')
+ {
+ // Skip everything that is in double quotes
+ if (ch == '"')
+ {
+ while (*szNames && *szNames++ != '"');
+ }
+ if (ch == '<')
+ {
+ // Extract the address from within the <>
+ while (*szNames && *szNames != '>')
+ {
+ if( dwLen >= *pdwLen )
+ {
+ return FALSE;
+ }
+ *szRecipients++ = *szNames++;
+ dwLen++;
+ }
+ if( dwLen >= *pdwLen )
+ {
+ return FALSE;
+ }
+ // End it with a comma
+ *szRecipients++ = ',';
+ dwLen++;
+ }
+ if (ch == '=')
+ {
+ // Skip any BEncoded or QEncoded parts
+ while (*szNames)
+ {
+ if (*szNames == '?' && *(szNames+1) == '=')
+ {
+ szNames+=2;
+ break;
+ }
+ szNames++;
+ }
+ }
+ szNames++;
+ }
+ if (dwLen != 0)
+ {
+ szRecipients--;
+ dwLen--;
+ }
+ *szRecipients = '\0';
+ *pdwLen = dwLen;
+
+ return TRUE;
+}
+
+// AtlMimeCharsetFromCodePage, AtlMimeConvertString
+// are MIME multilanguage support functions.
+
+// Get the MIME character set of the of the code page. The character set is copied
+// into szCharset.
+
+#ifndef ATLSMTP_DEFAULT_CSET
+ #define ATLSMTP_DEFAULT_CSET "iso-8859-1"
+#endif
+
+inline BOOL AtlMimeCharsetFromCodePage(_Out_z_cap_(cch) LPSTR szCharset, _In_ UINT uiCodePage, _In_opt_ IMultiLanguage* pMultiLanguage, _In_ size_t cch) throw()
+{
+ ATLASSERT(szCharset != NULL);
+
+ if (!pMultiLanguage)
+ {
+ if ((uiCodePage == 0) || (uiCodePage == ATL_MIME_DEFAULT_CP))
+ {
+ ATLASSERT(_countof(ATLSMTP_DEFAULT_CSET) <= cch);
+ Checked::strcpy_s(szCharset, cch, ATLSMTP_DEFAULT_CSET);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (uiCodePage == 0)
+ uiCodePage = GetACP();
+
+ HRESULT hr;
+ MIMECPINFO cpInfo;
+ memset(&cpInfo, 0x00, sizeof(cpInfo));
+
+#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
+
+ // if IMultiLanguage2 is available, use it
+ CComPtr<IMultiLanguage2> spMultiLanguage2;
+ hr = pMultiLanguage->QueryInterface(__uuidof(IMultiLanguage2), (void **)&spMultiLanguage2);
+ if (FAILED(hr) || !spMultiLanguage2.p)
+ hr = pMultiLanguage->GetCodePageInfo(uiCodePage, &cpInfo);
+ else
+ hr = spMultiLanguage2->GetCodePageInfo(uiCodePage,
+ LANGIDFROMLCID(GetThreadLocale()), &cpInfo);
+
+#else // __IMultiLanguage2_INTERFACE_DEFINED__
+
+ hr = pMultiLanguage->GetCodePageInfo(uiCodePage, &cpInfo);
+
+#endif // __IMultiLanguage2_INTERFACE_DEFINED__
+
+ if (hr != S_OK)
+ return FALSE;
+ _ATLTRY
+ {
+ CW2A charSet(cpInfo.wszWebCharset);
+ if (strlen(charSet) >= cch)
+ return FALSE;
+ Checked::strcpy_s(szCharset, cch, charSet);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+inline BOOL AtlMimeConvertStringW(
+ _In_ IMultiLanguage *pMultiLanguage,
+ _In_ UINT uiCodePage,
+ _In_ LPCWSTR wszIn,
+ _Out_z_cap_post_count_(*pnLen, *pnLen) LPSTR *ppszOut,
+ _Inout_ UINT *pnLen) throw()
+{
+ ATLENSURE_RETURN_VAL( pMultiLanguage != NULL, FALSE );
+ ATLENSURE_RETURN_VAL( wszIn != NULL, FALSE );
+ ATLENSURE_RETURN_VAL( ppszOut != NULL, FALSE );
+ ATLENSURE_RETURN_VAL( pnLen != NULL, FALSE );
+
+ *ppszOut = NULL;
+ *pnLen = 0;
+
+ if (uiCodePage == 0)
+ {
+ uiCodePage = GetACP();
+ }
+
+ DWORD dwMode = 0;
+ CHeapPtr<char> pszOut;
+
+ // get the length
+ HRESULT hr = pMultiLanguage->ConvertStringFromUnicode(&dwMode, uiCodePage, const_cast<LPWSTR>(wszIn), NULL, NULL, pnLen);
+ if (SUCCEEDED(hr))
+ {
+ // allocate the buffer
+ if (pszOut.Allocate(*pnLen))
+ {
+ dwMode = 0;
+ // do the conversion
+ hr = pMultiLanguage->ConvertStringFromUnicode(&dwMode, uiCodePage, const_cast<LPWSTR>(wszIn), NULL, pszOut, pnLen);
+ if (SUCCEEDED(hr))
+ {
+ *ppszOut = pszOut.Detach();
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+inline BOOL AtlMimeConvertStringA(
+ _In_ IMultiLanguage *pMultiLanguage,
+ _In_ UINT uiCodePage,
+ _In_ LPCSTR szIn,
+ _Out_z_cap_post_count_(*pnLen, *pnLen) LPSTR *ppszOut,
+ _Inout_ UINT *pnLen) throw()
+{
+ _ATLTRY
+ {
+ return AtlMimeConvertStringW(pMultiLanguage, uiCodePage, CA2W(szIn), ppszOut, pnLen);
+ }
+ _ATLCATCHALL()
+ {
+ return FALSE;
+ }
+}
+
+#ifdef _UNICODE
+ #define AtlMimeConvertString AtlMimeConvertStringW
+#else
+ #define AtlMimeConvertString AtlMimeConvertStringA
+#endif
+
+class CStreamOnSequentialStream :
+ public IStream
+{
+ CComPtr<ISequentialStream> m_spStream;
+public:
+ CStreamOnSequentialStream(ISequentialStream *pStream) throw()
+ {
+ ATLASSERT(pStream);
+ m_spStream = pStream;
+ }
+ virtual ~CStreamOnSequentialStream()
+ {
+ }
+
+ STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead) throw()
+ {
+ if (!m_spStream)
+ return E_UNEXPECTED;
+ return m_spStream->Read(pv, cb, pcbRead);
+ }
+
+ STDMETHOD(Write)(const void *pv, ULONG cb, ULONG *pcbWritten) throw()
+ {
+ if (!m_spStream)
+ return E_UNEXPECTED;
+ return m_spStream->Write(pv, cb, pcbWritten);
+ }
+
+ STDMETHOD(Seek)(LARGE_INTEGER , DWORD , ULARGE_INTEGER *) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(SetSize)(ULARGE_INTEGER ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(CopyTo)(IStream *, ULARGE_INTEGER , ULARGE_INTEGER *,
+ ULARGE_INTEGER *) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Commit)(DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Revert)( void) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(LockRegion)(ULARGE_INTEGER , ULARGE_INTEGER , DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(UnlockRegion)(ULARGE_INTEGER , ULARGE_INTEGER ,
+ DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Stat)(STATSTG *, DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Clone)(IStream **) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(QueryInterface)(REFIID iid, void **ppUnk) throw()
+ {
+ *ppUnk = NULL;
+ if (::InlineIsEqualGUID(iid, IID_IUnknown) ||
+ ::InlineIsEqualGUID(iid, IID_ISequentialStream) ||
+ ::InlineIsEqualGUID(iid, IID_IStream))
+ {
+ *ppUnk = (void*)(IStream*)this;
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef( void) throw()
+ {
+ return (ULONG)1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release( void) throw()
+ {
+ return (ULONG)1;
+ }
+};
+
+class CStreamOnByteArray :
+ public IStream
+{
+public:
+ BYTE *m_pArray;
+ DWORD m_dwRead;
+
+ CStreamOnByteArray(BYTE *pBytes) throw()
+ {
+ ATLASSERT(pBytes);
+ m_pArray = pBytes;
+ m_dwRead = 0;
+ }
+
+ STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead) throw()
+ {
+ if (!pv)
+ return E_INVALIDARG;
+
+ if (cb == 0)
+ return S_OK;
+
+ if (!m_pArray)
+ return E_UNEXPECTED;
+
+ BYTE *pCurr = m_pArray;
+ pCurr += m_dwRead;
+ Checked::memcpy_s(pv, cb, pCurr, cb);
+ if (pcbRead)
+ *pcbRead = cb;
+ m_dwRead += cb;
+ return S_OK;
+ }
+
+ STDMETHOD(Write)(const void* , ULONG , ULONG* ) throw()
+ {
+ return E_UNEXPECTED;
+ }
+
+ STDMETHOD(Seek)(LARGE_INTEGER , DWORD , ULARGE_INTEGER *) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(SetSize)(ULARGE_INTEGER ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(CopyTo)(IStream *, ULARGE_INTEGER , ULARGE_INTEGER *,
+ ULARGE_INTEGER *) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Commit)(DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Revert)( void) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(LockRegion)(ULARGE_INTEGER , ULARGE_INTEGER , DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(UnlockRegion)(ULARGE_INTEGER , ULARGE_INTEGER ,
+ DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Stat)(STATSTG *, DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Clone)(IStream **) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(QueryInterface)(REFIID iid, void **ppUnk) throw()
+ {
+ *ppUnk = NULL;
+ if (::InlineIsEqualGUID(iid, IID_IUnknown) ||
+ ::InlineIsEqualGUID(iid, IID_ISequentialStream) ||
+ ::InlineIsEqualGUID(iid, IID_IStream))
+ {
+ *ppUnk = (void*)(IStream*)this;
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef( void) throw()
+ {
+ return (ULONG)1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release( void) throw()
+ {
+ return (ULONG)1;
+ }
+};
+
+class CVariantStream :
+ public IStream
+{
+public:
+ CVariantStream() throw()
+ {
+ m_nCurrRead = 0;
+ m_nVariantSize = 0;
+ m_nRef = 1;
+ }
+ virtual ~CVariantStream()
+ {
+ }
+
+ // input variant is put into contained BYTE array.
+ HRESULT InsertVariant(const VARIANT *pVarIn) throw()
+ {
+ CComVariant vIn;
+ HRESULT hr = E_FAIL;
+ m_nCurrRead = 0;
+ m_nVariantSize = 0;
+ hr = vIn.Attach(const_cast<VARIANT*>(pVarIn));
+ if (hr == S_OK)
+ {
+ hr = vIn.WriteToStream(static_cast<IStream*>(this));
+ vIn.Detach(const_cast<VARIANT*>(pVarIn));
+ }
+ return hr;
+ }
+
+ // variant is read from contained byte array into
+ // out variant.
+ HRESULT RetrieveVariant(VARIANT *pVarOut) throw()
+ {
+ CComVariant vOut;
+ HRESULT hr = vOut.ReadFromStream(static_cast<IStream*>(this));
+ if (hr == S_OK)
+ hr = vOut.Detach(pVarOut);
+
+ m_nCurrRead = 0;
+ return hr;
+ }
+
+ HRESULT LoadFromStream(ISequentialStream *stream) throw()
+ {
+ m_nCurrRead = 0;
+ CStreamOnSequentialStream stm(stream);
+ CComVariant v;
+ HRESULT hr = v.ReadFromStream(&stm);
+ if (hr == S_OK)
+ hr = v.WriteToStream(static_cast<IStream*>(this));
+ return hr;
+ }
+
+ ISequentialStream* GetStream() throw()
+ {
+ return static_cast<ISequentialStream*>(this);
+ }
+
+ size_t GetVariantSize() throw()
+ {
+ return m_nVariantSize;
+ }
+
+// Implementation
+ // IStream implementation;
+ STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead) throw()
+ {
+ if (!pv)
+ return E_INVALIDARG;
+
+ if (cb == 0)
+ return S_OK;
+
+ if (pcbRead)
+ *pcbRead = 0;
+
+ if (!m_nVariantSize)
+ return S_OK; // nothing to do.
+
+ size_t nLeft = m_nVariantSize - m_nCurrRead;
+ if (nLeft > 0)
+ {
+ size_t nRead = __min(nLeft, cb);
+ BYTE *pCurr = m_stream;
+ pCurr += m_nCurrRead;
+ Checked::memcpy_s(pv, cb, pCurr, nRead);
+ m_nCurrRead += nRead;
+ if (pcbRead)
+ *pcbRead = (ULONG)nRead;
+ }
+
+ return S_OK;
+ }
+
+ STDMETHOD(Write)(const void *pv, ULONG cb, ULONG *pcbWritten) throw()
+ {
+ HRESULT hr = E_OUTOFMEMORY;
+ if (!pv)
+ return E_INVALIDARG;
+
+ if (cb == 0)
+ return S_OK;
+
+ if (pcbWritten)
+ *pcbWritten = 0;
+
+ ULONG newsz = cb + (ULONG)m_nVariantSize;
+ if (newsz < cb || newsz < m_nVariantSize)
+ {
+ return E_OUTOFMEMORY;
+ }
+ BYTE *pBytes = NULL;
+ ATLTRY(pBytes = m_stream.Reallocate(newsz));
+ if (pBytes)
+ {
+ pBytes += m_nVariantSize;
+ Checked::memcpy_s(pBytes, cb, pv, cb);
+ if (pcbWritten)
+ *pcbWritten = cb;
+ m_nVariantSize += cb;
+ hr = S_OK;
+ }
+ return hr;
+ }
+
+ STDMETHOD(Seek)(LARGE_INTEGER , DWORD , ULARGE_INTEGER *) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(SetSize)(ULARGE_INTEGER ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(CopyTo)(IStream *, ULARGE_INTEGER , ULARGE_INTEGER *,
+ ULARGE_INTEGER *) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Commit)(DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Revert)( void) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(LockRegion)(ULARGE_INTEGER , ULARGE_INTEGER , DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(UnlockRegion)(ULARGE_INTEGER , ULARGE_INTEGER ,
+ DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Stat)(STATSTG *, DWORD ) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Clone)(IStream **) throw()
+ {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(QueryInterface)(REFIID iid, void **ppUnk) throw()
+ {
+ *ppUnk = NULL;
+ if (::InlineIsEqualGUID(iid, IID_IUnknown))
+ {
+ *ppUnk = (void*)(IUnknown*)this;
+ }
+ else if (::InlineIsEqualGUID(iid, IID_ISequentialStream))
+ {
+ *ppUnk = (void*)(ISequentialStream*)this;
+ }
+ else if (::InlineIsEqualGUID(iid, IID_IStream))
+ {
+ *ppUnk = (void*)(IStream*)this;
+ }
+
+ if (*ppUnk)
+ {
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef( void) throw()
+ {
+ return (ULONG)1;
+ }
+
+ ULONG STDMETHODCALLTYPE Release( void) throw()
+ {
+ return (ULONG)1;
+ }
+
+ CTempBuffer<BYTE> m_stream;
+ size_t m_nVariantSize;
+ size_t m_nCurrRead;
+ long m_nRef;
+};
+
+// given a nCurrent and a pointer to a value representing the
+// maximum value that has been seen in nCurrent,
+// will update pnMax if nCurrent is greater
+inline void AtlInterlockedUpdateMax(long nCurrent, long* pnMax)
+{
+ ATLENSURE(pnMax != NULL);
+
+ long nMax;
+ long nOrigMax;
+
+ do
+ {
+ nMax = *pnMax;
+ nOrigMax = 0;
+ if (nCurrent > nMax)
+ nOrigMax = InterlockedCompareExchange(pnMax, nCurrent, nMax);
+ }
+ while (nOrigMax != 0 && nOrigMax != nMax);
+}
+
+// wrapper around InterlockedExchangeAdd
+inline LONG AtlInterlockedExchangeAdd(_Inout_ long volatile* pAddend, _In_ long nValue)
+{
+#if defined(_WIN64) && defined(_M_CEE)
+
+ // We use System::Threading::Interlocked::Add because InterlockedExchangeAdd is an intrisinc not supported in managed code with 64bits compilers.
+ // System::Threading::Interlocked::Add returns the value after the addition, but we maintain the same semantics as InterlockedExchangeAdd.
+ _STATIC_ASSERT(sizeof(int) == sizeof(long));
+ return (System::Threading::Interlocked::Add(*((int*)pAddend), nValue) - nValue);
+
+#else
+
+ return InterlockedExchangeAdd(pAddend, nValue);
+
+#endif
+}
+
+// SOAP helpers
+#define _ATLSOAP_DECLARE_WSDL_SRF() \
+__if_not_exists(s_szAtlsWSDLSrf) \
+{ \
+extern __declspec(selectany) const char * const s_szAtlsWSDLSrf = \
+"<?xml version=\"1.0\"?>\r\n" \
+"<!-- ATL Server generated Web Service Description -->\r\n" \
+"<definitions \r\n" \
+" xmlns:s=\"http://www.w3.org/2001/XMLSchema\" \r\n" \
+" xmlns:http=\"http://schemas.xmlsoap.org/wsdl/http/\" \r\n" \
+" xmlns:mime=\"http://schemas.xmlsoap.org/wsdl/mime/\" \r\n" \
+" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" \r\n" \
+" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" \r\n" \
+" xmlns:s0=\"{{GetNamespace}}\" \r\n" \
+" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\"\r\n" \
+" xmlns:atls=\"http://tempuri.org/vc/atl/server/\"\r\n" \
+" targetNamespace=\"{{GetNamespace}}\" \r\n" \
+" xmlns=\"http://schemas.xmlsoap.org/wsdl/\"\r\n" \
+">\r\n" \
+" <types>\r\n" \
+" <s:schema targetNamespace=\"{{GetNamespace}}\" attributeFormDefault=\"qualified\" elementFormDefault=\"qualified\">\r\n" \
+" <s:import namespace=\"http://schemas.xmlsoap.org/soap/encoding/\"/>\r\n" \
+"{{if IsRpcEncoded}}\r\n" \
+"{{while GetNextFunction}}\r\n" \
+"{{while GetNextParameter}}\r\n" \
+"{{if IsArrayParameter}}\r\n" \
+" <s:complexType name=\"{{GetFunctionName}}_{{GetParameterName}}_Array\">\r\n" \
+" <s:complexContent>\r\n" \
+" <s:restriction base=\"soapenc:Array\">\r\n" \
+" <s:attribute ref=\"soapenc:arrayType\" wsdl:arrayType=\"{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}{{if IsParameterDynamicArray}}[]{{else}}{{GetParameterArraySoapDims}}{{endif}}\"/>\r\n" \
+" </s:restriction>\r\n" \
+" </s:complexContent>\r\n" \
+" </s:complexType>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+"{{endwhile}}\r\n" \
+"{{endif}}\r\n" \
+"{{while GetNextHeader}}\r\n" \
+"{{if IsHeaderUDT}}\r\n" \
+"{{else}}\r\n" \
+"{{if IsArrayHeader}}\r\n" \
+"{{else}}\r\n" \
+" <s:simpleType name=\"{{GetHeaderName}}_wrapper\">\r\n" \
+" <s:restriction base=\"s:{{GetHeaderSoapType}}\"/>\r\n" \
+" </s:simpleType>\r\n" \
+"{{endif}}\r\n" \
+"{{endif}}\r\n" \
+"{{if IsRpcEncoded}}\r\n" \
+"{{if IsArrayHeader}}\r\n" \
+" <s:complexType name=\"{{GetHeaderName}}_Array\">\r\n" \
+" <s:complexContent>\r\n" \
+" <s:restriction base=\"soapenc:Array\">\r\n" \
+" <s:attribute ref=\"soapenc:arrayType\" wsdl:arrayType=\"{{if IsHeaderUDT}}s0:{{else}}s:{{endif}}{{GetHeaderSoapType}}{{GetHeaderArraySoapDims}}\"/>\r\n" \
+" </s:restriction>\r\n" \
+" </s:complexContent>\r\n" \
+" </s:complexType>\r\n" \
+"{{endif}}\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+"{{if IsDocumentLiteral}}\r\n" \
+"{{while GetNextFunction}}\r\n" \
+" <s:element name=\"{{GetFunctionName}}\">\r\n" \
+" <s:complexType>\r\n" \
+" <s:sequence>\r\n" \
+"{{while GetNextParameter}}\r\n" \
+"{{if IsInParameter}}\r\n" \
+" <s:element name=\"{{GetParameterName}}\" {{if NotIsArrayParameter}}type=\"{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}\"/{{else}}nillable=\"{{if IsParameterDynamicArray}}true{{else}}false{{endif}}\"{{endif}}>\r\n" \
+"{{if IsArrayParameter}}\r\n" \
+" <s:complexType>\r\n" \
+" <s:sequence>\r\n" \
+" <s:element name=\"{{GetParameterSoapType}}\" type=\"{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}\" {{if IsParameterDynamicArray}}minOccurs=\"0\" maxOccurs=\"unbounded\"{{else}}minOccurs=\"{{GetParameterArraySize}}\" maxOccurs=\"{{GetParameterArraySize}}\"{{endif}}/>\r\n" \
+" </s:sequence>\r\n" \
+" </s:complexType>\r\n" \
+" </s:element>\r\n" \
+"{{endif}}\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+" </s:sequence>\r\n" \
+" </s:complexType>\r\n" \
+" </s:element>\r\n" \
+" <s:element name=\"{{GetFunctionName}}Response\">\r\n" \
+" <s:complexType>\r\n" \
+" <s:sequence>\r\n" \
+"{{while GetNextParameter}}\r\n" \
+"{{if IsOutParameter}}\r\n" \
+" <s:element name=\"{{GetParameterName}}\" {{if NotIsArrayParameter}}type=\"{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}\"/{{else}}nillable=\"{{if IsParameterDynamicArray}}true{{else}}false{{endif}}\"{{endif}}>\r\n" \
+"{{if IsArrayParameter}}\r\n" \
+" <s:complexType>\r\n" \
+" <s:sequence>\r\n" \
+" <s:element name=\"{{GetParameterSoapType}}\" type=\"{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}\" {{if IsParameterDynamicArray}}minOccurs=\"0\" maxOccurs=\"unbounded\"{{else}}minOccurs=\"{{GetParameterArraySize}}\" maxOccurs=\"{{GetParameterArraySize}}\"{{endif}}/>\r\n" \
+" </s:sequence>\r\n" \
+" </s:complexType>\r\n" \
+" </s:element>\r\n" \
+"{{endif}}\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+" </s:sequence>\r\n" \
+" </s:complexType>\r\n" \
+" </s:element>\r\n" \
+"{{endwhile}}\r\n" \
+"{{endif}}\r\n" \
+"{{while GetNextEnum}}\r\n" \
+" <s:simpleType name=\"{{GetEnumName}}\">\r\n" \
+" <s:restriction base=\"s:string\">\r\n" \
+"{{while GetNextEnumElement}}\r\n" \
+" <s:enumeration value=\"{{GetEnumElementName}}\"/>\r\n" \
+"{{endwhile}}\r\n" \
+" </s:restriction>\r\n" \
+" </s:simpleType>\r\n" \
+"{{endwhile}}\r\n" \
+"{{while GetNextStruct}}\r\n" \
+" <s:complexType name=\"{{GetStructName}}\">\r\n" \
+" <s:sequence>\r\n" \
+"{{while GetNextStructField}}\r\n" \
+" <s:element name=\"{{GetStructFieldName}}\" {{if IsFieldDynamicArray}}atls:SizeIs=\"{{GetFieldSizeIsName}}\" {{endif}}{{if NotIsArrayField}}type=\"{{if IsFieldUDT}}s0:{{else}}s:{{endif}}{{GetStructFieldSoapType}}\"/{{else}}nillable=\"{{if IsFieldDynamicArray}}true{{else}}false{{endif}}\"{{endif}}>\r\n" \
+"{{if IsArrayField}}\r\n" \
+" <s:complexType>\r\n" \
+"{{if IsRpcEncoded}}\r\n" \
+" <s:complexContent>\r\n" \
+" <s:restriction base=\"soapenc:Array\">\r\n" \
+" <s:attribute ref=\"soapenc:arrayType\" wsdl:arrayType=\"{{if IsFieldUDT}}s0:{{else}}s:{{endif}}{{GetStructFieldSoapType}}{{if IsFieldDynamicArray}}[]{{else}}{{GetFieldArraySoapDims}}{{endif}}\"/>\r\n" \
+" </s:restriction>\r\n" \
+" </s:complexContent>\r\n" \
+"{{else}}\r\n" \
+" <s:sequence>\r\n" \
+" <s:element name=\"{{GetStructFieldSoapType}}\" type=\"{{if IsFieldUDT}}s0:{{else}}s:{{endif}}{{GetStructFieldSoapType}}\" {{if IsFieldDynamicArray}}minOccurs=\"0\" maxOccurs=\"unbounded\"{{else}}minOccurs=\"{{GetFieldArraySize}}\" maxOccurs=\"{{GetFieldArraySize}}\"{{endif}}/>\r\n" \
+" </s:sequence>\r\n" \
+"{{endif}}\r\n" \
+" </s:complexType>\r\n" \
+" </s:element>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+" </s:sequence>\r\n" \
+" </s:complexType>\r\n" \
+"{{endwhile}}\r\n" \
+"{{if IsDocumentLiteral}}\r\n" \
+"{{while GetNextHeader}}\r\n" \
+" <s:element name=\"{{GetHeaderName}}\" {{if NotIsArrayHeader}}type=\"s0:{{if IsHeaderUDT}}{{GetHeaderSoapType}}{{else}}{{GetHeaderName}}_wrapper{{endif}}\"/{{else}}nillable=\"false\"{{endif}}>\r\n" \
+"{{if IsArrayHeader}}\r\n" \
+" <s:complexType>\r\n" \
+" <s:sequence>\r\n" \
+" <s:element name=\"{{GetHeaderSoapType}}\" type=\"{{if IsHeaderUDT}}s0:{{GetHeaderSoapType}}{{else}}s:{{endif}}{{GetHeaderSoapType}}\" minOccurs=\"{{GetHeaderArraySize}}\" maxOccurs=\"{{GetHeaderArraySize}}\"/>\r\n" \
+" </s:sequence>\r\n" \
+" </s:complexType>\r\n" \
+" </s:element>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+"{{endif}}\r\n" \
+" </s:schema>\r\n" \
+" </types>\r\n" \
+"{{while GetNextFunction}}\r\n" \
+" <message name=\"{{GetFunctionName}}In\">\r\n" \
+"{{if IsDocumentLiteral}}\r\n" \
+" <part name=\"parameters\" element=\"s0:{{GetFunctionName}}\"/>\r\n" \
+"{{endif}}\r\n" \
+"{{if IsRpcEncoded}}\r\n" \
+"{{while GetNextParameter}}\r\n" \
+"{{if IsInParameter}}\r\n" \
+" <part name=\"{{GetParameterName}}\" type=\"{{if NotIsArrayParameter}}{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}{{else}}s0:{{GetFunctionName}}_{{GetParameterName}}_Array{{endif}}\"/>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+"{{endif}}\r\n" \
+" </message>\r\n" \
+" <message name=\"{{GetFunctionName}}Out\">\r\n" \
+"{{if IsDocumentLiteral}}\r\n" \
+" <part name=\"parameters\" element=\"s0:{{GetFunctionName}}Response\"/>\r\n" \
+"{{endif}}\r\n" \
+"{{if IsRpcEncoded}}\r\n" \
+"{{while GetNextParameter}}\r\n" \
+"{{if IsOutParameter}}\r\n" \
+" <part name=\"{{GetParameterName}}\" type=\"{{if NotIsArrayParameter}}{{if IsParameterUDT}}s0:{{else}}s:{{endif}}{{GetParameterSoapType}}{{else}}s0:{{GetFunctionName}}_{{GetParameterName}}_Array{{endif}}\"/>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+"{{endif}}\r\n" \
+" </message>\r\n" \
+"{{endwhile}}\r\n" \
+"{{while GetNextHeader}}\r\n" \
+" <message name=\"{{GetHeaderName}}\">\r\n" \
+"{{if IsDocumentLiteral}}\r\n" \
+" <part name=\"{{GetHeaderName}}\" element=\"s0:{{GetHeaderName}}\"/>\r\n" \
+"{{endif}}\r\n" \
+"{{if IsRpcEncoded}}\r\n" \
+" <part name=\"{{GetHeaderName}}\" type=\"{{if NotIsArrayHeader}}s0:{{if IsHeaderUDT}}{{GetHeaderSoapType}}{{else}}{{GetHeaderName}}_wrapper{{endif}}{{else}}s0:{{GetHeaderName}}_Array{{endif}}\"/>\r\n" \
+"{{endif}}\r\n" \
+" </message>\r\n" \
+"{{endwhile}}\r\n" \
+" <portType name=\"{{GetServiceName}}Soap\">\r\n" \
+"{{while GetNextFunction}}\r\n" \
+" <operation name=\"{{GetFunctionName}}\">\r\n" \
+" <input message=\"s0:{{GetFunctionName}}In\"/>\r\n" \
+" <output message=\"s0:{{GetFunctionName}}Out\"/>\r\n" \
+" </operation>\r\n" \
+"{{endwhile}}\r\n" \
+" </portType>\r\n" \
+" <binding name=\"{{GetServiceName}}Soap\" type=\"s0:{{GetServiceName}}Soap\">\r\n" \
+" <soap:binding transport=\"http://schemas.xmlsoap.org/soap/http\" style=\"{{if IsDocumentLiteral}}document{{endif}}{{if IsRpcEncoded}}rpc{{endif}}\"/>\r\n" \
+"{{while GetNextFunction}}\r\n" \
+" <operation name=\"{{GetFunctionName}}\">\r\n" \
+" <soap:operation soapAction=\"#{{GetFunctionName}}\" style=\"{{if IsDocumentLiteral}}document{{endif}}{{if IsRpcEncoded}}rpc{{endif}}\"/>\r\n" \
+" <input>\r\n" \
+" <soap:body {{if IsDocumentLiteral}}use=\"literal\"{{endif}}{{if IsRpcEncoded}}use=\"encoded\" namespace=\"{{GetNamespace}}\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"{{endif}}/>\r\n" \
+"{{while GetNextFunctionHeader}}\r\n" \
+"{{if IsInHeader}}\r\n" \
+" <soap:header message=\"s0:{{GetFunctionHeaderName}}\" part=\"{{GetFunctionHeaderName}}\"{{if IsRequiredHeader}} wsdl:required=\"true\"{{endif}} {{if IsDocumentLiteral}}use=\"literal\"{{endif}}{{if IsRpcEncoded}}use=\"encoded\" namespace=\"{{GetNamespace}}\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"{{endif}}/>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+" </input>\r\n" \
+" <output>\r\n" \
+" <soap:body {{if IsDocumentLiteral}}use=\"literal\"{{endif}}{{if IsRpcEncoded}}use=\"encoded\" namespace=\"{{GetNamespace}}\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"{{endif}}/>\r\n" \
+"{{while GetNextFunctionHeader}}\r\n" \
+"{{if IsOutHeader}}\r\n" \
+" <soap:header message=\"s0:{{GetFunctionHeaderName}}\" part=\"{{GetFunctionHeaderName}}\"{{if IsRequiredHeader}} wsdl:required=\"true\"{{endif}} {{if IsDocumentLiteral}}use=\"literal\"{{endif}}{{if IsRpcEncoded}}use=\"encoded\" namespace=\"{{GetNamespace}}\" encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"{{endif}}/>\r\n" \
+"{{endif}}\r\n" \
+"{{endwhile}}\r\n" \
+" </output>\r\n" \
+" </operation>\r\n" \
+"{{endwhile}}\r\n" \
+" </binding>\r\n" \
+" <service name=\"{{GetServiceName}}\">\r\n" \
+" <port name=\"{{GetServiceName}}Soap\" binding=\"s0:{{GetServiceName}}Soap\">\r\n" \
+" <soap:address location=\"{{GetURL}}\"/>\r\n" \
+" </port>\r\n" \
+" </service>\r\n" \
+"</definitions>"; \
+}
+
+#include <atlspriv.inl>
+}; // namespace ATL
+#pragma pack(pop)
+
+#endif // __ATLSPRIV_H__
diff --git a/include/atl/atlspriv.inl b/include/atl/atlspriv.inl
new file mode 100644
index 000000000..9693d33d5
--- /dev/null
+++ b/include/atl/atlspriv.inl
@@ -0,0 +1,361 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// ZEvtSyncSocket
+// ************ This is an implementation only class ************
+// Class ZEvtSyncSocket is a non-supported, implementation only
+// class used by the ATL HTTP client class CAtlHttpClient. Do not
+// use this class in your code. Use of this class is not supported by Microsoft.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+#ifndef __ATLSPRIV_INL__
+#define __ATLSPRIV_INL__
+
+#pragma once
+
+#pragma warning(push)
+#pragma warning(disable:4312)
+
+inline ZEvtSyncSocket::ZEvtSyncSocket()
+{
+ m_dwCreateFlags = WSA_FLAG_OVERLAPPED;
+ m_hEventRead = m_hEventWrite = m_hEventConnect = NULL;
+ m_socket = INVALID_SOCKET;
+ m_bConnected = false;
+ m_dwLastError = 0;
+ m_dwSocketTimeout = ATL_SOCK_TIMEOUT;
+ g_HttpInit.Init();
+}
+
+inline ZEvtSyncSocket::~ZEvtSyncSocket()
+{
+ Close();
+}
+
+inline ZEvtSyncSocket::operator SOCKET()
+{
+ return m_socket;
+}
+
+inline void ZEvtSyncSocket::Close()
+{
+ if (m_socket != INVALID_SOCKET)
+ {
+ m_bConnected = false;
+ closesocket(m_socket);
+ m_socket = INVALID_SOCKET;
+ Term();
+ }
+}
+
+inline void ZEvtSyncSocket::Term()
+{
+ if (m_hEventRead)
+ {
+ WSACloseEvent(m_hEventRead);
+ m_hEventRead = NULL;
+ }
+ if (m_hEventWrite)
+ {
+ WSACloseEvent(m_hEventWrite);
+ m_hEventWrite = NULL;
+ }
+ if (m_hEventConnect)
+ {
+ WSACloseEvent(m_hEventConnect);
+ m_hEventConnect = NULL;
+ }
+ m_socket = INVALID_SOCKET;
+}
+
+inline bool ZEvtSyncSocket::Create(const ADDRINFOT* pAI, WORD wFlags)
+{
+ return Create(pAI->ai_family, pAI->ai_socktype, pAI->ai_protocol, wFlags);
+}
+
+inline bool ZEvtSyncSocket::Create(int af, int st, int proto, WORD wFlags)
+{
+ bool bRet = true;
+ if (m_socket != INVALID_SOCKET)
+ {
+ m_dwLastError = WSAEALREADY;
+ return false; // Must close this socket first
+ }
+
+ m_socket = WSASocket(af, st, proto, NULL, 0,
+ wFlags | m_dwCreateFlags);
+ if (m_socket == INVALID_SOCKET)
+ {
+ m_dwLastError = ::WSAGetLastError();
+ bRet = false;
+ }
+ else
+ bRet = Init(m_socket, NULL);
+ return bRet;
+}
+
+inline bool ZEvtSyncSocket::Connect(LPCTSTR szAddr, unsigned short nPort) throw()
+{
+ if (m_bConnected)
+ return true;
+
+ bool bRet = true;
+ CSocketAddr address;
+ // Find address information
+ if ((m_dwLastError = address.FindAddr(szAddr, nPort, 0, PF_UNSPEC, SOCK_STREAM, 0)) != ERROR_SUCCESS)
+ {
+ bRet = false;
+ }
+ else
+ {
+ bRet = Connect(address.GetAddrInfo());
+ }
+ return bRet;
+}
+
+inline bool ZEvtSyncSocket::Connect(const ADDRINFOT *pAI)
+{
+ if (m_socket == INVALID_SOCKET && !Create(pAI))
+ return false;
+
+ return Connect((SOCKADDR*)pAI->ai_addr, (int)pAI->ai_addrlen);
+}
+
+inline bool ZEvtSyncSocket::Connect(const SOCKADDR* psa, int len)
+{
+ if (m_bConnected)
+ return true; // already connected
+
+ DWORD dwLastError;
+ bool bRet = true;
+
+ // if you try to connect the socket without
+ // creating it first it's reasonable to automatically
+ // try the create for you.
+ if (m_socket == INVALID_SOCKET)
+ return false;
+
+ if (WSAConnect(m_socket,
+ psa, len,
+ NULL, NULL, NULL, NULL))
+ {
+ dwLastError = WSAGetLastError();
+ if (dwLastError != WSAEWOULDBLOCK)
+ {
+ m_dwLastError = dwLastError;
+ bRet = false;
+ }
+ else
+ {
+ dwLastError = WaitForSingleObject((HANDLE)m_hEventConnect, m_dwSocketTimeout);
+ if (dwLastError == WAIT_OBJECT_0)
+ {
+ // make sure there were no connection errors.
+ WSANETWORKEVENTS wse;
+ ZeroMemory(&wse, sizeof(wse));
+ WSAEnumNetworkEvents(m_socket, NULL, &wse);
+ if (wse.iErrorCode[FD_CONNECT_BIT]!=0)
+ {
+ m_dwLastError = (DWORD)(wse.iErrorCode[FD_CONNECT_BIT]);
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+
+ }
+
+ m_bConnected = bRet;
+ return bRet;
+}
+
+inline bool ZEvtSyncSocket::Write(WSABUF *pBuffers, int nCount, DWORD *pdwSize)
+{
+ // if we aren't already connected we'll wait to see if the connect
+ // event happens
+ if (WAIT_OBJECT_0 != WaitForSingleObject((HANDLE)m_hEventConnect , m_dwSocketTimeout))
+ {
+ m_dwLastError = WSAENOTCONN;
+ return false; // not connected
+ }
+
+ // make sure we aren't already writing
+ if (WAIT_TIMEOUT == WaitForSingleObject((HANDLE)m_hEventWrite, 0))
+ {
+ m_dwLastError = WSAEINPROGRESS;
+ return false; // another write on is blocking this socket
+ }
+
+ bool bRet = true;
+ *pdwSize = 0;
+ WSAOVERLAPPED o;
+ m_csWrite.Lock();
+ o.hEvent = m_hEventWrite;
+ WSAResetEvent(o.hEvent);
+ if (WSASend(m_socket, pBuffers, nCount, pdwSize, 0, &o, 0))
+ {
+ DWORD dwLastError = WSAGetLastError();
+ if (dwLastError != WSA_IO_PENDING)
+ {
+ m_dwLastError = dwLastError;
+ bRet = false;
+ }
+ }
+
+ // wait for write to complete
+ if (bRet)
+ {
+ if (WaitForSingleObject((HANDLE)m_hEventWrite, m_dwSocketTimeout) == WAIT_OBJECT_0)
+ {
+ DWORD dwFlags = 0;
+ if (WSAGetOverlappedResult(m_socket, &o, pdwSize, FALSE, &dwFlags))
+ bRet = true;
+ else
+ {
+ m_dwLastError = ::GetLastError();
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+
+ m_csWrite.Unlock();
+ return bRet;
+}
+
+inline bool ZEvtSyncSocket::Write(const unsigned char *pBuffIn, DWORD *pdwSize)
+{
+ WSABUF buff;
+ buff.buf = (char*)pBuffIn;
+ buff.len = *pdwSize;
+ return Write(&buff, 1, pdwSize);
+}
+
+inline bool ZEvtSyncSocket::Read(const unsigned char *pBuff, DWORD *pdwSize)
+{
+ // if we aren't already connected we'll wait to see if the connect
+ // event happens
+ if (WAIT_OBJECT_0 != WaitForSingleObject((HANDLE)m_hEventConnect , m_dwSocketTimeout))
+ {
+ m_dwLastError = WSAENOTCONN;
+ return false; // not connected
+ }
+
+ if (WAIT_ABANDONED == WaitForSingleObject((HANDLE)m_hEventRead, 0))
+ {
+ m_dwLastError = WSAEINPROGRESS;
+ return false; // another write on is blocking this socket
+ }
+
+ bool bRet = true;
+ WSABUF buff;
+ buff.buf = (char*)pBuff;
+ buff.len = *pdwSize;
+ *pdwSize = 0;
+ DWORD dwFlags = 0;
+ WSAOVERLAPPED o;
+ ZeroMemory(&o, sizeof(o));
+
+ // protect against re-entrency
+ m_csRead.Lock();
+ o.hEvent = m_hEventRead;
+ WSAResetEvent(o.hEvent);
+ if (WSARecv(m_socket, &buff, 1, pdwSize, &dwFlags, &o, 0))
+ {
+ DWORD dwLastError = WSAGetLastError();
+ if (dwLastError != WSA_IO_PENDING)
+ {
+ m_dwLastError = dwLastError;
+ bRet = false;
+ }
+ }
+
+ // wait for the read to complete
+ if (bRet)
+ {
+ if (WAIT_OBJECT_0 == WaitForSingleObject((HANDLE)o.hEvent, m_dwSocketTimeout))
+ {
+ dwFlags = 0;
+ if (WSAGetOverlappedResult(m_socket, &o, pdwSize, FALSE, &dwFlags))
+ bRet = true;
+ else
+ {
+ m_dwLastError = ::GetLastError();
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+
+ m_csRead.Unlock();
+ return bRet;
+}
+
+inline bool ZEvtSyncSocket::Init(SOCKET hSocket, void * /*pData=NULL*/)
+{
+ ATLASSERT(hSocket != INVALID_SOCKET);
+
+ if (hSocket == INVALID_SOCKET)
+ {
+ m_dwLastError = WSAENOTSOCK;
+ return false;
+ }
+
+ m_socket = hSocket;
+
+ // Allocate Events. On error, any open event handles will be closed
+ // in the destructor
+ if (NULL != (m_hEventRead = WSACreateEvent()))
+ if (NULL != (m_hEventWrite = WSACreateEvent()))
+ if (NULL != (m_hEventConnect = WSACreateEvent()))
+ {
+ if (!WSASetEvent(m_hEventWrite) || !WSASetEvent(m_hEventRead))
+ {
+ m_dwLastError = ::GetLastError();
+ return false;
+ }
+
+ if (SOCKET_ERROR != WSAEventSelect(m_socket, m_hEventRead, FD_READ))
+ if (SOCKET_ERROR != WSAEventSelect(m_socket, m_hEventWrite, FD_WRITE))
+ if (SOCKET_ERROR != WSAEventSelect(m_socket, m_hEventConnect, FD_CONNECT))
+ return true;
+ }
+ m_dwLastError = ::GetLastError();
+ return false;
+}
+
+inline DWORD ZEvtSyncSocket::GetSocketTimeout() throw()
+{
+ return m_dwSocketTimeout;
+}
+
+inline DWORD ZEvtSyncSocket::SetSocketTimeout(DWORD dwNewTimeout) throw()
+{
+ DWORD dwOldTimeout = m_dwSocketTimeout;
+ m_dwSocketTimeout = dwNewTimeout;
+ return dwOldTimeout;
+}
+
+inline bool ZEvtSyncSocket::SupportsScheme(ATL_URL_SCHEME scheme) throw()
+{
+ // default only supports HTTP
+ return scheme == ATL_URL_SCHEME_HTTP ? true : false;
+}
+
+
+#pragma warning(pop)
+
+#endif // __ATLSPRIV_INL__
diff --git a/include/atl/atlsrv.rc b/include/atl/atlsrv.rc
new file mode 100644
index 000000000..a9d6b93a0
--- /dev/null
+++ b/include/atl/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>Bad Request</title></head><body>Bad Request</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>Authorization Required</title></head><body>Authorization is required</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Forbidden</title></head><body>Forbidden</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>Not Found</title></head><body>Not Found</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>Server Error</title></head><body>Server Error</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>Not Implemented</title></head><body>Not Implemented</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>Bad Gateway</title></head><body>Bad Gateway</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>Service Not Available</title></head><body>Service Not Available</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SRF file could not be loaded.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The requested SRF file was loaded but could not be successfully processed.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>A Windows system object could not be created.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>A File read operation failed.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The specified file could not be opened.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>LoadLibrary failed.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>Failed to retrieve the request handler interface.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>Server is out of memory.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The server encountered an unexpected error.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The server encountered an unexpected error while trying to parse the requested stencil.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The server failed to load the requested stencil. The stencil file may be damaged or missing on this web server.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>One of the handlers named in a handler tag for the requested stencil could not be found in the specified handler .dll.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>This stencil contains a handler tag that could not be properly parsed by the stencil processor or does not contain a handler tag at all. Check the requested stencil for proper stencil syntax.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The requested stencil does not contain a handler tag.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>A replacement tag was encountered in the requested stencil that had a replacement name that was too long. The maximum length of the replacment name must be less than or equal to the ATL_MAX_METHOD_NAME constant defined in atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>A replacement tag that uses the id.tagname syntax was encountered in the requested stencil that had a handler name that was too long. The maximum length of the handler name must be less than or equal to the ATL_MAX_METHOD_NAME constant defined in atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>An attempt to impersonate the client making the request failed.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>The ISAPI extension used to service this request failed to load correctly due to an unknown error.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "Request heap creation failed"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "Worker Thread initialization failed"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "Critical section initialization failed"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "Thread pool initialization failed"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "DLL cache initialization failed"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "Page cache initialization failed"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "Stencil cache initialization failed"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "Session state service initialization failed"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Blob cache initialization failed"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "File cache initialization failed"
+
+ IDS_PERFMON_CACHE "ATL Server:Cache"
+ IDS_PERFMON_CACHE_HELP "Information about the ATL Server cache"
+ IDS_PERFMON_HITCOUNT "Cache Hit Count"
+ IDS_PERFMON_HITCOUNT_HELP "Number of cache hits"
+ IDS_PERFMON_MISSCOUNT "Cache Miss Count"
+ IDS_PERFMON_MISSCOUNT_HELP "Number of cache misses"
+ IDS_PERFMON_CURRENTALLOCATIONS "Cache Current Allocations"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "Current Memory allocated for cache"
+ IDS_PERFMON_MAXALLOCATIONS "Cache Max Allocations"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "Maximum memory allocated for cache"
+ IDS_PERFMON_CURRENTENTRIES "Cache Current Entries"
+ IDS_PERFMON_CURRENTENTRIES_HELP "Current number of cache entries"
+ IDS_PERFMON_MAXENTRIES "Cache Max Entries"
+ IDS_PERFMON_MAXENTRIES_HELP "Maximum number of cache entries"
+ IDS_PERFMON_HITCOUNTRATE "Cache Hit Count Rate"
+ IDS_PERFMON_HITCOUNTRATE_HELP "Number of hit counts per second"
+ IDS_PERFMON_REQUEST "ATL Server:Request"
+ IDS_PERFMON_REQUEST_HELP "Statistics about the requests coming into the server"
+ IDS_PERFMON_REQUEST_TOTAL "Server Total Requests"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "The total number of requests"
+ IDS_PERFMON_REQUEST_FAILED "Server Failed Requests"
+ IDS_PERFMON_REQUEST_FAILED_HELP "The number of failed requests"
+ IDS_PERFMON_REQUEST_RATE "Server Requests /sec"
+ IDS_PERFMON_REQUEST_RATE_HELP "Number of requests per second"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "Average Response Time"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "Average time spent handling a request"
+ IDS_PERFMON_REQUEST_CURR_WAITING "Current Queued Requests"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "Current number of requests waiting to be handled"
+ IDS_PERFMON_REQUEST_MAX_WAITING "Maximum Queued Requests"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "Maximum number of requests waiting to be handled"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "Active Threads"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "The number of threads actively handling requests"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000> While trying to parse a stencil file, the following errors occurred:</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>Error type</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>Line number</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>Error text</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>Stencil output follows:\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} without {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} without {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} without {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} without {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} without {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} without {{if}} or {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "Invalid handler tag"
+ IDS_STENCIL_NULLPARAM "NULL parameter to ParseReplacements"
+ IDS_STENCIL_INVALIDSTRING "Empty or negative string passed to ParseReplacements"
+ IDS_STENCIL_EMBEDDED_NULL "Embedded null character in stencil"
+ IDS_STENCIL_UNMATCHED_TAG_START "Unmatched {{"
+ IDS_STENCIL_MISMATCHED_TAG_START "Mismatched {{"
+ IDS_STENCIL_BAD_PARAMETER "Bad parameter"
+ IDS_STENCIL_METHODNAME_TOO_LONG "Method name too long"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "Handler name too long"
+ IDS_STENCIL_INVALID_SUBHANDLER "Invalid subhandler tag"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "Unresolved replacement : '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Could not open included file"
+ IDS_STENCIL_INCLUDE_INVALID "Included file is not a disk file"
+
+ IDS_STENCIL_MLANG_COCREATE "Couldn't create CMultiLanguage"
+ IDS_STENCIL_MLANG_LCID "Error getting lcid"
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo failed"
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo failed"
+
+ IDS_STENCIL_OUTOFMEMORY "Out of memory"
+ IDS_STENCIL_UNEXPECTED "Unexpected error"
+END
+
+#endif
diff --git a/include/atl/atlsrvres.h b/include/atl/atlsrvres.h
new file mode 100644
index 000000000..ff2a1fc48
--- /dev/null
+++ b/include/atl/atlsrvres.h
@@ -0,0 +1,150 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+// Used by atlsrv.rc
+//
+#ifndef ATLSRV_RESID_BASE
+#define ATLSRV_RESID_BASE 0x6000
+#endif
+
+#ifndef PERFMON_RESID_BASE
+#define PERFMON_RESID_BASE 0x6100
+#endif
+
+#ifndef STENCIL_RESID_BASE
+#define STENCIL_RESID_BASE 0x6200
+#endif
+
+#define IDS_ATLSRV_BAD_REQUEST (ATLSRV_RESID_BASE+1)
+#define IDS_ATLSRV_AUTH_REQUIRED (ATLSRV_RESID_BASE+2)
+#define IDS_ATLSRV_FORBIDDEN (ATLSRV_RESID_BASE+3)
+#define IDS_ATLSRV_NOT_FOUND (ATLSRV_RESID_BASE+4)
+#define IDS_ATLSRV_SERVER_ERROR (ATLSRV_RESID_BASE+5)
+#define IDS_ATLSRV_NOT_IMPLEMENTED (ATLSRV_RESID_BASE+6)
+#define IDS_ATLSRV_BAD_GATEWAY (ATLSRV_RESID_BASE+7)
+#define IDS_ATLSRV_SERVICE_NOT_AVAILABLE (ATLSRV_RESID_BASE+8)
+#define IDS_ATLSRV_SERVER_ERROR_BADSRF (ATLSRV_RESID_BASE+9)
+#define IDS_ATLSRV_SERVER_ERROR_HNDLFAIL (ATLSRV_RESID_BASE+10)
+#define IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL (ATLSRV_RESID_BASE+11)
+#define IDS_ATLSRV_SERVER_ERROR_READFILEFAIL (ATLSRV_RESID_BASE+12)
+#define IDS_ATLSRV_SERVER_ERROR_LOADLIB (ATLSRV_RESID_BASE+13)
+#define IDS_ATLSRV_SERVER_ERROR_HANDLERIF (ATLSRV_RESID_BASE+14)
+#define IDS_ATLSRV_SERVER_ERROR_OUTOFMEM (ATLSRV_RESID_BASE+15)
+#define IDS_ATLSRV_SERVER_ERROR_UNEXPECTED (ATLSRV_RESID_BASE+16)
+#define IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL (ATLSRV_RESID_BASE+17)
+#define IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL (ATLSRV_RESID_BASE+18)
+#define IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND (ATLSRV_RESID_BASE+19)
+#define IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG (ATLSRV_RESID_BASE+20)
+#define IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG (ATLSRV_RESID_BASE+21)
+#define IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME (ATLSRV_RESID_BASE+22)
+#define IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME (ATLSRV_RESID_BASE+23)
+#define IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED (ATLSRV_RESID_BASE+24)
+#define IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED (ATLSRV_RESID_BASE+25)
+#define IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL (ATLSRV_RESID_BASE+26)
+#define IDS_ATLSRV_CRITICAL_LOGMESSAGE (ATLSRV_RESID_BASE+27)
+#define IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED (ATLSRV_RESID_BASE+28)
+#define IDS_ATLSRV_CRITICAL_WORKERINITFAILED (ATLSRV_RESID_BASE+29)
+#define IDS_ATLSRV_CRITICAL_CRITSECINITFAILED (ATLSRV_RESID_BASE+30)
+#define IDS_ATLSRV_CRITICAL_THREADPOOLFAILED (ATLSRV_RESID_BASE+31)
+#define IDS_ATLSRV_CRITICAL_DLLCACHEFAILED (ATLSRV_RESID_BASE+32)
+#define IDS_ATLSRV_CRITICAL_PAGECACHEFAILED (ATLSRV_RESID_BASE+33)
+#define IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED (ATLSRV_RESID_BASE+34)
+#define IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED (ATLSRV_RESID_BASE+35)
+#define IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED (ATLSRV_RESID_BASE+36)
+#define IDS_ATLSRV_CRITICAL_FILECACHEFAILED (ATLSRV_RESID_BASE+37)
+#define IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION (ATLSRV_RESID_BASE+38)
+
+#define IDS_PERFMON_CACHE (PERFMON_RESID_BASE+1)
+#define IDS_PERFMON_CACHE_HELP (PERFMON_RESID_BASE+2)
+#define IDS_PERFMON_HITCOUNT (PERFMON_RESID_BASE+3)
+#define IDS_PERFMON_HITCOUNT_HELP (PERFMON_RESID_BASE+4)
+#define IDS_PERFMON_MISSCOUNT (PERFMON_RESID_BASE+5)
+#define IDS_PERFMON_MISSCOUNT_HELP (PERFMON_RESID_BASE+6)
+#define IDS_PERFMON_CURRENTALLOCATIONS (PERFMON_RESID_BASE+7)
+#define IDS_PERFMON_CURRENTALLOCATIONS_HELP (PERFMON_RESID_BASE+8)
+#define IDS_PERFMON_MAXALLOCATIONS (PERFMON_RESID_BASE+9)
+#define IDS_PERFMON_MAXALLOCATIONS_HELP (PERFMON_RESID_BASE+10)
+#define IDS_PERFMON_CURRENTENTRIES (PERFMON_RESID_BASE+11)
+#define IDS_PERFMON_CURRENTENTRIES_HELP (PERFMON_RESID_BASE+12)
+#define IDS_PERFMON_MAXENTRIES (PERFMON_RESID_BASE+13)
+#define IDS_PERFMON_MAXENTRIES_HELP (PERFMON_RESID_BASE+14)
+#define IDS_PERFMON_HITCOUNTRATE (PERFMON_RESID_BASE+15)
+#define IDS_PERFMON_HITCOUNTRATE_HELP (PERFMON_RESID_BASE+16)
+
+#define IDS_PERFMON_REQUEST (PERFMON_RESID_BASE+17)
+#define IDS_PERFMON_REQUEST_HELP (PERFMON_RESID_BASE+18)
+#define IDS_PERFMON_REQUEST_TOTAL (PERFMON_RESID_BASE+19)
+#define IDS_PERFMON_REQUEST_TOTAL_HELP (PERFMON_RESID_BASE+20)
+#define IDS_PERFMON_REQUEST_FAILED (PERFMON_RESID_BASE+21)
+#define IDS_PERFMON_REQUEST_FAILED_HELP (PERFMON_RESID_BASE+22)
+#define IDS_PERFMON_REQUEST_RATE (PERFMON_RESID_BASE+23)
+#define IDS_PERFMON_REQUEST_RATE_HELP (PERFMON_RESID_BASE+24)
+#define IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME (PERFMON_RESID_BASE+25)
+#define IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP (PERFMON_RESID_BASE+26)
+#define IDS_PERFMON_REQUEST_CURR_WAITING (PERFMON_RESID_BASE+27)
+#define IDS_PERFMON_REQUEST_CURR_WAITING_HELP (PERFMON_RESID_BASE+28)
+#define IDS_PERFMON_REQUEST_MAX_WAITING (PERFMON_RESID_BASE+29)
+#define IDS_PERFMON_REQUEST_MAX_WAITING_HELP (PERFMON_RESID_BASE+30)
+#define IDS_PERFMON_REQUEST_ACTIVE_THREADS (PERFMON_RESID_BASE+31)
+#define IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP (PERFMON_RESID_BASE+32)
+
+
+//
+// Stencil parse error support
+//
+
+// the error stencil
+#define IDS_STENCIL_ERROR_STENCIL (STENCIL_RESID_BASE+1)
+
+// parse errors
+#define IDS_STENCIL_UNCLOSEDBLOCK_IF (STENCIL_RESID_BASE+2)
+#define IDS_STENCIL_UNCLOSEDBLOCK_ELSE (STENCIL_RESID_BASE+3)
+#define IDS_STENCIL_UNCLOSEDBLOCK_WHILE (STENCIL_RESID_BASE+4)
+#define IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE (STENCIL_RESID_BASE+5)
+#define IDS_STENCIL_UNOPENEDBLOCK_ELSE (STENCIL_RESID_BASE+6)
+#define IDS_STENCIL_UNOPENEDBLOCK_ENDIF (STENCIL_RESID_BASE+7)
+
+#define IDS_STENCIL_INVALID_HANDLER (STENCIL_RESID_BASE+8)
+#define IDS_STENCIL_NULLPARAM (STENCIL_RESID_BASE+9)
+#define IDS_STENCIL_INVALIDSTRING (STENCIL_RESID_BASE+10)
+#define IDS_STENCIL_EMBEDDED_NULL (STENCIL_RESID_BASE+11)
+#define IDS_STENCIL_UNMATCHED_TAG_START (STENCIL_RESID_BASE+12)
+#define IDS_STENCIL_MISMATCHED_TAG_START (STENCIL_RESID_BASE+13)
+#define IDS_STENCIL_BAD_PARAMETER (STENCIL_RESID_BASE+14)
+#define IDS_STENCIL_METHODNAME_TOO_LONG (STENCIL_RESID_BASE+15)
+#define IDS_STENCIL_HANDLERNAME_TOO_LONG (STENCIL_RESID_BASE+16)
+#define IDS_STENCIL_INCLUDE_ERROR (STENCIL_RESID_BASE+17)
+#define IDS_STENCIL_INCLUDE_INVALID (STENCIL_RESID_BASE+18)
+#define IDS_STENCIL_INVALID_SUBHANDLER (STENCIL_RESID_BASE+19)
+#define IDS_STENCIL_UNRESOLVED_REPLACEMENT (STENCIL_RESID_BASE+20)
+
+// mlang errors
+#define IDS_STENCIL_MLANG_COCREATE (STENCIL_RESID_BASE+21)
+#define IDS_STENCIL_MLANG_LCID (STENCIL_RESID_BASE+22)
+#define IDS_STENCIL_MLANG_GETLOCALE (STENCIL_RESID_BASE+23)
+#define IDS_STENCIL_MLANG_GETCHARSET (STENCIL_RESID_BASE+24)
+
+// misceallaneous
+#define IDS_STENCIL_OUTOFMEMORY (STENCIL_RESID_BASE+25)
+#define IDS_STENCIL_UNEXPECTED (STENCIL_RESID_BASE+26)
+
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
+
diff --git a/include/atl/atlstencil.h b/include/atl/atlstencil.h
new file mode 100644
index 000000000..8694a0f42
--- /dev/null
+++ b/include/atl/atlstencil.h
@@ -0,0 +1,4152 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#ifndef __ATLSTENCIL_H__
+#define __ATLSTENCIL_H__
+
+#pragma once
+
+#include <atlisapi.h>
+#include <atlfile.h>
+#include <atlutil.h>
+#include <math.h>
+
+#ifdef ATL_DEBUG_STENCILS
+#include <atlsrvres.h>
+
+ #ifndef ATL_STENCIL_MAX_ERROR_LEN
+ #define ATL_STENCIL_MAX_ERROR_LEN 256
+ #endif
+#endif // ATL_DEBUG_STENCILS
+
+#ifndef ATL_NO_MLANG
+#include <mlang.h>
+#endif
+
+#ifndef _ATL_NO_DEFAULT_LIBS
+#pragma comment(lib, "shlwapi.lib")
+#endif // !_ATL_NO_DEFAULT_LIBS
+
+#pragma warning( push )
+#pragma warning(disable: 4127) // conditional expression is constant
+#pragma warning(disable: 4511) // copy constructor could not be generated
+#pragma warning(disable: 4512) // assignment operator could not be generated
+#pragma warning(disable: 4702) // assignment operator could not be generated
+#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
+#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
+#pragma warning(disable: 4191) // unsafe conversion from 'functionptr1' to 'functionptr2'
+
+
+#pragma pack(push,_ATL_PACKING)
+namespace ATL {
+
+// Token types
+// These tags are token tags for the standard tag replacer implementation
+extern __declspec(selectany) const DWORD STENCIL_TEXTTAG = 0x00000000;
+extern __declspec(selectany) const DWORD STENCIL_REPLACEMENT = 0x00000001;
+extern __declspec(selectany) const DWORD STENCIL_ITERATORSTART = 0x00000002;
+extern __declspec(selectany) const DWORD STENCIL_ITERATOREND = 0x00000003;
+extern __declspec(selectany) const DWORD STENCIL_CONDITIONALSTART = 0x00000004;
+extern __declspec(selectany) const DWORD STENCIL_CONDITIONALELSE = 0x00000005;
+extern __declspec(selectany) const DWORD STENCIL_CONDITIONALEND = 0x00000006;
+extern __declspec(selectany) const DWORD STENCIL_STENCILINCLUDE = 0x00000007;
+extern __declspec(selectany) const DWORD STENCIL_STATICINCLUDE = 0x00000008;
+extern __declspec(selectany) const DWORD STENCIL_LOCALE = 0x00000009;
+extern __declspec(selectany) const DWORD STENCIL_CODEPAGE = 0x0000000a;
+
+// The base for user defined token types
+extern __declspec(selectany) const DWORD STENCIL_USER_TOKEN_BASE = 0x00001000;
+
+// Symbols to use in error handling in the stencil processor
+#define STENCIL_INVALIDINDEX 0xFFFFFFFF
+#define STENCIL_INVALIDOFFSET 0xFFFFFFFF
+
+// error codes
+#define STENCIL_SUCCESS HTTP_SUCCESS
+#define STENCL_FAIL HTTP_FAIL
+
+#define STENCIL_BASIC_MAP 0
+#define STENCIL_ATTR_MAP 1
+
+#ifndef ATL_MAX_METHOD_NAME_LEN
+ #define ATL_MAX_METHOD_NAME_LEN 64
+#endif
+
+#ifndef ATL_MAX_BLOCK_STACK
+ #define ATL_MAX_BLOCK_STACK 128
+#endif
+
+template <class TBase, typename T>
+struct CTagReplacerMethodsEx
+{
+ typedef HTTP_CODE (TBase::*REPLACE_FUNC)();
+ typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX)(T*);
+ typedef HTTP_CODE (TBase::*PARSE_FUNC)(IAtlMemMgr *, LPCSTR, T**);
+ typedef HTTP_CODE (TBase::*REPLACE_FUNC_EX_V)(void *);
+ typedef HTTP_CODE (TBase::*PARSE_FUNC_V)(IAtlMemMgr *, LPCSTR, void**);
+
+ static REPLACE_FUNC_EX_V CheckRepl(REPLACE_FUNC p) throw()
+ {
+ return (REPLACE_FUNC_EX_V) p;
+ }
+
+ static REPLACE_FUNC_EX_V CheckReplEx(REPLACE_FUNC_EX p) throw()
+ {
+ return (REPLACE_FUNC_EX_V) p;
+ }
+
+ static PARSE_FUNC_V CheckParse(PARSE_FUNC p) throw()
+ {
+ return (PARSE_FUNC_V) p;
+ }
+};
+
+template <class TBase>
+struct CTagReplacerMethods
+{
+ union
+ {
+ HTTP_CODE (TBase::*pfnMethodEx)(void *);
+ HTTP_CODE (TBase::*pfnMethod)();
+ };
+ HTTP_CODE (TBase::*pfnParse)(IAtlMemMgr *pMemMgr, LPCSTR, void **);
+};
+
+#define REPLACEMENT_ENTRY_DEFAULT 0
+#define REPLACEMENT_ENTRY_ARGS 1
+
+template <class TBase>
+struct CTagReplacerMethodEntry
+{
+ int nType; // REPLACEMENT_ENTRY_*
+ LPCSTR szMethodName;
+ CTagReplacerMethods<TBase> Methods;
+};
+
+
+#define BEGIN_REPLACEMENT_METHOD_MAP(className)\
+public:\
+ void GetReplacementMethodMap(const ATL::CTagReplacerMethodEntry<className> ** ppOut) const\
+ {\
+ typedef className __className;\
+ static const ATL::CTagReplacerMethodEntry<className> methods[] = {
+
+#define REPLACEMENT_METHOD_ENTRY(methodName, methodFunc)\
+ { 0, methodName, { ATL::CTagReplacerMethodsEx<__className, void>::CheckRepl(&__className::methodFunc), NULL } },
+
+#define REPLACEMENT_METHOD_ENTRY_EX(methodName, methodFunc, paramType, parseFunc)\
+ { 1, methodName, { ATL::CTagReplacerMethodsEx<__className, paramType>::CheckReplEx(&__className::methodFunc), ATL::CTagReplacerMethodsEx<__className, paramType>::CheckParse(&__className::parseFunc) } },
+
+#define REPLACEMENT_METHOD_ENTRY_EX_STR(methodName, methodFunc) \
+ { 1, methodName, { ATL::CTagReplacerMethodsEx<__className, char>::CheckReplEx(&__className::methodFunc), ATL::CTagReplacerMethodsEx<__className, char>::CheckParse(&__className::DefaultParseString) } },
+
+#define END_REPLACEMENT_METHOD_MAP()\
+ { 0, NULL, NULL } };\
+ *ppOut = methods;\
+ }
+
+#define BEGIN_ATTR_REPLACEMENT_METHOD_MAP(className)\
+public:\
+ void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<className> ** ppOut) const\
+ {\
+ typedef className __className;\
+ static const ATL::CTagReplacerMethodEntry<className> methods[] = {
+
+#define END_ATTR_REPLACEMENT_METHOD_MAP()\
+ { NULL, NULL, NULL } };\
+ *ppOut = methods;\
+ }
+
+template <class T>
+class ITagReplacerImpl : public ITagReplacer
+{
+protected:
+ IWriteStream *m_pStream;
+
+public:
+ typedef HTTP_CODE (T::*REPLACEMENT_METHOD)();
+ typedef HTTP_CODE (T::*REPLACEMENT_METHOD_EX)(void *pvParam);
+
+ ITagReplacerImpl() throw()
+ :m_pStream(NULL)
+ {
+ }
+
+ IWriteStream *SetStream(IWriteStream *pStream)
+ {
+ IWriteStream *pRetStream = m_pStream;
+ m_pStream = pStream;
+ return pRetStream;
+ }
+
+ // Looks up the replacement method offset. Optionally, it will
+ // look up the replacement method and object offset of an alternate
+ // tag replacer.
+ HTTP_CODE FindReplacementOffset(
+ LPCSTR szMethodName,
+ DWORD *pdwMethodOffset,
+ LPCSTR szHandlerName,
+ DWORD *pdwHandlerOffset,
+ DWORD *pdwMap, void **ppvParam, IAtlMemMgr *pMemMgr)
+ {
+ ATLENSURE(szMethodName != NULL);
+ ATLENSURE(pdwMethodOffset != NULL);
+ ATLENSURE((szHandlerName == NULL && pdwHandlerOffset == NULL) ||
+ (szHandlerName != NULL && pdwHandlerOffset != NULL));
+ ATLENSURE(pdwMap != NULL);
+ ATLENSURE(ppvParam != NULL);
+ ATLENSURE(pMemMgr != NULL);
+
+ // we at least have to be looking up a method offset
+ if (!pdwMethodOffset || !szMethodName)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+
+ *pdwMethodOffset = STENCIL_INVALIDOFFSET;
+ HTTP_CODE hcErr = HTTP_FAIL;
+ T *pT = static_cast<T *>(this);
+
+ char szName[ATL_MAX_METHOD_NAME_LEN+1];
+
+ // if a handler name was supplied, we will try to
+ // find a different object to handle the method
+ if (szHandlerName && *szHandlerName)
+ {
+ if (!pdwHandlerOffset)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+
+ hcErr = pT->GetHandlerOffset(szHandlerName, pdwHandlerOffset);
+ // got the alternate handler, now look up the method offset on
+ // the handler.
+ if (!hcErr)
+ {
+ CComPtr<ITagReplacer> spAltTagReplacer;
+ hcErr = pT->GetReplacementObject(*pdwHandlerOffset, &spAltTagReplacer);
+ if (!hcErr)
+ hcErr = spAltTagReplacer->FindReplacementOffset(szMethodName, pdwMethodOffset,
+ NULL, NULL, pdwMap, ppvParam, pMemMgr);
+ return hcErr;
+ }
+ else
+ return hcErr;
+ }
+
+ if (!SafeStringCopy(szName, szMethodName))
+ {
+ return AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
+ }
+
+ // check for params
+ char *szLeftPar = strchr(szName, '(');
+ if (szLeftPar)
+ {
+ *szLeftPar = '\0';
+ szLeftPar++;
+
+ char *szRightPar = strchr(szLeftPar, ')');
+ if (!szRightPar)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+
+ *szRightPar = '\0';
+
+ szMethodName = szName;
+
+ }
+
+ // No handler name is specified, so we look up the method name in
+ // T's replacement method map
+ const CTagReplacerMethodEntry<T> *pEntry = NULL;
+ pT->GetReplacementMethodMap(&pEntry);
+
+ hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
+ if (hcErr != HTTP_SUCCESS)
+ {
+ pT->GetAttrReplacementMethodMap(&pEntry);
+ hcErr = FindReplacementOffsetInMap(szMethodName, pdwMethodOffset, pEntry);
+ if (hcErr == HTTP_SUCCESS)
+ *pdwMap = STENCIL_ATTR_MAP;
+ }
+ else
+ {
+ *pdwMap = STENCIL_BASIC_MAP;
+ }
+
+ // This assert will be triggered if arguments are passed to a replacement method that doesn't handle them
+ ATLASSERT( szLeftPar == NULL || (szLeftPar != NULL && (pEntry != NULL && pEntry[*pdwMethodOffset].Methods.pfnParse != NULL)) );
+
+ if (hcErr == HTTP_SUCCESS && pEntry && pEntry[*pdwMethodOffset].Methods.pfnParse)
+ hcErr = (pT->*pEntry[*pdwMethodOffset].Methods.pfnParse)(pMemMgr, szLeftPar, ppvParam);
+ return hcErr;
+ }
+
+ HTTP_CODE FindReplacementOffsetInMap(
+ LPCSTR szMethodName,
+ LPDWORD pdwMethodOffset,
+ const CTagReplacerMethodEntry<T> *pEntry) throw()
+ {
+ if (pEntry == NULL)
+ return HTTP_FAIL;
+
+ const CTagReplacerMethodEntry<T> *pEntryHead = pEntry;
+
+ while (pEntry->szMethodName)
+ {
+ if (strcmp(pEntry->szMethodName, szMethodName) == 0)
+ {
+ if (pEntry->Methods.pfnMethod)
+ {
+ *pdwMethodOffset = (DWORD)(pEntry-pEntryHead);
+ return HTTP_SUCCESS;
+ }
+ }
+ pEntry++;
+ }
+
+ return HTTP_FAIL;
+ }
+
+
+ // Used to render a single replacement tag into a stream.
+ // Looks up a pointer to a member function in user code by offseting into the users
+ // replacement map. Much faster than the other overload of this function since
+ // no string compares are performed.
+ HTTP_CODE RenderReplacement(DWORD dwFnOffset, DWORD dwObjOffset, DWORD dwMap, void *pvParam)
+ {
+ HTTP_CODE hcErr = HTTP_FAIL;
+ T *pT = static_cast<T *>(this);
+
+ // if we were not passed an object offset, then we assume
+ // that the function at dwFnOffset is in T's replacement
+ // map
+ if (dwObjOffset == STENCIL_INVALIDOFFSET)
+ {
+ // call a function in T's replacement map
+ ATLASSERT(dwFnOffset != STENCIL_INVALIDOFFSET);
+ const CTagReplacerMethodEntry<T> *pEntry = NULL;
+ if (dwMap == STENCIL_BASIC_MAP)
+ pT->GetReplacementMethodMap(&pEntry);
+ else
+ pT->GetAttrReplacementMethodMap(&pEntry);
+ if (pEntry)
+ {
+ if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_DEFAULT)
+ {
+ REPLACEMENT_METHOD pfn = NULL;
+ pfn = pEntry[dwFnOffset].Methods.pfnMethod;
+ ATLASSERT(pfn);
+ if (pfn)
+ {
+ hcErr = (pT->*pfn)();
+ }
+ }
+ else if (pEntry[dwFnOffset].nType == REPLACEMENT_ENTRY_ARGS)
+ {
+ REPLACEMENT_METHOD_EX pfn = NULL;
+ pfn = pEntry[dwFnOffset].Methods.pfnMethodEx;
+ ATLASSERT(pfn);
+ if (pfn)
+ {
+ hcErr = (pT->*pfn)(pvParam);
+ }
+ }
+ else
+ {
+ // unknown entry type
+ ATLASSERT(FALSE);
+ }
+ }
+ }
+ else
+ {
+ // otherwise, we were passed an object offset. The object
+ // offset is a dword ID that T can use to look up the
+ // ITagReplacer* of a tag replacer that will render this
+ // replacement.
+ CComPtr<ITagReplacer> spAltReplacer = NULL;
+ if (!pT->GetReplacementObject(dwObjOffset, &spAltReplacer))
+ {
+ spAltReplacer->SetStream(m_pStream);
+ hcErr = spAltReplacer->RenderReplacement(dwFnOffset, STENCIL_INVALIDOFFSET, dwMap, pvParam);
+ }
+ }
+ return hcErr;
+ }
+
+ // Default GetHandlerOffset, does nothing
+ HTTP_CODE GetHandlerOffset(LPCSTR /*szHandlerName*/, DWORD* pdwOffset)
+ {
+ if (pdwOffset)
+ *pdwOffset = 0;
+ return HTTP_FAIL;
+ }
+
+ // Default GetReplacementObject, does nothing
+ HTTP_CODE GetReplacementObject(DWORD /*dwObjOffset*/, ITagReplacer **ppReplacer)
+ {
+ if (ppReplacer)
+ *ppReplacer = NULL;
+ return HTTP_FAIL;
+ }
+
+ void GetReplacementMethodMap(const CTagReplacerMethodEntry<T> ** ppOut) const
+ {
+ static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
+ *ppOut = methods;
+ }
+
+ void GetAttrReplacementMethodMap(const CTagReplacerMethodEntry<T> **ppOut) const
+ {
+ static const CTagReplacerMethodEntry<T> methods[] = { { NULL, NULL } };
+ *ppOut = methods;
+ }
+
+ HRESULT GetContext(REFIID, void**)
+ {
+ return E_NOINTERFACE;
+ }
+
+ virtual HINSTANCE GetResourceInstance()
+ {
+ return GetModuleHandle(NULL);
+ }
+
+ HTTP_CODE DefaultParseString(IAtlMemMgr *pMemMgr, LPCSTR szParams, char **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ size_t nLen = strlen(szParams);
+ if (nLen)
+ {
+ nLen++;
+ *ppParam = (char *) pMemMgr->Allocate(nLen);
+ if (*ppParam)
+ Checked::memcpy_s(*ppParam, nLen, szParams, nLen);
+ else
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseUChar(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned char **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (unsigned char *) pMemMgr->Allocate(sizeof(unsigned char));
+ if (*ppParam)
+ {
+ char *szEnd;
+ **ppParam = (unsigned char) strtoul(szParams, &szEnd, 10);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, short **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (short *) pMemMgr->Allocate(sizeof(short));
+ if (*ppParam)
+ {
+ **ppParam = (short)atoi(szParams);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseUShort(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned short **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (unsigned short *) pMemMgr->Allocate(sizeof(short));
+ if (*ppParam)
+ {
+ char *szEnd;
+ **ppParam = (unsigned short) strtoul(szParams, &szEnd, 10);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, int **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (int *) pMemMgr->Allocate(sizeof(int));
+ if (*ppParam)
+ {
+ **ppParam = atoi(szParams);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseUInt(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned int **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (unsigned int *) pMemMgr->Allocate(sizeof(unsigned int));
+ if (*ppParam)
+ {
+ char *szEnd;
+ **ppParam = strtoul(szParams, &szEnd, 10);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, __int64 **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (__int64 *) pMemMgr->Allocate(sizeof(__int64));
+ if (*ppParam)
+ {
+ **ppParam = _atoi64(szParams);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseUInt64(IAtlMemMgr *pMemMgr, LPCSTR szParams, unsigned __int64 **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (unsigned __int64 *) pMemMgr->Allocate(sizeof(unsigned __int64));
+ if (*ppParam)
+ {
+ char *szEnd;
+ **ppParam = _strtoui64(szParams, &szEnd, 10);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseBool(IAtlMemMgr *pMemMgr, LPCSTR szParams, bool **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (bool *) pMemMgr->Allocate(sizeof(bool));
+ if (*ppParam)
+ {
+ if (!_strnicmp(szParams, "true", sizeof("true")-sizeof('\0')))
+ **ppParam = true;
+ else
+ **ppParam = false;
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseDouble(IAtlMemMgr *pMemMgr, LPCSTR szParams, double **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ *ppParam = (double *) pMemMgr->Allocate(sizeof(double));
+ if (*ppParam)
+ {
+ **ppParam = atof(szParams);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE DefaultParseFloat(IAtlMemMgr *pMemMgr, LPCSTR szParams, float **ppParam) throw(...)
+ {
+ ATLENSURE( pMemMgr != NULL );
+ ATLENSURE( szParams != NULL );
+ ATLENSURE( ppParam != NULL );
+
+ errno_t errnoValue = 0;
+ *ppParam = (float *) pMemMgr->Allocate(sizeof(float));
+ if (*ppParam)
+ {
+ errno_t saveErrno = Checked::get_errno();
+ Checked::set_errno(0);
+ **ppParam = (float) atof(szParams);
+ errnoValue = Checked::get_errno();
+ Checked::set_errno(saveErrno);
+ }
+ else
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+ if ((**ppParam == -HUGE_VAL) || (**ppParam == HUGE_VAL) || (errnoValue == ERANGE))
+ {
+ return HTTP_FAIL;
+ }
+ return HTTP_SUCCESS;
+ }
+};
+
+inline LPCSTR SkipSpace(LPCSTR sz, WORD nCodePage) throw()
+{
+ if (sz == NULL)
+ return NULL;
+
+ while (isspace(static_cast<unsigned char>(*sz)))
+ sz = CharNextExA(nCodePage, sz, 0);
+ return sz;
+}
+
+inline LPCSTR RSkipSpace(LPCSTR pStart, LPCSTR sz, WORD nCodePage) throw()
+{
+ if (sz == NULL || pStart == NULL)
+ return NULL;
+
+ while (isspace(static_cast<unsigned char>(*sz)) && sz != pStart)
+ sz = CharPrevExA(nCodePage, pStart, sz, 0);
+ return sz;
+}
+
+//
+// StencilToken
+// The stencil class will create an array of these tokens during the parse
+// phase and use them during rendering to render the stencil
+struct StencilToken
+{
+ LPCSTR pStart; // Start of fragment to be rendered
+ LPCSTR pEnd; // End of fragment to be rendered
+ DWORD type; // Type of token
+ DWORD dwFnOffset; // Offset into the replacement map for the handler function.
+ DWORD dwMap;
+ DWORD dwObjOffset; // An identifier for the caller to use in identifiying the
+ // object that will render this token.
+ CHAR szHandlerName[ATL_MAX_HANDLER_NAME_LEN + 1]; // Name of handler object.
+ CHAR szMethodName[ATL_MAX_METHOD_NAME_LEN + 1]; // Name of handler method.
+ DWORD dwLoopIndex; // Offset into array of StencilTokens of the other loop tag
+ DWORD_PTR dwData;
+ BOOL bDynamicAlloc;
+};
+
+
+//
+// Class CStencil
+// The CStencil class is used to map in a stencil from a file or resource
+// and parse the stencil into an array of StencilTokens. We then render
+// the stencil from the array of tokens. This class's parse and render
+// functions depend on an IReplacementHandlerLookup interface pointer to be
+// passed so it can retrieve the IReplacementHandler interface pointer of the
+// handler object that will be called to render replacement tags
+class CStencil :
+ public IMemoryCacheClient
+{
+private:
+ LPCSTR m_pBufferStart; // Beginning of CHAR buffer that holds the stencil.
+ // For mapped files this is the beginning of the mapping.
+ LPCSTR m_pBufferEnd; // End of CHAR buffer that holds the stencil.
+ CAtlArray<StencilToken> m_arrTokens; //An array of tokens.
+ FILETIME m_ftLastModified; // Last modified time (0 for resource)
+ FILETIME m_ftLastChecked; // Last time we retrieved last modified time (0 for resource)
+ HCACHEITEM m_hCacheItem;
+ WORD m_nCodePage;
+ BOOL m_bUseLocaleACP;
+ char m_szDllPath[MAX_PATH];
+ char m_szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1]; // Room for the path, the handler
+ // the '/' and the '\0'
+#ifdef ATL_DEBUG_STENCILS
+ struct ParseError
+ {
+ char m_szError[ATL_STENCIL_MAX_ERROR_LEN];
+ LPCSTR m_szPosition;
+ LPCSTR m_szStartLine;
+ LPCSTR m_szEndLine;
+ int m_nLineNumber;
+
+ bool operator==(const ParseError& that) const throw()
+ {
+ return (m_nLineNumber == that.m_nLineNumber);
+ }
+ };
+
+ CSimpleArray<ParseError> m_Errors;
+ HINSTANCE m_hResInst;
+
+ class CParseErrorProvider : public ITagReplacerImpl<CParseErrorProvider>,
+ public CComObjectRootEx<CComSingleThreadModel>
+ {
+ public:
+ BEGIN_COM_MAP(CParseErrorProvider)
+ COM_INTERFACE_ENTRY(ITagReplacer)
+ END_COM_MAP()
+ CSimpleArray<ParseError> *m_pErrors;
+ int m_nCurrentError;
+
+ CParseErrorProvider() throw() :
+ m_pErrors(NULL),
+ m_nCurrentError(-1)
+ {
+ }
+
+ void Initialize(CSimpleArray<ParseError> *pErrors) throw()
+ {
+ m_pErrors = pErrors;
+ }
+
+ HTTP_CODE OnGetNextError() throw()
+ {
+ m_nCurrentError++;
+ if (m_nCurrentError >= m_pErrors->GetSize() ||
+ m_nCurrentError < 0 )
+ {
+ m_nCurrentError = -1;
+ return HTTP_S_FALSE;
+ }
+ else
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnGetErrorLineNumber() throw(...)
+ {
+ if (m_pErrors->GetSize() == 0)
+ return HTTP_SUCCESS;
+ if (m_nCurrentError > m_pErrors->GetSize() ||
+ m_nCurrentError < 0)
+ m_nCurrentError = 0;
+
+ CWriteStreamHelper c(m_pStream);
+ if (!c.Write((*m_pErrors)[m_nCurrentError].m_nLineNumber))
+ return HTTP_FAIL;
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnGetErrorText() throw(...)
+ {
+ if (m_pErrors->GetSize() == 0)
+ return HTTP_SUCCESS;
+ if (m_nCurrentError > m_pErrors->GetSize() ||
+ m_nCurrentError < 0)
+ m_nCurrentError = 0;
+
+ CWriteStreamHelper c(m_pStream);
+
+ if (!c.Write(static_cast<LPCSTR>((*m_pErrors)[m_nCurrentError].m_szError)))
+ return HTTP_FAIL;
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE OnGetErrorLine() throw(...)
+ {
+ ATLASSUME(m_pStream != NULL);
+
+ if (m_pErrors->GetSize() == 0)
+ return HTTP_SUCCESS;
+ if (m_nCurrentError > m_pErrors->GetSize() ||
+ m_nCurrentError < 0)
+ m_nCurrentError = 0;
+
+ m_pStream->WriteStream((*m_pErrors)[m_nCurrentError].m_szStartLine,
+ (int)((*m_pErrors)[m_nCurrentError].m_szEndLine - (*m_pErrors)[m_nCurrentError].m_szStartLine), NULL);
+
+ return HTTP_SUCCESS;
+ }
+
+ BEGIN_REPLACEMENT_METHOD_MAP(CParseErrorProvider)
+ REPLACEMENT_METHOD_ENTRY("GetNextError", OnGetNextError)
+ REPLACEMENT_METHOD_ENTRY("GetErrorText", OnGetErrorText)
+ REPLACEMENT_METHOD_ENTRY("GetErrorLine", OnGetErrorLine)
+ REPLACEMENT_METHOD_ENTRY("GetErrorLineNumber", OnGetErrorLineNumber)
+ END_REPLACEMENT_METHOD_MAP()
+ };
+
+#else
+ bool m_bErrorsOccurred;
+#endif
+
+ class CSaveThreadLocale
+ {
+ LCID m_locale;
+ public:
+ CSaveThreadLocale() throw()
+ {
+ m_locale = GetThreadLocale();
+ }
+ ~CSaveThreadLocale() throw()
+ {
+ SetThreadLocale(m_locale);
+ }
+ };
+
+ HTTP_CODE LoadFromResourceInternal(HINSTANCE hInstRes, HRSRC hRsrc) throw()
+ {
+ ATLASSERT( hRsrc != NULL );
+
+ HGLOBAL hgResource = NULL;
+ hgResource = LoadResource(hInstRes, hRsrc);
+ if (!hgResource)
+ {
+ return HTTP_FAIL;
+ }
+
+ DWORD dwSize = SizeofResource(hInstRes, hRsrc);
+ if (dwSize != 0)
+ {
+ m_pBufferStart = (LPSTR)LockResource(hgResource);
+ if (m_pBufferStart != NULL)
+ {
+ m_pBufferEnd = m_pBufferStart+dwSize;
+ return HTTP_SUCCESS;
+ }
+ }
+
+ // failed to load resource
+ return HTTP_FAIL;
+ }
+
+protected:
+
+ ITagReplacer *m_pReplacer;
+ IAtlMemMgr *m_pMemMgr;
+ static CCRTHeap m_crtHeap;
+
+ inline BOOL CheckTag(LPCSTR szTag, DWORD dwTagLen, LPCSTR szStart, DWORD dwLen) throw()
+ {
+ if (dwLen < dwTagLen)
+ return FALSE;
+
+ if (memcmp(szStart, szTag, dwTagLen))
+ return FALSE;
+
+ if (isspace(static_cast<unsigned char>(szStart[dwTagLen])) || szStart[dwTagLen] == '}')
+ return TRUE;
+
+ return FALSE;
+ }
+
+ inline void FindTagArgs(LPCSTR& szstart, LPCSTR& szend, int nKeywordChars) throw()
+ {
+ // this function should only be called after finding a valid tag
+ // the first two characters of szstart should be {{
+ ATLASSERT(szstart[0] == '{' && szstart[1] == '{');
+
+ if (*szstart == '{')
+ szstart += 2; // move past {{
+ szstart = SkipSpace(szstart, m_nCodePage); // move past whitespace
+ szstart += nKeywordChars; // move past keyword
+ szstart = SkipSpace(szstart, m_nCodePage); // move past whitespace after keyword
+ if (*szend == '}')
+ szend -=2; // chop off }}
+ szend = RSkipSpace(szstart, szend, m_nCodePage); // chop of trailing whitespace
+ }
+
+ DWORD CheckTopAndPop(DWORD *pBlockStack, DWORD *pdwTop, DWORD dwToken) throw()
+ {
+ if (*pdwTop == 0)
+ return STENCIL_INVALIDINDEX;
+ if (m_arrTokens[pBlockStack[*pdwTop]].type == dwToken)
+ {
+ *pdwTop = (*pdwTop) - 1;
+ return pBlockStack[(*pdwTop)+1];
+ }
+ return STENCIL_INVALIDINDEX;
+ }
+
+ DWORD PushToken(DWORD *pBlockStack, DWORD *pdwTop, DWORD dwIndex) throw()
+ {
+ if (*pdwTop < (ATL_MAX_BLOCK_STACK-1))
+ {
+ *pdwTop = (*pdwTop) + 1;
+ pBlockStack[*pdwTop] = dwIndex;
+ }
+ else
+ {
+ dwIndex = STENCIL_INVALIDINDEX;
+ }
+
+ return dwIndex;
+ }
+
+public:
+
+ enum PARSE_TOKEN_RESULT { INVALID_TOKEN, NORMAL_TOKEN, RESERVED_TOKEN };
+
+ CStencil(IAtlMemMgr *pMemMgr=NULL) throw()
+ {
+ m_pBufferStart = NULL;
+ m_pBufferEnd = NULL;
+ m_hCacheItem = NULL;
+ m_ftLastModified.dwLowDateTime = 0;
+ m_ftLastModified.dwHighDateTime = 0;
+ m_ftLastChecked.dwLowDateTime = 0;
+ m_ftLastChecked.dwHighDateTime = 0;
+ m_arrTokens.SetCount(0, 128);
+ m_nCodePage = CP_ACP;
+ m_bUseLocaleACP = TRUE;
+ m_szHandlerName[0] = '\0';
+ m_szDllPath[0] = '\0';
+ m_pMemMgr = pMemMgr;
+ if (!pMemMgr)
+ m_pMemMgr = &m_crtHeap;
+#ifdef ATL_DEBUG_STENCILS
+ m_hResInst = NULL;
+#else
+ m_bErrorsOccurred = false;
+#endif
+
+ }
+
+ virtual ~CStencil() throw()
+ {
+ Uninitialize();
+ }
+
+#ifdef ATL_DEBUG_STENCILS
+
+ bool RenderErrors(IWriteStream *pStream) throw(...)
+ {
+ if (pStream == NULL)
+ {
+ return false;
+ }
+
+ CComObjectStackEx<CParseErrorProvider> Errors;
+
+ Errors.Initialize(&m_Errors);
+ CStencil ErrorStencil;
+
+ if (m_hResInst != NULL)
+ {
+ CFixedStringT<CStringA, 256> strErrorStencil;
+ _ATLTRY
+ {
+ if (strErrorStencil.LoadString(m_hResInst, IDS_STENCIL_ERROR_STENCIL) == FALSE)
+ {
+ return false;
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+
+ HTTP_CODE hcRet = ErrorStencil.LoadFromString(strErrorStencil, strErrorStencil.GetLength());
+
+ if (hcRet == HTTP_SUCCESS)
+ {
+ if (ErrorStencil.ParseReplacements(static_cast<ITagReplacer *>(&Errors)) != false)
+ {
+ ErrorStencil.FinishParseReplacements();
+ if (ErrorStencil.ParseSuccessful() != false)
+ {
+ hcRet = ErrorStencil.Render(static_cast<ITagReplacer *>(&Errors), pStream);
+
+ if (HTTP_ERROR_CODE(hcRet) < 400)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ void SetErrorResource(HINSTANCE hResInst)
+ {
+ m_hResInst = hResInst;
+ }
+
+ bool ParseSuccessful()
+ {
+ return (m_Errors.GetSize() == 0);
+ }
+
+ bool AddErrorInternal(LPCSTR szErrorText, LPCSTR szPosition) throw()
+ {
+ int nLineNum = 0;
+ LPCSTR szStartLine = NULL;
+ LPCSTR szPtr = m_pBufferStart;
+ while (szPtr < szPosition)
+ {
+ if (*szPtr == '\n')
+ {
+ szStartLine = szPtr + 1;
+ nLineNum++;
+ }
+
+ LPSTR szNext = CharNextExA(m_nCodePage, szPtr, 0);
+ if (szNext == szPtr)
+ {
+ break;
+ }
+ szPtr = szNext;
+ }
+ LPCSTR szEndLine = szPtr;
+ while (*szPtr)
+ {
+ if (*szPtr == '\n')
+ break;
+ szEndLine = szPtr;
+ LPSTR szNext = CharNextExA(m_nCodePage, szPtr, 0);
+ if (szNext == szPtr)
+ {
+ break;
+ }
+ szPtr = szNext;
+ }
+
+ ParseError p;
+ SafeStringCopy(p.m_szError, szErrorText);
+ p.m_szPosition = szPosition;
+ p.m_nLineNumber = nLineNum;
+ p.m_szStartLine = szStartLine;
+ p.m_szEndLine = szEndLine;
+
+ return (m_Errors.Add(p) == TRUE);
+ }
+
+ bool AddError(UINT uID, LPCSTR szPosition) throw()
+ {
+ if (m_hResInst != NULL)
+ {
+ _ATLTRY
+ {
+ CFixedStringT<CStringA, 256> strRes;
+ if (strRes.LoadString(m_hResInst, uID) != FALSE)
+ {
+ return AddErrorInternal(strRes, szPosition);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+ return AddErrorInternal("Could not load resource for error string", szPosition);
+ }
+
+ bool AddReplacementError(LPCSTR szReplacement, LPCSTR szPosition) throw()
+ {
+ if (m_hResInst != NULL)
+ {
+ _ATLTRY
+ {
+ CFixedStringT<CStringA, 256> strRes;
+ if (strRes.LoadString(m_hResInst, IDS_STENCIL_UNRESOLVED_REPLACEMENT) != FALSE)
+ {
+ CFixedStringT<CStringA, 256> strErrorText;
+ strErrorText.Format(strRes, szReplacement);
+ return AddErrorInternal(strErrorText, szPosition);
+ }
+ else
+ {
+ return AddErrorInternal("Could not load resource for error string", szPosition);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+#else
+
+ bool ParseSuccessful()
+ {
+ return !m_bErrorsOccurred;
+ }
+
+ bool AddError(UINT /*uID*/, LPCSTR /*szPosition*/) throw()
+ {
+ m_bErrorsOccurred = true;
+ return true;
+ }
+
+ bool AddReplacementError(LPCSTR /*szReplacement*/, LPCSTR /*szPosition*/) throw()
+ {
+ m_bErrorsOccurred = true;
+ return true;
+ }
+
+ void SetErrorResource(HINSTANCE) throw()
+ {
+ }
+
+#endif
+
+ // Call Uninitialize if you want to re-use an already initialized CStencil
+ void Uninitialize() throw()
+ {
+ int nSize = (int) m_arrTokens.GetCount();
+ for (int nIndex = 0; nIndex < nSize; nIndex++)
+ {
+ if (m_arrTokens[nIndex].bDynamicAlloc)
+ delete [] m_arrTokens[nIndex].pStart;
+ if (m_arrTokens[nIndex].dwData != 0 && m_arrTokens[nIndex].type != STENCIL_LOCALE)
+ m_pMemMgr->Free((void *) m_arrTokens[nIndex].dwData);
+ }
+
+ m_arrTokens.RemoveAll();
+ if ((m_ftLastModified.dwLowDateTime || m_ftLastModified.dwHighDateTime) && m_pBufferStart)
+ {
+ delete [] m_pBufferStart;
+ }
+ m_pBufferStart = NULL;
+ m_pBufferEnd = NULL;
+ }
+
+ void GetLastModified(FILETIME *pftLastModified)
+ {
+ ATLENSURE(pftLastModified);
+ *pftLastModified = m_ftLastModified;
+ }
+
+ void GetLastChecked(FILETIME *pftLastChecked)
+ {
+ ATLENSURE(pftLastChecked);
+ *pftLastChecked = m_ftLastChecked;
+ }
+
+ void SetLastChecked(FILETIME *pftLastChecked)
+ {
+ ATLENSURE(pftLastChecked);
+ m_ftLastChecked = *pftLastChecked;
+ }
+
+ HCACHEITEM GetCacheItem()
+ {
+ return m_hCacheItem;
+ }
+
+ void SetCacheItem(HCACHEITEM hCacheItem)
+ {
+ ATLASSUME(m_hCacheItem == NULL);
+ m_hCacheItem = hCacheItem;
+ }
+
+ bool GetHandlerName(__out_ecount_z(nPathLen) LPSTR szDllPath, __in size_t nPathLen, __out_ecount_z(nHandlerNameLen) LPSTR szHandlerName, __in size_t nHandlerNameLen) throw()
+ {
+ if(strlen(m_szDllPath) >= nPathLen)
+ {
+ return false;
+ }
+
+ if(strlen(m_szHandlerName) >= nHandlerNameLen)
+ {
+ return false;
+ }
+
+ if(0 != strcpy_s(szDllPath, nPathLen, m_szDllPath))
+ {
+ return false;
+ }
+
+ if(0 != strcpy_s(szHandlerName, nHandlerNameLen, m_szHandlerName))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // Adds a token to the token array, handler name, method name
+ // and handler function offset are optional
+ ATL_NOINLINE DWORD AddToken(
+ LPCSTR pStart,
+ LPCSTR pEnd,
+ DWORD dwType,
+ LPCSTR szHandlerName = NULL,
+ LPCSTR szMethodName = NULL,
+ DWORD dwFnOffset = STENCIL_INVALIDOFFSET,
+ DWORD dwObjOffset = STENCIL_INVALIDOFFSET,
+ DWORD_PTR dwData = 0,
+ DWORD dwMap = 0,
+ BOOL bDynamicAlloc = 0) throw()
+ {
+ StencilToken t;
+
+ memset(&t, 0x00, sizeof(t));
+
+ t.pStart = pStart;
+ t.pEnd = pEnd;
+ t.type = dwType;
+ t.dwLoopIndex = STENCIL_INVALIDINDEX;
+ t.dwFnOffset = dwFnOffset;
+ t.dwObjOffset = dwObjOffset;
+ t.dwData = dwData;
+ t.dwMap = dwMap;
+ t.bDynamicAlloc = bDynamicAlloc;
+
+ // this should never assert unless the user has overriden something incorrectly
+ if ((szHandlerName != NULL) && (*szHandlerName))
+ {
+ ATLVERIFY( SafeStringCopy(t.szHandlerName, szHandlerName) );
+ }
+ if ((szMethodName != NULL) && (*szMethodName))
+ {
+ ATLVERIFY( SafeStringCopy(t.szMethodName, szMethodName) );
+ }
+
+ _ATLTRY
+ {
+ return (DWORD) m_arrTokens.Add(t);
+ }
+ _ATLCATCHALL()
+ {
+ return STENCIL_INVALIDINDEX;
+ }
+ }
+
+ HTTP_CODE LoadFromFile(LPCSTR szFileName) throw()
+ {
+ HRESULT hr = E_FAIL;
+ ULONGLONG dwLen = 0;
+ CAtlFile file;
+
+ _ATLTRY
+ {
+ hr = file.Create(CA2CTEX<MAX_PATH>(szFileName), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
+ if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
+ return AtlsHttpError(500, ISE_SUBERR_STENCIL_LOAD_FAIL); // couldn't load SRF!
+
+ if (GetFileTime(file, NULL, NULL, &m_ftLastModified))
+ {
+ if (SUCCEEDED(file.GetSize(dwLen)))
+ {
+ ATLASSERT(!m_pBufferStart);
+
+ GetSystemTimeAsFileTime(&m_ftLastChecked);
+
+ m_pBufferStart = NULL;
+ CAutoVectorPtr<char> buffer;
+ if (!buffer.Allocate((size_t) dwLen))
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // out of memory
+
+ DWORD dwRead = 0;
+ hr = file.Read(buffer, (DWORD) dwLen, dwRead);
+ if (FAILED(hr))
+ return AtlsHttpError(500, ISE_SUBERR_READFILEFAIL); // ReadFile failed
+
+ m_pBufferStart = buffer.Detach();
+ m_pBufferEnd = m_pBufferStart + dwRead;
+ }
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ }
+
+ return HTTP_SUCCESS;
+ }
+
+ // loads a stencil from the specified resource.
+ HTTP_CODE LoadFromResource(HINSTANCE hInstRes, LPCSTR szID, LPCSTR szType = NULL) throw()
+ {
+ if (szType == NULL)
+ {
+ szType = (LPCSTR) RT_HTML;
+ }
+
+ HRSRC hRsrc = FindResourceA(hInstRes, szID, szType);
+ if (hRsrc != NULL)
+ {
+ return LoadFromResourceInternal(hInstRes, hRsrc);
+ }
+
+ return HTTP_FAIL;
+ }
+
+ HTTP_CODE LoadFromResourceEx(HINSTANCE hInstRes, LPCSTR szID, WORD wLanguage, LPCSTR szType = NULL) throw()
+ {
+ if (szType == NULL)
+ {
+ szType = (LPCSTR) RT_HTML;
+ }
+
+ HRSRC hRsrc = FindResourceExA(hInstRes, szType, szID, wLanguage);
+ if (hRsrc != NULL)
+ {
+ return LoadFromResourceInternal(hInstRes, hRsrc);
+ }
+
+ return HTTP_FAIL;
+ }
+
+ // loads a stencil from the specified resource
+ HTTP_CODE LoadFromResource(HINSTANCE hInstRes, UINT nId, LPCSTR szType = NULL) throw()
+ {
+ return LoadFromResource(hInstRes, MAKEINTRESOURCEA(nId), szType);
+ }
+
+ HTTP_CODE LoadFromResourceEx(HINSTANCE hInstRes, UINT nId, WORD wLanguage, LPCSTR szType = NULL) throw()
+ {
+ return LoadFromResourceEx(hInstRes, MAKEINTRESOURCEA(nId), wLanguage, szType);
+ }
+
+ // loads a stencil from a string
+ HTTP_CODE LoadFromString(LPCSTR szString, DWORD dwSize) throw()
+ {
+ m_pBufferStart = szString;
+ m_pBufferEnd = m_pBufferStart+dwSize;
+ return HTTP_SUCCESS;
+ }
+
+ // Cracks the loaded stencil into an array of StencilTokens in preparation for
+ // rendering. LoadStencil must be called prior to calling this function.
+ virtual bool ParseReplacements(ITagReplacer* pReplacer) throw(...)
+ {
+ return ParseReplacementsFromBuffer(pReplacer, GetBufferStart(), GetBufferEnd());
+ }
+
+ virtual bool FinishParseReplacements() throw(...)
+ {
+ DWORD dwSize = (DWORD) m_arrTokens.GetCount();
+ for (DWORD dwIndex = 0; dwIndex < dwSize; dwIndex++)
+ {
+ StencilToken& token = m_arrTokens[dwIndex];
+ bool bUnclosedBlock = ((token.type == STENCIL_CONDITIONALSTART ||
+ token.type == STENCIL_CONDITIONALELSE ||
+ token.type == STENCIL_ITERATORSTART) &&
+ token.dwLoopIndex == STENCIL_INVALIDINDEX);
+ if ((token.szMethodName[0] && token.dwFnOffset == STENCIL_INVALIDOFFSET) || bUnclosedBlock)
+ {
+ if (bUnclosedBlock ||
+ m_pReplacer->FindReplacementOffset(
+ token.szMethodName, &token.dwFnOffset,
+ token.szHandlerName, &token.dwObjOffset,
+ &token.dwMap, (void **)(&token.dwData), m_pMemMgr) != HTTP_SUCCESS)
+ {
+ if (bUnclosedBlock && token.type == STENCIL_CONDITIONALSTART)
+ {
+ AddError(IDS_STENCIL_UNCLOSEDBLOCK_IF, token.pStart);
+ }
+ else if (bUnclosedBlock && token.type == STENCIL_CONDITIONALELSE)
+ {
+ AddError(IDS_STENCIL_UNCLOSEDBLOCK_ELSE, token.pStart);
+ }
+ else if (bUnclosedBlock && token.type == STENCIL_ITERATORSTART)
+ {
+ AddError(IDS_STENCIL_UNCLOSEDBLOCK_WHILE, token.pStart);
+ }
+ else
+ {
+ AddReplacementError(token.szMethodName, token.pStart);
+ }
+
+ // unresolved replacement, convert it to a text token
+ token.type = STENCIL_TEXTTAG;
+
+ // convert all linked tokens to text tokens as well
+ // this includes: endif, else, endwhile
+ DWORD dwLoopIndex = token.dwLoopIndex;
+ while (dwLoopIndex != dwIndex && dwLoopIndex != STENCIL_INVALIDINDEX)
+ {
+ m_arrTokens[dwLoopIndex].type = STENCIL_TEXTTAG;
+ dwLoopIndex = m_arrTokens[dwLoopIndex].dwLoopIndex;
+ }
+ }
+ }
+ }
+
+ return ParseSuccessful();
+ }
+
+ virtual bool Parse(ITagReplacer *pReplacer) throw( ... )
+ {
+ if (ParseReplacements(pReplacer))
+ {
+ return FinishParseReplacements();
+ }
+ return false;
+ }
+
+
+ DWORD ParseReplacement( LPCSTR szTokenStart,
+ LPCSTR szTokenEnd,
+ DWORD dwTokenType = STENCIL_REPLACEMENT,
+ DWORD dwKeywordLen = 0) throw()
+ {
+ // hold on to the start and end pointers (before removing curlies and whitespace)
+ // this is needed so that we can convert the token to a text token if the method
+ // is not resolved (in FinishParseReplacements)
+ LPCSTR szStart = szTokenStart;
+ LPCSTR szEnd = szTokenEnd;
+
+ FindTagArgs(szTokenStart, szTokenEnd, dwKeywordLen);
+
+ char szMethodName[ATL_MAX_METHOD_NAME_LEN+1];
+ char szHandlerName[ATL_MAX_HANDLER_NAME_LEN+1];
+
+ DWORD dwIndex;
+ //look up the handler name, method name and handler interface
+ if (HTTP_SUCCESS == GetHandlerAndMethodNames(szTokenStart, szTokenEnd,
+ szMethodName, ATL_MAX_METHOD_NAME_LEN+1,
+ szHandlerName, ATL_MAX_HANDLER_NAME_LEN+1))
+ dwIndex = AddToken(szStart, szEnd, dwTokenType,
+ szHandlerName, szMethodName,
+ STENCIL_INVALIDINDEX, STENCIL_INVALIDINDEX,
+ 0, STENCIL_BASIC_MAP);
+ else
+ dwIndex = STENCIL_INVALIDINDEX;
+ return dwIndex;
+ }
+
+ DWORD ParseWhile(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
+ {
+ DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_ITERATORSTART, sizeof("while")-1);
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ return dwIndex;
+ return PushToken(pBlockStack, pdwTop, dwIndex);
+ }
+
+ DWORD ParseEndWhile(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
+ {
+ DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_ITERATORSTART);
+ if (dwTopIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE, szTokenStart);
+ return dwTopIndex;
+ }
+
+ DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_ITERATOREND);
+ if (dwIndex != STENCIL_INVALIDINDEX)
+ {
+ m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
+ m_arrTokens[dwIndex].dwLoopIndex = dwTopIndex;
+ }
+ return dwIndex;
+ }
+
+ DWORD ParseIf(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
+ {
+ DWORD dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_CONDITIONALSTART, sizeof("if")-1);
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ return dwIndex;
+ return PushToken(pBlockStack, pdwTop, dwIndex);
+ }
+
+ DWORD ParseElse(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
+ {
+ DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
+ if (dwTopIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_UNOPENEDBLOCK_ELSE, szTokenStart);
+ return dwTopIndex;
+ }
+
+ DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALELSE);
+ if (dwIndex != STENCIL_INVALIDINDEX)
+ {
+ m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
+
+ return PushToken(pBlockStack, pdwTop, dwIndex);
+ }
+ return dwIndex;
+ }
+
+ DWORD ParseEndIf(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop) throw()
+ {
+ DWORD dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALSTART);
+ if (dwTopIndex == STENCIL_INVALIDINDEX)
+ {
+ dwTopIndex = CheckTopAndPop(pBlockStack, pdwTop, STENCIL_CONDITIONALELSE);
+ if (dwTopIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_UNOPENEDBLOCK_ENDIF, szTokenStart);
+ return dwTopIndex;
+ }
+ }
+ DWORD dwIndex = AddToken(szTokenStart, szTokenEnd, STENCIL_CONDITIONALEND);
+ if (dwIndex != STENCIL_INVALIDINDEX)
+ {
+ m_arrTokens[dwTopIndex].dwLoopIndex = dwIndex;
+ }
+ return dwIndex;
+ }
+
+ DWORD ParseLocale(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
+ {
+ LPCSTR szstart = szTokenStart;
+ LPCSTR szend = szTokenEnd;
+ LCID locale = 0xFFFFFFFF;
+ FindTagArgs(szstart, szend, 6);
+#ifndef ATL_NO_MLANG
+ if (isdigit(static_cast<unsigned char>(szstart[0])))
+ {
+ locale = (LCID) atoi(szstart);
+ }
+ else
+ {
+ HRESULT hr;
+
+ CComPtr<IMultiLanguage> pML;
+ hr = pML.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ {
+ ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
+ AddError(IDS_STENCIL_MLANG_COCREATE, szTokenStart);
+ }
+ else
+ {
+ CStringW str(szstart, (int)((szend-szstart)+1));
+
+#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
+
+ // use IMultiLanguage2 if possible
+ CComPtr<IMultiLanguage2> spML2;
+ hr = pML.QueryInterface(&spML2);
+ if (FAILED(hr) || !spML2.p)
+ hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
+ else
+ hr = spML2->GetLcidFromRfc1766(&locale, CComBSTR(str));
+
+#else // __IMultiLanguage2_INTERFACE_DEFINED__
+
+ hr = pML->GetLcidFromRfc1766(&locale, CComBSTR(str));
+
+#endif // __IMultiLanguage2_INTERFACE_DEFINED__
+
+ if (FAILED(hr))
+ {
+ AddError(IDS_STENCIL_MLANG_LCID, szTokenStart);
+ }
+ }
+ if (FAILED(hr))
+ locale = 0xFFFFFFFF;
+ }
+#else
+ locale = (LCID) atoi(szstart);
+#endif
+
+ if (m_bUseLocaleACP)
+ {
+ TCHAR szACP[7];
+ if (GetLocaleInfo(locale, LOCALE_IDEFAULTANSICODEPAGE, szACP, 7) != 0)
+ {
+ m_nCodePage = (WORD) _ttoi(szACP);
+ }
+ else
+ {
+ AddError(IDS_STENCIL_MLANG_GETLOCALE, szTokenStart);
+ }
+ }
+ DWORD dwCurrentTokenIndex = STENCIL_INVALIDINDEX;
+ if (locale != 0xFFFFFFFF)
+ dwCurrentTokenIndex = AddToken(NULL, NULL, STENCIL_LOCALE,
+ NULL, NULL, STENCIL_INVALIDOFFSET, STENCIL_INVALIDOFFSET, locale);
+ else
+ return STENCIL_INVALIDINDEX;
+ return dwCurrentTokenIndex;
+ }
+
+ DWORD ParseCodepage(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
+ {
+ LPCSTR szstart = szTokenStart;
+ LPCSTR szend = szTokenEnd;
+ WORD nCodePage = 0xFFFF;
+
+ FindTagArgs(szstart, szend, 8);
+#ifndef ATL_NO_MLANG
+ if (isdigit(static_cast<unsigned char>(szstart[0])))
+ {
+ nCodePage = (WORD) atoi(szstart);
+ }
+ else
+ {
+ HRESULT hr;
+
+ CComPtr<IMultiLanguage> pML;
+ hr = pML.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ {
+ ATLTRACE(atlTraceStencil, 0, _T("Couldn't create CMultiLanguage object. check MLANG installation."));
+ AddError(IDS_STENCIL_MLANG_COCREATE, szTokenStart);
+ }
+ else
+ {
+ CStringW str(szstart, (int)((szend-szstart)+1));
+ MIMECSETINFO info;
+
+#ifdef __IMultiLanguage2_INTERFACE_DEFINED__
+
+ // use IMultiLanguage2 if possible
+ CComPtr<IMultiLanguage2> spML2;
+ hr = pML.QueryInterface(&spML2);
+ if (FAILED(hr) || !spML2.p)
+ hr = pML->GetCharsetInfo(CComBSTR(str), &info);
+ else
+ hr = spML2->GetCharsetInfo(CComBSTR(str), &info);
+
+#else // __IMultiLanguage2_INTERFACE_DEFINED__
+
+ hr = pML->GetCharsetInfo(CComBSTR(str), &info);
+
+#endif // __IMultiLanguage2_INTERFACE_DEFINED__
+
+ // for most character sets, uiCodePage and uiInternetEncoding
+ // are the same. UTF-8 is the exception that we're concerned about.
+ // for that character set, we want uiInternetEncoding (65001 - UTF-8)
+ // instead of uiCodePage (1200 - UCS-2)
+ if (SUCCEEDED(hr))
+ {
+ nCodePage = (WORD) info.uiInternetEncoding;
+ }
+ else
+ {
+ AddError(IDS_STENCIL_MLANG_GETCHARSET, szTokenStart);
+ }
+ }
+ if (FAILED(hr))
+ nCodePage = 0xFFFF;
+ }
+#else
+ nCodePage = (WORD) atoi(szstart);
+#endif
+ if (nCodePage != 0xFFFF)
+ m_nCodePage = nCodePage;
+ m_bUseLocaleACP = FALSE;
+
+ return STENCIL_INVALIDINDEX;
+ }
+
+ PARSE_TOKEN_RESULT ParseHandler(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
+ {
+ LPCSTR szstart = szTokenStart;
+ LPCSTR szend = szTokenEnd;
+
+ if (m_szHandlerName[0] && m_szDllPath[0])
+ return RESERVED_TOKEN; // already found the handler and path dll names
+
+ FindTagArgs(szstart, szend, 7);
+ size_t nlen = (szend-szstart)+1;
+ char szHandlerDllName[MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1];
+ if (nlen < MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1)
+ {
+ Checked::memcpy_s(szHandlerDllName, MAX_PATH + ATL_MAX_HANDLER_NAME_LEN + 1, szstart, szend-szstart+1);
+ szHandlerDllName[szend-szstart+1] = '\0';
+
+ DWORD dwDllPathLen = MAX_PATH;
+ DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
+
+ if (!_AtlCrackHandler(szHandlerDllName, m_szDllPath, &dwDllPathLen, m_szHandlerName, &dwHandlerNameLen))
+ {
+ AddError(IDS_STENCIL_INVALID_HANDLER, szTokenStart);
+ return INVALID_TOKEN;
+ }
+ }
+
+ return RESERVED_TOKEN;
+ }
+
+ virtual PARSE_TOKEN_RESULT ParseToken(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop)
+ {
+ LPCSTR pStart = szTokenStart;
+ pStart += 2; //skip curlies
+ pStart = SkipSpace(pStart, m_nCodePage);
+ DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
+
+ DWORD dwIndex = STENCIL_INVALIDINDEX;
+ PARSE_TOKEN_RESULT ret = RESERVED_TOKEN;
+
+ if (CheckTag("endwhile", 8, pStart, dwLen))
+ dwIndex = ParseEndWhile(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
+ else if (CheckTag("while", 5, pStart, dwLen))
+ dwIndex = ParseWhile(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
+ else if (CheckTag("endif", 5, pStart, dwLen))
+ dwIndex = ParseEndIf(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
+ else if (CheckTag("else", 4, pStart, dwLen))
+ dwIndex = ParseElse(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
+ else if (CheckTag("if", 2, pStart, dwLen))
+ dwIndex = ParseIf(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
+ else if (CheckTag("locale", 6, pStart, dwLen))
+ dwIndex = ParseLocale(szTokenStart, szTokenEnd);
+ else if (CheckTag("handler", 7, pStart, dwLen))
+ {
+ return ParseHandler(szTokenStart, szTokenEnd);
+ }
+ else if (CheckTag("codepage", 8, pStart, dwLen))
+ {
+ ParseCodepage(szTokenStart, szTokenEnd);
+ return RESERVED_TOKEN;
+ }
+ else
+ {
+ dwIndex = ParseReplacement(szTokenStart, szTokenEnd, STENCIL_REPLACEMENT);
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ return INVALID_TOKEN;
+ ret = NORMAL_TOKEN;
+ }
+
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ return INVALID_TOKEN;
+ return ret;
+ }
+
+ virtual bool ParseReplacementsFromBuffer(ITagReplacer* pReplacer, LPCSTR pStart, LPCSTR pEnd)
+ {
+ LPCSTR szCurr = pStart;
+ DWORD BlockStack[ATL_MAX_BLOCK_STACK];
+ DWORD dwTop = 0;
+
+ m_pReplacer = pReplacer;
+
+ DWORD dwCurrentTokenIndex = 0;
+
+ if (!szCurr)
+ {
+ ATLASSERT(FALSE);
+ AddError(IDS_STENCIL_NULLPARAM, NULL);
+ return false;
+ }
+
+ LPCSTR szEnd = pEnd;
+ if (szEnd <= szCurr)
+ {
+ ATLASSERT(FALSE);
+ AddError(IDS_STENCIL_INVALIDSTRING, NULL);
+ return true;
+ }
+
+ while(szCurr < szEnd)
+ {
+ //mark the start of this block, then find the end of the block
+ //the end is denoted by an opening curly
+ LPCSTR szStart = szCurr;
+ while (szCurr < szEnd && (*szCurr != '{' || szCurr[1] != '{'))
+ {
+ LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
+ if (szNext == szCurr)
+ {
+ // embedded null
+ AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
+ return true;
+ }
+ szCurr = szNext;
+ }
+
+ //special case for the last text block, if there is one
+ if (szCurr >= szEnd)
+ {
+ // add the last token. This is everything after the last
+ // double curly block, which is text.
+ dwCurrentTokenIndex = AddToken(szStart, szEnd-1, STENCIL_TEXTTAG);
+ break;
+ }
+
+ //if there are any characters between szStart and szCurr inclusive,
+ //copy them to a text token.
+ if (szCurr-1 >= szStart)
+ dwCurrentTokenIndex = AddToken(szStart, szCurr-1, STENCIL_TEXTTAG);
+
+ if (dwCurrentTokenIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
+ return false;
+ }
+
+ //find the end of the tag
+ LPSTR szEndTag;
+ szStart = szCurr;
+ szCurr += 2; // Skip over the two '{' s
+ while (szCurr < szEnd)
+ {
+ if (szCurr[0] == '}' && szCurr[1] == '}')
+ break;
+ else if (szCurr[0] == '{')
+ break;
+
+ LPSTR szNext = CharNextExA(m_nCodePage, szCurr, 0);
+ if (szNext == szCurr)
+ {
+ // embedded null
+ AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
+ return true;
+ }
+ szCurr = szNext;
+ }
+
+ if (szCurr >= szEnd)
+ {
+ AddError(IDS_STENCIL_UNMATCHED_TAG_START, szStart);
+ if (AddToken(szStart, szCurr-1, STENCIL_TEXTTAG) == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
+ return false;
+ }
+
+ break;
+ }
+
+ if (szCurr[0] == '{')
+ {
+ if (szCurr[1] != '{')
+ {
+ szCurr--;
+ }
+ AddError(IDS_STENCIL_MISMATCHED_TAG_START, szStart);
+ if (AddToken(szStart, szCurr-1, STENCIL_TEXTTAG) == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
+ return false;
+ }
+
+ continue;
+ }
+
+ szEndTag = CharNextExA(m_nCodePage, szCurr, 0);
+ if (szEndTag == szCurr)
+ {
+ // embedded null
+ AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
+ return true;
+ }
+
+ PARSE_TOKEN_RESULT ret = ParseToken(szStart, szEndTag, BlockStack, &dwTop);
+
+ if (ret == INVALID_TOKEN)
+ {
+ dwCurrentTokenIndex = AddToken(szStart, szEndTag, STENCIL_TEXTTAG);
+ if (dwCurrentTokenIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, pStart);
+ return false;
+ }
+
+ szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
+ continue;
+ }
+
+ szCurr = CharNextExA(m_nCodePage, szEndTag, 0);
+ if (szEndTag == szCurr)
+ {
+ // embedded null
+ AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
+ return true;
+ }
+
+ if (ret == RESERVED_TOKEN)
+ {
+ if (szCurr < szEnd && *szCurr == '\n')
+ szCurr++;
+ else if ((szCurr+1 < szEnd && *szCurr == '\r' && *(szCurr+1) == '\n'))
+ szCurr += 2;
+ }
+ }
+
+ return true;
+ }
+
+ HTTP_CODE GetHandlerAndMethodNames(
+ __in LPCSTR pStart,
+ __in LPCSTR pEnd,
+ __out_ecount_z(nMethodNameLen) LPSTR pszMethodName,
+ __in size_t nMethodNameLen,
+ __out_ecount_z(nHandlerNameLen) LPSTR pszHandlerName,
+ __in size_t nHandlerNameLen) throw()
+ {
+
+ ATLASSERT(pStart);
+ ATLASSERT(pEnd);
+ ATLASSERT(pEnd > pStart);
+
+ if (!pszMethodName || !pszHandlerName || nMethodNameLen < 1 || nHandlerNameLen < 1)
+ {
+ ATLASSERT(FALSE);
+ AddError(IDS_STENCIL_BAD_PARAMETER, pStart);
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ }
+
+
+ *pszMethodName = '\0';
+ *pszHandlerName = '\0';
+ CHAR szMethodString[ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1];
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+ //
+ // copy the method string
+ //
+ size_t nMethodLen = (pEnd-pStart)+1;
+ if (nMethodLen >= (ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1))
+ {
+ AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart);
+ return AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
+ }
+
+ Checked::memcpy_s(szMethodString, ATL_MAX_METHOD_NAME_LEN + ATL_MAX_HANDLER_NAME_LEN+1, pStart, nMethodLen);
+ szMethodString[nMethodLen] = '\0';
+
+ //
+ // now crack the method string and get the handler
+ // id and function name
+ //
+ LPSTR szParen = strchr(szMethodString, '(');
+ LPSTR szDot = strchr(szMethodString, '.');
+ if (szDot && (!szParen || (szDot < szParen)))
+ {
+ *szDot = '\0';
+ szDot++;
+
+ // copy method name
+ if (strlen(szDot) < nMethodNameLen)
+ Checked::strcpy_s(pszMethodName, nMethodNameLen, szDot);
+ else
+ {
+ AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart + (szDot - szMethodString));
+ hcErr = AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
+ }
+ // copy handler name
+ if (!hcErr)
+ {
+ if (strlen(szMethodString) < nHandlerNameLen)
+ Checked::strcpy_s(pszHandlerName, nHandlerNameLen, szMethodString);
+ else
+ {
+ AddError(IDS_STENCIL_HANDLERNAME_TOO_LONG, pStart);
+ hcErr = AtlsHttpError(500, ISE_SUBERR_LONGHANDLERNAME);
+ }
+ }
+ }
+ else
+ {
+ // only a method name so just copy it.
+ if (strlen(szMethodString) < nMethodNameLen)
+ Checked::strcpy_s(pszMethodName, nMethodNameLen, szMethodString);
+ else
+ {
+ AddError(IDS_STENCIL_METHODNAME_TOO_LONG, pStart);
+ hcErr = AtlsHttpError(500, ISE_SUBERR_LONGMETHODNAME);
+ }
+ }
+ return hcErr;
+ }
+
+ virtual HTTP_CODE Render(
+ ITagReplacer *pReplacer,
+ IWriteStream *pWriteStream,
+ CStencilState* pState = NULL) const throw(...)
+ {
+ ATLENSURE(pReplacer != NULL);
+ ATLENSURE(pWriteStream != NULL);
+
+ HTTP_CODE hcErrorCode = HTTP_SUCCESS;
+ DWORD dwIndex = 0;
+ DWORD dwArraySize = GetTokenCount();
+
+ // set up locale info
+ CSaveThreadLocale lcidSave;
+
+ if (pState)
+ {
+ dwIndex = pState->dwIndex;
+
+ // restore the locale if we're restarting rendering
+ if (pState->locale != CP_ACP)
+ SetThreadLocale(pState->locale);
+ }
+
+ pReplacer->SetStream(pWriteStream);
+ while (dwIndex < dwArraySize)
+ {
+ // RenderToken advances dwIndex appropriately for us.
+ dwIndex = RenderToken(dwIndex, pReplacer, pWriteStream, &hcErrorCode, pState);
+
+ if (dwIndex == STENCIL_INVALIDINDEX ||
+ hcErrorCode != HTTP_SUCCESS)
+ break;
+ }
+
+ if (IsAsyncStatus(hcErrorCode))
+ {
+ ATLASSERT( pState != NULL ); // state is required for async
+ if (pState)
+ pState->dwIndex = dwIndex;
+ }
+ // lcidSave destructor will restore the locale info in case it was changed
+
+ return hcErrorCode;
+ }
+
+ inline BOOL IsValidIndex(DWORD dwIndex) const throw()
+ {
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ return FALSE;
+ if (dwIndex < GetTokenCount())
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ virtual DWORD RenderToken(
+ DWORD dwIndex,
+ ITagReplacer *pReplacer,
+ IWriteStream *pWriteStream,
+ HTTP_CODE *phcErrorCode,
+ CStencilState* pState = NULL) const throw(...)
+ {
+ ATLENSURE(pReplacer != NULL);
+ ATLENSURE(pWriteStream != NULL);
+
+ const StencilToken* pToken = GetToken(dwIndex);
+ DWORD dwNextToken = 0;
+ HTTP_CODE hcErrorCode = HTTP_SUCCESS;
+
+ if (!pToken)
+ return STENCIL_INVALIDINDEX;
+
+ switch (pToken->type)
+ {
+ case STENCIL_TEXTTAG:
+ {
+ pWriteStream->WriteStream(pToken->pStart,
+ (int)((pToken->pEnd-pToken->pStart)+1), NULL);
+ dwNextToken = dwIndex+1;
+ }
+ break;
+ case STENCIL_ITERATORSTART:
+ {
+ HTTP_CODE hcErr = STENCIL_SUCCESS;
+
+#ifdef ATL_DEBUG_STENCILS
+ // A 'while' token has to at least be followed by an endwhile!
+ if (!IsValidIndex(dwIndex+1))
+ {
+ // This should have been caught at parse time
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDINDEX);
+ ATLASSERT(FALSE);
+ break;
+ }
+
+ // End of loop should be valid
+ if (!IsValidIndex(pToken->dwLoopIndex))
+ {
+ // This should have been caught at parse time
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHWHILE);
+ ATLASSERT(FALSE);
+ break;
+ }
+
+ if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
+ {
+ // This should have been caught at parse time
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
+ ATLASSERT(FALSE);
+ break;
+ }
+#endif // ATL_DEBUG_STENCILS
+
+ DWORD dwLoopIndex = pToken->dwLoopIndex; // points to the end of the loop
+
+ // Call the replacement method
+ // if it returns HTTP_SUCCESS, enter the loop
+ // if it returns HTTP_S_FALSE, terminate the loop
+ hcErr = pReplacer->RenderReplacement(pToken->dwFnOffset,
+ pToken->dwObjOffset, pToken->dwMap, (void *) pToken->dwData);
+
+ if (hcErr == HTTP_SUCCESS)
+ {
+ dwNextToken = dwIndex+1;
+ hcErrorCode = HTTP_SUCCESS;
+ }
+ else if (hcErr == HTTP_S_FALSE)
+ {
+ dwNextToken = dwLoopIndex+1;
+ hcErrorCode = HTTP_SUCCESS;
+ }
+ else
+ {
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = hcErr;
+ break;
+ }
+ }
+ break;
+ case STENCIL_REPLACEMENT:
+ {
+#ifdef ATL_DEBUG_STENCILS
+ if (pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
+ {
+ // This should have been caught at parse time
+ ATLASSERT(FALSE);
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
+ break;
+ }
+#endif // ATL_DEBUG_STENCILS
+
+ hcErrorCode = pReplacer->RenderReplacement(pToken->dwFnOffset,
+ pToken->dwObjOffset, pToken->dwMap, (void *)pToken->dwData);
+
+ if (IsAsyncContinueStatus(hcErrorCode))
+ dwNextToken = dwIndex; // call the tag again after we get back
+ else
+ {
+ dwNextToken = dwIndex + 1;
+
+ // when returned from a handler, these indicate that the handler is done
+ // and that we should move on to the next handler when called back
+ if (hcErrorCode == HTTP_SUCCESS_ASYNC_DONE)
+ hcErrorCode = HTTP_SUCCESS_ASYNC;
+ else if (hcErrorCode == HTTP_SUCCESS_ASYNC_NOFLUSH_DONE)
+ hcErrorCode = HTTP_SUCCESS_ASYNC_NOFLUSH;
+ }
+ }
+ break;
+ case STENCIL_ITERATOREND:
+ {
+ dwNextToken = pToken->dwLoopIndex;
+ hcErrorCode = HTTP_SUCCESS;
+ ATLASSERT(GetToken(dwNextToken)->type == STENCIL_ITERATORSTART);
+ }
+ break;
+ case STENCIL_CONDITIONALSTART:
+ {
+#ifdef ATL_DEBUG_STENCILS
+ if (pToken->type == STENCIL_CONDITIONALSTART && pToken->dwFnOffset == STENCIL_INVALIDOFFSET)
+ {
+ // This should have been caught at parse time
+ ATLASSERT(FALSE);
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_INVALIDFUNCOFFSET);
+ break;
+ }
+
+ if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
+ {
+ // This should have been caught at parse time
+ ATLASSERT(FALSE);
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHIF);
+ break;
+ }
+#endif // ATL_DEBUG_STENCILS
+
+ DWORD dwLoopIndex = pToken->dwLoopIndex; // points to the end of the loop
+
+ HTTP_CODE hcErr;
+ // Call the replacement method.
+ // If it returns HTTP_SUCCESS, we render everything up to
+ // the end of the conditional.
+ // if it returns HTTP_S_FALSE, the condition is not met and we
+ // render the else part if it exists or jump past the endif otherwise
+ hcErr = pReplacer->RenderReplacement(pToken->dwFnOffset,
+ pToken->dwObjOffset, pToken->dwMap, (void *)pToken->dwData);
+
+ if (hcErr == HTTP_SUCCESS)
+ {
+ dwNextToken = dwIndex+1;
+ hcErrorCode = HTTP_SUCCESS;
+ }
+ else if (hcErr == HTTP_S_FALSE)
+ {
+ dwNextToken = dwLoopIndex+1;
+ hcErrorCode = HTTP_SUCCESS;
+ }
+ else
+ {
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = hcErr;
+ break;
+ }
+ }
+ break;
+ case STENCIL_CONDITIONALELSE:
+ {
+#ifdef ATL_DEBUG_STENCILS
+ if (pToken->dwLoopIndex == STENCIL_INVALIDINDEX)
+ {
+ // This should have been caught at parse time
+ ATLASSERT(FALSE);
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_MISMATCHIF);
+ break;
+ }
+#endif // ATL_DEBUG_STENCILS
+
+ dwNextToken = pToken->dwLoopIndex+1;
+ hcErrorCode = HTTP_SUCCESS;
+ }
+ break;
+ case STENCIL_CONDITIONALEND:
+ {
+ dwNextToken = dwIndex+1;
+ hcErrorCode = HTTP_SUCCESS;
+ }
+ break;
+ case STENCIL_LOCALE:
+ {
+ if (pState)
+ {
+ pState->locale = (LCID) pToken->dwData;
+ }
+ SetThreadLocale((LCID) pToken->dwData);
+ dwNextToken = dwIndex + 1;
+ }
+ break;
+ default:
+ {
+ ATLASSERT(FALSE);
+ dwNextToken = STENCIL_INVALIDINDEX;
+ hcErrorCode = AtlsHttpError(500, ISE_SUBERR_STENCIL_UNEXPECTEDTYPE);
+ break;
+ }
+ }
+
+ ATLASSERT(dwNextToken != dwIndex || IsAsyncContinueStatus(hcErrorCode));
+
+ if (phcErrorCode)
+ *phcErrorCode = hcErrorCode;
+
+ return dwNextToken;
+ }
+
+ DWORD GetTokenCount() const throw()
+ {
+ return (DWORD) m_arrTokens.GetCount();
+ }
+
+ const StencilToken* GetToken(DWORD dwIndex) const throw()
+ {
+ return &(m_arrTokens[dwIndex]);
+ }
+
+ StencilToken* GetToken(DWORD dwIndex) throw()
+ {
+ return &(m_arrTokens[dwIndex]);
+ }
+
+ LPCSTR GetBufferStart() const throw()
+ {
+ return m_pBufferStart;
+ }
+
+ LPCSTR GetBufferEnd() const throw()
+ {
+ return m_pBufferEnd;
+ }
+
+ WORD GetCodePage() const throw()
+ {
+ return m_nCodePage;
+ }
+
+ // IMemoryCacheClient
+ STDMETHOD(QueryInterface)(REFIID riid, void **ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+
+ if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
+ InlineIsEqualGUID(riid, __uuidof(IMemoryCacheClient)))
+ {
+ *ppv = static_cast<IMemoryCacheClient*>(this);
+ return S_OK;
+ }
+
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+
+ STDMETHOD_(ULONG, AddRef)()
+ {
+ return 1;
+ }
+
+ STDMETHOD_(ULONG, Release)()
+ {
+ return 1;
+ }
+
+ STDMETHOD(Free)(const void *pData)
+ {
+ if (!pData)
+ return E_POINTER;
+
+ ATLASSERT(*((void **) pData) == static_cast<void*>(this));
+
+ delete this;
+
+ return S_OK;
+ }
+}; // class CStencil
+
+struct StencilIncludeInfo
+{
+public:
+ CHAR m_szQueryString[ATL_URL_MAX_URL_LENGTH+1];
+ CHAR m_szFileName[MAX_PATH];
+};
+
+
+class CIncludeServerContext :
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public CWrappedServerContext
+{
+public:
+ BEGIN_COM_MAP(CIncludeServerContext)
+ COM_INTERFACE_ENTRY(IHttpServerContext)
+ END_COM_MAP()
+
+ IWriteStream * m_pStream;
+ const StencilIncludeInfo * m_pIncludeInfo;
+
+ CIncludeServerContext() throw()
+ {
+ m_pStream = NULL;
+ m_pIncludeInfo = NULL;
+ }
+
+ void Initialize(
+ IWriteStream *pStream,
+ IHttpServerContext* pServerContext,
+ const StencilIncludeInfo * pIncludeInfo) throw()
+ {
+ ATLASSERT(pStream != NULL);
+ ATLASSERT(pServerContext != NULL);
+ ATLASSERT(pIncludeInfo != NULL);
+ m_pStream = pStream;
+ m_spParent = pServerContext;
+ m_pIncludeInfo = pIncludeInfo;
+ }
+
+ void Initialize(CIncludeServerContext *pOtherContext)
+ {
+ ATLENSURE(pOtherContext != NULL);
+ m_pStream = pOtherContext->m_pStream;
+ m_spParent = pOtherContext->m_spParent;
+ m_pIncludeInfo = pOtherContext->m_pIncludeInfo;
+ }
+
+ LPCSTR GetRequestMethod()
+ {
+ return "GET";
+ }
+
+ LPCSTR GetQueryString()
+ {
+ ATLASSUME(m_pIncludeInfo != NULL);
+ return m_pIncludeInfo->m_szQueryString;
+ }
+
+ LPCSTR GetPathTranslated()
+ {
+ ATLASSUME(m_pIncludeInfo != NULL);
+ return m_pIncludeInfo->m_szFileName;
+ }
+
+ LPCSTR GetScriptPathTranslated()
+ {
+ ATLASSUME(m_pIncludeInfo != NULL);
+ return m_pIncludeInfo->m_szFileName;
+ }
+
+ DWORD GetTotalBytes()
+ {
+ return 0;
+ }
+
+ DWORD GetAvailableBytes()
+ {
+ return 0;
+ }
+
+ BYTE *GetAvailableData()
+ {
+ return NULL;
+ }
+
+ LPCSTR GetContentType()
+ {
+ return 0;
+ }
+
+ BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes)
+ {
+ ATLASSUME(m_pStream != NULL);
+ ATLENSURE(pvBuffer != NULL);
+ ATLENSURE(pdwBytes != NULL);
+
+ HRESULT hr = S_OK;
+ _ATLTRY
+ {
+ hr = m_pStream->WriteStream((LPCSTR) pvBuffer, *pdwBytes, pdwBytes);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_FAIL;
+ }
+
+ return SUCCEEDED(hr);
+ }
+
+ BOOL ReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
+ {
+ return FALSE;
+ }
+
+ BOOL AsyncReadClient(void * /*pvBuffer*/, DWORD * /*pdwSize*/)
+ {
+ return FALSE;
+ }
+
+ BOOL SendRedirectResponse(LPCSTR /*pszRedirectURL*/)
+ {
+ return FALSE;
+ }
+
+ BOOL SendResponseHeader(
+ LPCSTR /*pszHeader*/,
+ LPCSTR /*pszStatusCode*/,
+ BOOL /*fKeepConn*/)
+ {
+ return TRUE;
+ }
+
+ BOOL DoneWithSession(DWORD /*dwHttpStatusCode*/)
+ {
+ return TRUE;
+ }
+
+ BOOL RequestIOCompletion(PFN_HSE_IO_COMPLETION /*pfn*/, DWORD * /*pdwContext*/)
+ {
+ return FALSE;
+ }
+}; // class CIncludeServerContext
+
+class CIDServerContext :
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public CWrappedServerContext
+{
+public:
+ CHttpResponse *m_pResponse;
+ CHttpRequest *m_pRequest;
+
+ BEGIN_COM_MAP(CIDServerContext)
+ COM_INTERFACE_ENTRY(IHttpServerContext)
+ END_COM_MAP()
+
+ CIDServerContext() throw()
+ : m_pResponse(NULL), m_pRequest(NULL)
+ {
+ }
+
+ BOOL Initialize(
+ CHttpResponse *pResponse,
+ CHttpRequest *pRequest) throw()
+ {
+ ATLASSERT(pResponse != NULL);
+ ATLASSERT(pRequest != NULL);
+ m_pResponse = pResponse;
+ m_pRequest = pRequest;
+ if(!m_pRequest)
+ {
+ return FALSE;
+ }
+
+ HRESULT hr = m_pRequest->GetServerContext(&m_spParent);
+ return (SUCCEEDED(hr));
+ }
+
+ LPCSTR GetRequestMethod()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetMethodString();
+ }
+
+ LPCSTR GetQueryString()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetQueryString();
+ }
+
+ LPCSTR GetPathInfo()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetPathInfo();
+ }
+
+ LPCSTR GetPathTranslated()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetPathTranslated();
+ }
+
+ DWORD GetTotalBytes()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetTotalBytes();
+ }
+
+ DWORD GetAvailableBytes()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetAvailableBytes();
+ }
+
+ BYTE *GetAvailableData()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetAvailableData();
+ }
+
+ LPCSTR GetContentType()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetContentType();
+ }
+
+ LPCSTR GetScriptPathTranslated()
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->GetScriptPathTranslated();
+ }
+
+ BOOL WriteClient(void *pvBuffer, DWORD *pdwBytes)
+ {
+ ATLASSUME(m_pResponse != NULL);
+ return m_pResponse->WriteLen((LPCSTR)pvBuffer, *pdwBytes);
+ }
+
+ BOOL ReadClient(void *pvBuffer, DWORD *pdwSize)
+ {
+ ATLASSUME(m_pRequest != NULL);
+ return m_pRequest->ReadData((LPSTR)pvBuffer, pdwSize);
+ }
+
+ BOOL SendRedirectResponse(LPCSTR pszRedirectURL)
+ {
+ ATLASSUME(m_pResponse != NULL);
+ return m_pResponse->Redirect(pszRedirectURL);
+ }
+
+ BOOL TransmitFile(
+ HANDLE hFile,
+ PFN_HSE_IO_COMPLETION pfn,
+ void *pContext,
+ LPCSTR szStatusCode,
+ DWORD dwBytesToWrite,
+ DWORD dwOffset,
+ void *pvHead,
+ DWORD dwHeadLen,
+ void *pvTail,
+ DWORD dwTailLen,
+ DWORD dwFlags)
+ {
+ ATLASSUME(m_pResponse != NULL);
+ ATLASSUME(m_spParent != NULL);
+
+ m_pResponse->Flush();
+ return m_spParent->TransmitFile(hFile, pfn, pContext, szStatusCode,
+ dwBytesToWrite, dwOffset, pvHead, dwHeadLen, pvTail, dwTailLen, dwFlags);
+ }
+
+}; // class CIDServerContext
+
+//
+// CHtmlStencil
+// CHtmlStencil is a specialization of CStencil. CHtmlStencil adds the following
+// capabilities to CStencil:
+//
+// Support for rendering {{include }} tags
+// The {{include }} tags specify another stencil to be included in-place during
+// stencil rendering. The {{include }} tag takes a single parameter which is the
+// URL of the stencil to include. That URL can optionally include parameters.
+// An example:
+// {{include mystencil.srf?param1=value1}}
+//
+// We also grab the handler name and the name of any subhandlers. The syntax for the
+// handler specification is:
+// {{handler MyDynamicHandler.dll/Default}}
+// which would cause the MyDynamicHandler.dll to be loaded. Once loaded, the stencil
+// processor will ask for the IReplacementHandler interface of the object named "Default".
+//
+// Additional handlers can be specified after the default handler. An example of an
+// additional handler would be:
+// {{subhandler OtherHandler MyOtherHandler.dll/Default}}
+// would cause the MyOtherHandler.dll to be loaded. Once loaded, the stencil processor will
+// ask for the IReplacementHandler interface of the object named "Default" and use it in
+// processing the stencil anywhere it sees a stencil tag of the form
+// {{OtherHandler.RenderReplacement}}
+
+struct CStringPair
+{
+ typedef CFixedStringT<CStringA, MAX_PATH> PathStrType;
+ typedef CFixedStringT<CStringA, ATL_MAX_HANDLER_NAME_LEN+1> HdlrNameStrType;
+ PathStrType strDllPath;
+ HdlrNameStrType strHandlerName;
+
+ CStringPair()throw()
+ {
+ }
+
+ CStringPair(PathStrType &strDllPath_, HdlrNameStrType &strHandlerName_) throw(...)
+ :strDllPath(strDllPath_), strHandlerName(strHandlerName_)
+ {
+ }
+
+ CStringPair(CStringA &strDllPath_, CStringA &strHandlerName_) throw(...)
+ :strDllPath(strDllPath_), strHandlerName(strHandlerName_)
+ {
+ }
+};
+
+class CStringPairElementTraits :
+ public CElementTraitsBase< CStringPair >
+{
+private:
+
+ static ULONG HashStr( ULONG nHash, CStringElementTraits<CStringA>::INARGTYPE str )
+ {
+ ATLENSURE( str != NULL );
+ const CStringA::XCHAR* pch = str;
+ while( *pch != 0 )
+ {
+ nHash = (nHash<<5)+nHash+(*pch);
+ pch++;
+ }
+
+ return( nHash );
+ }
+
+public:
+ static ULONG Hash( INARGTYPE pair ) throw()
+ {
+ ULONG nHash = HashStr(0, pair.strDllPath);
+ return HashStr(nHash, pair.strHandlerName);
+ }
+
+ static bool CompareElements( INARGTYPE pair1, INARGTYPE pair2 ) throw()
+ {
+ return( (pair1.strDllPath == pair2.strDllPath) && (pair1.strHandlerName == pair2.strHandlerName) );
+ }
+
+ static int CompareElementsOrdered( INARGTYPE pair1, INARGTYPE pair2 ) throw()
+ {
+ return( pair1.strDllPath.Compare( pair2.strDllPath ) );
+ }
+};
+
+class CHtmlStencil : public CStencil
+{
+private:
+
+ ATL_NOINLINE HTTP_CODE RenderInclude(
+ ITagReplacer *pReplacer,
+ const StencilToken *pToken,
+ IWriteStream *pWriteStream,
+ CStencilState *pState) const
+ {
+ ATLASSUME(m_spServiceProvider);
+ CComPtr<IHttpServerContext> spServerContext;
+ CComPtr<IHttpRequestLookup> spLookup;
+ if (FAILED(pReplacer->GetContext(__uuidof(IHttpServerContext), (VOID**) &spServerContext)))
+ {
+ return AtlsHttpError(500, 0);
+ }
+ if (FAILED(pReplacer->GetContext(__uuidof(IHttpRequestLookup), (VOID**) &spLookup)))
+ {
+ return AtlsHttpError(500, 0);
+ }
+ return RenderInclude(m_spServiceProvider, pWriteStream,
+ (StencilIncludeInfo *)pToken->dwData, spServerContext, spLookup,
+ pState);
+ }
+
+ ATL_NOINLINE HTTP_CODE NoCachePage(ITagReplacer *pReplacer) const
+ {
+ CComPtr<IHttpServerContext> spContext;
+ HRESULT hr = pReplacer->GetContext(__uuidof(IHttpServerContext), (void **)&spContext);
+ if (hr == S_OK && spContext)
+ {
+ CComQIPtr<IPageCacheControl> spControl;
+ spControl = spContext;
+ if (spControl)
+ spControl->Cache(FALSE);
+ }
+ return HTTP_SUCCESS;
+ }
+
+
+ // CAllocIncludeAsyncContext is an unsupported implementation detail of RenderInclude
+ class CAllocIncludeAsyncContext :
+ public CAllocContextBase
+ {
+ public:
+ CAllocIncludeAsyncContext(CIncludeServerContext *pBase) :
+ m_pBase(pBase)
+ {
+
+ }
+ HTTP_CODE Alloc(IHttpServerContext **ppNewContext)
+ {
+ ATLASSUME(m_pBase);
+ if (!ppNewContext)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+ *ppNewContext = NULL;
+ CComObjectNoLock<CIncludeServerContext>* pNewServerContext = NULL;
+ ATLTRY(pNewServerContext = new CComObjectNoLock<CIncludeServerContext>);
+ if (pNewServerContext == NULL)
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM);
+ pNewServerContext->Initialize(m_pBase);
+ pNewServerContext->AddRef();
+ *ppNewContext = pNewServerContext;
+ return HTTP_SUCCESS;
+ }
+
+ private:
+ CIncludeServerContext *m_pBase;
+ }; // CAllocIncludeAsyncContext
+
+
+ ATL_NOINLINE HTTP_CODE RenderInclude(
+ IServiceProvider *pServiceProvider,
+ IWriteStream *pWriteStream,
+ const StencilIncludeInfo *pIncludeInfo,
+ IHttpServerContext *pServerContext,
+ IHttpRequestLookup *pLookup,
+ CStencilState* pState = NULL) const throw(...)
+ {
+ CComObjectStackEx<CIncludeServerContext> serverContext;
+ serverContext.Initialize(pWriteStream, pServerContext, pIncludeInfo);
+ CAllocIncludeAsyncContext AsyncAllocObj(&serverContext);
+ return _AtlRenderInclude(static_cast<IHttpServerContext*>(&serverContext),
+ pIncludeInfo->m_szFileName,
+ pIncludeInfo->m_szQueryString,
+ GetCodePage(),
+ &AsyncAllocObj,
+ pServiceProvider,
+ pLookup,
+ pState);
+
+ }
+
+protected:
+ CAtlMap<CStringA, CStringPair,
+ CStringElementTraits<CStringA>, CStringPairElementTraits > m_arrExtraHandlers;
+ CHAR m_szBaseDir[MAX_PATH];
+ CComPtr<IServiceProvider> m_spServiceProvider;
+ CComPtr<IIsapiExtension> m_spExtension;
+ CComPtr<IStencilCache> m_spStencilCache;
+ CComPtr<IDllCache> m_spDllCache;
+
+public:
+ typedef CAtlMap<CStringA, CStringPair,
+ CStringElementTraits<CStringA>, CStringPairElementTraits > mapType;
+ typedef CStencil baseType;
+
+ CHtmlStencil(IAtlMemMgr *pMemMgr=NULL) throw() :
+ CStencil(pMemMgr)
+ {
+
+ }
+
+ void Initialize(IServiceProvider *pProvider) throw(...)
+ {
+ ATLENSURE(pProvider);
+ if (m_spServiceProvider)
+ m_spServiceProvider.Release();
+
+ m_spServiceProvider = pProvider;
+ if (!m_spDllCache)
+ pProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void **) &m_spDllCache);
+
+ if (!m_spExtension)
+ pProvider->QueryInterface(__uuidof(IIsapiExtension), (void **) &m_spExtension);
+ }
+
+ BOOL GetIncludeInfo(LPCSTR szParamBegin, LPCSTR szParamEnd, StencilIncludeInfo *pInfo) const
+ {
+ ATLENSURE(szParamBegin != NULL);
+ ATLENSURE(szParamEnd != NULL);
+ ATLENSURE(pInfo != NULL);
+
+ LPCSTR szQueryBegin = szParamBegin;
+
+ while (*szQueryBegin && *szQueryBegin != '?' && *szQueryBegin != '}')
+ {
+ LPSTR szNext = CharNextExA(GetCodePage(), szQueryBegin, 0);
+ if (szNext == szQueryBegin)
+ {
+ return FALSE;
+ }
+
+ szQueryBegin = szNext;
+ }
+
+ CFixedStringT<CStringA, MAX_PATH> strPath;
+
+ _ATLTRY
+ {
+ DWORD dwPrefixLen = 0;
+ if (*szParamBegin == '"')
+ {
+ szParamBegin++;
+ }
+ if (!IsFullPathA(szParamBegin))
+ {
+ if (*szParamBegin != '\\')
+ {
+ strPath = m_szBaseDir;
+ }
+ else
+ {
+ LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
+ if (szBackslash)
+ {
+#pragma warning(push)
+#pragma warning(disable: 6204)
+ /* prefast noise VSW 492749 */
+ strPath.SetString(m_szBaseDir, (int)(szBackslash-m_szBaseDir));
+#pragma warning(pop)
+ }
+ else
+ {
+ strPath = m_szBaseDir;
+ }
+ }
+ dwPrefixLen = strPath.GetLength();
+ }
+
+ if (*szQueryBegin=='?')
+ {
+ size_t nMinus = (*(szQueryBegin-1) == '"') ? 1 : 0;
+ strPath.Append(szParamBegin, (int)(szQueryBegin-szParamBegin-nMinus));
+ if ((szParamEnd-szQueryBegin) > ATL_URL_MAX_PATH_LENGTH || ((szParamEnd - szQueryBegin) < 0))
+ {
+ // query string is too long
+ return FALSE;
+ }
+ Checked::memcpy_s(pInfo->m_szQueryString, ATL_URL_MAX_URL_LENGTH+1, szQueryBegin + 1, szParamEnd - szQueryBegin);
+ pInfo->m_szQueryString[szParamEnd - szQueryBegin] = '\0';
+ }
+ else
+ {
+ pInfo->m_szQueryString[0] = '\0';
+ size_t nAdd = (*szParamEnd == '"') ? 0 : 1;
+ strPath.Append(szParamBegin, (int)(szParamEnd - szParamBegin + nAdd));
+ }
+ }
+ _ATLCATCHALL()
+ {
+ // out of memory
+ return FALSE;
+ }
+
+ if (strPath.GetLength() > MAX_PATH-1)
+ {
+ // path is too long
+ return FALSE;
+ }
+
+ // strPath is <= MAX_PATH-1
+ return PathCanonicalizeA(pInfo->m_szFileName, strPath);
+ }
+
+ DWORD ParseInclude(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw(...)
+ {
+ ATLENSURE(szTokenStart != NULL);
+ ATLENSURE(szTokenEnd != NULL);
+
+ LPCSTR szStart = szTokenStart;
+ LPCSTR szEnd = szTokenEnd;
+
+ FindTagArgs(szStart, szEnd, 7);
+
+ CFixedStringT<CStringA, MAX_PATH> strFileNameRelative;
+ CFixedStringT<CString, MAX_PATH> strFileName;
+
+ _ATLTRY
+ {
+ strFileNameRelative.SetString(szStart, (int)(szEnd-szStart + 1));
+
+ if (!IsFullPathA(strFileNameRelative))
+ {
+ CFixedStringT<CStringA, MAX_PATH> strTemp;
+ if (*((LPCSTR)strFileNameRelative) != '\\')
+ {
+ strTemp = m_szBaseDir;
+ }
+ else
+ {
+ LPCSTR szBackslash = strchr(m_szBaseDir, '\\');
+ if (szBackslash)
+ {
+#pragma warning(push)
+#pragma warning(disable: 6204)
+#pragma warning(disable: 6535)
+ /* prefast noise VSW 492749 */
+ /* prefast noise VSW 493256 */
+ strTemp.SetString(m_szBaseDir, (int)(szBackslash-m_szBaseDir));
+#pragma warning(pop)
+ }
+ else
+ {
+ strTemp = m_szBaseDir;
+ }
+ }
+
+ strTemp.Append(strFileNameRelative, strFileNameRelative.GetLength());
+ CFixedStringT<CString, MAX_PATH> strConv = (LPCTSTR) CA2CT(strTemp);
+ LPTSTR szFileBuf = strFileName.GetBuffer(strConv.GetLength()+1);
+ if (szFileBuf == NULL)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ if (!PathCanonicalize(szFileBuf, strConv))
+ {
+ return STENCIL_INVALIDINDEX;
+ }
+
+ strFileName.ReleaseBuffer();
+ }
+ else
+ {
+ strFileName = CA2CTEX<MAX_PATH>(strFileNameRelative);
+ }
+ }
+ _ATLCATCHALL()
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ LPCTSTR szFileName = strFileName;
+
+ LPCTSTR szDot = NULL;
+ LPCTSTR szExtra = _tcschr(szFileName, '?');
+ if (!szExtra)
+ {
+ szExtra = _tcschr(szFileName, '#');
+ if (!szExtra)
+ {
+ szDot = _tcsrchr(szFileName, '.');
+ }
+ }
+
+ if (szExtra != NULL)
+ {
+ // there is some extra information
+ LPCTSTR szDotTmp = szFileName;
+ do
+ {
+ szDot = szDotTmp;
+ szDotTmp = _tcschr(szDotTmp+1, '.');
+ } while (szDotTmp && szDotTmp < szExtra);
+ }
+
+ if (!szDot || *szDot != '.')
+ {
+ AddError(IDS_STENCIL_UNEXPECTED, szTokenStart);
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ LPCTSTR szExtEnd = szDot;
+
+ while (true)
+ {
+ szExtEnd++;
+ if (!*szExtEnd || *szExtEnd == '/' || *szExtEnd == '\\' || *szExtEnd == '?' || *szExtEnd == '#' || *szExtEnd == '"')
+ break;
+ }
+
+ if (szDot && (size_t)(szExtEnd-szDot) == _tcslen(c_tAtlDLLExtension) &&
+ !_tcsnicmp(szDot, c_tAtlDLLExtension, _tcslen(c_tAtlDLLExtension)))
+ {
+ // Do .dll stuff
+ DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+ StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
+ if (!pInfo)
+ {
+ return STENCIL_INVALIDINDEX;
+ }
+
+ if (!GetIncludeInfo(szStart, szEnd, pInfo))
+ {
+ return STENCIL_INVALIDINDEX;
+ }
+
+ GetToken(dwIndex)->dwData = (DWORD_PTR) pInfo;
+ return dwIndex;
+ }
+ else if (szDot && (size_t)(szExtEnd-szDot) == _tcslen(c_tAtlSRFExtension) &&
+ !_tcsnicmp(szDot, c_tAtlSRFExtension, _tcslen(c_tAtlSRFExtension)))
+ {
+ // Do .srf stuff
+ DWORD dwIndex = AddToken(szStart, szEnd, STENCIL_STENCILINCLUDE);
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+ StencilIncludeInfo *pInfo = (StencilIncludeInfo *)m_pMemMgr->Allocate(sizeof(StencilIncludeInfo));
+ if (!pInfo)
+ {
+ return STENCIL_INVALIDINDEX;
+ }
+
+ if (!GetIncludeInfo(szStart, szEnd, pInfo))
+ {
+ return STENCIL_INVALIDINDEX;
+ }
+
+ GetToken(dwIndex)->dwData = (DWORD_PTR) pInfo;
+ return dwIndex;
+ }
+ else
+ {
+ // Assume static content
+ CAtlFile file;
+
+ HRESULT hr = file.Create(szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
+ if (FAILED(hr) || GetFileType(file) != FILE_TYPE_DISK)
+ {
+ if (FAILED(hr))
+ {
+ AddError(IDS_STENCIL_INCLUDE_ERROR, szTokenStart);
+ }
+ else
+ {
+ AddError(IDS_STENCIL_INCLUDE_INVALID, szTokenStart);
+ }
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ CAutoVectorPtr<CHAR> szBufferStart;
+ LPSTR szBufferEnd = NULL;
+ ULONGLONG dwLen = 0;
+ if (FAILED(file.GetSize(dwLen)))
+ {
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ if (!szBufferStart.Allocate((size_t) dwLen))
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, szTokenStart);
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ DWORD dwRead;
+ if (FAILED(file.Read(szBufferStart, (DWORD) dwLen, dwRead)))
+ {
+ return AddToken(szTokenStart, szTokenEnd, STENCIL_TEXTTAG);
+ }
+
+ szBufferEnd = szBufferStart + dwRead-1;
+
+ DWORD dwIndex = AddToken(szBufferStart, szBufferEnd, STENCIL_STATICINCLUDE);
+ if (dwIndex != STENCIL_INVALIDINDEX)
+ {
+ GetToken(dwIndex)->bDynamicAlloc = TRUE;
+ szBufferStart.Detach();
+ }
+
+ return dwIndex;
+ }
+ }
+
+ PARSE_TOKEN_RESULT ParseSubhandler(LPCSTR szTokenStart, LPCSTR szTokenEnd) throw()
+ {
+ ATLASSERT(szTokenStart != NULL);
+ ATLASSERT(szTokenEnd != NULL);
+
+ LPCSTR szStart = szTokenStart;
+ LPCSTR szEnd = szTokenEnd;
+
+ // move to the start of the arguments
+ // (the first char past 'subhandler'
+ FindTagArgs(szStart, szEnd, 10);
+
+ // skip any space to bring us to the start
+ // of the id for the subhandler.
+ szStart = SkipSpace(szStart, GetCodePage());
+
+ // id names cannot contain spaces. Mark the
+ // beginning and end if the subhandler id
+ LPCSTR szIdStart = szStart;
+ while (!isspace(static_cast<unsigned char>(*szStart)) && *szStart != '}')
+ {
+ if (!isalnum(static_cast<unsigned char>(*szStart)))
+ {
+ // id names can only contain alphanumeric characters
+ return INVALID_TOKEN;
+ }
+
+ LPSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
+ if (szNext == szStart)
+ {
+ // embedded null
+ AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
+ return INVALID_TOKEN;
+ }
+ szStart = szNext;
+ }
+ LPCSTR szIdEnd = szStart;
+
+ // skip space to bring us to the beginning of the
+ // the dllpath/handlername
+ szStart = SkipSpace(szStart, GetCodePage());
+
+ // everything up to the end if the tag is
+ // part of the dllpath/handlername
+ LPCSTR szHandlerStart = szStart;
+ while (*szStart != '}')
+ {
+ LPCSTR szNext = CharNextExA(GetCodePage(), szStart, 0);
+ if (szNext == szStart)
+ {
+ // embedded null
+ AddError(IDS_STENCIL_EMBEDDED_NULL, NULL);
+ return INVALID_TOKEN;
+ }
+ szStart = szNext;
+ }
+ LPCSTR szHandlerEnd = szStart;
+
+ _ATLTRY
+ {
+ CStringPair::HdlrNameStrType strName(szIdStart, (int)(szIdEnd-szIdStart));
+ CStringPair::PathStrType strPath(szHandlerStart, (int)(szHandlerEnd-szHandlerStart));
+
+ CStringPair::PathStrType strDllPath;
+ CStringPair::HdlrNameStrType strHandlerName;
+ DWORD dwDllPathLen = MAX_PATH;
+ DWORD dwHandlerNameLen = ATL_MAX_HANDLER_NAME_LEN+1;
+
+ LPSTR szDllPath = strDllPath.GetBuffer(dwDllPathLen);
+ LPSTR szHandlerName = strHandlerName.GetBuffer(dwHandlerNameLen);
+
+ if (!_AtlCrackHandler(strPath, szDllPath, &dwDllPathLen, szHandlerName, &dwHandlerNameLen))
+ {
+ strDllPath.ReleaseBuffer();
+ strHandlerName.ReleaseBuffer();
+ AddError(IDS_STENCIL_INVALID_SUBHANDLER, szTokenStart);
+ return INVALID_TOKEN;
+ }
+
+ strDllPath.ReleaseBuffer(dwDllPathLen);
+ strHandlerName.ReleaseBuffer(dwHandlerNameLen);
+
+ m_arrExtraHandlers.SetAt(strName, CStringPair(strDllPath, strHandlerName));
+ }
+ _ATLCATCHALL()
+ {
+ AddError(IDS_STENCIL_OUTOFMEMORY, NULL);
+ return INVALID_TOKEN;
+ }
+ return RESERVED_TOKEN;
+ }
+
+ virtual PARSE_TOKEN_RESULT ParseToken(LPCSTR szTokenStart, LPCSTR szTokenEnd, DWORD *pBlockStack, DWORD *pdwTop)
+ {
+ ATLASSERT(szTokenStart != NULL);
+ ATLASSERT(szTokenEnd != NULL);
+
+ LPCSTR pStart = szTokenStart;
+ pStart += 2; //skip curlies
+ pStart = SkipSpace(pStart, GetCodePage());
+ DWORD dwLen = (DWORD)(szTokenEnd - szTokenStart);
+
+ DWORD dwIndex = STENCIL_INVALIDINDEX;
+
+ if (CheckTag("include", sizeof("include")-1, pStart, dwLen))
+ {
+ dwIndex = ParseInclude(szTokenStart, szTokenEnd);
+ }
+ else if (dwLen > 3 && !memcmp("!--", pStart, 3))
+ {
+ return RESERVED_TOKEN;
+ }
+ else if (dwLen > 2 && !memcmp("//", pStart, 2))
+ {
+ return RESERVED_TOKEN;
+ }
+ else if (CheckTag("subhandler", sizeof("subhandler")-1, pStart, dwLen))
+ {
+ return ParseSubhandler(szTokenStart, szTokenEnd);
+ }
+ else
+ {
+ return CStencil::ParseToken(szTokenStart, szTokenEnd, pBlockStack, pdwTop);
+ }
+ if (dwIndex == STENCIL_INVALIDINDEX)
+ {
+ return INVALID_TOKEN;
+ }
+ return RESERVED_TOKEN;
+ }
+
+ mapType* GetExtraHandlers() throw()
+ {
+ return &m_arrExtraHandlers;
+ }
+
+ BOOL SetBaseDirFromFile(LPCSTR szBaseDir)
+ {
+ if (!SafeStringCopy(m_szBaseDir, szBaseDir))
+ {
+ return FALSE;
+ }
+
+ LPSTR szSlash = strrchr(m_szBaseDir, '\\');
+ if (szSlash)
+ {
+ szSlash++;
+ *szSlash = '\0';
+ }
+ else
+ {
+ *m_szBaseDir = '\0';
+ }
+
+ return TRUE;
+ }
+
+ LPCSTR GetBaseDir()
+ {
+ return m_szBaseDir;
+ }
+
+ DWORD RenderToken(
+ DWORD dwIndex,
+ ITagReplacer* pReplacer,
+ IWriteStream *pWriteStream,
+ HTTP_CODE *phcErrorCode,
+ CStencilState* pState = NULL) const throw(...)
+ {
+ DWORD dwNextToken = STENCIL_INVALIDINDEX;
+ HTTP_CODE hcErrorCode = HTTP_SUCCESS;
+ const StencilToken* pToken = GetToken(dwIndex);
+ if (pToken)
+ {
+ if (pToken->type == STENCIL_STENCILINCLUDE)
+ {
+ hcErrorCode = RenderInclude(pReplacer, pToken, pWriteStream, pState);
+ if (hcErrorCode == HTTP_SUCCESS || IsAsyncDoneStatus(hcErrorCode))
+ {
+ dwNextToken = dwIndex+1;
+ }
+ else if (IsAsyncContinueStatus(hcErrorCode))
+ {
+ dwNextToken = dwIndex;
+ }
+ }
+ else if (pToken->type == STENCIL_STATICINCLUDE)
+ {
+ pWriteStream->WriteStream(pToken->pStart,
+ (int)((pToken->pEnd-pToken->pStart)+1), NULL);
+ dwNextToken = dwIndex+1;
+ }
+ else
+ {
+ dwNextToken = baseType::RenderToken(dwIndex, pReplacer,
+ pWriteStream, &hcErrorCode, pState);
+ }
+ }
+
+ if (hcErrorCode == HTTP_SUCCESS_NO_CACHE)
+ {
+ hcErrorCode = NoCachePage(pReplacer);
+ }
+
+ if (phcErrorCode)
+ {
+ *phcErrorCode = hcErrorCode;
+ }
+ return dwNextToken;
+ }
+}; // class CHtmlStencil
+
+
+__declspec(selectany) CCRTHeap CStencil::m_crtHeap;
+
+//
+// CHtmlTagReplacer
+// This class manages CStencil based objects for HTTP requests. This class will retrieve
+// CStencil based objects from the stencil cache, store CStencil based objects in the
+// stencil cache and allocate and initialize CStencil based objects on a per reqeust
+// basis. Typically, one instance of this class is created for each HTTP request. The
+// instance is destroyed once the request has been completed.
+template <class THandler, class StencilType=CHtmlStencil>
+class CHtmlTagReplacer :
+ public ITagReplacerImpl<THandler>
+{
+protected:
+ typedef StencilType StencilType;
+
+ CSimpleArray<HINSTANCE> m_hInstHandlers;
+ typedef CAtlMap<CStringA, IRequestHandler*, CStringElementTraits<CStringA> > mapType;
+ mapType m_Handlers;
+ StencilType *m_pLoadedStencil;
+ WORD m_nCodePage;
+ CComPtr<IStencilCache> m_spStencilCache;
+
+ AtlServerRequest m_RequestInfo;
+
+public:
+ // public members
+
+ CHtmlTagReplacer() throw() :
+ m_pLoadedStencil(NULL)
+ {
+ memset(&m_RequestInfo, 0x00, sizeof(m_RequestInfo));
+ m_nCodePage = CP_THREAD_ACP;
+ }
+
+ ~CHtmlTagReplacer() throw()
+ {
+ // you should call FreeHandlers before
+ // the object is destructed
+ ATLASSUME(m_hInstHandlers.GetSize() == 0);
+ }
+
+ HTTP_CODE Initialize(AtlServerRequest *pRequestInfo, IHttpServerContext *pSafeSrvCtx=NULL) throw(...)
+ {
+ ATLASSERT(pRequestInfo != NULL);
+
+ CComPtr<IServiceProvider> spServiceProvider;
+ THandler *pT = static_cast<THandler*>(this);
+ HRESULT hr = pT->GetContext(__uuidof(IServiceProvider), (void **)&spServiceProvider);
+ if (FAILED(hr))
+ return HTTP_FAIL;
+
+ spServiceProvider->QueryService(__uuidof(IStencilCache), __uuidof(IStencilCache), (void **) &m_spStencilCache);
+ if (!m_spStencilCache)
+ {
+ ATLASSERT(FALSE);
+ return HTTP_FAIL;
+ }
+
+ // copy the AtlServerRequest into the safe version
+ Checked::memcpy_s(&m_RequestInfo, sizeof(m_RequestInfo), pRequestInfo, sizeof(m_RequestInfo));
+
+ // override appropriate fields
+ m_RequestInfo.cbSize = sizeof(m_RequestInfo);
+ m_RequestInfo.pServerContext = pSafeSrvCtx;
+
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE LoadStencilResource(
+ HINSTANCE hInstResource,
+ LPCSTR szResourceID,
+ LPCSTR szResourceType = NULL, LPCSTR szStencilName=NULL) throw(...)
+ {
+ if (!szResourceType)
+ szResourceType = (LPCSTR) (RT_HTML);
+ // look up stencil in cache
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+
+ // check the cache first
+ StencilType *pStencil = FindCacheStencil(szStencilName ? szStencilName : szResourceID);
+ if (!pStencil)
+ {
+ // create a new stencil
+ pStencil = GetNewCacheStencil();
+ if (!pStencil)
+ {
+ return AtlsHttpError(500,ISE_SUBERR_OUTOFMEM);
+ }
+
+ THandler *pT = static_cast<THandler*>(this);
+ LPCSTR szFileName = pT->m_spServerContext->GetScriptPathTranslated();
+
+ if (!szFileName)
+ return HTTP_FAIL;
+
+ if (!pStencil->SetBaseDirFromFile(szFileName))
+ {
+ return HTTP_FAIL;
+ }
+
+ pStencil->SetErrorResource(GetResourceInstance());
+
+ // load the stencil and parse its replacements
+ if (HTTP_SUCCESS == pStencil->LoadFromResource(hInstResource,
+ szResourceID, szResourceType))
+ {
+ _ATLTRY
+ {
+ if (!pStencil->ParseReplacements(this))
+ {
+ return AtlsHttpError(500, ISE_SUBERR_BADSRF);
+ }
+
+ hcErr = FinishLoadStencil(pStencil, NULL);
+ if (!hcErr)
+ {
+#ifdef ATL_DEBUG_STENCILS
+ pStencil->FinishParseReplacements();
+#else
+ if (!pStencil->FinishParseReplacements())
+ {
+ return AtlsHttpError(500, ISE_SUBERR_BADSRF);
+ }
+#endif // ATL_DEBUG_STENCILS
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+ }
+ else
+ {
+ hcErr = HTTP_FAIL;
+ }
+
+ // if everything went OK, put the stencil in the stencil cache.
+ if (!hcErr)
+ {
+ hcErr = CacheStencil(szStencilName ? szStencilName : szResourceID, pStencil);
+ }
+
+ if (pStencil && hcErr) // something went wrong, free the stencil data
+ {
+ FreeCacheStencil(pStencil);
+ }
+ }
+ else
+ {
+ hcErr = FinishLoadStencil(pStencil);
+ }
+
+ return hcErr;
+ }
+
+ HTTP_CODE LoadStencilResource(HINSTANCE hInstResource, UINT nID, LPCSTR szResourceType = NULL) throw(...)
+ {
+ if (!szResourceType)
+ szResourceType = (LPCSTR) RT_HTML;
+ char szName[80];
+ int nResult = sprintf_s(szName, sizeof(szName), "%p/%u", hInstResource, nID);
+ if ((nResult < 0) || (nResult == sizeof(szName)))
+ {
+ return HTTP_FAIL;
+ }
+ return LoadStencilResource(hInstResource, MAKEINTRESOURCEA(nID), szResourceType, szName);
+ }
+
+ HTTP_CODE LoadStencil(LPCSTR szFileName, IHttpRequestLookup * pLookup = NULL) throw(...)
+ {
+ if (!szFileName)
+ {
+ return HTTP_FAIL;
+ }
+
+ HTTP_CODE hcErr = HTTP_FAIL;
+ // try to find the stencil in the cache
+ StencilType *pStencil = FindCacheStencil(szFileName);
+
+ if (!pStencil)
+ {
+ // not in cache. Create a new one
+ pStencil = GetNewCacheStencil();
+ if (!pStencil)
+ {
+ return AtlsHttpError(500, ISE_SUBERR_OUTOFMEM); // out of memory!
+ }
+
+ if (!pStencil->SetBaseDirFromFile(szFileName))
+ {
+ return HTTP_FAIL;
+ }
+
+ pStencil->SetErrorResource(GetResourceInstance());
+
+ // finish loading
+ hcErr = pStencil->LoadFromFile(szFileName);
+ if (!hcErr)
+ {
+ _ATLTRY
+ {
+ if (!pStencil->ParseReplacements(static_cast<ITagReplacer*>(this)))
+ {
+ return AtlsHttpError(500, ISE_SUBERR_BADSRF);
+ }
+
+ hcErr = FinishLoadStencil(pStencil, pLookup);
+ if (!hcErr)
+ {
+#ifdef ATL_DEBUG_STENCILS
+ pStencil->FinishParseReplacements();
+#else
+ if (!pStencil->FinishParseReplacements())
+ {
+ return AtlsHttpError(500, ISE_SUBERR_BADSRF);
+ }
+#endif // ATL_DEBUG_STENCILS
+ }
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+ }
+
+ // if everything is OK, cache the stencil
+ if (!hcErr)
+ {
+ hcErr = CacheStencil(szFileName, pStencil);
+ }
+
+ if (pStencil && hcErr) // something went wrong, free stencil data
+ FreeCacheStencil(pStencil);
+ }
+ else
+ {
+ hcErr = FinishLoadStencil(pStencil, pLookup);
+ }
+ return hcErr;
+ }
+
+ HTTP_CODE RenderStencil(IWriteStream* pStream, CStencilState* pState = NULL) throw(...)
+ {
+ if (!m_pLoadedStencil)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED);
+
+ WORD nCodePage = m_pLoadedStencil->GetCodePage();
+ if (nCodePage != CP_ACP)
+ m_nCodePage = nCodePage;
+
+ HTTP_CODE hcErr = HTTP_FAIL;
+
+ hcErr = m_pLoadedStencil->Render(static_cast<ITagReplacer*>(this),
+ pStream, pState);
+
+ if (!IsAsyncStatus(hcErr) && m_pLoadedStencil->GetCacheItem())
+ m_spStencilCache->ReleaseStencil(m_pLoadedStencil->GetCacheItem());
+
+ return hcErr;
+ }
+
+
+//Implementation
+
+ void FreeHandlers() throw(...)
+ {
+ POSITION pos = m_Handlers.GetStartPosition();
+ while (pos)
+ {
+ m_Handlers.GetValueAt(pos)->UninitializeHandler();
+ m_Handlers.GetNextValue(pos)->Release();
+ }
+ m_Handlers.RemoveAll();
+
+ int nLen = m_hInstHandlers.GetSize();
+ if (nLen != 0)
+ {
+ THandler *pT = static_cast<THandler *>(this);
+ CComPtr<IDllCache> spDllCache;
+ pT->m_spServiceProvider->QueryService(__uuidof(IDllCache), __uuidof(IDllCache),
+ (void **)&spDllCache);
+ for (int i=0; i<nLen; i++)
+ {
+ spDllCache->Free(m_hInstHandlers[i]);
+ }
+ m_hInstHandlers.RemoveAll();
+ }
+ }
+
+ StencilType* GetNewCacheStencil() throw(...)
+ {
+ StencilType *pStencil = NULL;
+ THandler *pT = static_cast<THandler *>(this);
+ IAtlMemMgr *pMemMgr;
+ if (FAILED(pT->m_spServiceProvider->QueryService(__uuidof(IAtlMemMgr), __uuidof(IAtlMemMgr), (void **)&pMemMgr)))
+ pMemMgr = NULL;
+
+ ATLTRY(pStencil = new StencilType(pMemMgr));
+ if (pStencil != NULL)
+ {
+ pStencil->Initialize(pT->m_spServiceProvider);
+ }
+ return pStencil;
+ }
+
+ HTTP_CODE CacheStencil(
+ LPCSTR szName,
+ StencilType* pStencilData) throw()
+ {
+ THandler *pT = static_cast<THandler *>(this);
+ HRESULT hr = E_FAIL;
+
+ HCACHEITEM hCacheItem = NULL;
+
+ hr = m_spStencilCache->CacheStencil(szName,
+ pStencilData,
+ sizeof(StencilType*),
+ &hCacheItem,
+ pT->m_hInstHandler,
+ static_cast<IMemoryCacheClient*>(pStencilData));
+
+ if (hr == S_OK && hCacheItem)
+ {
+ _ATLTRY
+ {
+ pStencilData->SetCacheItem(hCacheItem);
+ }
+ _ATLCATCHALL()
+ {
+ hr = E_FAIL;
+ }
+ }
+
+ return (hr == S_OK) ? HTTP_SUCCESS : HTTP_FAIL;
+ }
+
+ StencilType *FindCacheStencil(LPCSTR szName) throw()
+ {
+ if (!szName || !m_spStencilCache)
+ return NULL;
+
+ StencilType *pStencilData = NULL;
+
+ HCACHEITEM hStencil;
+
+ if (m_spStencilCache->LookupStencil(szName, &hStencil) != S_OK)
+ return NULL;
+
+ m_spStencilCache->GetStencil(hStencil, reinterpret_cast<void **>(&pStencilData));
+
+ return pStencilData;
+ }
+
+ void FreeCacheStencil(StencilType* pStencilData)
+ {
+ ATLASSERT( pStencilData != NULL );
+
+ if(!pStencilData)
+ {
+ return;
+ }
+
+ IMemoryCacheClient *pMemCacheClient = static_cast<IMemoryCacheClient *>(pStencilData);
+
+ if(!pMemCacheClient)
+ {
+ return;
+ }
+
+ _ATLTRY
+ {
+ pMemCacheClient->Free(pStencilData);
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+
+ HTTP_CODE GetHandlerOffset(LPCSTR szHandlerName, DWORD* pdwOffset)
+ {
+ if (!pdwOffset)
+ return HTTP_FAIL;
+
+ mapType::CPair *p = m_Handlers.Lookup(szHandlerName);
+ if (p)
+ {
+ DWORD dwIndex = 0;
+ POSITION pos = m_Handlers.GetStartPosition();
+ while (pos)
+ {
+ const mapType::CPair *p1 = m_Handlers.GetNext(pos);
+ if (p1 == p)
+ {
+ *pdwOffset = dwIndex;
+ return HTTP_SUCCESS;
+ }
+ dwIndex++;
+ }
+ ATLASSERT(FALSE);
+ }
+ *pdwOffset = 0;
+ return HTTP_FAIL;
+ }
+
+ HTTP_CODE GetReplacementObject(DWORD dwObjOffset, ITagReplacer **ppReplacer)
+ {
+ HRESULT hr = E_FAIL;
+
+ POSITION pos = m_Handlers.GetStartPosition();
+ for (DWORD dwIndex=0; dwIndex < dwObjOffset; dwIndex++)
+ m_Handlers.GetNext(pos);
+
+ ATLASSERT(pos != NULL);
+
+ IRequestHandler *pHandler = NULL;
+ pHandler = m_Handlers.GetValueAt(pos);
+
+ ATLENSURE(pHandler != NULL);
+
+ hr = pHandler->QueryInterface(__uuidof(ITagReplacer), (void**)ppReplacer);
+
+ if (hr != S_OK)
+ return HTTP_FAIL;
+
+ return HTTP_SUCCESS;
+ }
+
+ // This is where we would actually load any extra request
+ // handlers the HTML stencil might have parsed for us.
+ HTTP_CODE FinishLoadStencil(StencilType *pStencil, IHttpRequestLookup * pLookup = NULL) throw(...)
+ {
+ THandler *pT = static_cast<THandler *>(this);
+ ATLASSERT(pStencil);
+ if (!pStencil)
+ return AtlsHttpError(500, ISE_SUBERR_UNEXPECTED); // unexpected condition
+ m_pLoadedStencil = pStencil;
+ //load extra handlers if there are any
+ StencilType::mapType *pExtraHandlersMap =
+ pStencil->GetExtraHandlers();
+
+ if (pExtraHandlersMap)
+ {
+ POSITION pos = pExtraHandlersMap->GetStartPosition();
+ CStringA name;
+ CStringPair path;
+ IRequestHandler *pHandler;
+ HINSTANCE hInstHandler;
+ while(pos)
+ {
+ pExtraHandlersMap->GetNextAssoc(pos, name, path);
+ pHandler = NULL;
+ hInstHandler = NULL;
+ HTTP_CODE hcErr = pT->m_spExtension->LoadRequestHandler(path.strDllPath, path.strHandlerName,
+ pT->m_spServerContext,
+ &hInstHandler,
+ &pHandler);
+ if (!hcErr)
+ {
+ _ATLTRY
+ {
+ //map the name to the pointer to request handler
+ m_Handlers.SetAt(name, pHandler);
+ //store HINSTANCE of handler
+ m_hInstHandlers.Add(hInstHandler);
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+
+ if (pLookup)
+ {
+ hcErr = pHandler->InitializeChild(&m_RequestInfo, pT->m_spServiceProvider, pLookup);
+ if (hcErr != HTTP_SUCCESS)
+ return hcErr;
+ }
+
+ }
+ else
+ return hcErr;
+ }
+ }
+ return HTTP_SUCCESS;
+ }
+}; // class CHtmlTagReplacer
+
+
+// CRequestHandlerT
+// This is the base class for all user request handlers. This class implements
+// the IReplacementHandler interface whose methods will be called to render HTML
+// into a stream. The stream will be returned as the HTTP response upon completion
+// of the HTTP request.
+template < class THandler,
+ class ThreadModel=CComSingleThreadModel,
+ class TagReplacerType=CHtmlTagReplacer<THandler>
+ >
+class CRequestHandlerT :
+ public TagReplacerType,
+ public CComObjectRootEx<ThreadModel>,
+ public IRequestHandlerImpl<THandler>
+{
+protected:
+ CStencilState m_state;
+ CComObjectStackEx<CIDServerContext> m_SafeSrvCtx;
+ typedef CRequestHandlerT<THandler, ThreadModel, TagReplacerType> _requestHandler;
+
+public:
+ BEGIN_COM_MAP(_requestHandler)
+ COM_INTERFACE_ENTRY(IRequestHandler)
+ COM_INTERFACE_ENTRY(ITagReplacer)
+ END_COM_MAP()
+
+ // public CRequestHandlerT members
+ CHttpResponse m_HttpResponse;
+ CHttpRequest m_HttpRequest;
+ ATLSRV_REQUESTTYPE m_dwRequestType;
+ AtlServerRequest* m_pRequestInfo;
+
+ CRequestHandlerT() throw()
+ {
+ m_hInstHandler = NULL;
+ m_dwAsyncFlags = 0;
+ m_pRequestInfo = NULL;
+ }
+
+ ~CRequestHandlerT() throw()
+ {
+ _ATLTRY
+ {
+ FreeHandlers(); // free handlers held by CTagReplacer
+ }
+ _ATLCATCHALL()
+ {
+ }
+ }
+
+ void ClearResponse() throw()
+ {
+ m_HttpResponse.ClearResponse();
+ }
+ // Where user initialization should take place
+ HTTP_CODE ValidateAndExchange()
+ {
+ return HTTP_SUCCESS; // continue processing request
+ }
+
+ // Where user Uninitialization should take place
+ HTTP_CODE Uninitialize(HTTP_CODE hcError)
+ {
+ return hcError;
+ }
+
+ HTTP_CODE InitializeInternal(AtlServerRequest *pRequestInfo, IServiceProvider *pProvider)
+ {
+ // Initialize our internal references to required services
+ m_pRequestInfo = pRequestInfo;
+ m_state.pParentInfo = pRequestInfo;
+ m_hInstHandler = pRequestInfo->hInstDll;
+ m_spServerContext = pRequestInfo->pServerContext;
+ m_spServiceProvider = pProvider;
+ return HTTP_SUCCESS;
+ }
+
+ HTTP_CODE InitializeHandler(
+ AtlServerRequest *pRequestInfo,
+ IServiceProvider *pProvider)
+ {
+ HTTP_CODE hcErr = HTTP_FAIL;
+ ATLASSERT(pRequestInfo);
+ ATLASSERT(pProvider);
+
+ THandler* pT = static_cast<THandler*>(this);
+ hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
+ if (!hcErr)
+ {
+ m_HttpResponse.Initialize(m_spServerContext);
+ hcErr = pT->CheckValidRequest();
+ if (!hcErr)
+ {
+ hcErr = HTTP_FAIL;
+ if (m_HttpRequest.Initialize(m_spServerContext,
+ pT->MaxFormSize(),
+ pT->FormFlags()))
+ {
+ if (m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
+ {
+ hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
+ if (!hcErr)
+ {
+ hcErr = pT->ValidateAndExchange();
+ }
+ }
+ }
+ }
+ }
+ return hcErr;
+ }
+
+ HTTP_CODE InitializeChild(
+ AtlServerRequest *pRequestInfo,
+ IServiceProvider *pProvider,
+ IHttpRequestLookup *pRequestLookup)
+ {
+ ATLASSERT(pRequestInfo);
+ ATLASSERT(pProvider);
+
+ THandler *pT = static_cast<THandler*>(this);
+ HTTP_CODE hcErr = pT->InitializeInternal(pRequestInfo, pProvider);
+ if (hcErr)
+ return hcErr;
+
+ if (pRequestLookup)
+ {
+ // initialize with the pRequestLookup
+ if(!m_HttpResponse.Initialize(pRequestLookup))
+ {
+ return HTTP_FAIL;
+ }
+
+ // Initialize with the IHttpServerContext if it exists
+ // the only time this is different than the previous call to
+ // initialize is if the user passes a different IHttpServerContext
+ // in pRequestInfo than the one extracted from pRequestLookup.
+ if (m_spServerContext)
+ {
+ if(!m_HttpResponse.Initialize(m_spServerContext))
+ {
+ return HTTP_FAIL;
+ }
+ }
+ hcErr = pT->CheckValidRequest();
+ if (hcErr)
+ {
+ return hcErr;
+ }
+
+ // initialize with the pRequestLookup to chain query parameters
+ m_HttpRequest.Initialize(pRequestLookup);
+
+ // initialize with the m_spServerContext to get additional query params
+ // if they exist.
+ if (m_spServerContext)
+ {
+ m_HttpRequest.Initialize(m_spServerContext);
+ }
+ }
+
+ m_HttpResponse.SetBufferOutput(false); // child cannot buffer
+
+ // initialize the safe server context
+ if (!m_SafeSrvCtx.Initialize(&m_HttpResponse, &m_HttpRequest))
+ {
+ return HTTP_FAIL;
+ }
+
+ hcErr = TagReplacerType::Initialize(pRequestInfo, &m_SafeSrvCtx);
+ if (hcErr)
+ {
+ return hcErr;
+ }
+
+ return pT->ValidateAndExchange();
+ }
+
+ // HandleRequest is called to perform default processing of HTTP requests. Users
+ // can override this function in their derived classes if they need to perform
+ // specific initialization prior to processing this request or want to change the
+ // way the request is processed.
+ HTTP_CODE HandleRequest(
+ AtlServerRequest *pRequestInfo,
+ IServiceProvider* /*pServiceProvider*/)
+ {
+ ATLENSURE(pRequestInfo);
+
+ THandler *pT = static_cast<THandler *>(this);
+ HTTP_CODE hcErr = HTTP_SUCCESS;
+
+ if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN)
+ {
+ m_dwRequestType = pRequestInfo->dwRequestType;
+
+ if (pRequestInfo->dwRequestType==ATLSRV_REQUEST_STENCIL)
+ {
+ LPCSTR szFileName = pRequestInfo->pServerContext->GetScriptPathTranslated();
+ hcErr = HTTP_FAIL;
+ if (szFileName)
+ hcErr = pT->LoadStencil(szFileName, static_cast<IHttpRequestLookup *>(&m_HttpRequest));
+ }
+ }
+ else if (pRequestInfo->dwRequestState == ATLSRV_STATE_CONTINUE)
+ m_HttpResponse.ClearContent();
+
+#ifdef ATL_DEBUG_STENCILS
+ if (m_pLoadedStencil && !m_pLoadedStencil->ParseSuccessful())
+ {
+ // An error or series of errors occurred in parsing the stencil
+ _ATLTRY
+ {
+ m_pLoadedStencil->RenderErrors(static_cast<IWriteStream*>(&m_HttpResponse));
+ }
+ _ATLCATCHALL()
+ {
+ return HTTP_FAIL;
+ }
+ }
+#endif
+
+ if (hcErr == HTTP_SUCCESS && m_pLoadedStencil)
+ {
+ // if anything other than HTTP_SUCCESS is returned during
+ // the rendering of replacement tags, we return that value
+ // here.
+ hcErr = pT->RenderStencil(static_cast<IWriteStream*>(&m_HttpResponse), &m_state);
+
+ if (hcErr == HTTP_SUCCESS && !m_HttpResponse.Flush(TRUE))
+ hcErr = HTTP_FAIL;
+ }
+
+ if (IsAsyncFlushStatus(hcErr))
+ {
+ pRequestInfo->pszBuffer = LPCSTR(m_HttpResponse.m_strContent);
+ pRequestInfo->dwBufferLen = m_HttpResponse.m_strContent.GetLength();
+ }
+
+ if (pRequestInfo->dwRequestState == ATLSRV_STATE_BEGIN || IsAsyncDoneStatus(hcErr))
+ return pT->Uninitialize(hcErr);
+
+ else if (!IsAsyncStatus(hcErr))
+ m_HttpResponse.ClearContent();
+
+ return hcErr;
+ }
+
+ HTTP_CODE ServerTransferRequest(LPCSTR szRequest, bool bContinueAfterTransfer=false,
+ WORD nCodePage = 0, CStencilState *pState = NULL) throw(...)
+ {
+ return m_spExtension->TransferRequest(
+ m_pRequestInfo,
+ m_spServiceProvider,
+ static_cast<IWriteStream*>(&m_HttpResponse),
+ static_cast<IHttpRequestLookup*>(&m_HttpRequest),
+ szRequest,
+ nCodePage == 0 ? m_nCodePage : nCodePage,
+ bContinueAfterTransfer,
+ pState);
+ }
+
+ inline DWORD MaxFormSize()
+ {
+ return DEFAULT_MAX_FORM_SIZE;
+ }
+
+ inline DWORD FormFlags()
+ {
+ return ATL_FORM_FLAG_IGNORE_FILES;
+ }
+
+ // Override this function to check if the request
+ // is valid. This function is called after m_HttpResponse
+ // has been initialized, so you can use it if you need
+ // to return an error to the client. This is also a
+ // good place to initialize any internal class data needed
+ // to handle the request. CRequestHandlerT::CheckValidRequest
+ // is called after CRequestHandlerT::InitializeInternal is
+ // called, so your override of this method will have access to
+ // m_pRequestInfo (this request's AtlServerRequest structure),
+ // m_hInstHandler (the HINSTANCE of this handler dll),
+ // m_spServerContext (the IHttpServerContext interface for this request),
+ // m_spServiceProvider (the IServiceProvider interface for this request).
+ // You should call CRequestHandlerT::CheckValidRequest in your override
+ // if you override this function.
+ //
+ // Note that m_HttpRequest has not been initialized, so
+ // you cannot use it. This function is intended to
+ // do simple checking throught IHttpServerContext to avoid
+ // expensive initialization of m_HttpRequest.
+ HTTP_CODE CheckValidRequest()
+ {
+ LPCSTR szMethod = NULL;
+ ATLASSUME(m_pRequestInfo);
+ szMethod = m_pRequestInfo->pServerContext->GetRequestMethod();
+ if (strcmp(szMethod, "GET") && strcmp(szMethod, "POST") && strcmp(szMethod, "HEAD"))
+ return HTTP_NOT_IMPLEMENTED;
+
+ return HTTP_SUCCESS;
+ }
+
+ HRESULT GetContext(REFIID riid, void** ppv)
+ {
+ if (!ppv)
+ return E_POINTER;
+ if (InlineIsEqualGUID(riid, __uuidof(IHttpServerContext)))
+ {
+ return m_spServerContext.CopyTo((IHttpServerContext **)ppv);
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IHttpRequestLookup)))
+ {
+ *ppv = static_cast<IHttpRequestLookup*>(&m_HttpRequest);
+ m_HttpRequest.AddRef();
+ return S_OK;
+ }
+ if (InlineIsEqualGUID(riid, __uuidof(IServiceProvider)))
+ {
+ *ppv = m_spServiceProvider;
+ m_spServiceProvider.p->AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+ }
+
+ HINSTANCE GetResourceInstance()
+ {
+ if (m_pRequestInfo != NULL)
+ {
+ return m_pRequestInfo->hInstDll;
+ }
+
+ return NULL;
+ }
+
+ template <typename Interface>
+ HRESULT GetContext(Interface** ppInterface) throw(...)
+ {
+ return GetContext(__uuidof(Interface), reinterpret_cast<void**>(ppInterface));
+ }
+}; // class CRequestHandlerT
+
+} // namespace ATL
+#pragma pack(pop)
+
+#pragma warning( pop )
+
+#endif // __ATLSTENCIL_H__
diff --git a/include/atl/l.chs/atlsrv.rc b/include/atl/l.chs/atlsrv.rc
new file mode 100644
index 000000000..281ab2371
--- /dev/null
+++ b/include/atl/l.chs/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title></title></head><body></body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>Ҫ֤</title></head><body>Ҫ֤</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>ֹ</title></head><body>ֹ</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>δҵ</title></head><body>δҵ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title></title></head><body></body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>δʵ</title></head><body>δʵ</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title></title></head><body></body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>񲻿</title></head><body>񲻿</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>Server Error</title></head><body><H1></H1><P>޷ SRF ļ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title></title></head><body><H1></H1><P> SRF Ѽأ޷ȷ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title></title></head><body><H1></H1><P>޷ Windows ϵͳ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title></title></head><body><H1></H1><P>ļʧܡ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title></title></head><body><H1></H1><P>ָļ޷򿪡</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title></title></head><body><H1></H1><P>LoadLibrary ʧܡ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title></title></head><body><H1></H1><P>ӿʧܡ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title></title></head><body><H1></H1><P>ڴ治㡣</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title></title></head><body><H1></H1><P></body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title></title></head><body><H1></H1><P>ͼģʱ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title></title></head><body><H1></H1><P>δܼģ塣ģļ𻵻ڸ Web ϡ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title></title></head><body><H1></H1><P>ָ .dll δҵģĴij</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title></title></head><body><H1></H1><P>ģһ޷ģ崦ȷĴǣ߸ģûаǡ鿴ģ壬˽ȷģ﷨</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title></title></head><body><H1></H1><P>ģûадǡ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title></title></head><body><H1></H1><P>ģзһ滻ǣ滻ǵ滻ƹ滻Ƶ󳤶ȱСڻ atlstencil.h жij ATL_MAX_METHOD_NAME</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title></title></head><body><H1></H1><P>ģзһʹ id.tagname ﷨滻ǡƵ󳤶ȱСڻ atlstencil.h жij ATL_MAX_METHOD_NAME</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title></title></head><body><H1></H1><P>ģͻijʧܡ</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title></title></head><body><H1></H1><P>ڷδ֪ڴ ISAPI չ޷ȷء</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "Ѵʧ"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "̳߳ʼʧ"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "ٽʼʧ"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "̳߳سʼʧ"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "DLL ʼʧ"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "Page ʼʧ"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "ģ建ʼʧ"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "Ự״̬ʼʧ"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Blob ʼʧ"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "ļʼʧ"
+
+ IDS_PERFMON_CACHE "ATL Server:"
+ IDS_PERFMON_CACHE_HELP "ATL Server Ϣ"
+ IDS_PERFMON_HITCOUNT ""
+ IDS_PERFMON_HITCOUNT_HELP "лĿ"
+ IDS_PERFMON_MISSCOUNT "δ"
+ IDS_PERFMON_MISSCOUNT_HELP "δлĿ"
+ IDS_PERFMON_CURRENTALLOCATIONS "浱ǰ"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "ĵǰڴС"
+ IDS_PERFMON_MAXALLOCATIONS ""
+ IDS_PERFMON_MAXALLOCATIONS_HELP "ڴС"
+ IDS_PERFMON_CURRENTENTRIES "ǰ"
+ IDS_PERFMON_CURRENTENTRIES_HELP "ǰĿ"
+ IDS_PERFMON_MAXENTRIES ""
+ IDS_PERFMON_MAXENTRIES_HELP "Ŀ"
+ IDS_PERFMON_HITCOUNTRATE ""
+ IDS_PERFMON_HITCOUNTRATE_HELP "ÿлĴ"
+ IDS_PERFMON_REQUEST "ATL Server:"
+ IDS_PERFMON_REQUEST_HELP "ڽ÷ͳϢ"
+ IDS_PERFMON_REQUEST_TOTAL ""
+ IDS_PERFMON_REQUEST_TOTAL_HELP ""
+ IDS_PERFMON_REQUEST_FAILED "ʧ"
+ IDS_PERFMON_REQUEST_FAILED_HELP "ʧܵĿ"
+ IDS_PERFMON_REQUEST_RATE "/"
+ IDS_PERFMON_REQUEST_RATE_HELP "ÿĿ"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "ƽӦʱ"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "ƽʱ"
+ IDS_PERFMON_REQUEST_CURR_WAITING "ǰе"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "ǰȴĿ"
+ IDS_PERFMON_REQUEST_MAX_WAITING "Ŀ"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "ȴĿ"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "߳"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "ڴĻ߳"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000>ͼģļʱ´</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%></td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>к</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>ı</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>ģΪ\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} ȱ {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} ȱ {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} ȱ {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} ȱ {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} ȱ {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} ȱ {{if}} or {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "ЧĴ"
+ IDS_STENCIL_NULLPARAM "ParseReplacements Ϊ Null "
+ IDS_STENCIL_INVALIDSTRING "ݸ ParseReplacements ַΪջΪ"
+ IDS_STENCIL_EMBEDDED_NULL "ǶģеĿַ"
+ IDS_STENCIL_UNMATCHED_TAG_START "ƥ {{"
+ IDS_STENCIL_MISMATCHED_TAG_START "ƥ {{"
+ IDS_STENCIL_BAD_PARAMETER "IJ"
+ IDS_STENCIL_METHODNAME_TOO_LONG "ƹ"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "ƹ"
+ IDS_STENCIL_INVALID_SUBHANDLER "ЧӴ"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "޷滻 : '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "޷򿪱ļ"
+ IDS_STENCIL_INCLUDE_INVALID "ļǴļ"
+
+ IDS_STENCIL_MLANG_COCREATE "޷ CMultiLanguage"
+ IDS_STENCIL_MLANG_LCID "ȡ lcid "
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo ʧ"
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo ʧ"
+
+ IDS_STENCIL_OUTOFMEMORY "ڴ治"
+ IDS_STENCIL_UNEXPECTED "쳣"
+END
+
+#endif
diff --git a/include/atl/l.cht/atlsrv.rc b/include/atl/l.cht/atlsrv.rc
new file mode 100644
index 000000000..2d9f7340d
--- /dev/null
+++ b/include/atl/l.cht/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>TnD</title></head><body>TnD</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>ݭnv</title></head><body>ݭnv</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Tϥ</title></head><body>Tϥ</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>䤣</title></head><body>䤣</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>A~</title></head><body>A~</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>@</title></head><body>@</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>ThD</title></head><body>ThD</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>LkoA</title></head><body>LkoA</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>A~</title></head><body><H1>A~</H1><P>LkJ SRF ɡC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>A~</title></head><body><H1>A~</H1><P>wgJnD SRF ɡALk\BzC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>A~</title></head><body><H1>A~</H1><P>Lkإ Windows tΪC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>A~</title></head><body><H1>A~</H1><P>ɮŪ@~wѡC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>A~</title></head><body><H1>A~</H1><P>Lk}ҫwɮסC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>A~</title></head><body><H1>A~</H1><P>LoadLibrary wѡC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>A~</title></head><body><H1>A~</H1><P>Lk^nDBz`C</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>A~</title></head><body><H1>A~</H1><P>AO餣C</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>A~</title></head><body><H1>A~</H1><P>Aoͥw~C</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>A~</title></head><body><H1>A~</H1><P>խRnDҪOɡAAoͥw~C</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>A~</title></head><body><H1>A~</H1><P>ALkJnDҪOCҪOɥiwlΤb Web AWC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>A~</title></head><body><H1>A~</H1><P>bw handler .dll A䤣bnDҪOBz`аORWYӳBz`C</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>A~</title></head><body><H1>A~</H1><P>ҪO]tҪOBzLkARBz`аOAΥ]tBz`аOCˬdnDҪOO_ϥξAXҪOykC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>A~</title></head><body><H1>A~</H1><P>nDҪO]tBz`аOC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>A~</title></head><body><H1>A~</H1><P>bnDҪOo{NаONW٤ӪCNWٳ̪ץpεb atlstencil.h ҩwq ATL_MAX_METHOD_NAME `ơC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>A~</title></head><body><H1>A~</H1><P>bnDҪOϥ id.tagname ykNаOBz`W٤ӪCBz`Wٳ̪ץpεb atlstencil.h ҩwq ATL_MAX_METHOD_NAME `ơC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>A~</title></head><body><H1>A~</H1><P>ռΤݳynDѡC</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>A~</title></head><body><H1>A~</H1><P>]oͥ~ALkTJΨӪAȦnD ISAPI XR{C</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "nDnإߥ"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "Iu@lƥ"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "Ϭqlƥ"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "Ϫlƥ"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "DLL ֨lƥ"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "֨lƥ"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "ҪO֨lƥ"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "u@qAAȪlƥ"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Blob ֨lƥ"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "ɮק֨lƥ"
+
+ IDS_PERFMON_CACHE "ATL Server:֨"
+ IDS_PERFMON_CACHE_HELP " ATL Server ֨T"
+ IDS_PERFMON_HITCOUNT "֨sΦ"
+ IDS_PERFMON_HITCOUNT_HELP "֨sΪ"
+ IDS_PERFMON_MISSCOUNT "֨|"
+ IDS_PERFMON_MISSCOUNT_HELP "֨|"
+ IDS_PERFMON_CURRENTALLOCATIONS "֨ثetm"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "֨ثeOtm"
+ IDS_PERFMON_MAXALLOCATIONS "֨tmW"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "̤֨jOtm"
+ IDS_PERFMON_CURRENTENTRIES "֨ثe"
+ IDS_PERFMON_CURRENTENTRIES_HELP "ثe֨ؼ"
+ IDS_PERFMON_MAXENTRIES "֨ؤW"
+ IDS_PERFMON_MAXENTRIES_HELP "֨ت̤j"
+ IDS_PERFMON_HITCOUNTRATE "֨sβv"
+ IDS_PERFMON_HITCOUNTRATE_HELP "C֨sΪƥ"
+ IDS_PERFMON_REQUEST "ATL Server:nD"
+ IDS_PERFMON_REQUEST_HELP "iJAnDέp"
+ IDS_PERFMON_REQUEST_TOTAL "AnD`"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "nD`"
+ IDS_PERFMON_REQUEST_FAILED "AѭnD"
+ IDS_PERFMON_REQUEST_FAILED_HELP "ѭnDƥ"
+ IDS_PERFMON_REQUEST_RATE "AnD /"
+ IDS_PERFMON_REQUEST_RATE_HELP "CnDƥ"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "^ɶ"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "BznDҪOɶ"
+ IDS_PERFMON_REQUEST_CURR_WAITING "ثewƤJCnD"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "ثeԳBznDƥ"
+ IDS_PERFMON_REQUEST_MAX_WAITING "CnDW"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "ԳBznD̤j"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "@Τ"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "bBznDƥ"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000> խRҪOɮɡAoͤUC~:</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>~</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>渹</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>~r</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>᪺ҪOX:\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF " {{if}} S {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE " {{else}} S {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE " {{while}} S {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE " {{endwhile}} S {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE " {{else}} S {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF " {{endif}} S {{if}} {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "LĪBz`аO"
+ IDS_STENCIL_NULLPARAM "ParseReplacements NULL Ѽ"
+ IDS_STENCIL_INVALIDSTRING "ǻ ParseReplacements ŪέtȦr"
+ IDS_STENCIL_EMBEDDED_NULL "ҪOOJ Null r"
+ IDS_STENCIL_UNMATCHED_TAG_START "۲Ū {{"
+ IDS_STENCIL_MISMATCHED_TAG_START "۲Ū {{"
+ IDS_STENCIL_BAD_PARAMETER "TѼ"
+ IDS_STENCIL_METHODNAME_TOO_LONG "kW٤Ӫ"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "Bz`W٤Ӫ"
+ IDS_STENCIL_INVALID_SUBHANDLER "LĪlBz`аO"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "LkѪRN : '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Lk}ҥ]tɮ"
+ IDS_STENCIL_INCLUDE_INVALID "]tɮפOϺɮ"
+
+ IDS_STENCIL_MLANG_COCREATE "Lkإ CMultiLanguage"
+ IDS_STENCIL_MLANG_LCID "o lcid oͿ~"
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo "
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo "
+
+ IDS_STENCIL_OUTOFMEMORY "O餣"
+ IDS_STENCIL_UNEXPECTED "w~"
+END
+
+#endif
diff --git a/include/atl/l.deu/atlsrv.rc b/include/atl/l.deu/atlsrv.rc
new file mode 100644
index 000000000..2ffca35c6
--- /dev/null
+++ b/include/atl/l.deu/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>Ungltige Anforderung</title></head><body>Ungltige Anforderung</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>Authorisierung erforderlich</title></head><body>Authorisierung erforderlich</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Unzulssig</title></head><body>Unzulssig</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>Nicht gefunden</title></head><body>Nicht gefunden</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>Serverfehler</title></head><body>Serverfehler</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>Nicht implementiert</title></head><body>Nicht implementiert</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>Ungltiger Gateway</title></head><body>Ungltiger Gateway</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>Dienst nicht verfgbar</title></head><body>Dienst nicht verfgbar</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>SRF-Datei konnte nicht geladen werden.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Die angeforderte SRF-Datei wurde geladen, aber konnte nicht verarbeitet werden.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Ein Windows-Systemobjekt konnte nicht erstellt werden.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Ein Dateilesevorgang ist fehlgeschlagen.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Die angegebene Datei konnte nicht geffnet werden.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>LoadLibrary fehlgeschlagen.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Die Anforderungshandlerschnittstelle konnte nicht abgerufen werden.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Auf dem Server ist nicht gengend Arbeitsspeicher verfgbar.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Auf dem Server ist ein unerwarteter Fehler aufgetreten.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Beim Bearbeiten des angeforderten Stencils ist ein unerwarteter Fehler auf dem Server aufgetreten.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Der Server konnte den angeforderten Stencil nicht laden. Die Stencildatei ist mglicherweise beschdigt oder nicht auf dem Webserver vorhanden.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Ein in einem Handlertag benannter Handler fr den angeforderten Stencil konnte nicht im angegebenen Handler gefunden werden .dll.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Der Stencil enthlt ein Handlertag, das vom Stencilprozessor nicht richtig bearbeitet werden konnte und kein Handlertag enthlt. berprfen Sie die Stencilsyntax fr den angeforderten Stencil.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Der angeforderte Stencil enthlt kein Handlertag.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Im angeforderten Stencil wurde ein Ersetzungstag mit einem zu langen Namen gefunden. Die maximale Lnge des Ersetzungsnamen muss kleiner oder gleich der ATL_MAX_METHOD_NAME-Konstante sein, die in atlstencil.h definiert ist.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Im angeforderten Stencil wurde ein Ersetzungstag mit einem zu langen Handlernamen gefunden, der die id.tagname-Syntax verwendet. Die maximale Lnge des Handlernamens muss kleiner oder gleich der ATL_MAX_METHOD_NAME-Konstante sein, die in atlstencil.h definiert ist.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Die Anforderung ist fehlgeschlagen, da versucht wurde, den Client zu imitieren.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>Serverfehler</title></head><body><H1>Serverfehler</H1><P>Die ISAPI-Erweiterung fr die Anforderung konnte nicht geladen werden, da ein unbekannter Fehler aufgetreten ist.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "Anforderungsheap konnte nicht erstellt werden"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "Workerthread konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "Kritischer Abschnitt konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "Threadpool konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "DLL-Cache konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "Seitencache konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "Stencilcache konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "Sitzungsstatusdienst konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Blobcache konnte nicht initialisiert werden"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "Dateicache konnte nicht initialisiert werden"
+
+ IDS_PERFMON_CACHE "ATL-Server:Cache"
+ IDS_PERFMON_CACHE_HELP "Informationen ber den ATL-Servercache"
+ IDS_PERFMON_HITCOUNT "Cachetrefferanzahl"
+ IDS_PERFMON_HITCOUNT_HELP "Anzahl der Cachetreffer"
+ IDS_PERFMON_MISSCOUNT "Fehlgeschlagene Cachezugriffe"
+ IDS_PERFMON_MISSCOUNT_HELP "Anzahl der fehlgeschlagenen Cachezugriffe"
+ IDS_PERFMON_CURRENTALLOCATIONS "Aktuelle Cachezuordnungen"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "Aktuelle Speicherzuordnung fr den Cache"
+ IDS_PERFMON_MAXALLOCATIONS "Max. Cachezuordnungen"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "Maximale Speicherzuordnung fr den Cache"
+ IDS_PERFMON_CURRENTENTRIES "Aktuelle Cacheeintrge"
+ IDS_PERFMON_CURRENTENTRIES_HELP "Aktuelle Anzahl der Cacheeintrge"
+ IDS_PERFMON_MAXENTRIES "Max. Cacheeintrge"
+ IDS_PERFMON_MAXENTRIES_HELP "Maximale Anzahl der Cacheeintrge"
+ IDS_PERFMON_HITCOUNTRATE "Cachetrefferanzahl"
+ IDS_PERFMON_HITCOUNTRATE_HELP "Anzahl der Treffer pro Sekunde"
+ IDS_PERFMON_REQUEST "ATL-Server:Request"
+ IDS_PERFMON_REQUEST_HELP "Statistik ber die eingehenden Serveranforderungen"
+ IDS_PERFMON_REQUEST_TOTAL "Serveranforderungen insgesamt"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "Gesamtanzahl der Anforderungen"
+ IDS_PERFMON_REQUEST_FAILED "Fehlgeschlagene Serveranforderungen"
+ IDS_PERFMON_REQUEST_FAILED_HELP "Anzahl der fehlgeschlagenen Anforderungen"
+ IDS_PERFMON_REQUEST_RATE "Serveranforderungen /Sek."
+ IDS_PERFMON_REQUEST_RATE_HELP "Anzahl der Anforderungen pro Sekunde"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "Durchschnittliche Antwortzeit"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "Durchschnittliche Bearbeitungszeit"
+ IDS_PERFMON_REQUEST_CURR_WAITING "Aktuelle Anforderungen in der Warteschlange"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "Die aktuvelle Anzahl der Anforderungen, die auf die Bearbeitung warten"
+ IDS_PERFMON_REQUEST_MAX_WAITING "Maximale Anforderungen in der Warteschlange"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "Die maximale Anzahl der Anforderungen, die auf die Bearbeitung warten"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "AKtive Threads"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "Die Anzahl der Threads, die Anforderungen bearbeiten"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000>Beim Bearbeiten einer Stencildatei sind folgende Fehler aufgetreten:</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>Fehlertyp</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>Zeilennummer</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>Fehlertext</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>Stencilausgabe:\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} ohne {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} ohne {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} ohne {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} ohne {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} ohne {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} ohne {{if}} oder {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "Ungltiges Handlertag"
+ IDS_STENCIL_NULLPARAM "NULL-Parameter fr ParseReplacements"
+ IDS_STENCIL_INVALIDSTRING "An ParseReplacements wurde eine leere oder negative Zeichenfolge bergeben"
+ IDS_STENCIL_EMBEDDED_NULL "Eingebettetes Nullzeichen in Stencil"
+ IDS_STENCIL_UNMATCHED_TAG_START "Unterschiedlich {{"
+ IDS_STENCIL_MISMATCHED_TAG_START "Nicht bereinstimmend {{"
+ IDS_STENCIL_BAD_PARAMETER "Ungltiger Parameter"
+ IDS_STENCIL_METHODNAME_TOO_LONG "Methodenname zu lang"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "Handlername zu lang"
+ IDS_STENCIL_INVALID_SUBHANDLER "Ungltiges Unterhandlertag"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "Nicht aufgelste Ersetzung : '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Die einbezogene Datei konnte nicht geffnet werden"
+ IDS_STENCIL_INCLUDE_INVALID "Die einbezogene Datei ist keine Datentrgerdatei"
+
+ IDS_STENCIL_MLANG_COCREATE "CMultiLanguage konnte nicht erstellt werden"
+ IDS_STENCIL_MLANG_LCID "Beim Abfragen der lcid ist ein Fehler aufgetreten"
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo fehlgeschlagen"
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo fehlgeschlagen"
+
+ IDS_STENCIL_OUTOFMEMORY "Nicht gengend Speicher verfgbar"
+ IDS_STENCIL_UNEXPECTED "Unerwarteter Fehler"
+END
+
+#endif
diff --git a/include/atl/l.esp/atlsrv.rc b/include/atl/l.esp/atlsrv.rc
new file mode 100644
index 000000000..15bf88daa
--- /dev/null
+++ b/include/atl/l.esp/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>Solicitud incorrecta</title></head><body>Solicitud incorrecta</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>Autorizacin requerida</title></head><body>Se necesita autorizacin</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Prohibido</title></head><body>Prohibido</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>No se encontr</title></head><body>No se encontr</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>Error del servidor</title></head><body>Error del servidor</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>No implementado</title></head><body>No implementado</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>Puerta de enlace no vlida</title></head><body>Puerta de enlace no vlida</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>Servicio no disponible</title></head><body>Servicio no disponible</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>No se puede cargar el archivo SRF.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>El archivo SRF solicitado se carg pero no se pudo procesar correctamente.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>No se puede crear un objeto del sistema Windows.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Error en la operacin de lectura de archivo.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>Error en el servidor</title></head><body><H1>Error en el servidor</H1><P>No se puede abrir el archivo especificado.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Error de LoadLibrary.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Error al recuperar la interfaz del controlador de solicitudes.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>El servidor no tiene suficiente memoria.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>El servidor detect un error inesperado.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>El servidor detect un error inesperado al intentar analizar el clich solicitado.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Error del servidor al cargar el clich solicitado. Es posible que el archivo de clich est daado o no se encuentre en este servidor Web.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>No se encontr uno de los controladores nombrado en una etiqueta de controlador para el clich solicitado en el archivo DLL del controlador especificado.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Este clich contiene una etiqueta de controlador que el procesador de clich no puede analizar correctamente o bien no contiene ninguna etiqueta de controlador. Compruebe que la sintaxis del clich solicitado sea correcta.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>El clich solicitado no contiene una etiqueta de controlador.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Se encontr una etiqueta de reemplazo en el clich solicitado con un nombre de reemplazo demasiado largo. La longitud mxima del nombre de reemplazo debe ser inferior o igual a la constante ATL_MAX_METHOD_NAME definida en atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Se encontr una etiqueta de reemplazo que utiliza la sintaxis id.tagname en el clich seleccionado con un nombre de controlador demasiado largo. La longitud mxima del nombre del controlador debe ser inferior o igual a la constante ATL_MAX_METHOD_NAME definida en atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Error al intentar identificar al cliente que realiza la solicitud.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>Error del servidor</title></head><body><H1>Error del servidor</H1><P>Error desconocido de la extensin ISAPI utilizada para dar servicio a esta solicitud al realizar la carga.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "Error al crear el montn de solicitudes"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "Error al inicializar el subproceso de trabajo"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "Error al inicializar la seccin crtica"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "Error al inicializar el grupo de subprocesos"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "Error al inicializar la cach de archivos DLL"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "Error al inicializar la cach de pginas"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "Error al inicializar la cach de clichs"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "Error al inicializar el servicio de estado de sesin"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Error al inicializar la cach de objetos binarios"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "Error al inicializar la cach de archivos"
+
+ IDS_PERFMON_CACHE "Servidor ATL:cach"
+ IDS_PERFMON_CACHE_HELP "Informacin sobre la cach del servidor ATL"
+ IDS_PERFMON_HITCOUNT "Recuento de visitas de cach"
+ IDS_PERFMON_HITCOUNT_HELP "Nmero de visitas de cach"
+ IDS_PERFMON_MISSCOUNT "Recuento de errores de cach"
+ IDS_PERFMON_MISSCOUNT_HELP "Nmero de errores de cach"
+ IDS_PERFMON_CURRENTALLOCATIONS "Asignaciones actuales de cach"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "Memoria actual asignada a cach"
+ IDS_PERFMON_MAXALLOCATIONS "Asignaciones mximas de cach"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "Memoria mxima asignada a cach"
+ IDS_PERFMON_CURRENTENTRIES "Entradas actuales de cach"
+ IDS_PERFMON_CURRENTENTRIES_HELP "Nmero actual de entradas de cach"
+ IDS_PERFMON_MAXENTRIES "Entradas mximas de cach"
+ IDS_PERFMON_MAXENTRIES_HELP "Nmero mximo de entradas de cach"
+ IDS_PERFMON_HITCOUNTRATE "Velocidad de recuento de visitas de cach"
+ IDS_PERFMON_HITCOUNTRATE_HELP "Nmero de recuentos de visitas por segundo"
+ IDS_PERFMON_REQUEST "Servidor ATL:solicitud"
+ IDS_PERFMON_REQUEST_HELP "Estadsticas de las solicitudes que entran en el servidor"
+ IDS_PERFMON_REQUEST_TOTAL "Solicitudes totales del servidor"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "Nmero total de solicitudes"
+ IDS_PERFMON_REQUEST_FAILED "Solicitudes no procesadas del servidor"
+ IDS_PERFMON_REQUEST_FAILED_HELP "Nmero de solitudes no procesadas"
+ IDS_PERFMON_REQUEST_RATE "Solicitudes del servidor /seg"
+ IDS_PERFMON_REQUEST_RATE_HELP "Nmero de solicitudes por segundo"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "Tiempo medio de respuesta"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "Tiempo medio empleado en administrar una solicitud"
+ IDS_PERFMON_REQUEST_CURR_WAITING "Solicitudes en cola actuales"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "Nmero actual de solicitudes en espera de administracin"
+ IDS_PERFMON_REQUEST_MAX_WAITING "Solicitudes mximas en cola"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "Nmero mximo de solicitudes en espera de administracin"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "Subprocesos activos"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "Nmero de subprocesos que administran solicitudes activamente"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000> Errores al analizar un archivo de clichs:</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>Tipo de error</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>Nmero de lnea</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>Texto del error</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>Resultados de clich:\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} sin {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} sin {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} sin {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} sin {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} sin {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} sin {{if}} o {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "Etiqueta de controlador no vlida"
+ IDS_STENCIL_NULLPARAM "Parmetro NULL para ParseReplacements"
+ IDS_STENCIL_INVALIDSTRING "Se pas una cadena vaca o negativa a ParseReplacements"
+ IDS_STENCIL_EMBEDDED_NULL "Carcter null incrustado en el clich"
+ IDS_STENCIL_UNMATCHED_TAG_START "Las llaves {{ no coinciden"
+ IDS_STENCIL_MISMATCHED_TAG_START "Diferente nmero de llaves {{"
+ IDS_STENCIL_BAD_PARAMETER "Parmetro errneo"
+ IDS_STENCIL_METHODNAME_TOO_LONG "Nombre de mtodo demasiado largo"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "Nombre de controlador demasiado largo"
+ IDS_STENCIL_INVALID_SUBHANDLER "Etiqueta de controlador secundario no vlida"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "Reemplazo no resuelto: '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "No se puede abrir el archivo incluido"
+ IDS_STENCIL_INCLUDE_INVALID "El archivo incluido no es un archivo de disco"
+
+ IDS_STENCIL_MLANG_COCREATE "No se puede crear CMultiLanguage"
+ IDS_STENCIL_MLANG_LCID "Error al obtener el identificador LCID"
+ IDS_STENCIL_MLANG_GETLOCALE "Error en GetLocaleInfo"
+ IDS_STENCIL_MLANG_GETCHARSET "Error en GetCharsetInfo"
+
+ IDS_STENCIL_OUTOFMEMORY "Memoria insuficiente"
+ IDS_STENCIL_UNEXPECTED "Error inesperado"
+END
+
+#endif
diff --git a/include/atl/l.fra/atlsrv.rc b/include/atl/l.fra/atlsrv.rc
new file mode 100644
index 000000000..dc26cb83a
--- /dev/null
+++ b/include/atl/l.fra/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>Demande incorrecte</title></head><body>Demande incorrecte</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>Autorisation requise</title></head><body>Une autorisation est requise</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Interdit</title></head><body>Interdit</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>Introuvable</title></head><body>Introuvable</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>Erreur serveur</title></head><body>Erreur serveur</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>Non implment</title></head><body>Non implment</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>Passerelle incorrecte</title></head><body>Passerelle incorrecte</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>Service non disponible</title></head><body>Service non disponible</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Impossible de charger le fichier SRF.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le fichier SRF requis a t charg mais n'a pas pu tre trait correctement.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Un objet systme Windows n'a pas pu tre cr.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Une opration de lecture de fichier a chou.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le fichier spcifi n'a pas pu tre ouvert.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>chec de LoadLibrary.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>chec de la rcupration de l'interface du gestionnaire des demandes.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>La mmoire du serveur est sature.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le serveur a rencontr une erreur inattendue.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le serveur a rencontr une erreur inattendue lors de l'analyse du stencil requis.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le serveur n'a pas pu charger le stencil requis. Le fichier stencil est peut-tre endommag ou manquant sur ce serveur Web.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Un gestionnaire spcifi dans une balise de gestionnaire pour le stencil requis n'a pas pu tre trouv dans le fichier .dll de gestionnaire spcifi.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Soit ce stencil contient une balise de gestionnaire qui n'a pas pu tre analyse correctement par le processeur de stencil, soit il ne contient aucune balise de gestionnaire. Vrifiez la syntaxe correcte utiliser pour le stencil requis.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le stencil requis ne contient aucune balise de gestionnaire.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le stencil requis contient une balise de remplacement dans laquelle un nom de remplacement est trop long. La longueur maximale du nom de remplacement doit tre infrieure ou gale la valeur de la constante ATL_MAX_METHOD_NAME dfinie dans atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Le stencil requis contient une balise de remplacement, utilisant la syntaxe id.nombalise, dans laquelle un nom de gestionnaire est trop long. La longueur maximale du nom d'un gestionnaire doit tre infrieure ou gale la valeur de la constante ATL_MAX_METHOD_NAME dfinie dans atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>Impossible d'emprunter l'identit du client qui a mis la demande.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>Erreur serveur</title></head><body><H1>Erreur serveur</H1><P>L'extension ISAPI utilise pour traiter cette demande n'a pas pu tre charge en raison d'une erreur inconnue.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "chec de la cration du segment de mmoire demande"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "chec de l'initialisation de la thread de traitement"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "chec de l'initialisation de la section critique"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "chec de l'initialisation du pool de threads"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "chec de l'initialisation du cache de DLL"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "chec de l'initialisation du cache de page"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "chec de l'initialisation du cache du stencil"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "chec de l'initialisation du service d'tat de session"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "chec de l'initialisation du cache BLOB"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "chec de l'initialisation du cache du fichier"
+
+ IDS_PERFMON_CACHE "Serveur ATL : Cache"
+ IDS_PERFMON_CACHE_HELP "Information sur le cache du serveur ATL"
+ IDS_PERFMON_HITCOUNT "Accs cache avec rsultat"
+ IDS_PERFMON_HITCOUNT_HELP "Nombre des accs cache avec rsultat"
+ IDS_PERFMON_MISSCOUNT "Accs cache sans rsultat"
+ IDS_PERFMON_MISSCOUNT_HELP "Nombre des accs cache sans rsultat"
+ IDS_PERFMON_CURRENTALLOCATIONS "Allocations cache actuelles"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "Mmoire actuellement alloue au cache"
+ IDS_PERFMON_MAXALLOCATIONS "Allocations cache maximales"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "Mmoire maximale alloue au cache"
+ IDS_PERFMON_CURRENTENTRIES "Entres cache actuelles"
+ IDS_PERFMON_CURRENTENTRIES_HELP "Nombre actuel d'entres du cache"
+ IDS_PERFMON_MAXENTRIES "Entres cache maximales"
+ IDS_PERFMON_MAXENTRIES_HELP "Nombre maximal d'entres du cache"
+ IDS_PERFMON_HITCOUNTRATE "Taux d'accs au cache"
+ IDS_PERFMON_HITCOUNTRATE_HELP "Nombre d'accs par seconde"
+ IDS_PERFMON_REQUEST "Serveur ATL : Demande"
+ IDS_PERFMON_REQUEST_HELP "Statistiques sur les demandes serveur entrantes"
+ IDS_PERFMON_REQUEST_TOTAL "Total des demandes serveur"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "Nombre total des demandes"
+ IDS_PERFMON_REQUEST_FAILED "Demandes serveur choues"
+ IDS_PERFMON_REQUEST_FAILED_HELP "Nombre de demandes ayant chou"
+ IDS_PERFMON_REQUEST_RATE "Demandes serveur/s"
+ IDS_PERFMON_REQUEST_RATE_HELP "Nombre de demandes par seconde"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "Temps de rponse moyen"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "Dure moyenne du traitement d'une demande"
+ IDS_PERFMON_REQUEST_CURR_WAITING "Demandes actuelles en attente"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "Nombre de demandes actuellement en attente d'tre traites"
+ IDS_PERFMON_REQUEST_MAX_WAITING "Demandes maximales en attente"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "Nombre maximal de demandes en attente d'tre traites"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "Threads actives"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "Nombre de threads qui traitent activement des demandes"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000> Lors de la tentative d'analyse d'un fichier stencil, les erreurs suivantes se sont produites :</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>Type d'erreur</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>Numro de ligne</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>Texte de l'erreur</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>La sortie du stencil est la suivante :\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} sans {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} sans {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} sans {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} sans {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} sans {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} sans {{if}} ou {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "Balise de gestionnaire non valide"
+ IDS_STENCIL_NULLPARAM "Paramtre NULL ParseReplacements"
+ IDS_STENCIL_INVALIDSTRING "Chane vide ou ngative passe ParseReplacements"
+ IDS_STENCIL_EMBEDDED_NULL "Caractre null incorpor dans le stencil"
+ IDS_STENCIL_UNMATCHED_TAG_START "{{ non appari"
+ IDS_STENCIL_MISMATCHED_TAG_START "{{ incompatible"
+ IDS_STENCIL_BAD_PARAMETER "Paramtre incorrect"
+ IDS_STENCIL_METHODNAME_TOO_LONG "Nom de mthode trop long"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "Nom de gestionnaire trop long"
+ IDS_STENCIL_INVALID_SUBHANDLER "Balise de sous-gestionnaire non valide"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "Remplacement non rsolu : '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Impossible d'ouvrir le fichier inclus"
+ IDS_STENCIL_INCLUDE_INVALID "Le fichier inclus n'est pas un fichier disque"
+
+ IDS_STENCIL_MLANG_COCREATE "Impossible de crer CMultiLanguage"
+ IDS_STENCIL_MLANG_LCID "Erreur lors de l'obtention du lcid"
+ IDS_STENCIL_MLANG_GETLOCALE "chec de GetLocaleInfo"
+ IDS_STENCIL_MLANG_GETCHARSET "chec de GetCharsetInfo"
+
+ IDS_STENCIL_OUTOFMEMORY "Mmoire insuffisante"
+ IDS_STENCIL_UNEXPECTED "Erreur inattendue"
+END
+
+#endif
diff --git a/include/atl/l.ita/atlsrv.rc b/include/atl/l.ita/atlsrv.rc
new file mode 100644
index 000000000..009e0b2db
--- /dev/null
+++ b/include/atl/l.ita/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>Richiesta non valida</title></head><body>Richiesta non valida</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>Autorizzazione richiesta</title></head><body>Autorizzazione richiesta</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Non consentito</title></head><body>Non consentito</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>Non trovato</title></head><body>Non trovato</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>Errore del server</title></head><body>Errore del server</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>Non implementato</title></head><body>Non implementato</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>Gateway non valido</title></head><body>Gateway non valido</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>Servizio non disponibile</title></head><body>Servizio non disponibile</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Impossibile caricare il file SRF.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Il file SRF richiesto stato caricato ma non elaborato correttamente.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Impossibile creare un oggetto di sistema Windows.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Operazione di lettura file non riuscita.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Impossibile aprire il file specificato.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>LoadLibrary non riuscito.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Impossibile recuperare l'interfaccia del gestore delle richieste.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Memoria del server esaurita.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Errore imprevisto del server.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Errore imprevisto del server durante il tentativo di analizzare il file SRF richiesto.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Impossibile caricare il file SRF richiesto. Il file potrebbe essere danneggiato o mancante sul server Web.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Impossibile trovare nella DLL del gestore specificata uno dei gestori denominati in un tag di gestione per il file SRF richiesto.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Questo file SRF contiene un tag di gestione che non pu essere analizzato correttamente dal processore dei file SRF o non contiene affatto un tag di gestione. Cercare nel file SRF richiesto la sintassi appropriata.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Il file SRF richiesto non contiene un tag di gestione.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Rilevato un tag di sostituzione nel file SRF richiesto con un nome di sostituzione troppo lungo. Il nome di sostituzione deve contenere un numero di caratteri inferiore o uguale alla costante ATL_MAX_METHOD_NAME definita in atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Rilevato nel file SRF richiesto un tag di sostituzione che utilizza la sintassi id.tagname con un nome di gestore troppo lungo. Un nome di gestore deve contenere un numero di caratteri inferiore o uguale alla costante ATL_MAX_METHOD_NAME definita in atlstencil.h</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>Tentativo di rappresentare il client eseguendo la richiesta non riuscito.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>Errore del server</title></head><body><H1>Errore del server</H1><P>L'estensione ISAPI utilizzata per soddisfare questa richiesta non riuscita a eseguire il caricamento correttamente. Errore sconosciuto.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "Creazione heap richiesta non riuscita"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "Inizializzazione thread di lavoro non riuscita"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "Inizializzazione sezione critica non riuscita"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "Inizializzazione pool di thread non riuscita"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "Inizializzazione cache DLL non riuscita"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "Inizializzazione cache pagina non riuscita"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "Inizializzazione cache file SRF non riuscita"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "Inizializzazione servizio di stato sessione non riuscita"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Inizializzazione cache dati BLOB non riuscita"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "Inizializzazione cache file non riuscita"
+
+ IDS_PERFMON_CACHE "Server ATL:Cache"
+ IDS_PERFMON_CACHE_HELP "Informazioni sulla cache del server ATL"
+ IDS_PERFMON_HITCOUNT "Passaggi cache"
+ IDS_PERFMON_HITCOUNT_HELP "Numero di riscontri cache"
+ IDS_PERFMON_MISSCOUNT "Conteggio mancati riscontri cache"
+ IDS_PERFMON_MISSCOUNT_HELP "Numero di mancati riscontri cache"
+ IDS_PERFMON_CURRENTALLOCATIONS "Allocazioni correnti cache"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "Memoria corrente allocata per la cache"
+ IDS_PERFMON_MAXALLOCATIONS "Numero massimo allocazioni cache"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "Quantit massima di memoria allocata per la cache"
+ IDS_PERFMON_CURRENTENTRIES "Voci correnti cache"
+ IDS_PERFMON_CURRENTENTRIES_HELP "Numero corrente di voci della cache"
+ IDS_PERFMON_MAXENTRIES "Numero massimo voci cache"
+ IDS_PERFMON_MAXENTRIES_HELP "Numero massimo di voci della cache"
+ IDS_PERFMON_HITCOUNTRATE "Frequenza passaggi cache"
+ IDS_PERFMON_HITCOUNTRATE_HELP "Numero di passaggi al secondo"
+ IDS_PERFMON_REQUEST "Server ATL:Richiesta"
+ IDS_PERFMON_REQUEST_HELP "Statistica sulle richieste pervenute al server"
+ IDS_PERFMON_REQUEST_TOTAL "Richieste totali al server"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "Numero totale di richieste"
+ IDS_PERFMON_REQUEST_FAILED "Richieste al server non riuscite"
+ IDS_PERFMON_REQUEST_FAILED_HELP "Numero di richieste non riuscite"
+ IDS_PERFMON_REQUEST_RATE "Richieste al server /sec"
+ IDS_PERFMON_REQUEST_RATE_HELP "Numero di richieste al secondo"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "Tempo medio di risposta"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "Tempo medio impiegato per la gestione di una richiesta"
+ IDS_PERFMON_REQUEST_CURR_WAITING "Richieste correnti in coda"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "Numero corrente di richieste in attesa di essere gestite"
+ IDS_PERFMON_REQUEST_MAX_WAITING "Numero massimo di richieste in coda"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "Numero massimo di richieste in attesa di essere gestite"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "Thread attivi"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "Numero di thread che gestiscono attivamente le richieste"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000> Durante l'analisi di un file SRF, si verificato il seguente errore:</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>Tipo di errore</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>Numero della riga</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>Testo dell'errore</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>Output file SRF:\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} senza {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} senza {{endif}}"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} senza {{endwhile}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} senza {{while}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} senza {{if}}"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} senza {{if}} o {{else}}"
+
+ IDS_STENCIL_INVALID_HANDLER "Tag di gestione non valido"
+ IDS_STENCIL_NULLPARAM "Parametro NULL in ParseReplacements"
+ IDS_STENCIL_INVALIDSTRING "Stringa vuota o negativa passata a ParseReplacements"
+ IDS_STENCIL_EMBEDDED_NULL "Carattere null incorporato in file SRF"
+ IDS_STENCIL_UNMATCHED_TAG_START "{{ non corrispondenti"
+ IDS_STENCIL_MISMATCHED_TAG_START "{{ non corrispondenti"
+ IDS_STENCIL_BAD_PARAMETER "Parametro non valido"
+ IDS_STENCIL_METHODNAME_TOO_LONG "Nome metodo troppo lungo"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "Nome gestore troppo lungo"
+ IDS_STENCIL_INVALID_SUBHANDLER "Tag di gestione secondaria non valida"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "Sostituzione non risolta: '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Impossibile aprire il file incluso"
+ IDS_STENCIL_INCLUDE_INVALID "Il file incluso non un file su disco"
+
+ IDS_STENCIL_MLANG_COCREATE "Impossibile creare CMultiLanguage"
+ IDS_STENCIL_MLANG_LCID "Errore durante la ricerca di lcid"
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo non riuscito"
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo non riuscito"
+
+ IDS_STENCIL_OUTOFMEMORY "Memoria insufficiente"
+ IDS_STENCIL_UNEXPECTED "Errore imprevisto"
+END
+
+#endif
diff --git a/include/atl/l.jpn/atlsrv.rc b/include/atl/l.jpn/atlsrv.rc
new file mode 100644
index 000000000..d908accdc
--- /dev/null
+++ b/include/atl/l.jpn/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>sK؂ȗv</title></head><body>sK؂ȗv</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title>F؂Kvł</title></head><body>F؂Kvł</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title>Ȃ/title></head><body>Ȃ</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>‚܂</title></head><body>‚܂</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title>T[o[ G[</title></head><body>T[o[ G[</body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title>Ă܂</title></head><body>Ă܂</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>sK؂ȃQ[gEFC</title></head><body>sK؂ȃQ[gEFC</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>T[rXgpł܂</title></head><body>T[rXgpł܂</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title>T[o[ G[</title></head><body><H1>Server Error</H1><P>SRF t@Cǂݍ߂܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>vꂽ SRF t@C͓ǂݍ߂܂AɃvZXł܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>Windows VXe IuWFNg쐬ł܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>t@C̓ǂݎ葀삪ł܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>w肳ꂽt@CJƂł܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>LoadLibrary s܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>nhv C^[tFCX擾ł܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>T[o[̃sĂ܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>T[o[ɗ\ȂG[܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>vꂽXeV͒ɁAT[o[ɗ\ȂG[܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>T[o[ ͗vꂽXeVǂݍ߂܂łBXeV t@CĂ邩A Web T[o[ɂȂ”\܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>vꂽXeṼnh ^OŖOw肳ꂽnh 1 ‚w肳ꂽnh .dll Ɍ‚܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>̃XeV́AXeV vZbTɂēK؂ɉ͂łȂnh ^O܂ł邩A܂̓nh ^OĂ܂BvXeṼXeV\K؂ł邩ǂmׂĂB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>vꂽXeV̓nh ^OĂ܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>vꂽXeVŒu^O‚܂Au܂Bu̍ő̒́Aatlstencil.h Œ`ꂽ ATL_MAX_METHOD_NAME 萔 ƓZȂĂ͂Ȃ܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>ID.tagname \gpu^OAvꂽXeVŌ‚܂Ã^Õnh͒܂Bnh̍ő̒́Aatlstencil.h Œ`ꂽ ATL_MAX_METHOD_NAME 萔ƓZȂĂ͂Ȃ܂B</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>An attempt to impersonate the client making the request failed.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title>T[o[ G[</title></head><body><H1>T[o[ G[</H1><P>̗vɓ邽߂Ɏgp ISAPI gq́A\ȂG[̂ߐɓǂݍ݂ł܂łB</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "q[v̍쐬vł܂łB"
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "[J[ Xbh̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "NeBJȃZNV̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED "Xbh v[̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "DLL LbV̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED "y[W LbV̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "XeV LbV̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED "ZbVԃT[rX̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Blob LbV̏ł܂łB"
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED "t@C LbV̏ł܂łB"
+
+ IDS_PERFMON_CACHE "ATL T[o[ : LbV"
+ IDS_PERFMON_CACHE_HELP "ATL T[o[ LbV̏"
+ IDS_PERFMON_HITCOUNT "LbṼqbg JEg"
+ IDS_PERFMON_HITCOUNT_HELP "LbV qbg̉"
+ IDS_PERFMON_MISSCOUNT "LbṼ~X JEg"
+ IDS_PERFMON_MISSCOUNT_HELP "LbṼ~X JEg̉"
+ IDS_PERFMON_CURRENTALLOCATIONS "LbV݂̌̊蓖"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "LbVɌ݊蓖ĂĂ郁"
+ IDS_PERFMON_MAXALLOCATIONS "LbV̍ő̊蓖"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "LbVɊ蓖Ăꂽő僁"
+ IDS_PERFMON_CURRENTENTRIES "݂̃LbV Gg"
+ IDS_PERFMON_CURRENTENTRIES_HELP "݂̃LbV Gg̐"
+ IDS_PERFMON_MAXENTRIES "ő̃LbV Gg"
+ IDS_PERFMON_MAXENTRIES_HELP "LbV Gg̍ő吔"
+ IDS_PERFMON_HITCOUNTRATE "LbṼqbg JEg"
+ IDS_PERFMON_HITCOUNTRATE_HELP "b̃qbg JEg"
+ IDS_PERFMON_REQUEST "ATL T[o[ : v"
+ IDS_PERFMON_REQUEST_HELP "T[o[ɓĂv̓v"
+ IDS_PERFMON_REQUEST_TOTAL "T[o[v̑"
+ IDS_PERFMON_REQUEST_TOTAL_HELP "v̑"
+ IDS_PERFMON_REQUEST_FAILED "T[o[Ȃv "
+ IDS_PERFMON_REQUEST_FAILED_HELP "Ȃv"
+ IDS_PERFMON_REQUEST_RATE "T[o[v /sec"
+ IDS_PERFMON_REQUEST_RATE_HELP "b̗v"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME "ς̉Ύ"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "v̏ɂώ"
+ IDS_PERFMON_REQUEST_CURR_WAITING "݃L[ɂv"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "݉΂҂Ăv"
+ IDS_PERFMON_REQUEST_MAX_WAITING "L[ɂv̍ő吔"
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "΂҂Ăv̍ő吔"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "ANeBu Xbh"
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "vݏ̃Xbh"
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{codepage 0}}<h1><font color=#ff0000> XeV t@C͒Ɏ̃G[܂ :</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%>G[̎</td><td>{{GetErrorText}}</td></tr>\r\n<tr><td>sio</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td>G[ eLXg</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>XeVo͎͂̂悤ɂȂĂ܂ :\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} {{endif}} Ă܂"
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} {{endif}} Ă܂"
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} {{endwhile}} Ă܂"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} {{while}} Ă܂"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} {{if}} Ă܂"
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} {{if}} or {{else}} Ă܂"
+
+ IDS_STENCIL_INVALID_HANDLER "ȃnh ^O"
+ IDS_STENCIL_NULLPARAM "ParseReplacements ւ NULL p[^"
+ IDS_STENCIL_INVALIDSTRING "ParseReplacements ɋA܂͕̕񂪓n܂"
+ IDS_STENCIL_EMBEDDED_NULL "XeV ߍ݂ null ܂"
+ IDS_STENCIL_UNMATCHED_TAG_START "v܂ {{"
+ IDS_STENCIL_MISMATCHED_TAG_START "v܂ {{"
+ IDS_STENCIL_BAD_PARAMETER "sK؂ȃp[^ł"
+ IDS_STENCIL_METHODNAME_TOO_LONG "\bh܂"
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "nh܂"
+ IDS_STENCIL_INVALID_SUBHANDLER "Tunh ^Oł"
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "uł܂ : '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Ytt@CJ܂"
+ IDS_STENCIL_INCLUDE_INVALID "Ytt@C̓fBXN t@Cł͂܂"
+
+ IDS_STENCIL_MLANG_COCREATE "CMultiLanguage 쐬ł܂ł"
+ IDS_STENCIL_MLANG_LCID "lcid 擾̃G[ł"
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo ł܂ł"
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo ł܂ł"
+
+ IDS_STENCIL_OUTOFMEMORY "܂"
+ IDS_STENCIL_UNEXPECTED "\ȂG[ł"
+END
+
+#endif
diff --git a/include/atl/l.kor/atlsrv.rc b/include/atl/l.kor/atlsrv.rc
new file mode 100644
index 000000000..54208d9ca
--- /dev/null
+++ b/include/atl/l.kor/atlsrv.rc
@@ -0,0 +1,146 @@
+// This is a part of the Active Template Library.
+// Copyright (C) Microsoft Corporation
+// All rights reserved.
+//
+// This source code is only intended as a supplement to the
+// Active Template Library Reference and related
+// electronic documentation provided with the library.
+// See these sources for detailed information regarding the
+// Active Template Library product.
+
+#include <winresrc.h>
+#include "atlsrvres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ATLSRV_BAD_REQUEST "<html><head><title>߸ û</title></head><body>߸ ûԴϴ.</body></html>"
+ IDS_ATLSRV_AUTH_REQUIRED
+ "<html><head><title> ʿ</title></head><body> ʿմϴ.</body></html>"
+ IDS_ATLSRV_FORBIDDEN "<html><head><title></title></head><body>Ǿϴ.</body></html>"
+ IDS_ATLSRV_NOT_FOUND "<html><head><title>ã </title></head><body>ã ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR "<html><head><title> </title></head><body> </body></html>"
+ IDS_ATLSRV_NOT_IMPLEMENTED
+ "<html><head><title> ȵ</title></head><body> ʾҽϴ.</body></html>"
+ IDS_ATLSRV_BAD_GATEWAY "<html><head><title>߸ Ʈ</title></head><body>߸ ƮԴϴ.</body></html>"
+ IDS_ATLSRV_SERVICE_NOT_AVAILABLE
+ "<html><head><title>񽺸 </title></head><body>񽺸 ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADSRF "<html><head><title> </title></head><body><H1> </H1><P>SRF ε ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HNDLFAIL "<html><head><title> </title></head><body><H1> </H1><P>û SRF ε ó ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SYSOBJFAIL "<html><head><title> </title></head><body><H1> </H1><P>Windows ý ü ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_READFILEFAIL "<html><head><title> </title></head><body><H1> </H1><P> б ۾ ߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADFILEFAIL "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P> ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LOADLIB "<html><head><title> </title></head><body><H1> </H1><P>LoadLibrary ȣ ߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERIF "<html><head><title> </title></head><body><H1> </H1><P>û ó ̽ ߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_OUTOFMEM "<html><head><title> </title></head><body><H1> </H1><P> ޸𸮰 մϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_UNEXPECTED "<html><head><title> </title></head><body><H1> </H1><P> ġ ߻߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILPARSEFAIL "<html><head><title> </title></head><body><H1> </H1><P>û ٽ мϴ ġ ߻߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_STENCILLOADFAIL "<html><head><title> </title></head><body><H1> </H1><P>û ٽ ε ߽ϴ. ٽ ջǾų ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_HANDLERNOTFOUND "<html><head><title> </title></head><body><H1> </H1><P> ó .dll û ٽ ó ±׿ ó ϳ ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_BADHANDLERTAG "<html><head><title> </title></head><body><H1> </H1><P> ٽǿ ó ±װ ų ٽ μ ó ִ ó ±װ ϴ. û ٽ ȮϿ ٽ ùٸ ʽÿ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_NOHANDLERTAG "<html><head><title> </title></head><body><H1> Server Error</H1><P>û ٽǿ ó ±װ ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGMETHODNAME "<html><head><title> </title></head><body><H1> </H1><P>û ٽ ü ± ̸ ʹ ϴ. ٲ ̸ ִ ̴ atlstencil.h ǵ ATL_MAX_METHOD_NAME ۰ų ƾ մϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_LONGHANDLERNAME "<html><head><title> </title></head><body><H1> </H1><P>û ٽ id.tagname ϴ ü ± ó ̸ ʹ ϴ. ó ̸ ִ ̴ atlstencil.h ǵ ATL_MAX_METHOD_NAME ۰ų ƾ մϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_IMPERSONATIONFAILED "<html><head><title> </title></head><body><H1> </H1><P>Ŭ̾Ʈ ߱ û ߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_ISAPISTARTUPFAILED "<html><head><title> </title></head><body><H1> </H1><P> û óϴ ISAPI ͽټ ε ߽ϴ.</body></html>"
+ IDS_ATLSRV_SERVER_ERROR_SOAPNOSOAPACTION "<html><head><title>Server Error</title></head><body><H1>Server Error</H1><P>SOAP request did not provide SOAPACTION header.</body></html>"
+
+ IDS_ATLSRV_CRITICAL_HEAPCREATEFAILED "û ۼ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_WORKERINITFAILED "۾ 带 ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_CRITSECINITFAILED "Ӱ ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_THREADPOOLFAILED " Ǯ ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_DLLCACHEFAILED "DLL ijø ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_PAGECACHEFAILED " ijø ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_STENCILCACHEFAILED "ٽ ijø ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_SESSIONSTATEFAILED " 񽺸 ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_BLOBCACHEFAILED "Blob ijø ʱȭ ߽ϴ."
+ IDS_ATLSRV_CRITICAL_FILECACHEFAILED " ijø ʱȭ ߽ϴ."
+
+ IDS_PERFMON_CACHE "ATL :ij"
+ IDS_PERFMON_CACHE_HELP "ATL ijÿ "
+ IDS_PERFMON_HITCOUNT "ij Ƚ"
+ IDS_PERFMON_HITCOUNT_HELP "ij Ƚ"
+ IDS_PERFMON_MISSCOUNT "ij Ƚ"
+ IDS_PERFMON_MISSCOUNT_HELP "ij Ƚ"
+ IDS_PERFMON_CURRENTALLOCATIONS " ij Ҵ緮"
+ IDS_PERFMON_CURRENTALLOCATIONS_HELP "ij÷ Ҵ ޸ "
+ IDS_PERFMON_MAXALLOCATIONS "ִ ij Ҵ緮"
+ IDS_PERFMON_MAXALLOCATIONS_HELP "ij÷ Ҵ ִ ޸ "
+ IDS_PERFMON_CURRENTENTRIES " ij Ʈ"
+ IDS_PERFMON_CURRENTENTRIES_HELP " ij Ʈ "
+ IDS_PERFMON_MAXENTRIES "ִ ij Ʈ"
+ IDS_PERFMON_MAXENTRIES_HELP "ִ ij Ʈ "
+ IDS_PERFMON_HITCOUNTRATE "ij ߷"
+ IDS_PERFMON_HITCOUNTRATE_HELP " Ƚ"
+ IDS_PERFMON_REQUEST "ATL :û"
+ IDS_PERFMON_REQUEST_HELP " ޵Ǵ û "
+ IDS_PERFMON_REQUEST_TOTAL " û "
+ IDS_PERFMON_REQUEST_TOTAL_HELP " û "
+ IDS_PERFMON_REQUEST_FAILED " û "
+ IDS_PERFMON_REQUEST_FAILED_HELP " û "
+ IDS_PERFMON_REQUEST_RATE " û /"
+ IDS_PERFMON_REQUEST_RATE_HELP " û "
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME " ð"
+ IDS_PERFMON_REQUEST_AVG_RESPONSE_TIME_HELP "û ó ҿ ð"
+ IDS_PERFMON_REQUEST_CURR_WAITING "⿭ ִ û"
+ IDS_PERFMON_REQUEST_CURR_WAITING_HELP "ó û "
+ IDS_PERFMON_REQUEST_MAX_WAITING "ִ û "
+ IDS_PERFMON_REQUEST_MAX_WAITING_HELP "ó ִ û "
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS "Ȱ "
+ IDS_PERFMON_REQUEST_ACTIVE_THREADS_HELP "û ȭ óϴ "
+END
+
+
+#ifndef ATL_NO_DEFAULT_STENCIL_RESOURCE
+dllmgr.srf HTML "res\\dllmgr.srf"
+stencilmgr.srf HTML "res\\stencilmgr.srf"
+threadmgr.srf HTML "res\\threadmgr.srf"
+#endif
+
+
+//
+// Stencil parse error support
+//
+#ifdef ATL_DEBUG_STENCILS
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STENCIL_ERROR_STENCIL
+"{{ڵ 0}}<h1><font color=#ff0000> ٽ мϴ ߻߽ϴ.</font></h1>\r\n{{while GetNextError}}<table border=1 width=50%>\r\n<tr><td width=25%> </td><td>{{GetErrorText}}</td></tr>\r\n<tr><td> ȣ</td><td>{{GetErrorLineNumber}}</td></tr>\r\n<tr><td> ؽƮ</td><td><pre>{{GetErrorLine}}</pre></td></tr>\r\n</table>\r\n{{endwhile}}<br>ٽ ϴ.\r\n<hr>"
+
+ IDS_STENCIL_UNCLOSEDBLOCK_IF "{{if}} {{endif}} ϴ."
+ IDS_STENCIL_UNCLOSEDBLOCK_ELSE "{{else}} {{endif}} ϴ."
+ IDS_STENCIL_UNCLOSEDBLOCK_WHILE "{{while}} {{endwhile}} ϴ."
+ IDS_STENCIL_UNOPENEDBLOCK_ENDWHILE "{{endwhile}} {{while}} ϴ"
+ IDS_STENCIL_UNOPENEDBLOCK_ELSE "{{else}} {{if}} ϴ."
+ IDS_STENCIL_UNOPENEDBLOCK_ENDIF "{{endif}} {{if}} Ǵ {{else}} ϴ."
+
+ IDS_STENCIL_INVALID_HANDLER "ó ±װ ߸Ǿϴ."
+ IDS_STENCIL_NULLPARAM "ParseReplacements Ű NULLԴϴ."
+ IDS_STENCIL_INVALIDSTRING " ְų ڿ ParseReplacements ޵Ǿϴ."
+ IDS_STENCIL_EMBEDDED_NULL "ٽǿ Ե null ڰ ֽϴ."
+ IDS_STENCIL_UNMATCHED_TAG_START "{{ ġ ʽϴ."
+ IDS_STENCIL_MISMATCHED_TAG_START "{{ ġ ʽϴ."
+ IDS_STENCIL_BAD_PARAMETER "Ű ߸Ǿϴ."
+ IDS_STENCIL_METHODNAME_TOO_LONG "޼ ̸ ʹ ϴ."
+ IDS_STENCIL_HANDLERNAME_TOO_LONG "ó ̸ ʹ ϴ."
+ IDS_STENCIL_INVALID_SUBHANDLER " ó ±װ ߸Ǿϴ."
+ IDS_STENCIL_UNRESOLVED_REPLACEMENT "ü Ȯ ϴ: '%s'"
+
+ IDS_STENCIL_INCLUDE_ERROR "Ե ϴ."
+ IDS_STENCIL_INCLUDE_INVALID "Ե ũ ƴմϴ."
+
+ IDS_STENCIL_MLANG_COCREATE "CMultiLanguage ϴ."
+ IDS_STENCIL_MLANG_LCID "lcid ߻߽ϴ."
+ IDS_STENCIL_MLANG_GETLOCALE "GetLocaleInfo ߽ϴ."
+ IDS_STENCIL_MLANG_GETCHARSET "GetCharsetInfo ߽ϴ."
+
+ IDS_STENCIL_OUTOFMEMORY "޸𸮰 մϴ."
+ IDS_STENCIL_UNEXPECTED "ġ Դϴ."
+END
+
+#endif
diff --git a/include/atl/res/dllmgr.srf b/include/atl/res/dllmgr.srf
new file mode 100644
index 000000000..45f9c7049
--- /dev/null
+++ b/include/atl/res/dllmgr.srf
@@ -0,0 +1,29 @@
+<!--
+ This is a part of the Active Template Library.
+ Copyright (C) Microsoft Corporation
+ All rights reserved.
+
+ This source code is only intended as a supplement to the
+ Active Template Library Reference and related
+ electronic documentation provided with the library.
+ See these sources for detailed information regarding the
+ Active Template Library product.
+-->
+
+<html>
+<body bgcolor={{GetBodyColor}}>
+<H1 align=left> ATL Server DLL Cache Manager </H1>
+
+Current DLL Cache Entries: {{GetNumEntries}}<p>
+
+<table border=1>
+<tr bgcolor={{GetTRColor}}><th>DLL Path</th><th>Ref Count</th></tr>
+
+{{while EnumEntries}}
+<tr bgcolor={{GetTRColor}}><td align=center>{{GetDllName}}</td><td align=center>{{GetDllReferences}}</tr>
+{{endwhile}}
+
+</table>
+
+</body>
+</html> \ No newline at end of file
diff --git a/include/atl/res/stencilmgr.srf b/include/atl/res/stencilmgr.srf
new file mode 100644
index 000000000..f0ca82fa3
--- /dev/null
+++ b/include/atl/res/stencilmgr.srf
@@ -0,0 +1,69 @@
+<!--
+ This is a part of the Active Template Library.
+ Copyright (C) Microsoft Corporation
+ All rights reserved.
+
+ This source code is only intended as a supplement to the
+ Active Template Library Reference and related
+ electronic documentation provided with the library.
+ See these sources for detailed information regarding the
+ Active Template Library product.
+-->
+<html>
+ <body bgcolor="{{GetBodyColor}}">
+ <H1 align="left">
+ ATL Server Stencil Cache Management
+ </H1>
+ <table border="1" width="50%">
+ <tr bgcolor="{{GetTRColor}}">
+ <th align="center">
+ Value</th><th align="center">Quantity</th></tr>
+ {{while GetNextStencilCacheStats}}
+ <tr bgcolor="{{GetTRColor}}">
+ <td>{{GetCacheValue}}</td>
+ <td>{{GetCacheQuantity}}</td>
+ </tr>
+ {{endwhile}}
+ </table>
+ <form method="post">
+ <input type="hidden" name="Handler" value="StencilMgrSrf"> <input type="hidden" name="Method" value="ExecuteCommand">
+ <select name="command" onchange="HandleFormOnChange(this);">
+ <option value="0">Clear All Statistics</option>
+ <option value="1">Remove Stencil</option>
+ <option value="2">Remove All Stencils</option>
+ <option value="3">Set default Lifespan</option>
+ </select>
+ <br>
+ <span id="DynValueCap"></span><input type="text" name="DynValue" style="visibility=hidden">
+ <br>
+ <input type="submit" value="Execute Command">
+ </form>
+ <script language="JavaScript">
+function HandleFormOnChange(select_object)
+{
+
+ //alert(select_object.form.elements["StencilName"].type);
+
+ if (select_object.value==1)
+ {
+ select_object.form.elements["DynValue"].style.visibility="visible";
+ window.document.all["DynValueCap"].innerHTML = "Stencil Name:&nbsp;";
+
+ }
+ else if (select_object.value==3)
+ {
+ select_object.form.elements["DynValue"].style.visibility="visible";
+ window.document.all["DynValueCap"].innerHTML = "New Lifetime:&nbsp;";
+ }
+ else
+ {
+ select_object.form.elements["DynValue"].style.visibility="hidden";
+ window.document.all["DynValueCap"].innerHTML="";
+ }
+}
+
+
+
+ </script>
+ </body>
+</html>
diff --git a/include/atl/res/threadmgr.srf b/include/atl/res/threadmgr.srf
new file mode 100644
index 000000000..fd322be77
--- /dev/null
+++ b/include/atl/res/threadmgr.srf
@@ -0,0 +1,24 @@
+<!--
+ This is a part of the Active Template Library.
+ Copyright (C) Microsoft Corporation
+ All rights reserved.
+
+ This source code is only intended as a supplement to the
+ Active Template Library Reference and related
+ electronic documentation provided with the library.
+ See these sources for detailed information regarding the
+ Active Template Library product.
+-->
+<html>
+ <body bgcolor="{{GetBodyColor}}">
+ <H1 align="left">
+ ATL Server Thread Pool Management
+ </H1>
+ This server's thread pool is currently using {{GetSize}} threads.<p>
+ <form method="post">
+ <input type="hidden" name="Handler" value="ThreadMgrSrf"> <input type="hidden" name="Method" value="ExecuteCommand">
+ <input type="hidden" name="command" value="0"> Enter a new thread pool size: <input type="text" name="DynValue">
+ <input type="submit" value="Change Thread Pool">
+ </form>
+ </body>
+</html>