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.
981 lines
31 KiB
981 lines
31 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// File: aqutil.cpp
|
|
//
|
|
// Description:
|
|
//
|
|
// Author: Mike Swafford (MikeSwa)
|
|
//
|
|
// History:
|
|
// 7/20/98 - MikeSwa Created
|
|
//
|
|
// Copyright (C) 1998 Microsoft Corporation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "aqprecmp.h"
|
|
#include "aqutil.h"
|
|
|
|
//---[ HrIncrementIMailMsgUsageCount ]-------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Calls IMailMsgQueueMgmt::AddUsage. Handles calling QueryInterface
|
|
// for the right interface
|
|
// Parameters:
|
|
// pIUnknown - ptr to IUknown for MailMsg
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 7/20/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrIncrementIMailMsgUsageCount(IUnknown *pIUnknown)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIUnknown, "HrIncrementIMailMsgUsageCount");
|
|
HRESULT hr = S_OK;
|
|
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
|
|
_ASSERT(pIUnknown);
|
|
|
|
hr = pIUnknown->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
|
|
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pIMailMsgQueueMgmt->AddUsage();
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
Exit:
|
|
if (pIMailMsgQueueMgmt)
|
|
pIMailMsgQueueMgmt->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ HrReleaseIMailMsgUsageCount ]-------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Calls IMailMsgQueueMgmt::ReleaseUsage. Handles calling QueryInterface
|
|
// for the right interface
|
|
// Parameters:
|
|
// pIUnknown - ptr to IUknown for MailMsg
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 7/20/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrReleaseIMailMsgUsageCount(IUnknown *pIUnknown)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIUnknown, "HrReleaseIMailMsgUsageCount");
|
|
HRESULT hr = S_OK;
|
|
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
|
|
_ASSERT(pIUnknown);
|
|
|
|
hr = pIUnknown->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
|
|
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pIMailMsgQueueMgmt->ReleaseUsage();
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
Exit:
|
|
if (pIMailMsgQueueMgmt)
|
|
pIMailMsgQueueMgmt->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ HrDeleteIMailMsg ]------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Deletes a Msg and releases its usage count
|
|
// Parameters:
|
|
// pIUnknown Ptr to mailmsg
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 7/21/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrDeleteIMailMsg(IUnknown *pIUnknown)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIUnknown, "HrDeleteIMailMsg");
|
|
HRESULT hr = S_OK;
|
|
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
|
|
_ASSERT(pIUnknown);
|
|
|
|
hr = pIUnknown->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
|
|
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pIMailMsgQueueMgmt->Delete(NULL);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
Exit:
|
|
if (pIMailMsgQueueMgmt)
|
|
pIMailMsgQueueMgmt->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//---[ HrWalkMailMsgQueueForShutdown ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Function to walk an IMailMsg queue at shutdown and clear out all of the
|
|
// IMailMsgs
|
|
// Parameters:
|
|
// IN pIMailMsgProperties //ptr to data on queue
|
|
// IN PVOID pvContext //list of queues to prepare for DSN
|
|
// OUT BOOL *pfContinue, //TRUE if we should continue
|
|
// OUT BOOL *pfDelete); //TRUE if item should be deleted
|
|
// Returns:
|
|
// S_OK
|
|
// History:
|
|
// 7/20/98 - MikeSwa Created
|
|
// 7/7/99 - Added async shutdown
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrWalkMailMsgQueueForShutdown(IN IMailMsgProperties *pIMailMsgProperties,
|
|
IN PVOID pvContext, OUT BOOL *pfContinue,
|
|
OUT BOOL *pfDelete)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrWalkMailMsgQueueForShutdown");
|
|
Assert(pfContinue);
|
|
Assert(pfDelete);
|
|
HRESULT hrTmp = S_OK;
|
|
CAQSvrInst *paqinst = (CAQSvrInst *) pvContext;
|
|
|
|
_ASSERT(pIMailMsgProperties);
|
|
_ASSERT(paqinst);
|
|
|
|
|
|
*pfContinue = TRUE;
|
|
*pfDelete = TRUE;
|
|
|
|
//call server stop hint function
|
|
paqinst->ServerStopHintFunction();
|
|
|
|
//Add to queue so async thread will have final release and the associated I/O
|
|
pIMailMsgProperties->AddRef();
|
|
paqinst->HrQueueWorkItem(pIMailMsgProperties, fMailMsgShutdownCompletion);
|
|
|
|
TraceFunctLeave();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//---[ fMailMsgShutdownCompletion ]---------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// CAsyncWorkQueue completion function to allow multi-threaded shutdown
|
|
// of a MailMsgQueue
|
|
// Parameters:
|
|
// IN pvContext - A mailmsg to release
|
|
// IN dwStatus - The status passed in by the async work queue
|
|
// Returns:
|
|
// TRUE always
|
|
// History:
|
|
// 7/7/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL fMailMsgShutdownCompletion(PVOID pvContext, DWORD dwStatus)
|
|
{
|
|
IMailMsgProperties *pIMailMsgProperties = (IMailMsgProperties *) pvContext;
|
|
HRESULT hr = S_OK;
|
|
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
|
|
_ASSERT(pIMailMsgProperties);
|
|
|
|
if (!pIMailMsgProperties)
|
|
goto Exit;
|
|
|
|
//Bounce the usage count to force this thread to do any necessary commits
|
|
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt,
|
|
(PVOID *) &pIMailMsgQueueMgmt);
|
|
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgQueueMgmt FAILED");
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pIMailMsgQueueMgmt->ReleaseUsage();
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pIMailMsgQueueMgmt->AddUsage();
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
Exit:
|
|
|
|
if (pIMailMsgProperties)
|
|
pIMailMsgProperties->Release();
|
|
|
|
if (pIMailMsgQueueMgmt)
|
|
pIMailMsgQueueMgmt->Release();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//---[ HrWalkMsgRefQueueForShutdown ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Function to walk a queue containing msgrefs at shutdown and
|
|
// clear out all of the IMailMsgs
|
|
// Parameters:
|
|
// IN CMsgRef pmsgref, //ptr to data on queue
|
|
// IN PVOID pvContext //list of queues to prepare for DSN
|
|
// OUT BOOL *pfContinue, //TRUE if we should continue
|
|
// OUT BOOL *pfDelete); //TRUE if item should be deleted
|
|
// Returns:
|
|
// S_OK - *always*
|
|
// History:
|
|
// 7/20/98 - MikeSwa Created
|
|
// 7/7/99 - MikeSwa Added async shutdown
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrWalkMsgRefQueueForShutdown(IN CMsgRef *pmsgref,
|
|
IN PVOID pvContext, OUT BOOL *pfContinue,
|
|
OUT BOOL *pfDelete)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pmsgref, "HrWalkMsgRefQueueForShutdown");
|
|
Assert(pfContinue);
|
|
Assert(pfDelete);
|
|
CAQSvrInst *paqinst = (CAQSvrInst *) pvContext;
|
|
_ASSERT(pmsgref);
|
|
_ASSERT(paqinst);
|
|
|
|
*pfContinue = TRUE;
|
|
*pfDelete = TRUE;
|
|
|
|
//call server stop hint function
|
|
if (paqinst)
|
|
paqinst->ServerStopHintFunction();
|
|
|
|
//Add to queue so async thread will have final release and the associated I/O
|
|
pmsgref->AddRef();
|
|
paqinst->HrQueueWorkItem(pmsgref, fMsgRefShutdownCompletion);
|
|
|
|
TraceFunctLeave();
|
|
return S_OK;
|
|
}
|
|
|
|
//---[ fMsgRefShutdownCompletion ]---------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// CAsyncWorkQueue completion function to allow multi-threaded shutdown
|
|
// of a MailMsgQueue
|
|
// Parameters:
|
|
// IN pvContext - A mailmsg to release
|
|
// IN dwStatus - The status passed in by the async work queue
|
|
// Returns:
|
|
// TRUE always
|
|
// History:
|
|
// 7/7/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL fMsgRefShutdownCompletion(PVOID pvContext, DWORD dwStatus)
|
|
{
|
|
CMsgRef *pmsgref = (CMsgRef *) pvContext;
|
|
_ASSERT(pmsgref);
|
|
|
|
if (!pmsgref)
|
|
return TRUE;
|
|
|
|
//Call prepare to shutdown to force async threads to be the ones
|
|
//doing that actual IO
|
|
pmsgref->PrepareForShutdown();
|
|
pmsgref->Release();
|
|
return TRUE;
|
|
}
|
|
|
|
//---[ CalcDMTPerfCountersIteratorFn ]-----------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Iterator function used to walk the DMT and generate the perf counters
|
|
// we are interested in.
|
|
// Parameters:
|
|
// IN pvContext - pointer to context (current total of pending msgs)
|
|
// IN pvData - CDomainEntry for the given domain
|
|
// IN fWildcardData - TRUE if data is a wildcard entry (ignored)
|
|
// OUT pfContinue - TRUE if iterator should continue to the next entry
|
|
// OUT pfDelete - TRUE if entry should be deleted
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 7/29/98 - MikeSwa Created
|
|
// 7/31/98 - MikeSwa Modified (Added link state counters)
|
|
// 9/22/98 - MikeSwa Changed from domain flags to link flags
|
|
//
|
|
// Note:
|
|
// Currently the status is stored on the domain entry (and not the link).
|
|
// At some point we will have to differentiate this, and add flags to
|
|
// the link. In this funciton the cLinkCount variable is a temporary
|
|
// workaround.
|
|
//-----------------------------------------------------------------------------
|
|
VOID CalcDMTPerfCountersIteratorFn(PVOID pvContext, PVOID pvData,
|
|
BOOL fWildcard, BOOL *pfContinue,
|
|
BOOL *pfDelete)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pvData, "CalcMsgsPendingRetryIteratorFn");
|
|
CDomainEntry *pdentry = (CDomainEntry *) pvData;
|
|
AQPerfCounters *pAQPerfCounters = (AQPerfCounters *) pvContext;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
CDomainEntryLinkIterator delit;
|
|
DWORD cRetryMsgsOnCurrentLink = 0;
|
|
DWORD dwLinkStateFlags = 0;
|
|
HRESULT hr = S_OK;
|
|
BOOL fLinkEnabled = TRUE;
|
|
|
|
_ASSERT(pvContext);
|
|
_ASSERT(pvData);
|
|
_ASSERT(pfContinue);
|
|
_ASSERT(pfDelete);
|
|
_ASSERT(sizeof(AQPerfCounters) == pAQPerfCounters->cbVersion);
|
|
|
|
|
|
//Always continue, and never delete
|
|
*pfContinue = TRUE;
|
|
*pfDelete = FALSE;
|
|
|
|
hr = delit.HrInitialize(pdentry);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
while (plmq = delit.plmqGetNextLinkMsgQueue(plmq))
|
|
{
|
|
dwLinkStateFlags = plmq->dwGetLinkState();
|
|
fLinkEnabled = TRUE;
|
|
|
|
// msgs on the retry queue should be added to remote retry queue counter
|
|
cRetryMsgsOnCurrentLink = plmq->cGetRetryMsgCount();
|
|
if (!(LINK_STATE_RETRY_ENABLED & dwLinkStateFlags))
|
|
{
|
|
//Link is pending retry
|
|
fLinkEnabled = FALSE;
|
|
|
|
// also add #of msgs NOT on retry queue
|
|
cRetryMsgsOnCurrentLink += plmq->cGetTotalMsgCount();
|
|
}
|
|
|
|
pAQPerfCounters->cCurrentMsgsPendingRemoteRetry += cRetryMsgsOnCurrentLink;
|
|
|
|
if (!(LINK_STATE_SCHED_ENABLED & dwLinkStateFlags))
|
|
{
|
|
//Link is pending a scheduled connection
|
|
fLinkEnabled = FALSE;
|
|
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingScheduling++;
|
|
}
|
|
|
|
if (LINK_STATE_PRIV_CONFIG_TURN_ETRN & dwLinkStateFlags)
|
|
{
|
|
//Link is a TURN/ETRN link
|
|
if (!((LINK_STATE_PRIV_ETRN_ENABLED | LINK_STATE_PRIV_TURN_ENABLED)
|
|
& dwLinkStateFlags))
|
|
fLinkEnabled = FALSE; //link is not currently being serviced
|
|
|
|
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingTURNETRN++;
|
|
}
|
|
|
|
if (LINK_STATE_ADMIN_HALT & dwLinkStateFlags)
|
|
{
|
|
//Link is currently frozen by admin
|
|
fLinkEnabled = FALSE;
|
|
pAQPerfCounters->cCurrentRemoteNextHopLinksFrozenByAdmin++;
|
|
}
|
|
|
|
if (fLinkEnabled)
|
|
{
|
|
//There are no flags set that indicate this link is not enabled
|
|
pAQPerfCounters->cCurrentRemoteNextHopLinksEnabled++;
|
|
}
|
|
|
|
}
|
|
|
|
Exit:
|
|
if (plmq)
|
|
plmq->Release();
|
|
|
|
TraceFunctLeave();
|
|
}
|
|
|
|
|
|
//---[ dwInterlockedSetBits ]--------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Set bits in a DWORD in a thread sate manner
|
|
// Parameters:
|
|
// IN pdwTarget Ptr to DWORD to modify
|
|
// IN dwFlagMask bits to set
|
|
// Returns:
|
|
// Original value
|
|
// History:
|
|
// 8/3/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DWORD dwInterlockedSetBits(DWORD *pdwTarget, DWORD dwFlagMask)
|
|
{
|
|
DWORD dwChk;
|
|
DWORD dwTmp;
|
|
|
|
_ASSERT(pdwTarget);
|
|
do
|
|
{
|
|
dwChk = *pdwTarget;
|
|
dwTmp = dwChk | dwFlagMask;
|
|
if (dwChk == dwTmp) //no work to be done
|
|
break;
|
|
} while (InterlockedCompareExchange((PLONG) pdwTarget,
|
|
(LONG) dwTmp,
|
|
(LONG) dwChk) != (LONG) dwChk);
|
|
|
|
return dwChk;
|
|
}
|
|
|
|
//---[ dwInterlockedUnsetBits ]------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Unset bits in a DWORD in a thread sate manner
|
|
// Parameters:
|
|
// IN pdwTarget Ptr to DWORD to modify
|
|
// IN dwFlagMask bits to unset
|
|
// Returns:
|
|
// Original value
|
|
// History:
|
|
// 8/3/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DWORD dwInterlockedUnsetBits(DWORD *pdwTarget, DWORD dwFlagMask)
|
|
{
|
|
DWORD dwChk;
|
|
DWORD dwTmp;
|
|
|
|
_ASSERT(pdwTarget);
|
|
do
|
|
{
|
|
dwChk = *pdwTarget;
|
|
dwTmp = dwChk & ~dwFlagMask;
|
|
if (dwChk == dwTmp) //no work to be done
|
|
break;
|
|
} while (InterlockedCompareExchange((PLONG) pdwTarget,
|
|
(LONG) dwTmp,
|
|
(LONG) dwChk) != (LONG) dwChk);
|
|
|
|
return dwChk;
|
|
}
|
|
|
|
//---[ HrWalkPreLocalQueueForDSN ]---------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Function to walk the pre-local delivery queue for DSN generation
|
|
// Parameters:
|
|
// IN CMsgRef pmsgref ptr to data on queue
|
|
// IN PVOID pvContext ptr to CAQSvrInst
|
|
// OUT BOOL *pfContinue TRUE if we should continue
|
|
// OUT BOOL *pfDelete TRUE if item should be deleted
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 8/14/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrWalkPreLocalQueueForDSN(IN CMsgRef *pmsgref, IN PVOID pvContext,
|
|
OUT BOOL *pfContinue, OUT BOOL *pfDelete)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pmsgref, "HrWalkPreLocalQueueForDSN");
|
|
HRESULT hr = S_OK;
|
|
DWORD dwDSNFlags = 0;
|
|
CLinkMsgQueue *plmq = NULL;
|
|
BOOL fShutdownLock = FALSE;
|
|
CAQStats aqstats;
|
|
CAQSvrInst *paqinst = (CAQSvrInst *)pvContext;
|
|
|
|
_ASSERT(pfContinue);
|
|
_ASSERT(pfDelete);
|
|
_ASSERT(paqinst);
|
|
|
|
*pfContinue = TRUE; //always keep walking queue
|
|
*pfDelete = FALSE; //keep message in queue unless we NDR it
|
|
|
|
if (!paqinst)
|
|
goto Exit;
|
|
|
|
if (!paqinst->fTryShutdownLock())
|
|
{
|
|
//If we got a shutdown hint...we should bail
|
|
*pfContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
fShutdownLock = TRUE;
|
|
|
|
hr = pmsgref->HrSendDelayOrNDR(CMsgRef::MSGREF_DSN_LOCAL_QUEUE |
|
|
CMsgRef::MSGREF_DSN_SEND_DELAY,
|
|
NULL, AQUEUE_E_MSG_EXPIRED, &dwDSNFlags);
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) pmsgref, "ERROR: HrSendDelayOrNDR failed - hr 0x%08X", hr);
|
|
goto Exit;
|
|
}
|
|
|
|
//We need to remove this message from the queue
|
|
if ((CMsgRef::MSGREF_DSN_SENT_NDR | CMsgRef::MSGREF_HANDLED) & dwDSNFlags)
|
|
{
|
|
*pfDelete = TRUE;
|
|
|
|
//Update relevant counters
|
|
paqinst->DecPendingLocal();
|
|
|
|
//
|
|
// Get local link and update stats
|
|
//
|
|
plmq = paqinst->pdmtGetDMT()->plmqGetLocalLink();
|
|
|
|
if (plmq)
|
|
{
|
|
pmsgref->GetStatsForMsg(&aqstats);
|
|
plmq->HrNotify(&aqstats, FALSE);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
if (fShutdownLock)
|
|
paqinst->ShutdownUnlock();
|
|
|
|
if (plmq)
|
|
plmq->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---[ HrReGetMessageType ]-----------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Regets the message type after a retry failure
|
|
// Parameters:
|
|
// IN pIMailMsgProperties Message we are interested in
|
|
// IN pIMessageRouter Router for that message
|
|
// IN OUT pdwMessageType Old/New message type for the message
|
|
// Returns:
|
|
// S_OK on success
|
|
// Passes through errors from ReleaseMessageType & GetMessageType
|
|
// History:
|
|
// 9/14/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrReGetMessageType(IN IMailMsgProperties *pIMailMsgProperties,
|
|
IN IMessageRouter *pIMessageRouter,
|
|
IN OUT DWORD *pdwMessageType)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrReGetMessageType");
|
|
HRESULT hr = S_OK;
|
|
|
|
//$$REVIEW - we might not have to get a new message type here... we
|
|
//might only need it on a specific error code returned by HrInitialize.
|
|
//Get New Messagetype... in case that changed
|
|
hr = pIMessageRouter->ReleaseMessageType(*pdwMessageType, 1);
|
|
if (FAILED(hr))
|
|
{
|
|
_ASSERT(SUCCEEDED(hr) && "ReleaseMessageType failed... may leak message types");
|
|
ErrorTrace((LPARAM) pIMailMsgProperties,
|
|
"ERROR: ReleaseMessageType failed! - hr 0x%08X", hr);
|
|
hr = S_OK; //we are about to retry anyway
|
|
}
|
|
|
|
hr = pIMessageRouter->GetMessageType(pIMailMsgProperties, pdwMessageType);
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) pIMailMsgProperties,
|
|
"ERROR: Unable to re-get message type - HR 0x%08X", hr);
|
|
goto Exit; //we cannot recover from this
|
|
}
|
|
|
|
Exit:
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
//Used to guarantee uniqueue files names
|
|
DWORD g_cUniqueueFileNames = 0;
|
|
|
|
//---[ GetUniqueFileName ]-----------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Creates a uniqueue file name
|
|
// Parameters:
|
|
// pft Ptr to current filetime
|
|
// szFileBuffer Buffer to put string into... should be
|
|
// at least UNIQUEUE_FILENAME_BUFFER_SIZE
|
|
// szExtension Extension for file name... if longer than three chars,
|
|
// you will need to increase the size of szFileBuffer
|
|
// accordingly.
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 10/9/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void GetUniqueFileName(IN FILETIME *pft, IN LPSTR szFileBuffer,
|
|
IN LPSTR szExtension)
|
|
{
|
|
DWORD cbFileNameSize = 0;
|
|
DWORD cUnique = InterlockedIncrement((PLONG) &g_cUniqueueFileNames);
|
|
SYSTEMTIME systime;
|
|
|
|
_ASSERT(szFileBuffer);
|
|
_ASSERT(szExtension);
|
|
|
|
FileTimeToSystemTime(pft, &systime);
|
|
|
|
cbFileNameSize = wsprintf(
|
|
szFileBuffer,
|
|
"%05.5x%02.2d%02.2d%02.2d%02.2d%02.2d%04.4d%08X.%s",
|
|
systime.wMilliseconds,
|
|
systime.wSecond, systime.wMinute, systime.wHour,
|
|
systime.wDay, systime.wMonth, systime.wYear,
|
|
cUnique, szExtension);
|
|
|
|
//Assert that are constant is big enough
|
|
//By default... allow room for ".eml" extension
|
|
_ASSERT((cbFileNameSize + 4 - lstrlen(szExtension)) < UNIQUEUE_FILENAME_BUFFER_SIZE);
|
|
}
|
|
|
|
|
|
|
|
//---[ HrLinkAllDomains ]-------------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Ultility function to link all domains together for recipient enumeration
|
|
// (primarly used to NDR an entire message).
|
|
//
|
|
// Parameters:
|
|
// pIMailMsgProperties IMailMsgProperties to link domains together for
|
|
// Returns:
|
|
// S_OK on success
|
|
// History:
|
|
// 10/14/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrLinkAllDomains(IN IMailMsgProperties *pIMailMsgProperties)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrLinkAllDomains");
|
|
HRESULT hr = S_OK;
|
|
DWORD cDomains = 0;
|
|
DWORD iCurrentDomain = 1;
|
|
IMailMsgRecipients *pIMailMsgRecipients = NULL;
|
|
|
|
_ASSERT(pIMailMsgProperties);
|
|
|
|
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
|
|
(void **) &pIMailMsgRecipients);
|
|
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgRecipients failed");
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
hr = pIMailMsgRecipients->DomainCount(&cDomains);
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: Unable to get DomainCount - hr 0x%08X", hr);
|
|
goto Exit;
|
|
}
|
|
|
|
//Set up domain list for all domains
|
|
for (iCurrentDomain = 1; iCurrentDomain < cDomains; iCurrentDomain++)
|
|
{
|
|
hr = pIMailMsgRecipients->SetNextDomain(iCurrentDomain-1, iCurrentDomain,
|
|
FLAG_OVERWRITE_EXISTING_LINKS);
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: SetNextDomain Failed - hr 0x%08X", hr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//handle single domain case
|
|
if (1 == cDomains)
|
|
{
|
|
hr = pIMailMsgRecipients->SetNextDomain(0, 0, FLAG_SET_FIRST_DOMAIN);
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: SetNextDomain Failed for single domain- hr 0x%08X", hr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pIMailMsgRecipients)
|
|
pIMailMsgRecipients->Release();
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|
|
|
|
|
|
//Parses a GUID from a string... returns TRUE on success
|
|
//---[ fAQParseGuidString ]----------------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Attempts to parse a GUID from a string of hex digits..
|
|
// Can handle punctuation, spaces and even leading "0x"'s.
|
|
// Parameters:
|
|
// IN szGuid String to parse GUID from
|
|
// IN cbGuid Max size of GUID string buffer
|
|
// OUT guidID GUID parsed from string
|
|
// Returns:
|
|
// TRUE if a guid value could be parsed from the string
|
|
// FALSE if a guid value could not be parsed from the string
|
|
// History:
|
|
// 10/15/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL fAQParseGuidString(LPSTR szGuid, DWORD cbGuid, GUID *pguid)
|
|
{
|
|
const DWORD NO_SUCH_VALUE = 0xFF;
|
|
BOOL fParsed = FALSE;
|
|
BOOL fLastCharZero = FALSE; //Used to handle "0x"
|
|
DWORD *pdwGuid = (DWORD *) pguid;
|
|
DWORD cDigits = 0;
|
|
DWORD dwValue = NO_SUCH_VALUE;
|
|
LPSTR szCurrent = szGuid;
|
|
LPSTR szStop = szGuid + cbGuid/sizeof(CHAR);
|
|
|
|
//Use DWORD array to populate GUID
|
|
*pdwGuid = 0;
|
|
while ((szStop > szCurrent) && (*szCurrent))
|
|
{
|
|
dwValue = NO_SUCH_VALUE;
|
|
if (('0' <= *szCurrent) && ('9' >= *szCurrent))
|
|
dwValue = *szCurrent-'0';
|
|
else if (('a' <= *szCurrent) && ('f' >= *szCurrent))
|
|
dwValue = 10 + *szCurrent-'a';
|
|
else if (('A' <= *szCurrent) && ('F' >= *szCurrent))
|
|
dwValue = 10 + *szCurrent-'A';
|
|
else if (fLastCharZero &&
|
|
(('x' == *szCurrent) || ('X' == *szCurrent)))
|
|
{
|
|
//back out last shift (we don't have to subtract anything, since
|
|
//the value was zero).
|
|
_ASSERT(cDigits);
|
|
if (0 == (cDigits % 8)) //happened when we changed DWORDs
|
|
pdwGuid--;
|
|
else
|
|
*pdwGuid /= 16; //undo last shift
|
|
cDigits--;
|
|
}
|
|
|
|
//Set flag for handling 0x sequence
|
|
if (0 != dwValue)
|
|
fLastCharZero = FALSE;
|
|
else
|
|
fLastCharZero = TRUE;
|
|
|
|
//In all string guid representations... a valid hex number is at least
|
|
//2 characters long... so 0x0 should be mapped to 0x00 and 0xa should
|
|
//be mapped to 0x0a. Check and see if such a situation is happening
|
|
if ((NO_SUCH_VALUE == dwValue) && (0 != (cDigits % 2)) &&
|
|
(',' == *szCurrent))
|
|
{
|
|
//undo last add and shift. The next if clause will re-write
|
|
//the last value in at the proper point
|
|
*pdwGuid /= 16;
|
|
dwValue = *pdwGuid & 0x0000000F;
|
|
*pdwGuid &= 0xFFFFFFF0;
|
|
*pdwGuid *= 16;
|
|
}
|
|
|
|
//Add value to GUID if hex character
|
|
if (NO_SUCH_VALUE != dwValue)
|
|
{
|
|
*pdwGuid += dwValue;
|
|
if (0 == (++cDigits % 8))
|
|
{
|
|
//We have reached a DWORD boundary... move on
|
|
if (32 == cDigits)
|
|
{
|
|
//quit when we have enough
|
|
fParsed = TRUE;
|
|
break;
|
|
}
|
|
pdwGuid++;
|
|
*pdwGuid = 0;
|
|
}
|
|
else
|
|
*pdwGuid *= 16;
|
|
}
|
|
szCurrent++;
|
|
}
|
|
|
|
//Handle ending 0xa (should be 0x0a) digits
|
|
if (!fParsed && (31 == cDigits))
|
|
{
|
|
dwValue = *pdwGuid & 0x000000FF;
|
|
_ASSERT(!(dwValue & 0x0000000F));
|
|
*pdwGuid &= 0xFFFFFF00;
|
|
dwValue /= 16;
|
|
*pdwGuid += dwValue;
|
|
fParsed = TRUE;
|
|
}
|
|
|
|
return fParsed;
|
|
}
|
|
|
|
|
|
//---[ InterlockedAddSubtractULARGE ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Performs "interlocked" Add/Subtract on ULARGE_INTEGER structures.
|
|
//
|
|
// Uses s_slUtilityData to synchronize if neccessary.
|
|
// Parameters:
|
|
// IN puliValue ULARGE to modify
|
|
// IN puliNew ULARGE to modify value with
|
|
// IN fAdd TRUE if we are adding new value
|
|
// FALSE if we are subtracting
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 11/2/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void InterlockedAddSubtractULARGE(ULARGE_INTEGER *puliValue,
|
|
ULARGE_INTEGER *puliNew, BOOL fAdd)
|
|
{
|
|
_ASSERT(puliValue);
|
|
_ASSERT(puliNew);
|
|
ULARGE_INTEGER uliTmp = {0};
|
|
BOOL fDone = FALSE;
|
|
DWORD dwTmp = 0;
|
|
DWORD dwHighPart = 0;
|
|
DWORD dwLowPart = 0;
|
|
static CShareLockNH s_slUtilityData; //Used to synchronize global updates of ULONG
|
|
|
|
s_slUtilityData.ShareLock();
|
|
BOOL fShareLock = TRUE; //FALSE implies Exclusive lock
|
|
|
|
while (!fDone)
|
|
{
|
|
uliTmp.QuadPart = puliValue->QuadPart;
|
|
dwHighPart = uliTmp.HighPart;
|
|
dwLowPart = uliTmp.LowPart;
|
|
|
|
if (fAdd)
|
|
uliTmp.QuadPart += puliNew->QuadPart; //add volume
|
|
else
|
|
uliTmp.QuadPart -= puliNew->QuadPart;
|
|
|
|
//First see of the high part needs updating
|
|
if (dwHighPart != uliTmp.HighPart)
|
|
{
|
|
if (fShareLock)
|
|
{
|
|
//This only happens every 4GB of data per queue..
|
|
//which means we shouldn't be hitting this lock that
|
|
//often
|
|
s_slUtilityData.ShareUnlock();
|
|
s_slUtilityData.ExclusiveLock();
|
|
fShareLock = FALSE;
|
|
|
|
//Go back to top of loop and re-get data
|
|
continue;
|
|
}
|
|
|
|
//At this point it is just safe for us to update the values
|
|
puliValue->QuadPart = uliTmp.QuadPart;
|
|
}
|
|
else if (dwLowPart != uliTmp.LowPart)
|
|
{
|
|
//Only need to update the low DWORD
|
|
dwTmp = (DWORD) InterlockedCompareExchange(
|
|
(PLONG) &(puliValue->LowPart),
|
|
(LONG) uliTmp.LowPart,
|
|
(LONG) dwLowPart);
|
|
if (dwLowPart != dwTmp)
|
|
continue; //update failed
|
|
}
|
|
|
|
fDone = TRUE;
|
|
}
|
|
|
|
if (fShareLock)
|
|
s_slUtilityData.ShareUnlock();
|
|
else
|
|
s_slUtilityData.ExclusiveUnlock();
|
|
|
|
}
|
|
|
|
//---[ HrValidateMessageContent ]----------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Validates a message based on its content handle. If the backing store
|
|
// has been deleted, and the handle is not cached, we should detect this.
|
|
// Parameters:
|
|
// pIMailMsgProperties - MailMsg to validate
|
|
// Returns:
|
|
// HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) Message belongs to this store
|
|
// driver but is no longer valid
|
|
// other error code from store driver interface or mailmsg
|
|
// History:
|
|
// 4/13/2000 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT HrValidateMessageContent(IMailMsgProperties *pIMailMsgProperties)
|
|
{
|
|
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrValidateMessageContent");
|
|
IMailMsgBind *pBindInterface = NULL;
|
|
PFIO_CONTEXT pIMsgFileHandle = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Attempt to query interface for the binding interface
|
|
//
|
|
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgBind,
|
|
(void **)&pBindInterface);
|
|
if (FAILED(hr) || !pBindInterface)
|
|
{
|
|
ErrorTrace((LPARAM) pIMailMsgProperties,
|
|
"Unable to QI for IID_IMailMsgBind - hr 0x%08X", hr);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Request the PFIO_CONTEXT for this message
|
|
//
|
|
hr = pBindInterface->GetBinding(&pIMsgFileHandle, NULL);
|
|
DebugTrace((LPARAM) pIMailMsgProperties,
|
|
"GetBinding return hr - 0x%08X", hr);
|
|
|
|
Exit:
|
|
|
|
if (pBindInterface)
|
|
{
|
|
pBindInterface->ReleaseContext();
|
|
pBindInterface->Release();
|
|
}
|
|
|
|
TraceFunctLeave();
|
|
return hr;
|
|
}
|