640 lines
15 KiB
C++
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
|