Windows2003-3790/inetsrv/iis/svcs/smtp/server/pe_dispi.hxx
2020-09-30 16:53:55 +02:00

640 lines
15 KiB
C++

/*++
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