Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1599 lines
48 KiB

//-----------------------------------------------------------------------------
//
//
// File: mailadmq.cpp
//
// Description: Implementation for CMailMsgAdminLink
//
// Author: Gautam Pulla (GPulla)
//
// History:
// 6/24/1999 - GPulla Created
//
// Copyright (C) 1999 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include "aqprecmp.h"
#include "linkmsgq.h"
#include "mailadmq.h"
#include "dcontext.h"
#include "dsnevent.h"
#include "asyncq.inl"
#include "asyncadm.inl"
//---[ CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueNDR ]-----------------------
//
//
// Description:
// Wraps call to NDR MailMsg
// Parameters:
// *pIUnknown - IUnkown of MailMsg
// Returns:
// S_OK on success
// History:
// 12/7/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueNDR(
IUnknown *pIUnknownMsg)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueNDR");
HRESULT hr = S_OK;
IMailMsgProperties *pIMailMsgProperties = NULL;
CDSNParams dsnparams;
_ASSERT(pIUnknownMsg);
_ASSERT(m_paqinst);
hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!");
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr);
goto Exit;
}
//
// Initialize DSN params
//
SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
dsnparams.dwStartDomain = 0;
dsnparams.dwDSNActions = DSN_ACTION_FAILURE_ALL;
dsnparams.pIMailMsgProperties = pIMailMsgProperties;
dsnparams.hrStatus = AQUEUE_E_QADMIN_NDR;
//
// Attempt to NDR message
//
hr = HrLinkAllDomains(pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to link all domains for DSN generation", hr);
goto Exit;
}
//
// Fire DSN Generation event
//
hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to NDR message via QAPI 0x%08X", hr);
goto Exit;
}
//
// Now that we have generated an NDR... we need to delete the
// Message.
//
hr = HrDeleteMsgFromQueueSilent(pIUnknownMsg);
if (FAILED(hr))
goto Exit;
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueSilent ]--------------------
//
//
// Description:
// Wrapper function to silently delete a message from a queue
// Parameters:
// *pIUnknown - IUnkown of MailMsg
// Returns:
// S_OK on success
// History:
// 12/7/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueSilent(
IUnknown *pIUnknownMsg)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrDeleteMsgFromQueueSilent");
HRESULT hr = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
_ASSERT(pIUnknownMsg);
hr = pIUnknownMsg->QueryInterface(IID_IMailMsgQueueMgmt,
(void **) &pIMailMsgQueueMgmt);
_ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!");
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr);
goto Exit;
}
//
// Attempt to delete the message
//
hr = pIMailMsgQueueMgmt->Delete(NULL);
if (FAILED(hr))
ErrorTrace((LPARAM) this, "Unable to delete msg 0x%08X", hr);
Exit:
if (pIMailMsgQueueMgmt)
pIMailMsgQueueMgmt->Release();
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::HrFreezeMsg ]-----------------------------------
//
//
// Description:
// Wrapper to freeze a pIMailMsgProperties
// Parameters:
// *pIUnknown - IUnkown of MailMsg
// Returns:
// S_OK on success
// History:
// 12/7/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrFreezeMsg(IUnknown *pIUnknownMsg)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrFreezeMsg");
HRESULT hr = S_OK;
IMailMsgProperties *pIMailMsgProperties = NULL;
_ASSERT(pIUnknownMsg);
hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!");
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr);
goto Exit;
}
//
// $$TODO - Attempt to freeze the message -- Not supported for this type of queue
//
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::HrThawMsg ]-------------------------------------
//
//
// Description:
// Wrapper function to thaw a message
// Parameters:
// *pIUnknown - IUnkown of MailMsg
// Returns:
// S_OK on success
// History:
// 12/7/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrThawMsg(IUnknown *pIUnknownMsg)
{
TraceFunctEnterEx((LPARAM) this, "AsyncAdminMailMsgQueue::HrThawMsg");
HRESULT hr = S_OK;
IMailMsgProperties *pIMailMsgProperties = NULL;
_ASSERT(pIUnknownMsg);
hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!");
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr);
goto Exit;
}
//
// $$TODO - Attempt to thaw message -- Not supported for this type of queue
//
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::HrGetStatsForMsg ]------------------------------
//
//
// Description:
// Wrapper function to fill in the CAQStats struct for a message
// Parameters:
// *pIUnknown - IUnkown of MailMsg
// *paqstats - Ptr to aqstats struction to fill in.
// Returns:
// S_OK on success
// History:
// 12/7/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrGetStatsForMsg(
IUnknown *pIUnknownMsg,
CAQStats *paqstats)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrGetStatsForMsg");
HRESULT hr = S_OK;
IMailMsgProperties *pIMailMsgProperties = NULL;
_ASSERT(pIUnknownMsg);
_ASSERT(paqstats);
hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a MailMsg!!");
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "QI for MailMsg failed with hr 0x%08X", hr);
goto Exit;
}
//
// $$TODO - GetStats for Msg -- Not supported for this type of queue
//
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::HrSendDelayOrNDR ]-----------------------------
//
//
// Description:
// Checks the MailMsg to see if it has expired or needs a delay DSN sent
// and acts accordingly
// Parameters:
// IMailMsgProperties - The MailMsg that needs to be checked
// Returns:
// S_OK : OK, may have sent delay NDR
// S_FALSE : OK, MailMsg handled (NDR'd or nothing left to do)
// Or returns error from called fnct.
// History:
// 5/15/2001 - dbraun Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrSendDelayOrNDR(IMailMsgProperties *pIMailMsgProperties)
{
HRESULT hr = S_OK;
DWORD cbProp = 0;
DWORD dwTimeContext = 0;
CDSNParams dsnparams;
BOOL fSentDelay = FALSE;
BOOL fSentNDR = FALSE;
FILETIME ftExpireTimeNDR;
FILETIME ftExpireTimeDelay;
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrSendDelayOrNDR");
_ASSERT(m_paqinst);
// Try to get the expire time from the message, otherwise calculate it
// from the file time
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_NDR,
sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeNDR);
if (MAILMSG_E_PROPNOTFOUND == hr)
{
// Prop not set ... calculate it from the file time
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeNDR);
if (FAILED(hr))
{
// Message should not make it this far without being stamped
_ASSERT(MAILMSG_E_PROPNOTFOUND != hr);
// Prop not set or other failure, we cannot expire this message
goto Exit;
}
m_paqinst->CalcExpireTimeNDR(ftExpireTimeNDR, TRUE, &ftExpireTimeNDR);
}
else if (FAILED(hr))
{
goto Exit;
}
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_DELAY,
sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeDelay);
if (MAILMSG_E_PROPNOTFOUND == hr)
{
// Prop not set ... calculate it from the file time
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME), &cbProp, (BYTE *) &ftExpireTimeDelay);
if (FAILED(hr))
{
// Message should not make it this far without being stamped
_ASSERT(MAILMSG_E_PROPNOTFOUND != hr);
// Prop not set or other failure, we cannot expire this message
goto Exit;
}
m_paqinst->CalcExpireTimeDelay(ftExpireTimeDelay, TRUE, &ftExpireTimeDelay);
}
else if (FAILED(hr))
{
goto Exit;
}
//
// Initialize DSN params
//
SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
dsnparams.dwStartDomain = 0;
dsnparams.dwDSNActions = 0;
dsnparams.pIMailMsgProperties = pIMailMsgProperties;
dsnparams.hrStatus = 0;
// Check if we have passed either expire time
if (m_paqinst->fInPast(&ftExpireTimeNDR, &dwTimeContext))
{
dsnparams.dwDSNActions |= DSN_ACTION_FAILURE_ALL;
dsnparams.hrStatus = AQUEUE_E_MSG_EXPIRED;
fSentNDR = TRUE;
}
else if (m_paqinst->fInPast(&ftExpireTimeDelay, &dwTimeContext))
{
dsnparams.dwDSNActions |= DSN_ACTION_DELAYED;
dsnparams.hrStatus = AQUEUE_E_MSG_EXPIRED;
fSentDelay = TRUE;
}
// If we are going to generate an NDR
if (dsnparams.hrStatus)
{
//
// Attempt to NDR message
//
hr = HrLinkAllDomains(pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to link all domains for DSN generation", hr);
goto Exit;
}
//
// Fire DSN Generation event
//
hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to NDR message via QAPI 0x%08X", hr);
goto Exit;
}
// Return based on what we did
if (fSentNDR)
{
// This message has been handled, delete it
hr = HrDeleteMsgFromQueueSilent(pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Failed to delete message after sending NDR", hr);
goto Exit;
}
// NDR'd and successfully deleted message
hr = S_FALSE;
}
else if (fSentDelay)
{
hr = S_OK;
}
}
Exit:
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::fHandleCompletionFailure ]---------------------
//
//
// Description:
// Overrides base class and checks to see if message has expired before
// putting it on the retry queue
// Parameters:
// IMailMsgProperties - The MailMsg that triggered failure
// Returns:
// -
// History:
// 5/15/2001 - dbraun Created
//
//-----------------------------------------------------------------------------
BOOL CAsyncAdminMailMsgQueue::fHandleCompletionFailure(IMailMsgProperties *pIMailMsgProperties)
{
HRESULT hr = S_OK;
// Has this message expired?
hr = HrSendDelayOrNDR (pIMailMsgProperties);
if (hr == S_FALSE)
{
// This message was NDR'd, we are done
return TRUE;
}
else
{
return CAsyncAdminQueue<IMailMsgProperties *, ASYNC_QUEUE_MAILMSG_SIG>::fHandleCompletionFailure(pIMailMsgProperties);
}
}
//---[ CAsyncAdminMailMsgQueue::HrQueueRequest ]-------------------------------
//
//
// Description:
// Function that will queue a request to the async queue and close
// the handles associated with a message if we are above our simple
// "throttle" limit.
// Parameters:
// pIMailMsgProperties The IMailMsgProperties interface to queue
// fRetry TRUE - if this message is being retried
// FALSE - otherwise
// cMsgsInSystem The total number of messages in the system
// Returns:
// S_OK on success
// Error code from async queue on failure.
// History:
// 10/7/1999 - MikeSwa Created
// 12/7/2000 - MikeSwa Moved to CAsyncAdminMailMsgQueue from asyncq.cpp
// 4/6/2001 - MikeSwa Modified to take into account queue length
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrQueueRequest(IMailMsgProperties *pIMailMsgProperties,
BOOL fRetry,
DWORD cMsgsInSystem)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrQueueRequest");
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
HRESULT hr = S_OK;
DWORD cThresholdUsed = g_cMaxIMsgHandlesThreshold;
DWORD cItemsPending = dwGetTotalThreads()+m_cItemsPending;
if (m_qhmgr.fShouldCloseHandle(cItemsPending, m_cPendingAsyncCompletions,
cMsgsInSystem))
{
DebugTrace((LPARAM) this,
"INFO: Closing IMsg Content - %d messsages in system", cMsgsInSystem);
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt,
(void **) &pIMailMsgQueueMgmt);
if (SUCCEEDED(hr))
{
//bounce usage count off of zero
pIMailMsgQueueMgmt->ReleaseUsage();
pIMailMsgQueueMgmt->AddUsage();
pIMailMsgQueueMgmt->Release();
}
else
{
ErrorTrace((LPARAM) this,
"Unable to QI for IMailMsgQueueMgmt - hr 0x%08X", hr);
}
}
TraceFunctLeave();
return CAsyncAdminQueue<IMailMsgProperties *, ASYNC_QUEUE_MAILMSG_SIG>::HrQueueRequest(pIMailMsgProperties, fRetry);
}
//---[ CAsyncAdminMailMsgQueue::HrQueueRequest ]-------------------------------
//
//
// Description:
// Since we inherit from AsyncQueue who implmenents this, we should assert
// so that a dev adding a new call to this class later on, will use the
// version that closes handles.
//
// In RTL this will force the handles closed and queue the request
// Parameters:
// pIMailMsgProperties The IMailMsgProperties interface to queue
// fRetry TRUE - if this message is being retried
// FALSE - otherwise
// Returns:
// returns return value from proper version of HrQueueRequest
// History:
// 10/7/1999 - MikeSwa Created
// 12/7/2000 - MikeSwa Moved to CAsyncAdminMailMsgQueue from asyncq.cpp
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrQueueRequest(IMailMsgProperties *pIMailMsgProperties,
BOOL fRetry)
{
_ASSERT(0 && "Should use HrQueueRequest with 3 parameters");
return HrQueueRequest(pIMailMsgProperties, fRetry,
g_cMaxIMsgHandlesThreshold+1);
}
//---[ CAsyncAdminMailMsgQueue::HrApplyQueueAdminFunction ]--------------------
//
//
// Description:
// Will call the IQueueAdminMessageFilter::Process message for every
// message in this queue. If the message passes the filter, then
// HrApplyActionToMessage on this object will be called.
//
// This is different from other implemenentations in that the
// location of the message implies something about the state of the
// message. Messages in the retry... or frozen queue are considered
// failed or frozen.
//
// We do this instead of writing a mailmsg property, because of the
// *huge* perf hit (we would ruin our async message flow by blocking
// to check if a message is frozen). We already have the retry
// queue, so it makes sense to use it in a similar manner
//
// Parameters:
// IN pIQueueAdminMessageFilter
// Returns:
// S_OK on success
// History:
// 2/23/99 - MikeSwa Created
// 12/7/2000 - MikeSwa Modified - Made template base class
// 12/13/2000 - MikeSwa Modified from CAsyncAdminQueue
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAsyncAdminMailMsgQueue::HrApplyQueueAdminFunction(
IQueueAdminMessageFilter *pIQueueAdminMessageFilter)
{
HRESULT hr = S_OK;
CQueueAdminContext qapictx(m_pAQNotify, m_paqinst);
_ASSERT(pIQueueAdminMessageFilter);
hr = pIQueueAdminMessageFilter->HrSetQueueAdminAction(
(IQueueAdminAction *) this);
//This is an internal interface that should not fail
_ASSERT(SUCCEEDED(hr) && "HrSetQueueAdminAction");
if (FAILED(hr))
goto Exit;
hr = pIQueueAdminMessageFilter->HrSetCurrentUserContext(&qapictx);
//This is an internal interface that should not fail
_ASSERT(SUCCEEDED(hr) && "HrSetCurrentUserContext");
if (FAILED(hr))
goto Exit;
//
// Iterate over messages in the base queue
//
qapictx.SetQueueState(LI_READY);
hr = HrMapFnBaseQueue(m_pfnMessageAction, pIQueueAdminMessageFilter);
//
// Iterate over messages in the retry queue
//
qapictx.SetQueueState(LI_RETRY);
hr = HrMapFnRetryQueue(m_pfnMessageAction, pIQueueAdminMessageFilter);
hr = pIQueueAdminMessageFilter->HrSetCurrentUserContext(NULL);
//This is an internal interface that should not fail
_ASSERT(SUCCEEDED(hr) && "HrSetCurrentUserContext");
if (FAILED(hr))
goto Exit;
Exit:
return hr;
}
//-----------------------------------------------------------------------------
// Description:
// Used to query the admin interfaces for CMailMsgAdminLink
// Parameters:
// IN REFIID riid GUID for interface
// OUT LPVOID *ppvObj Ptr to Interface.
//
// Returns:
// S_OK Interface supported by this class.
// E_POINTER NULL parameter.
// E_NOINTERFACE No such interface exists.
// History:
// 6/25/1999 - GPulla Created
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
HRESULT hr = S_OK;
if (!ppvObj)
{
hr = E_POINTER;
goto Exit;
}
if (IID_IUnknown == riid)
{
*ppvObj = static_cast<IQueueAdminAction *>(this);
}
else if (IID_IQueueAdminAction == riid)
{
*ppvObj = static_cast<IQueueAdminAction *>(this);
}
else if (IID_IQueueAdminLink == riid)
{
*ppvObj = static_cast<IQueueAdminLink *>(this);
}
else
{
*ppvObj = NULL;
hr = E_NOINTERFACE;
goto Exit;
}
static_cast<IUnknown *>(*ppvObj)->AddRef();
Exit:
return hr;
}
//---[ CMailMsgAdminLink::CMailMsgAdminLink]---------------------------------
//
//
// Description:
// Default constructor for CMailMsgAdminLink
// Parameters:
// IN guidLink GUID to associate with this object
// IN szQueueName Name to associate with admin object
// IN *pasyncmmq Async MailMsg queue for precat or prerouting
// IN dwLinkType Bit-Field identifying this admin object
// IN paqinst CAQSvrInst object
//
// Returns:
// -
// History:
// 6/25/1999 - GPulla Created
//
//-----------------------------------------------------------------------------
CMailMsgAdminLink::CMailMsgAdminLink(
GUID guid,
LPSTR szQueueName,
CAsyncAdminMailMsgQueue *pasyncmmq,
DWORD dwLinkType,
CAQSvrInst *paqinst
)
: m_aqsched(guid, 0)
{
_ASSERT(pasyncmmq);
_ASSERT(szQueueName);
_ASSERT(paqinst);
m_guid = guid;
m_cbQueueName = lstrlen(szQueueName);
m_szQueueName = (LPSTR) pvMalloc(m_cbQueueName+1);
_ASSERT(m_szQueueName);
if(m_szQueueName)
lstrcpy(m_szQueueName, szQueueName);
m_pasyncmmq = pasyncmmq;
m_dwLinkType = dwLinkType;
m_dwSignature = MAIL_MSG_ADMIN_QUEUE_VALID_SIGNATURE;
if (m_pasyncmmq)
m_pasyncmmq->SetAQNotify((IAQNotify *) this);
m_paqinst = paqinst;
ZeroMemory(&m_ftRetry, sizeof(m_ftRetry));
}
//---[CMailMsgAdminLink::~CMailMsgAdminLink]---------------------------------
// Description:
// Destructor.
// Parameters:
// -
// Returns:
// -
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
CMailMsgAdminLink::~CMailMsgAdminLink()
{
if (m_szQueueName)
FreePv(m_szQueueName);
m_dwSignature = MAIL_MSG_ADMIN_QUEUE_INVALID_SIGNATURE;
}
//---[ CMailMsgAdminLink::HrApplyQueueAdminFunction ]---------------------------
//
//
// Description:
// Wrapper to call into underlying queue's implementation
// Parameters:
// IN pIQueueAdminMessageFilter
// Returns:
// S_OK on success
// S_FALSE if no contained queue (will assert as well)
// History:
// 12/11/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::HrApplyQueueAdminFunction(
IQueueAdminMessageFilter *pIQueueAdminMessageFilter)
{
HRESULT hr = S_FALSE;
_ASSERT(m_pasyncmmq);
if (m_pasyncmmq)
hr = m_pasyncmmq->HrApplyQueueAdminFunction(pIQueueAdminMessageFilter);
return hr;
}
//---[ CMailMsgAdminLink::HrApplyActionToMessage ]-----------------------------
//
//
// Description:
// Wrapper function to pass call on to queue implementation
// Parameters:
// IN *pIUnknownMsg ptr to message abstraction
// IN ma Message action to perform
// IN pvContext Context set on IQueueAdminFilter
// OUT pfShouldDelete TRUE if the message should be deleted
// Returns:
// S_OK on success
// S_FALSE if no contained queue (will assert as well)
// History:
// 12/11/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::HrApplyActionToMessage(
IUnknown *pIUnknownMsg,
MESSAGE_ACTION ma,
PVOID pvContext,
BOOL *pfShouldDelete)
{
HRESULT hr = S_FALSE;
_ASSERT(m_pasyncmmq);
if (m_pasyncmmq)
{
hr = m_pasyncmmq->HrApplyActionToMessage(pIUnknownMsg, ma,
pvContext, pfShouldDelete);
}
return hr;
}
//---[CMailMsgAdminLink::HrGetLinkInfo]---------------------------------------
// Description:
// Gets information about this admin object. Note that the diagnostic error
// is not implemented for this object but the parameter is supported purely
// to support the IQueueAction interface.
// Parameters:
// OUT LINK_INFO *pliLinkInfo Struct to fill information into.
// OUT HRESULT *phrDiagnosticError Diagnostic error if any, for this link
// Returns:
// S_OK on success
// E_POINTER if argument is NULL
// E_OUTOFMEMORY if unable to allocate memory for returning information.
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::HrGetLinkInfo(LINK_INFO *pliLinkInfo, HRESULT *phrDiagnosticError)
{
TraceFunctEnterEx((LPARAM) this, "CMailMsgAdminLink::HrGetLinkInfo");
HRESULT hr = S_OK;
_ASSERT(m_pasyncmmq);
_ASSERT(pliLinkInfo);
if(!m_pasyncmmq)
{
hr = S_FALSE;
goto Exit;
}
if(!pliLinkInfo)
{
hr = E_POINTER;
goto Exit;
}
//
// Get the link state from our base queue implementation
//
pliLinkInfo->fStateFlags = m_pasyncmmq->dwQueueAdminLinkGetLinkState();
//
// If we are in retry... try to report the time
//
if (LI_RETRY & pliLinkInfo->fStateFlags)
QueueAdminFileTimeToSystemTime(&m_ftRetry, &(pliLinkInfo->stNextScheduledConnection));
pliLinkInfo->fStateFlags |= GetLinkType();
pliLinkInfo->szLinkName = wszQueueAdminConvertToUnicode(m_szQueueName, m_cbQueueName);
if (!pliLinkInfo->szLinkName)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
//We return 0 since size statistics are not calculated
pliLinkInfo->cbLinkVolume.QuadPart = 0;
//
// Include the items queued for retry in the total count
//
pliLinkInfo->cMessages = m_pasyncmmq->cQueueAdminGetNumItems();
pliLinkInfo->dwSupportedLinkActions = LA_KICK | LA_THAW | LA_FREEZE;
//Write diagnostic
*phrDiagnosticError = S_OK;
Exit:
TraceFunctLeave();
return hr;
}
//---[CMailMsgAdminLink::HrGetNumQueues]-------------------------------------
// Description:
// Used to query number of queues in object. Since this class does not
// expose the one queue it contains, 0 is returned,
// Parameters:
// OUT DWORD *pcQueues # of queues (0) written to this.
// Returns:
// S_OK unless...
// E_POINTER parameter is not allocated
// History:
// 6/24/1999 - GPulla created
// 12/11/2000 - MikeSwa Updated to support sub-queues
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::HrGetNumQueues(DWORD *pcQueues)
{
_ASSERT (pcQueues);
if (!pcQueues)
return E_POINTER;
*pcQueues = 1;
return S_OK;
}
//---[CMailMsgAdminLink::HrApplyActionToLink]---------------------------------
// Description:
// Applies action to the embedded queue. Only kicking the queue is supported.
// Parameters:
// IN LINK_ACTION la Action to apply.
// Returns:
// S_OK Action was successfully applied.
// S_FALSE Action not supported or severe error.
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::HrApplyActionToLink(LINK_ACTION la)
{
HRESULT hr = S_OK;
_ASSERT(m_pasyncmmq);
if (!m_pasyncmmq)
{
hr = S_FALSE;
goto Exit;
}
if (LA_KICK == la)
m_pasyncmmq->StartRetry(); //kick off processing
else if (LA_FREEZE == la)
m_pasyncmmq->FreezeQueue();
else if (LA_THAW == la)
m_pasyncmmq->ThawQueue();
else
hr = S_FALSE;
Exit:
return hr;
}
//---[CMailMsgAdminLink::HrGetQueueIDs]---------------------------------------
// Description:
// Returns an enumeration of embedded queues in this object. Since the one
// emmbedded queue is not exposed, zero queues are returned.
// Parameters:
// OUT DWORD *pcQueues Number of queues (0)
// OUT QUEUELINK_ID *rgQueues Array into which queueIDs are returned.
// Returns:
// S_OK Success
// E_POINTER pcQueues is NULL
// History:
// 6/24/1999 - GPulla created
// 12/11/2000 - MikeSwa Modified to expose queues
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::HrGetQueueIDs(DWORD *pcQueues, QUEUELINK_ID *rgQueues)
{
TraceFunctEnterEx((LPARAM) this, "CMailMsgAdminLink::HrGetQueueIDs");
_ASSERT(pcQueues);
_ASSERT(rgQueues);
HRESULT hr = S_OK;
QUEUELINK_ID* pCurrentQueueID = rgQueues;
if(!pcQueues)
{
hr = E_POINTER;
goto Exit;
}
if (*pcQueues < 1)
{
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
*pcQueues = 0;
_ASSERT(m_pasyncmmq);
hr = m_pasyncmmq->HrGetQueueID(pCurrentQueueID);
if (FAILED(hr))
goto Exit;
*pcQueues = 1;
Exit:
TraceFunctLeave();
return hr;
}
//---[CMailMsgAdminLink::fMatchesID]------------------------------------------
// Description:
// Checks if this admin object matches a specified ID.
// Parameters:
// IN QUEUELINK_ID *pQueueLinkID Ptr to ID to be matched against.
// Returns:
// TRUE on match.
// FALSE if did not matched or unrecoverable error (m_szQueueName not alloced)
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
BOOL STDMETHODCALLTYPE CMailMsgAdminLink::fMatchesID(QUEUELINK_ID *pQueueLinkID)
{
_ASSERT(pQueueLinkID);
_ASSERT(pQueueLinkID->szName);
_ASSERT(m_szQueueName);
if(!m_szQueueName)
return FALSE;
CAQScheduleID aqsched(pQueueLinkID->uuid, pQueueLinkID->dwId);
if (!fIsSameScheduleID(&aqsched))
return FALSE;
if (!fBiStrcmpi(m_szQueueName, pQueueLinkID->szName))
return FALSE;
//Everything matched!
return TRUE;
}
//---[ CMailMsgAdminLink::QuerySupportedActions ]------------------------------
//
//
// Description:
// Returns the actions and filters that this implementation supports
// Parameters:
// pdwSupportedActions - QAPI MsgActions that this queue suppprts
// pdwSupportedFilterFlags - QAPI filter flags that this queue supports
// Returns:
// S_OK on success
// History:
// 12/12/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CMailMsgAdminLink::QuerySupportedActions(
DWORD *pdwSupportedActions,
DWORD *pdwSupportedFilterFlags)
{
HRESULT hr = S_OK;
_ASSERT(m_pasyncmmq);
if (m_pasyncmmq)
{
hr = m_pasyncmmq->QuerySupportedActions(pdwSupportedActions,
pdwSupportedFilterFlags);
}
return hr;
}
//---[CMailMsgAdminLink::fIsSameScheduleID]-----------------------------------
// Description:
// Helper function for fMatchesID()
// Parameters:
// Returns:
// TRUE if schedule IDs are identical
// FALSE otherwise.
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
BOOL CMailMsgAdminLink::fIsSameScheduleID(CAQScheduleID *paqsched)
{
return (m_aqsched.fIsEqual(paqsched));
}
//---[CMailMsgAdminLink::HrGetLinkID]-----------------------------------------
// Description:
// Get the ID for this admin object.
// Parameters:
// OUT QUEUELINK_ID *pLinkID struct into which to put ID.
// Returns:
// S_OK Successfully copied out ID.
// E_POINTER out struct is NULL.
// E_OUTOFMEMORY Cannot allocate memory for output of ID name.
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
HRESULT CMailMsgAdminLink::HrGetLinkID(QUEUELINK_ID *pLinkID)
{
HRESULT hr = S_OK;
_ASSERT(pLinkID);
if(!pLinkID)
{
hr = E_POINTER;
goto Exit;
}
pLinkID->qltType = QLT_LINK;
pLinkID->dwId = m_aqsched.dwGetScheduleID();
m_aqsched.GetGUID(&pLinkID->uuid);
if (!fRPCCopyName(&pLinkID->szName))
hr = E_OUTOFMEMORY;
else
hr = S_OK;
Exit:
return hr;
}
//---[CMailMsgAdminLink::fRPCCopyName]----------------------------------------
// Description:
// Helper function to create a unicode copy of the string identifying
// this admin object. The unicode string is de-allocated by RPC.
// Parameters:
// OUT LPWSTR *pwszLinkName Ptr to wchar string allocated and written
// into by this function.
// Returns:
// TRUE On success.
// FALSE if there is no name for this object
// FALSE if memory cannot be allocated for unicode string.
// History:
// 6/24/1999 - GPulla created
//-----------------------------------------------------------------------------
BOOL CMailMsgAdminLink::fRPCCopyName(OUT LPWSTR *pwszLinkName)
{
_ASSERT(pwszLinkName);
if (!m_cbQueueName || !m_szQueueName)
return FALSE;
*pwszLinkName = wszQueueAdminConvertToUnicode(m_szQueueName,
m_cbQueueName);
if (!*pwszLinkName)
return FALSE;
return TRUE;
}
//---[ CAsyncAdminMailMsgLink::HrNotify ]--------------------------------------
//
//
// Description:
// Notification for stats purposes
// Parameters:
//
// Returns:
//
// History:
// 1/10/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CMailMsgAdminLink::HrNotify(CAQStats *aqstats, BOOL fAdd)
{
UpdateCountersForLinkType(m_paqinst, m_dwLinkType);
return S_OK;
}
//---[ CMailMsgAdminLink::SetNextRetry ]---------------------------------------
//
//
// Description:
// Updates internal retry time
// Parameters:
// pft Filetime to update to
// Returns:
// -
// History:
// 1/16/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CMailMsgAdminLink::SetNextRetry(FILETIME *pft)
{
if (pft)
memcpy(&m_ftRetry, pft, sizeof(FILETIME));
}
//--------[ CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter ]-----------------
//
// Description:
// Checks a message against a queue admin message filter to see if it
// is a match
// Parameters:
// IN pIMailMsgProperties mail msg object to perform check on
// IN paqmf Message Filter to check against
// Returns:
// TRUE if it matches
// FALSE if it does not
// History:
// 8/8/00 - t-toddc created
// 12/11/2000 - MikeSwa Merged for checkin
//
//-----------------------------------------------------------------------------
BOOL CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter(
IN IMailMsgProperties* pIMailMsgProperties,
IN CAQAdminMessageFilter* paqmf)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties,
"CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter");
BOOL fMatch = TRUE;
DWORD dwFilterFlags = 0;
DWORD cbMsgSize = 0;
DWORD cbProp = 0;
FILETIME ftQueueEntry = {0, 0};
LPSTR szSender = NULL;
LPSTR szMsgId = NULL;
LPSTR szRecip = NULL;
BOOL fFoundRecipString = FALSE;
HRESULT hr = S_OK;
DWORD cOpenHandlesForMsg = 1; //don't close by default
_ASSERT(pIMailMsgProperties);
_ASSERT(paqmf);
dwFilterFlags = paqmf->dwGetMsgFilterFlags();
if (!dwFilterFlags)
{
fMatch = FALSE;
goto Exit;
}
if (AQ_MSG_FILTER_ALL & dwFilterFlags)
{
fMatch = TRUE;
goto Exit;
}
// check size.
if (AQ_MSG_FILTER_LARGER_THAN & dwFilterFlags)
{
//Get the size of the message
hr = HrQADMGetMsgSize(pIMailMsgProperties, &cbMsgSize);
if (FAILED(hr))
{
fMatch = FALSE;
goto Exit;
}
fMatch = paqmf->fMatchesSize(cbMsgSize);
if (!fMatch)
goto Exit;
}
if (AQ_MSG_FILTER_OLDER_THAN & dwFilterFlags)
{
//Get time message was queued
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME),
&cbProp,
(BYTE *) &ftQueueEntry);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to get arrival time 0x%08X", hr);
fMatch = FALSE;
goto Exit;
}
fMatch = paqmf->fMatchesTime(&ftQueueEntry);
if (!fMatch)
goto Exit;
}
if (AQ_MSG_FILTER_FROZEN & dwFilterFlags)
{
// obtaining state information about freezing/thawing not supported yet.
fMatch = FALSE;
if (AQ_MSG_FILTER_INVERTSENSE & dwFilterFlags)
fMatch = !fMatch;
if (!fMatch)
goto Exit;
}
if (AQ_MSG_FILTER_FAILED & dwFilterFlags)
{
// fMatch was originally set to TRUE
// currently, no information about failures is available
// for IMailMsgProperties mail msg objects
fMatch = FALSE;
if (AQ_MSG_FILTER_INVERTSENSE & dwFilterFlags)
fMatch = !fMatch;
if (!fMatch)
goto Exit;
}
//If we haven't failed by this point, we may need to AddUsage and read
//props from the mailmsg. Double-check to make sure that we need to
//add usage.
if (!((AQ_MSG_FILTER_MESSAGEID | AQ_MSG_FILTER_SENDER | AQ_MSG_FILTER_RECIPIENT) &
dwFilterFlags))
goto Exit;
//
// Check to see if the message is already open
//
hr = pIMailMsgProperties->GetDWORD(
IMMPID_MPV_MESSAGE_OPEN_HANDLES,
&cOpenHandlesForMsg);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties, "Not running SP1 of W2K");
cOpenHandlesForMsg = 0;
hr = S_OK;
}
if (AQ_MSG_FILTER_MESSAGEID & dwFilterFlags)
{
hr = HrQueueAdminGetStringProp(pIMailMsgProperties,
IMMPID_MP_RFC822_MSG_ID,
&szMsgId);
if (FAILED(hr))
szMsgId = NULL;
fMatch = paqmf->fMatchesId(szMsgId);
if (!fMatch)
goto Exit;
}
if (AQ_MSG_FILTER_SENDER & dwFilterFlags)
{
fMatch = paqmf->fMatchesMailMsgSender(pIMailMsgProperties);
if (!fMatch)
goto Exit;
}
if (AQ_MSG_FILTER_RECIPIENT & dwFilterFlags)
{
fMatch = paqmf->fMatchesMailMsgRecipient(pIMailMsgProperties);
if (!fMatch)
goto Exit;
}
Exit:
//
// If this operation resulted in opening the message, we should close it
// The message is not dirty (we did not write anything), so it should not
// need to commit
//
if (!cOpenHandlesForMsg)
{
HRESULT hrTmp = S_OK;
hrTmp = HrReleaseIMailMsgUsageCount(pIMailMsgProperties);
if (SUCCEEDED(hrTmp))
HrIncrementIMailMsgUsageCount(pIMailMsgProperties);
}
if (szMsgId)
QueueAdminFree(szMsgId);
TraceFunctLeave();
return fMatch;
}
//---[ CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo ]-----------------------
//
// Description:
// Fills out a queue admin MESSAGE_INFO structure. All allocations are
// done with pvQueueAdminAlloc to be freed by the RPC code
// Parameters:
// IN pIMailMsgProperties mail msg object to get info from
// IN OUT pMsgInfo MESSAGE_INFO struct to dump data to
// Returns:
// S_OK on success
// AQUEUE_E_MESSAGE_HANDLED if the underlying message has been deleted
// E_OUTOFMEMORY if an allocation failure
// History:
// 8/8/00 - t-toddc created
// 12/11/2000 - MikeSwa Merged for checkin
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo(
IMailMsgProperties* pIMailMsgProperties,
MESSAGE_INFO* pMsgInfo,
PVOID pvContext)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties,
"CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo");
HRESULT hr = S_OK;
DWORD cbProp = 0;
DWORD cOpenHandlesForMsg = 0;
LPSTR szRecipients = NULL;
LPSTR szCCRecipients = NULL;
LPSTR szBCCRecipients = NULL;
FILETIME ftSubmitted = {0,0}; //Origination time property buffer
FILETIME ftQueueEntry = {0,0};
FILETIME ftExpire = {0,0};
DWORD cbMsgSize = 0;
CQueueAdminContext *pqapictx = (CQueueAdminContext *) pvContext;
_ASSERT(pIMailMsgProperties);
_ASSERT(pMsgInfo);
_ASSERT(pqapictx);
_ASSERT(pqapictx->fIsValid());
//
// If we have a context... it better be valid
//
if (pqapictx && !pqapictx->fIsValid()) {
_ASSERT(FALSE && "CQueueAdminContext is not valid");
pqapictx = NULL; //be defensive... don't use it.
}
//
// Check to see if the message is already open
//
hr = pIMailMsgProperties->GetDWORD(
IMMPID_MPV_MESSAGE_OPEN_HANDLES,
&cOpenHandlesForMsg);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties, "Not running SP1 of W2K");
cOpenHandlesForMsg = 0;
hr = S_OK;
}
//
// Extract properties that are stored only on mailmsg (this is shared
// with all QAPI code).
//
hr = HrGetMsgInfoFromIMailMsgProperty(pIMailMsgProperties,
pMsgInfo);
if (FAILED(hr))
goto Exit;
//can't report the number of failures from IMailMsgProperties
pMsgInfo->cFailures = 0;
//Get the size of the message
hr = HrQADMGetMsgSize(pIMailMsgProperties, &cbMsgSize);
if (FAILED(hr))
goto Exit;
pMsgInfo->cbMessageSize = cbMsgSize;
//Get time message was queued
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME),
&cbProp,
(BYTE *) &ftQueueEntry);
if (FAILED(hr))
{
// there is a possibility that the msg will not have entry time
// (i.e. presubmission queue)
if (MAILMSG_E_PROPNOTFOUND == hr)
{
ZeroMemory(&ftQueueEntry, sizeof(FILETIME));
hr = S_OK;
}
else
goto Exit;
}
//Get submission and expiration times
QueueAdminFileTimeToSystemTime(&ftQueueEntry,
&pMsgInfo->stReceived);
//Get the time the message entered the org
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ORIGINAL_ARRIVAL_TIME,
sizeof(FILETIME), &cbProp, (BYTE *) &ftSubmitted);
if (FAILED(hr))
{
//Time was not written... use entry time.
hr = S_OK;
memcpy(&ftSubmitted, &ftQueueEntry, sizeof(FILETIME));
}
QueueAdminFileTimeToSystemTime(&ftSubmitted, &pMsgInfo->stSubmission);
// Try to get the expire time from the message, otherwise calculate it
// from the file time
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_NDR,
sizeof(FILETIME), &cbProp, (BYTE *) &ftExpire);
if (MAILMSG_E_PROPNOTFOUND == hr)
{
if (pqapictx->paqinstGetAQ())
{
// Prop not set ... calculate it from the file time
pqapictx->paqinstGetAQ()->CalcExpireTimeNDR(ftQueueEntry, TRUE, &ftExpire);
// This is OK
hr = S_OK;
}
else
{
// This shouldn't happen but we don't want to crash in RTL over it
_ASSERT(FALSE && "AQInst was not set in context!");
// We can return this field blank
ZeroMemory(&ftExpire, sizeof(FILETIME));
hr = S_OK;
}
}
else if (FAILED(hr))
{
goto Exit;
}
QueueAdminFileTimeToSystemTime(&ftExpire, &pMsgInfo->stExpiry);
//
// Get the state of the message
//
pMsgInfo->fMsgFlags = MP_NORMAL;
if (pqapictx)
{
if (LI_RETRY == pqapictx->lfGetQueueState())
pMsgInfo->fMsgFlags |= MP_MSG_RETRY;
else if (LI_FROZEN == pqapictx->lfGetQueueState())
pMsgInfo->fMsgFlags |= MP_MSG_FROZEN;
}
Exit:
//
// If this operation resulted in opening the message, we should close it
// The message is not dirty (we did not write anything), so it should not
// need to commit
//
if (!cOpenHandlesForMsg)
{
HRESULT hrTmp = S_OK;
hrTmp = HrReleaseIMailMsgUsageCount(pIMailMsgProperties);
if (SUCCEEDED(hrTmp))
HrIncrementIMailMsgUsageCount(pIMailMsgProperties);
}
TraceFunctLeave();
return hr;
}
//---[ CAsyncAdminMailMsgQueue::HrInternalQuerySupportedActions ]---------------
//
//
// Description:
// Returns the actions and filters that this implementation supports
// Parameters:
// pdwSupportedActions - QAPI MsgActions that this queue suppprts
// pdwSupportedFilterFlags - QAPI filter flags that this queue supports
// Returns:
// S_OK on success
// History:
// 12/12/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAsyncAdminMailMsgQueue::HrInternalQuerySupportedActions(
DWORD *pdwSupportedActions,
DWORD *pdwSupportedFilterFlags)
{
TraceFunctEnterEx((LPARAM) this, "CAsyncAdminMailMsgQueue::HrInternalQuerySupportedActions");
HRESULT hr = S_OK;
hr = QueryDefaultSupportedActions(pdwSupportedActions, pdwSupportedFilterFlags);
if (FAILED(hr))
goto Exit;
//
// This queue implementation does not support all of the default flags.
//
_ASSERT(pdwSupportedActions);
_ASSERT(pdwSupportedFilterFlags);
//
// We don't support:
// - Freeze global - No status to set on a mailmsg
// - Thaw global - can't freeze... therefore cannot thaw
//
*pdwSupportedActions &= ~(MA_FREEZE_GLOBAL | MA_THAW_GLOBAL);
//
// We don't support
// - Checking for frozen messages (we have no status to indicate
// that a message is frozen)
//
*pdwSupportedFilterFlags &= ~(MF_FROZEN);
Exit:
return hr;
}