|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name :
pe_dispi.hxx
Abstract:
This module provides the implementation for the protocol event dispatchers
Author:
Keith Lau (KeithLau) 6/24/98
Project:
SMTP Server DLL
Revision History:
KeithLau Created
--*/
#ifndef _PE_DISPI_HXX_
#define _PE_DISPI_HXX_
#ifndef PRIO_LOW
#define PRIO_LOW 24575
#endif
#ifndef PRIO_LOWEST
#define PRIO_LOWEST 32767
#endif
#ifndef PRIO_DEFAULT
#define PRIO_DEFAULT PRIO_LOW
#endif
//
// Define a prime number for default hash sizes
//
#define PERE_HASH_SIZE 13
//
// Enumerated types for different event types
//
typedef enum _OUTBOUND_EVENT_TYPES { PE_OET_SESSION_START = 0, PE_OET_MESSAGE_START, PE_OET_PER_RECIPIENT, PE_OET_BEFORE_DATA, PE_OET_SESSION_END, PE_OET_INVALID_EVENT_TYPE
} OUTBOUND_EVENT_TYPES;
// =================================================================
// Generic dispatcher routines
//
class CGenericProtoclEventDispatcher { public:
static HRESULT GetLowerAnsiStringFromVariant( CComVariant &vString, LPSTR pszString, DWORD *pdwLength );
static HRESULT InsertBinding( LPPE_COMMAND_NODE *ppHeadNode, LPPE_BINDING_NODE pBinding, LPSTR pszCommandKeyword, DWORD dwCommandKeywordSize );
static HRESULT InsertBindingWithHash( LPPE_COMMAND_NODE *rgpHeadNodes, DWORD dwHashSize, LPPE_BINDING_NODE pBinding, LPSTR pszCommandKeyword, DWORD dwCommandKeywordSize );
static HRESULT FindCommandFromHash( LPPE_COMMAND_NODE *rgpHeadNodes, DWORD dwHashSize, LPSTR pszCommandKeyword, DWORD dwCommandKeywordSize, LPPE_COMMAND_NODE *ppCommandNode );
static DWORD GetHashValue( DWORD dwBuckets, LPSTR pszKey, DWORD dwKeySize ) { DWORD dwHash = 0; if (dwKeySize > 3) dwKeySize = 3; while (dwKeySize--) dwHash ^= (DWORD)*pszKey++; return(dwHash % dwBuckets); }
static HRESULT CleanupCommandNodes( LPPE_COMMAND_NODE pHeadNode, LPPE_COMMAND_NODE pSkipNode );
};
// =================================================================
// Inbound command dispatcher
//
class CInboundDispatcher : public CEventBaseDispatcher, public CGenericProtoclEventDispatcher, public ISmtpInboundCommandDispatcher { public:
CInboundDispatcher() { m_lRefCount = 0; m_fSinksInstalled = FALSE;
// Initialize the hash
m_dwHashSize = sizeof(m_rgpCommandList) / sizeof(LPPE_COMMAND_NODE); for (DWORD i = 0 ; i < m_dwHashSize; i++) m_rgpCommandList[i] = NULL; } ~CInboundDispatcher() { // Clean up any command nodes allocated
for (DWORD i = 0 ; i < m_dwHashSize; i++) { _VERIFY(CGenericProtoclEventDispatcher::CleanupCommandNodes( m_rgpCommandList[i], NULL) == S_OK); } }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) { if (riid == IID_IUnknown) *ppvObj = (IUnknown *)(ISmtpInboundCommandDispatcher *)this; else if (riid == IID_ISmtpInboundCommandDispatcher) *ppvObj = (ISmtpInboundCommandDispatcher *)this; else if (riid == IID_IEventDispatcher) *ppvObj = (IEventDispatcher *)this; else return(E_NOINTERFACE); AddRef(); return(S_OK); }
unsigned long STDMETHODCALLTYPE AddRef() { return(InterlockedIncrement(&m_lRefCount)); }
unsigned long STDMETHODCALLTYPE Release() { LONG lTemp = InterlockedDecrement(&m_lRefCount); _ASSERT(lTemp >= 0); if (!lTemp) delete this; return(lTemp); }
HRESULT STDMETHODCALLTYPE ChainSinks( IUnknown *pServer, IUnknown *pSession, IMailMsgProperties *pMsg, ISmtpInCommandContext *pContext, DWORD dwStopAtPriority, LPPE_COMMAND_NODE pCommandNode, LPPE_BINDING_NODE *ppResumeFrom );
HRESULT STDMETHODCALLTYPE SinksInstalled( LPSTR szCommandKeyword, LPPE_COMMAND_NODE *ppCommandNode ) { return(CGenericProtoclEventDispatcher::FindCommandFromHash( m_rgpCommandList, m_dwHashSize, szCommandKeyword, strlen(szCommandKeyword), ppCommandNode)); }
HRESULT AllocBinding( REFGUID rguidEventType, IEventBinding *piBinding, CBinding **ppNewBinding );
//
// Local binding class
//
class CInboundBinding : public CGenericProtoclEventDispatcher, public CEventBaseDispatcher::CBinding { public: CInboundBinding( CInboundDispatcher *pDispatcher ) { _ASSERT(pDispatcher); m_pDispatcher = pDispatcher; }
HRESULT Init(IEventBinding *piBinding);
PE_BINDING_NODE m_bnInfo; CInboundDispatcher *m_pDispatcher; };
public: LPPE_COMMAND_NODE m_rgpCommandList[PERE_HASH_SIZE]; DWORD m_dwHashSize; BOOL m_fSinksInstalled;
private:
LONG m_lRefCount; };
// =================================================================
// Outbound command generation dispatcher
//
class COutboundDispatcher : public CEventBaseDispatcher, public CGenericProtoclEventDispatcher, public ISmtpOutboundCommandDispatcher { public:
COutboundDispatcher() { m_lRefCount = 0; m_fSinksInstalled = FALSE;
InitializeDefaultCommandBindings(); } ~COutboundDispatcher() { // Clean up any command nodes allocated, make sure
// we don't try to delete our default node
for (DWORD i = 0; i < PE_STATE_MAX_STATES; i++) { _VERIFY(CGenericProtoclEventDispatcher::CleanupCommandNodes( m_rgpCommandList[i], &(m_rgcnDefaultCommand[i])) == S_OK); } }
void InitializeDefaultCommandBindings();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) { if (riid == IID_IUnknown) *ppvObj = (IUnknown *)(ISmtpOutboundCommandDispatcher *)this; else if (riid == IID_ISmtpOutboundCommandDispatcher) *ppvObj = (ISmtpOutboundCommandDispatcher *)this; else if (riid == IID_IEventDispatcher) *ppvObj = (IEventDispatcher *)this; else return(E_NOINTERFACE); AddRef(); return(S_OK); }
unsigned long STDMETHODCALLTYPE AddRef() { return(InterlockedIncrement(&m_lRefCount)); }
unsigned long STDMETHODCALLTYPE Release() { LONG lTemp = InterlockedDecrement(&m_lRefCount); _ASSERT(lTemp >= 0); if (!lTemp) delete this; return(lTemp); }
HRESULT STDMETHODCALLTYPE ChainSinks( IUnknown *pServer, IUnknown *pSession, IMailMsgProperties *pMsg, ISmtpOutCommandContext *pContext, DWORD dwEventType, LPPE_COMMAND_NODE *ppPreviousCommand, LPPE_BINDING_NODE *ppResumeFrom );
HRESULT STDMETHODCALLTYPE SinksInstalled( DWORD dwEventType ) { return((m_rgpCommandList[dwEventType] != NULL)?S_OK:S_FALSE); }
HRESULT AllocBinding( REFGUID rguidEventType, IEventBinding *piBinding, CBinding **ppNewBinding );
//
// Local binding class
//
class COutboundBinding : public CGenericProtoclEventDispatcher, public CEventBaseDispatcher::CBinding { public: COutboundBinding( COutboundDispatcher *pDispatcher, REFGUID rguidEventType );
HRESULT Init(IEventBinding *piBinding);
DWORD m_dwEventType; PE_BINDING_NODE m_bnInfo; COutboundDispatcher *m_pDispatcher; };
public: LPPE_COMMAND_NODE m_rgpCommandList[PE_STATE_MAX_STATES]; BOOL m_fSinksInstalled;
static const GUID *s_rgrguidEventTypes[PE_STATE_MAX_STATES]; static char *s_rgszDefaultCommand[PE_STATE_MAX_STATES];
private:
LONG m_lRefCount; PE_COMMAND_NODE m_rgcnDefaultCommand[PE_STATE_MAX_STATES]; PE_BINDING_NODE m_rgbnDefaultCommand[PE_STATE_MAX_STATES]; };
// =================================================================
// Server response dispatcher
//
class CResponseDispatcher : public CEventBaseDispatcher, public CGenericProtoclEventDispatcher, public ISmtpServerResponseDispatcher { public:
CResponseDispatcher() { m_lRefCount = 0; m_fSinksInstalled = FALSE;
// Initialize the hash
m_dwHashSize = sizeof(m_rgpCommandList) / sizeof(LPPE_COMMAND_NODE); for (DWORD i = 0 ; i < m_dwHashSize; i++) m_rgpCommandList[i] = NULL; } ~CResponseDispatcher() { // Clean up any command nodes allocated
for (DWORD i = 0 ; i < m_dwHashSize; i++) { _VERIFY(CGenericProtoclEventDispatcher::CleanupCommandNodes( m_rgpCommandList[i], NULL) == S_OK); } }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) { if (riid == IID_IUnknown) *ppvObj = (IUnknown *)(ISmtpServerResponseDispatcher *)this; else if (riid == IID_ISmtpServerResponseDispatcher) *ppvObj = (ISmtpServerResponseDispatcher *)this; else if (riid == IID_IEventDispatcher) *ppvObj = (IEventDispatcher *)this; else return(E_NOINTERFACE); AddRef(); return(S_OK); }
unsigned long STDMETHODCALLTYPE AddRef() { return(InterlockedIncrement(&m_lRefCount)); }
unsigned long STDMETHODCALLTYPE Release() { LONG lTemp = InterlockedDecrement(&m_lRefCount); _ASSERT(lTemp >= 0); if (!lTemp) delete this; return(lTemp); }
HRESULT STDMETHODCALLTYPE ChainSinks( IUnknown *pServer, IUnknown *pSession, IMailMsgProperties *pMsg, ISmtpServerResponseContext *pContext, DWORD dwStopAtPriority, LPPE_COMMAND_NODE pCommandNode, LPPE_BINDING_NODE *ppResumeFrom );
HRESULT STDMETHODCALLTYPE SinksInstalled( LPSTR szCommandKeyword, LPPE_COMMAND_NODE *ppCommandNode ) { return(CGenericProtoclEventDispatcher::FindCommandFromHash( m_rgpCommandList, m_dwHashSize, szCommandKeyword, strlen(szCommandKeyword), ppCommandNode)); }
HRESULT AllocBinding( REFGUID rguidEventType, IEventBinding *piBinding, CBinding **ppNewBinding );
//
// Local binding class
//
class CResponseBinding : public CGenericProtoclEventDispatcher, public CEventBaseDispatcher::CBinding { public: CResponseBinding( CResponseDispatcher *pDispatcher ) { _ASSERT(pDispatcher); m_pDispatcher = pDispatcher; }
HRESULT Init(IEventBinding *piBinding);
PE_BINDING_NODE m_bnInfo; CResponseDispatcher *m_pDispatcher; }; //
// Map from our internal Protocol Event state to the value
// published in smtpevent.idl
//
static PE_STATES PeStateFromOutboundEventType( OUTBOUND_EVENT_TYPES EventType) { switch(EventType) { case PE_OET_SESSION_START: return PE_STATE_SESSION_START; case PE_OET_MESSAGE_START: return PE_STATE_MESSAGE_START; case PE_OET_PER_RECIPIENT: return PE_STATE_PER_RECIPIENT; case PE_OET_BEFORE_DATA: return PE_STATE_DATA_OR_BDAT; case PE_OET_SESSION_END: return PE_STATE_SESSION_END;
case PE_OET_INVALID_EVENT_TYPE: default: _ASSERT(0 && "Invalid event type"); return PE_STATE_DEFAULT; } }
public: LPPE_COMMAND_NODE m_rgpCommandList[PERE_HASH_SIZE]; DWORD m_dwHashSize; BOOL m_fSinksInstalled;
private:
LONG m_lRefCount; };
// =================================================================
// Class factories
//
class CInboundDispatcherClassFactory : public IClassFactory { HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) { _ASSERT(FALSE); return E_NOTIMPL; } unsigned long STDMETHODCALLTYPE AddRef () { _ASSERT(FALSE); return 0; } unsigned long STDMETHODCALLTYPE Release () { _ASSERT(FALSE); return 0; }
HRESULT STDMETHODCALLTYPE CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) { // The dispatcher cannot be part of an aggregate
if (pUnkOuter) return(CLASS_E_NOAGGREGATION);
if (!ppvObj) return(E_POINTER); *ppvObj = NULL;
CInboundDispatcher *pDispatcher = new CInboundDispatcher(); if (!pDispatcher) return(E_OUTOFMEMORY); // QI to give it the initial refcount
HRESULT hrRes = pDispatcher->QueryInterface(riid, ppvObj); if (FAILED(hrRes)) delete pDispatcher; else *ppvObj = (LPVOID)pDispatcher; return(hrRes); }
HRESULT STDMETHODCALLTYPE LockServer (int fLock) { _ASSERT(FALSE); return E_NOTIMPL; } };
class COutboundDispatcherClassFactory : public IClassFactory { HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) { _ASSERT(FALSE); return E_NOTIMPL; } unsigned long STDMETHODCALLTYPE AddRef () { _ASSERT(FALSE); return 0; } unsigned long STDMETHODCALLTYPE Release () { _ASSERT(FALSE); return 0; }
HRESULT STDMETHODCALLTYPE CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) { // The dispatcher cannot be part of an aggregate
if (pUnkOuter) return(CLASS_E_NOAGGREGATION);
if (!ppvObj) return(E_POINTER); *ppvObj = NULL;
COutboundDispatcher *pDispatcher = new COutboundDispatcher(); if (!pDispatcher) return(E_OUTOFMEMORY); // QI to give it the initial refcount
HRESULT hrRes = pDispatcher->QueryInterface(riid, ppvObj); if (FAILED(hrRes)) delete pDispatcher; else *ppvObj = (LPVOID)pDispatcher; return(hrRes); }
HRESULT STDMETHODCALLTYPE LockServer (int fLock) { _ASSERT(FALSE); return E_NOTIMPL; } };
class CResponseDispatcherClassFactory : public IClassFactory { HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) { _ASSERT(FALSE); return E_NOTIMPL; } unsigned long STDMETHODCALLTYPE AddRef () { _ASSERT(FALSE); return 0; } unsigned long STDMETHODCALLTYPE Release () { _ASSERT(FALSE); return 0; }
HRESULT STDMETHODCALLTYPE CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) { // The dispatcher cannot be part of an aggregate
if (pUnkOuter) return(CLASS_E_NOAGGREGATION);
if (!ppvObj) return(E_POINTER); *ppvObj = NULL;
CResponseDispatcher *pDispatcher = new CResponseDispatcher(); if (!pDispatcher) return(E_OUTOFMEMORY); // QI to give it the initial refcount
HRESULT hrRes = pDispatcher->QueryInterface(riid, ppvObj); if (FAILED(hrRes)) delete pDispatcher; else *ppvObj = (LPVOID)pDispatcher; return(hrRes); }
HRESULT STDMETHODCALLTYPE LockServer (int fLock) { _ASSERT(FALSE); return E_NOTIMPL; } };
//
// External declaration of the dispatcher class factories
//
extern CInboundDispatcherClassFactory g_cfInbound; extern COutboundDispatcherClassFactory g_cfOutbound; extern CResponseDispatcherClassFactory g_cfResponse;
#endif
|