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.
 
 
 
 
 
 

6423 lines
204 KiB

//-----------------------------------------------------------------------------
//
//
// File: aqinst.cpp
//
// Description: Implementation of the Advanced Queueing Server Instance
//
// Author: mikeswa
//
// Copyright (C) 1997 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include <aqprecmp.h>
#include "dcontext.h"
#include "connmgr.h"
#include <smtpseo.h>
#include <cat.h>
#include "dsnevent.h"
#include "asyncq.inl"
#include "aqutil.h"
#include "smtpconn.h"
#include "aqrpcsvr.h"
#include "aqsize.h"
#include "propstrm.h"
#include "tran_evntlog.h"
#include "asyncadm.inl"
#define PRELOCAL_QUEUE_ID 0x00000001
#define PRECAT_QUEUE_ID 0x00000002
#define PREROUTING_QUEUE_ID 0x00000004
#define PRESUBMIT_QUEUE_ID 0x00000008
// externs defined in blockmgr and cmmprops to control debug code
DWORD g_fFillPropertyPages = 0;
DWORD g_fValidateSignatures = 0;
DWORD g_fForceCrashOnError = 0;
HRESULT MailTransport_Completion_SubmitMessage(HRESULT hrStatus, PVOID pvContext);
HRESULT MailTransport_Completion_PreCategorization(HRESULT hrStatus, PVOID pvContext);
HRESULT MailTransport_Completion_PostCategorization(HRESULT hrStatus, PVOID pvContext);
const CLSID CLSID_ExchangeStoreDriver = {0x7BD80399,0xE37E,0x11d1,{0x9B,0xE2,0x00,0xA0,0xC9,0x5E,0x61,0x43}};
CPool CAQSvrInst::CAQLocalDeliveryNotify::s_pool(AQLD_SIG);
//---[ CAQSvrInst::fShouldRetryMessage ]---------------------------------------
//
//
// Description:
// Attempts to determine if the message has hit a hard failure (like the
// backing store has been deleted). This uses GetBinding to determine
// The error returned by the store driver. if it is FILE_NOT_FOUND,
// then the backing store for the message has been deleted... or is no
// longer valid (i.e. - the store restarting).
// Parameters:
// pIMailMsgProperties
// fShouldBounceUsageIfRetry TRUE - Should bounce usage on retry
// FALSE - Never bounce usage
// If the message is alreade associated with a msgref, this
// should always be FALSE since bouncing the usage count
// is done through the CMsgRef.
// Returns:
// TRUE If we think we should retry the message
// FALSE If new *know* that the message should be dropped. If unsure,
// we will return TRUE.
// History:
// 1/4/2000 - MikeSwa Created
// 4/10/2000 - MikeSwa Modified to better detect store shutdown/failure
//
//-----------------------------------------------------------------------------
BOOL CAQSvrInst::fShouldRetryMessage(IMailMsgProperties *pIMailMsgProperties,
BOOL fShouldBounceUsageIfRetry)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "fShouldRetryMessage");
BOOL fShouldRetry = TRUE;
BOOL fHasShutdownLock = FALSE;
HRESULT hr = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
IMailMsgValidateContext *pIMailMsgValidateContext = NULL;
_ASSERT(pIMailMsgProperties);
if (!fTryShutdownLock())
goto Exit;
fHasShutdownLock = TRUE;
//
// First check and see if the message context is still OK - if that
// doesn't work, we use the HrValidateMessageConteNt call below
// and force a RFC822 rendering of the message (which can be a
// huge perf hit).
//
// QI for validation interface
hr = pIMailMsgProperties->QueryInterface(
IID_IMailMsgValidateContext,
(LPVOID *)&pIMailMsgValidateContext);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to QI for IMailMsgValidateContext 0x%08X",hr);
goto Exit;
}
// Validate the message context
hr = pIMailMsgValidateContext->ValidateContext();
DebugTrace((LPARAM) this,
"ValidateContext returned 0x%08X", hr);
if (hr == S_OK) //this message is fine
goto Exit;
else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
fShouldRetry = FALSE;
goto Exit;
}
//
// If the above didn't work... try harder by verifying content. This
// will open the handles... so we need to close them
//
hr = HrValidateMessageContent(pIMailMsgProperties);
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
//The mailmsg has been deleted... we can just drop it.
DebugTrace((LPARAM) pIMailMsgProperties,
"WARNING: Backing store for mailmsg has been deleted.");
fShouldRetry = FALSE;
goto Exit;
}
else if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"GetBinding failed with hr - 0x%08X", hr);
goto Exit;
}
Exit:
//
// Bounce usage count if we have are sticking it back in the queue
// and the caller does not object
//
if (fShouldRetry && fShouldBounceUsageIfRetry)
{
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt,
(void **) &pIMailMsgQueueMgmt);
if (SUCCEEDED(hr) && pIMailMsgQueueMgmt)
{
pIMailMsgQueueMgmt->ReleaseUsage();
pIMailMsgQueueMgmt->AddUsage();
pIMailMsgQueueMgmt->Release();
}
}
if (pIMailMsgValidateContext)
pIMailMsgValidateContext->Release();
if (fHasShutdownLock)
ShutdownUnlock();
TraceFunctLeave();
return fShouldRetry;
}
//thin wrapper for CAQSvrInst::fPreCatQueueCompletion member
BOOL fPreCatQueueCompletionWrapper(IMailMsgProperties *pIMailMsgProperties,
PVOID pvContext)
{
// Testing : Cause intermittent failures in this queue
if (fShouldFail(g_cPreCatQueueFailurePercent))
{
((CAQSvrInst *)pvContext)->ScheduleInternalRetry(LI_TYPE_PENDING_CAT);
return FALSE;
}
if (!((CAQSvrInst *)pvContext)->fPreCatQueueCompletion(pIMailMsgProperties))
return !((CAQSvrInst *)pvContext)->fShouldRetryMessage(pIMailMsgProperties);
else
return TRUE;
}
//thin wrapper for CAQSvrInst::fPreLocalDeliveryCompletion
BOOL fPreLocalDeliveryQueueCompletionWrapper(CMsgRef *pmsgref,
PVOID pvContext)
{
if (!((CAQSvrInst *)pvContext)->fPreLocalDeliveryQueueCompletion(pmsgref))
return !(pmsgref->fShouldRetry());
else
return TRUE;
}
//thin wrapper for CAQSvrInst::fPostDSNQueueCompletion member
BOOL fPostDSNQueueCompletionWrapper(IMailMsgProperties *pIMailMsgProperties,
PVOID pvContext)
{
return (SUCCEEDED(((CAQSvrInst *)pvContext)->HrInternalSubmitMessage(pIMailMsgProperties)));
}
//thin wrapper for CAQSvrInst::fPreRoutingQueueCompletion
BOOL fPreRoutingQueueCompletionWrapper(IMailMsgProperties *pIMailMsgProperties,
PVOID pvContext)
{
// Testing : Cause intermittent failures in this queue
if (fShouldFail(g_cPreRoutingQueueFailurePercent))
{
((CAQSvrInst *)pvContext)->ScheduleInternalRetry(LI_TYPE_PENDING_ROUTING);
return FALSE;
}
if (!((CAQSvrInst *)pvContext)->fRouteAndQueueMsg(pIMailMsgProperties))
return !((CAQSvrInst *)pvContext)->fShouldRetryMessage(pIMailMsgProperties);
else
return TRUE;
}
//thin wrappers for handling internal asyncq queue failures
BOOL fAsyncQHandleFailedMailMsg(IMailMsgProperties *pIMailMsgProperties,
PVOID pvContext)
{
((CAQSvrInst *)pvContext)->HandleAQFailure(AQ_FAILURE_INTERNAL_ASYNCQ,
E_OUTOFMEMORY, pIMailMsgProperties);
return TRUE;
}
BOOL fAsyncQHandleFailedMsgRef(CMsgRef *pmsgref, PVOID pvContext)
{
_ASSERT(pmsgref);
if (pmsgref)
pmsgref->RetryOnDelete();
return TRUE;
}
//Thin wrapper(s) for AsyncQueueRetry - kick starting queues
void LocalDeliveryRetry(PVOID pvContext)
{
((CAQSvrInst *)pvContext)->AsyncQueueRetry(PRELOCAL_QUEUE_ID);
}
void CatRetry(PVOID pvContext)
{
((CAQSvrInst *)pvContext)->AsyncQueueRetry(PRECAT_QUEUE_ID);
}
void RoutingRetry(PVOID pvContext)
{
((CAQSvrInst *)pvContext)->AsyncQueueRetry(PREROUTING_QUEUE_ID);
}
void SubmitRetry(PVOID pvContext)
{
((CAQSvrInst *)pvContext)->AsyncQueueRetry(PRESUBMIT_QUEUE_ID);
}
//---[ CAQSvrInst::fPreSubmissionQueueCompletionWrapper ]----------------------
//
//
// Description:
// Completion function for PreSubmit Queue
// Parameters:
// pIMailMsgPropeties IMailMsg to submit
// pvContext Ptr to CAQSvrInst
// Returns:
// TRUE completed successfully
// FALSE message needs to be retried
// History:
// 10/8/1999 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CAQSvrInst::fPreSubmissionQueueCompletionWrapper(
IMailMsgProperties *pIMailMsgProperties,
PVOID pvContext)
{
BOOL fRetry = FALSE;
HRESULT hr = S_OK;
CAQSvrInst *paqinst = (CAQSvrInst *)pvContext;
_ASSERT(paqinst);
// Testing : Cause intermittent failures in this queue
if (fShouldFail(g_cPreSubmitQueueFailurePercent))
{
paqinst->ScheduleInternalRetry(LI_TYPE_PENDING_SUBMIT);
return FALSE;
}
InterlockedDecrement((PLONG) &(paqinst->m_cCurrentMsgsPendingSubmit));
hr = (paqinst->HrInternalSubmitMessage(pIMailMsgProperties));
if (FAILED(hr))
{
if (paqinst->fShouldRetryMessage(pIMailMsgProperties))
{
fRetry = TRUE;
InterlockedIncrement((PLONG) &(paqinst->m_cCurrentMsgsPendingSubmit));
//
// We need to kick off a retry as well for the presubmit queue
//
paqinst->ScheduleInternalRetry(LI_TYPE_PENDING_SUBMIT);
}
}
SleepForPerfAnalysis(g_dwSubmitQueueSleepMilliseconds);
return (!fRetry);
}
DEBUG_DO_IT(CAQSvrInst *g_paqinstLastDeleted = NULL;); //used to find the last deleted in CDB
#define TRACE_COUNTERS \
{\
DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending submission event for server 0x%08X", m_cCurrentMsgsPendingSubmitEvent, this); \
DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending pre-cat event for server 0x%08X", m_cCurrentMsgsPendingPreCatEvent, this); \
DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending post-cat event for server 0x%08X", m_cCurrentMsgsPendingPostCatEvent, this); \
DebugTrace(0xC0DEC0DE, "INFO: %d msgs submited (total post cat) for delivery on server 0x%08X", m_cTotalMsgsQueued, this);\
DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending categorization for server 0x%08X", m_cCurrentMsgsPendingCat, this);\
DebugTrace(0xC0DEC0DE, "INFO: %d msgs ack'd on server 0x%08X", m_cMsgsAcked, this);\
DebugTrace(0xC0DEC0DE, "INFO: %d msgs ack'd for retry on server 0x%08X", m_cMsgsAckedRetry, this);\
DebugTrace(0xC0DEC0DE, "INFO: %d msgs delivered local on server 0x%08X", m_cMsgsDeliveredLocal, this);\
}
// {407525AC-62B5-11d2-A694-00C04FA3490A}
static const GUID g_guidDefaultRouter =
{ 0x407525ac, 0x62b5, 0x11d2, { 0xa6, 0x94, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } };
//GUID for local queue
// {34E2DCCC-C91A-11d2-A6B1-00C04FA3490A}
static const GUID g_guidLocalQueue =
{ 0x34e2dccc, 0xc91a, 0x11d2, { 0xa6, 0xb1, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } };
// GUID for presubmission queue
// {D99AAC44-BEE9-4f9f-8D47-FB12E1443B9A}
static const GUID g_guidPreSubmissionQueue =
{ 0xd99aac44, 0xbee9, 0x4f9f, { 0x8d, 0x47, 0xfb, 0x12, 0xe1, 0x44, 0x3b, 0x9a } };
// GUID for precat queue
// {B608067E-85DB-4f4e-9FE9-008A4072CCDC}
static const GUID g_guidPreCatQueue =
{ 0xb608067e, 0x85db, 0x4f4e, { 0x9f, 0xe9, 0x0, 0x8a, 0x40, 0x72, 0xcc, 0xdc } };
// GUID for prerouting queue
// {F1B4C8FD-2928-427d-AC0D-23AF0DCFC31F}
static const GUID g_guidPreRoutingQueue =
{ 0xf1b4c8fd, 0x2928, 0x427d, { 0xac, 0xd, 0x23, 0xaf, 0xd, 0xcf, 0xc3, 0x1f } };
// GUID for post-DSN generation queue
// {D076B629-6030-405f-ADC9-D888703E072E}
static const GUID g_guidPostDSNGenerationQueue =
{ 0xd076b629, 0x6030, 0x405f, { 0xad, 0xc9, 0xd8, 0x88, 0x70, 0x3e, 0x7, 0x2e } };
//---[ CAQSvrInst::CAQSvrInst ]------------------------------------------------
//
//
// Description:
// Class constuctor
// Parameters:
// SMTP_SERVER_INSTANCE *pssi - ptr to SMTP server instance object
// pISMTPServer - interface used to handle local deliverys
// Returns:
// -
//
//-----------------------------------------------------------------------------
CAQSvrInst::CAQSvrInst(DWORD dwServerInstance,
ISMTPServer *pISMTPServer)
: m_mglSupersedeIDs(&m_cSupersededMsgs),
#if _MSC_VER >= 1200
#pragma warning(push)
#endif
#pragma warning(disable:4355)
m_asyncqPreLocalDeliveryQueue("LocalAsyncQueue", LOCAL_LINK_NAME,
&g_guidLocalQueue, 0, this),
m_asyncqPreSubmissionQueue("PreSubmissionQueue",
PRESUBMISSION_QUEUE_NAME,
&g_guidPreSubmissionQueue,
0,
this),
m_asyncqPreCatQueue("PreCatQueue",
PRECAT_QUEUE_NAME,
&g_guidPreCatQueue,
0,
this),
m_asyncqPreRoutingQueue("PreRoutingQueue",
PREROUTING_QUEUE_NAME,
&g_guidPreRoutingQueue,
0,
this),
m_asyncqPostDSNQueue("PostDSNGenerationQueue",
POSTDSN_QUEUE_NAME,
&g_guidPostDSNGenerationQueue,
0,
this),
m_dsnsink((IUnknown *)(IAQServerEvent *)this),
#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#pragma warning(default:4355)
#endif
m_slPrivateData("CAQSvrInst",
SHARE_LOCK_INST_TRACK_DEFAULTS |
SHARE_LOCK_INST_TRACK_SHARED_THREADS |
SHARE_LOCK_INST_TRACK_CONTENTION, 500)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::CAQSvrInst");
_ASSERT(pISMTPServer);
m_dwSignature = CATMSGQ_SIG;
m_dwFlavorSignature = g_dwFlavorSignature;
m_cbClasses = g_cbClasses;
//Init counters
m_cTotalMsgsQueued = 0; //# of messages on dest queues (after fanout)
m_cMsgsAcked = 0; //# of messages that have been acknowledged
m_cMsgsAckedRetry = 0; //# of messages acked with retry all
m_cMsgsDeliveredLocal= 0; //# of messages delivered to local store
m_cCurrentMsgsPendingSubmitEvent = 0; //current # of messages in
//submission event
m_cCurrentMsgsPendingPreCatEvent = 0; // current # of messages in
// precat event
m_cCurrentMsgsPendingPostCatEvent = 0; //current # of messages in
//post-categorization event
m_cCurrentMsgsSubmitted = 0; //# total msgs in system
m_cCurrentMsgsPendingCat = 0; //# Msgs that have not be categorized
m_cCurrentMsgsPendingRouting = 0; //# Msgs that have been cat.
//but have not been completely queued
m_cCurrentMsgsPendingDelivery = 0; //# Msgs pending remote delivery
m_cCurrentMsgsPendingLocal = 0; //# Msgs pending local delivery
m_cCurrentMsgsPendingRetry = 0; //# Msgs with unsuccessful attempts
m_cCurrentQueueMsgInstances = 0; //# of msgs instances pending
//remote deliver (>= #msgs)
m_cCurrentRemoteDestQueues = 0; //# of DestMsgQueues created
m_cCurrentRemoteNextHops = 0; //# of Next Hop links created
m_cCurrentRemoteNextHopsEnabled = 0; //# of links that can have connections
m_cCurrentRemoteNextHopsPendingRetry = 0; //# of links pending retry
m_cCurrentRemoteNextHopsPendingSchedule = 0; //# of links pending schedule
m_cCurrentRemoteNextHopsFrozenByAdmin = 0; //# of links frozen by admin
m_cTotalMsgsSubmitted = 0; //total # of messages submitted to AQ
m_cTotalExternalMsgsSubmitted = 0; //Sumitted via an external interface
m_cMsgsAckedRetryLocal = 0;
m_cCurrentMsgsPendingLocalRetry = 0;
m_cDMTRetries = 0;
m_cTotalMsgsTURNETRNDelivered = 0;
m_cCurrentMsgsPendingDeferredDelivery = 0;
m_cCurrentResourceFailedMsgsPendingRetry = 0;
m_cTotalMsgsBadmailed = 0;
m_cBadmailNoRecipients = 0;
m_cBadmailHopCountExceeded = 0;
m_cBadmailFailureGeneral = 0;
m_cBadmailBadPickupFile = 0;
m_cBadmailEvent = 0;
m_cBadmailNdrOfDsn = 0;
m_cTotalDSNFailures = 0;
m_cCurrentMsgsInLocalDelivery = 0;
m_cTotalResetRoutes = 0;
m_cCurrentPendingResetRoutes = 0;
m_cCurrentMsgsPendingSubmit = 0;
//Counters to keep track of the number of messages in Cat
m_cCatMsgCalled = 0;
m_cCatCompletionCalled = 0;
//DSN Related counters
m_cDelayedDSNs = 0;
m_cNDRs = 0;
m_cDeliveredDSNs = 0;
m_cRelayedDSNs = 0;
m_cExpandedDSNs = 0;
m_cSupersededMsgs = 0; //number of messages superseded
m_dwDelayExpireMinutes = g_dwDelayExpireMinutes;
m_dwNDRExpireMinutes = g_dwNDRExpireMinutes;
m_dwLocalDelayExpireMinutes = g_dwDelayExpireMinutes;
m_dwLocalNDRExpireMinutes = g_dwNDRExpireMinutes;
m_dwInitMask = 0;
m_prstrDefaultDomain = NULL;
m_prstrBadMailDir = NULL;
m_prstrCopyNDRTo = NULL;
m_prstrServerFQDN = NULL;
m_dwDSNLanguageID = 0;
m_dwDSNOptions = DSN_OPTIONS_DEFAULT;
if (pISMTPServer)
pISMTPServer->AddRef();
m_pISMTPServer = pISMTPServer;
// Get the ISMTPServerEx interface
{
HRESULT hr;
m_pISMTPServerEx = NULL;
hr = m_pISMTPServer->QueryInterface(
IID_ISMTPServerEx,
(LPVOID *)&m_pISMTPServerEx);
if (FAILED(hr))
{
ErrorTrace((LPARAM) m_pISMTPServer,
"Unable to QI for ISMTPServerEx 0x%08X",hr);
m_pISMTPServerEx = NULL;
}
m_pISMTPServerAsync = NULL;
hr = m_pISMTPServer->QueryInterface(
IID_ISMTPServerAsync,
(LPVOID *)&m_pISMTPServerAsync);
if (FAILED(hr))
{
ErrorTrace((LPARAM) m_pISMTPServer,
"Unable to QI for ISMTPServerAsync 0x%08X",hr);
m_pISMTPServerAsync = NULL;
}
}
m_hCat = INVALID_HANDLE_VALUE;
m_dwServerInstance = dwServerInstance;
m_pConnMgr = NULL;
m_pIMessageRouterDefault = NULL;
//Retry stuff
m_dwFirstTierRetrySeconds = g_dwFirstTierRetrySeconds;
m_cLocalRetriesPending = 0; //used for moderating local retries
m_cCatRetriesPending = 0; //used for moderating cat retires
m_cRoutingRetriesPending = 0; //used for moderating routing retries
m_cSubmitRetriesPending = 0; //used for moderating submit retries
m_pIRouterReset = NULL;
//Add to global list of virtual servers
m_liVirtualServers.Blink = &g_liVirtualServers;
g_pslGlobals->ExclusiveLock();
m_liVirtualServers.Flink = g_liVirtualServers.Flink;
g_liVirtualServers.Flink->Blink = &m_liVirtualServers;
g_liVirtualServers.Flink = &m_liVirtualServers;
g_pslGlobals->ExclusiveUnlock();
//
// Assume (until proven otherwise) mailmsg returns the handle count
//
m_fMailMsgReportsNumHandles = TRUE;
m_defq.Initialize(this);
TraceFunctLeave();
}
//---[ CAQSvrInst::~CAQSvrInst ]--------------------------------------------
//
//
// Description:
// Class destuctor
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
CAQSvrInst::~CAQSvrInst()
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::~CAQSvrInst");
//make sure that all cleanup was done
HrDeinitialize(); //can be called multiple times
if (m_pISMTPServer)
m_pISMTPServer->Release();
if (m_pISMTPServerEx)
m_pISMTPServerEx->Release();
if (m_pISMTPServerAsync)
m_pISMTPServerAsync->Release();
if (m_pConnMgr)
{
m_pConnMgr->Release();
m_pConnMgr = NULL;
}
if (m_prstrDefaultDomain)
m_prstrDefaultDomain->Release();
if (m_prstrBadMailDir)
m_prstrBadMailDir->Release();
if (m_prstrCopyNDRTo)
m_prstrCopyNDRTo->Release();
if (m_prstrServerFQDN)
m_prstrServerFQDN->Release();
//Take out of list of global list
g_pslGlobals->ExclusiveLock();
m_liVirtualServers.Flink->Blink = m_liVirtualServers.Blink;
m_liVirtualServers.Blink->Flink = m_liVirtualServers.Flink;
g_pslGlobals->ExclusiveUnlock();
m_liVirtualServers.Flink = NULL;
m_liVirtualServers.Flink = NULL;
MARK_SIG_AS_DELETED(m_dwSignature);
DEBUG_DO_IT(g_paqinstLastDeleted = this;);
TraceFunctLeave();
}
//---[ CAQSvrInst::HrInitialize ]--------------------------------------------
//
//
// Description:
// Initialization of CAQSvrInst virtual server instance object.
// Parameters:
// IN szUserName User name to log on DS with
// IN szDomainName Domain name to log on to DS with
// IN szPassword Password to authenticate to DS with
// IN pServiceStatusFn Server status callback function
// IN pvServiceContext Context to pass back for callback function
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrInitialize(
IN LPSTR szUserName,
IN LPSTR szDomainName,
IN LPSTR szPassword,
IN PSRVFN pServiceStatusFn,
IN PVOID pvServiceContext)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrInitialize");
HRESULT hr = S_OK;
IMailTransportSetRouterReset *pISetRouterReset = NULL;
//
// init SEO
//
hr = m_CSMTPSeoMgr.HrInit(m_dwServerInstance);
if (FAILED(hr)) {
goto Exit;
}
m_pIMessageRouterDefault = new CAQDefaultMessageRouter(
(GUID *) &g_guidDefaultRouter, this);
if (!m_pIMessageRouterDefault)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
m_fmq.Initialize(this);
hr = CAQRpcSvrInst::HrInitializeAQServerInstanceRPC(this,
m_dwServerInstance,
m_pISMTPServer);
if (FAILED(hr))
goto Exit;
//Initialize Message Categorization
hr = CatInit(
NULL,
pServiceStatusFn,
pvServiceContext,
m_pISMTPServer,
(IAdvQueueDomainType *) this,
m_dwServerInstance,
&m_hCat);
if (FAILED(hr))
{
m_hCat = INVALID_HANDLE_VALUE;
goto Exit;
}
//Pass RouterReset Interface to ISMTPServer
if (m_pISMTPServer)
{
hr = m_pISMTPServer->QueryInterface(IID_IMailTransportSetRouterReset,
(void **) &pISetRouterReset);
if (SUCCEEDED(hr))
{
m_dwInitMask |= CMQ_INIT_ROUTER_RESET;
_ASSERT(pISetRouterReset);
hr = pISetRouterReset->RegisterResetInterface(m_dwServerInstance,
(IMailTransportRouterReset *) this);
_ASSERT(SUCCEEDED(hr)); //something is wrong if this failed
pISetRouterReset->Release();
pISetRouterReset = NULL;
}
hr = m_pISMTPServer->QueryInterface(IID_IMailTransportRouterReset,
(void **) &m_pIRouterReset);
if (FAILED(hr))
m_pIRouterReset = NULL;
}
hr = m_dmt.HrInitialize(this, &m_asyncqPreLocalDeliveryQueue,
&m_asyncqPreCatQueue, &m_asyncqPreRoutingQueue,
&m_asyncqPreSubmissionQueue);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_DMT;
hr = m_dct.HrInit();
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_DCT;
m_pConnMgr = new CConnMgr;
if (NULL == m_pConnMgr)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
hr = m_pConnMgr->HrInitialize(this);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_CONMGR;
hr = m_dsnsink.HrInitialize();
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_DSN;
hr = m_asyncqPreCatQueue.HrInitialize(g_cMaxSyncCatQThreads,
g_cItemsPerCatQAsyncThread,
g_cItemsPerCatQSyncThread,
this,
fPreCatQueueCompletionWrapper,
fAsyncQHandleFailedMailMsg,
NULL,
g_cMaxPendingCat);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_PRECATQ;
hr = m_asyncqPreLocalDeliveryQueue.HrInitialize(g_cMaxSyncLocalQThreads,
g_cItemsPerLocalQAsyncThread,
g_cItemsPerLocalQSyncThread,
this,
fPreLocalDeliveryQueueCompletionWrapper,
fAsyncQHandleFailedMsgRef,
HrWalkPreLocalQueueForDSN,
g_cMaxPendingLocal);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_PRELOCQ;
hr = m_asyncqPostDSNQueue.HrInitialize(g_cMaxSyncPostDSNQThreads,
g_cItemsPerPostDSNQAsyncThread,
g_cItemsPerPostDSNQSyncThread,
this,
fPostDSNQueueCompletionWrapper,
fAsyncQHandleFailedMailMsg,
NULL);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_POSTDSNQ;
hr = m_asyncqPreRoutingQueue.HrInitialize(g_cMaxSyncRoutingQThreads,
g_cItemsPerRoutingQAsyncThread,
g_cItemsPerRoutingQSyncThread,
this,
fPreRoutingQueueCompletionWrapper,
fAsyncQHandleFailedMailMsg,
NULL);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_ROUTINGQ;
hr = m_asyncqPreSubmissionQueue.HrInitialize(g_cMaxSyncSubmitQThreads,
g_cItemsPerSubmitQAsyncThread,
g_cItemsPerSubmitQSyncThread,
this,
fPreSubmissionQueueCompletionWrapper,
fAsyncQHandleFailedMailMsg,
NULL);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_SUBMISSIONQ;
hr = m_aqwWorkQueue.HrInitialize(g_cItemsPerWorkQAsyncThread);
if (FAILED(hr))
goto Exit;
m_dwInitMask |= CMQ_INIT_WORKQ;
m_dwInitMask |= CMQ_INIT_OK; //everything was initialized
// create the router object
hr = HrTriggerInitRouter();
Exit:
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::HrDeinitialize() ]----------------------------------------
//
//
// Description:
// Signals server shutdown
// Parameters:
// -
// Returns:
// S_OK on success
// Whatever error codes are generated during the shutdown process
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrDeinitialize()
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrDeinitialize");
HRESULT hr = S_OK;
HRESULT hrTmp = S_OK;
DWORD i = 0;
IMailTransportSetRouterReset *pISetRouterReset = NULL;
#ifdef DEBUG
DWORD cLocalDeliveryShutdownSeconds = 60;
#endif
//
// Tell categorizer to stop categorizing before we block on the
// shutdown lock.
//
if (INVALID_HANDLE_VALUE != m_hCat)
{
CatPrepareForShutdown(m_hCat);
}
//
// We have hit this assert a few times due to NT stress failures.
// This is only really useful if at least one messsage has been
// sent. The assert is basically useless if we hit it in KD during
// NT stress runs.
//
if (m_cTotalMsgsSubmitted)
m_dbgcnt.StartCountdown();
ServerStopHintFunction();
//Get Exclusive shutdown lock
SignalShutdown();
ServerStopHintFunction();
//Turn off RPC for this instance
hrTmp = CAQRpcSvrInst::HrDeinitializeAQServerInstanceRPC(this, m_dwServerInstance);
if (FAILED(hrTmp))
{
ErrorTrace((LPARAM) this,
"Error shutting down Aqueue RPC hr - 0x%08X", hrTmp);
if (SUCCEEDED(hr))
hr = hrTmp;
}
// wait for all outstanding local deliveries to complete
while (!(m_asyncqPreLocalDeliveryQueue.fNoPendingAsyncCompletions())) {
_ASSERT(--cLocalDeliveryShutdownSeconds > 0);
ServerStopHintFunction();
Sleep(1000);
}
ServerStopHintFunction();
m_fmq.Deinitialize();
ServerStopHintFunction();
m_defq.Deinitialize();
m_dwInitMask &= ~CMQ_INIT_DCT; //no de-initialize function to call
//stop any pending categorization
ServerStopHintFunction();
if (INVALID_HANDLE_VALUE != m_hCat)
{
m_dbgcnt.SuspendCountdown();
hrTmp = CatCancel(m_hCat);
if FAILED(hrTmp)
{
ErrorTrace((LPARAM) this,
"ERROR: Categorization shutdown error hr - 0x%08X", hrTmp);
if (SUCCEEDED(hr))
hr = hrTmp;
}
//shutdow message categorization
CatTerm(m_hCat);
m_dbgcnt.ResetCountdown();
m_hCat = INVALID_HANDLE_VALUE;
}
//Tell ISMTPServer to set RouterReset Interface to NULL
ServerStopHintFunction();
if (m_pISMTPServer)
{
hr = m_pISMTPServer->QueryInterface(IID_IMailTransportSetRouterReset,
(void **) &pISetRouterReset);
if (SUCCEEDED(hr))
{
m_dwInitMask &= ~CMQ_INIT_ROUTER_RESET;
_ASSERT(pISetRouterReset);
hr = pISetRouterReset->RegisterResetInterface(m_dwServerInstance,
NULL);
_ASSERT(SUCCEEDED(hr)); //something is wrong if this failed
pISetRouterReset->Release();
pISetRouterReset = NULL;
}
}
ServerStopHintFunction();
if (m_pIRouterReset)
{
m_pIRouterReset->Release();
m_pIRouterReset = NULL;
}
ServerStopHintFunction();
if (CMQ_INIT_DMT & m_dwInitMask)
{
m_dwInitMask ^= CMQ_INIT_DMT;
hrTmp = m_dmt.HrDeinitialize();
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
}
//Deinitializing the connection manager will also release the retry
//sink to make all it's callbacks.
ServerStopHintFunction();
if (NULL != m_pConnMgr)
{
if (CMQ_INIT_CONMGR & m_dwInitMask)
{
_ASSERT(m_pISMTPServer);
m_dwInitMask ^= CMQ_INIT_CONMGR;
hrTmp = m_pConnMgr->HrDeinitialize();
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
}
m_pConnMgr->Release();
m_pConnMgr = NULL;
}
//deinitialize pre-local delivery queue
ServerStopHintFunction();
if (CMQ_INIT_PRELOCQ & m_dwInitMask)
{
hrTmp = m_asyncqPreLocalDeliveryQueue.HrDeinitialize(
HrWalkMsgRefQueueForShutdown, this);
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
m_dwInitMask ^= CMQ_INIT_PRELOCQ;
}
//deinitialize pre-cat delivery queue
ServerStopHintFunction();
if (CMQ_INIT_PRECATQ & m_dwInitMask)
{
hrTmp = m_asyncqPreCatQueue.HrDeinitialize(
HrWalkMailMsgQueueForShutdown, this);
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
m_dwInitMask ^= CMQ_INIT_PRECATQ;
}
//deinitialize post DNS queue
ServerStopHintFunction();
if (CMQ_INIT_POSTDSNQ & m_dwInitMask)
{
hrTmp = m_asyncqPostDSNQueue.HrDeinitialize(
HrWalkMailMsgQueueForShutdown, this);
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
m_dwInitMask ^= CMQ_INIT_POSTDSNQ;
}
//deinitialize pre-routing queue
ServerStopHintFunction();
if (CMQ_INIT_ROUTINGQ & m_dwInitMask)
{
hrTmp = m_asyncqPreRoutingQueue.HrDeinitialize(
HrWalkMailMsgQueueForShutdown, this);
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
m_dwInitMask ^= CMQ_INIT_ROUTINGQ;
}
//deinitialize pre-submit queue
ServerStopHintFunction();
if (CMQ_INIT_SUBMISSIONQ & m_dwInitMask)
{
hrTmp = m_asyncqPreSubmissionQueue.HrDeinitialize(
HrWalkMailMsgQueueForShutdown, this);
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
m_dwInitMask ^= CMQ_INIT_SUBMISSIONQ;
}
ServerStopHintFunction();
m_mglSupersedeIDs.Deinitialize(this);
ServerStopHintFunction();
if (CMQ_INIT_WORKQ & m_dwInitMask)
{
hrTmp = m_aqwWorkQueue.HrDeinitialize(this);
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
m_dwInitMask ^= CMQ_INIT_WORKQ;
}
//the following bits don't have specific delinitialize functions
m_dwInitMask &= ~(CMQ_INIT_DSN | CMQ_INIT_OK);
ServerStopHintFunction();
if (m_pIMessageRouterDefault)
{
m_pIMessageRouterDefault->Release();
m_pIMessageRouterDefault = NULL;
}
// let SEO do it's cleanup
m_CSMTPSeoMgr.Deinit();
m_dbgcnt.EndCountdown();
_ASSERT((!m_dwInitMask) || FAILED(hr));
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::HrGetIConnectionManager ]---------------------------------
//
//
// Description:
// Returns the IConnectionManager interface for this AdvancedQueuing instance
// Parameters:
// OUT ppIConnectionManger Returned interface
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrGetIConnectionManager(
OUT IConnectionManager **ppIConnectionManager)
{
HRESULT hr = S_OK;
_ASSERT(ppIConnectionManager);
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
m_pConnMgr->AddRef();
*ppIConnectionManager = m_pConnMgr;
ShutdownUnlock();
Exit:
return hr;
}
//---[ CAQSvrInst::cCountMsgsForHandleThrottling ]-----------------------------
//
//
// Description:
// returns the number of messages in the system that
// is used elsewhere to decide to start/stop handle throttling
//
//
// Parameters:
// pIMailMsgProperties pointer to MailMsg interface to query
//
// Returns:
// DWORD returned is:
//
// - With Windows2000 RTM mailmsg.dll:
// count of all messages in the system
//
// - With Windows2000 SP1 mailmsg.dll:
// count of open property stream handles
// OR
// count of all messages in the system
// if the former can't be obtained
//
// History:
// 1/28/00 aszafer Created
//-----------------------------------------------------------------------------
#ifndef IMMPID_MPV_TOTAL_OPEN_PROPERTY_STREAM_HANDLES
#define IMMPID_MPV_TOTAL_OPEN_PROPERTY_STREAM_HANDLES 0x3004
#endif
DWORD CAQSvrInst::cCountMsgsForHandleThrottling(IN IMailMsgProperties *pIMailMsgProperties)
{
HRESULT hr = MAILMSG_E_PROPNOTFOUND; //Count as failure if we do not call
DWORD dwStreamOpenHandlesCount = 0;
TraceFunctEnterEx((LPARAM) this, "Entering CAQSvrInst::cCountMsgsForHandleThrottling");
//
// We should never call into mailmsg if we know we do not have the correct version.
// This will load the property stream and cause us to potentially access the
// properties in an unsafe way.
//
if (m_fMailMsgReportsNumHandles && pIMailMsgProperties)
{
hr = pIMailMsgProperties->GetDWORD(
IMMPID_MPV_TOTAL_OPEN_PROPERTY_STREAM_HANDLES,
&dwStreamOpenHandlesCount);
if(FAILED(hr))
{
m_fMailMsgReportsNumHandles = FALSE;
//must be RTM version of mailmsg.dll
DebugTrace((LPARAM) this, "GetDWORD(IMMPID*OPEN_PROPERTY_STREAM_HANDLES) failed hr %08lx", hr);
DebugTrace((LPARAM) this, "returning g_cIMsgInSystem + m_cCurrentMsgsPendingSubmit");
}
}
TraceFunctLeaveEx((LPARAM) this);
return SUCCEEDED(hr) ? dwStreamOpenHandlesCount : g_cIMsgInSystem + m_cCurrentMsgsPendingSubmit ;
}
//---[ CAQSvrInst::QueueMsgForLocalDelivery ]----------------------------------
//
//
// Description:
// Queues a single message for local delivery
// Parameters:
// IN pmsgref Message Ref to deliver locally
// Returns:
// -
// History:
// 1/26/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::QueueMsgForLocalDelivery(CMsgRef *pmsgref, BOOL fLocalLink)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::QueueMsgForLocalDelivery");
HRESULT hr = S_OK;
CAQStats aqstat;
CLinkMsgQueue *plmq = NULL;
//
// Get the stats from the msgref
//
pmsgref->GetStatsForMsg(&aqstat);
//
// Get the local link and update the stats
//
plmq = m_dmt.plmqGetLocalLink();
if (plmq)
{
hr = plmq->HrNotify(&aqstat, TRUE);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"HrNotify failed... local stats innaccurate 0x%08X", hr);
hr = S_OK;
}
}
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingLocal);
hr = m_asyncqPreLocalDeliveryQueue.HrQueueRequest(pmsgref);
if (FAILED(hr))
{
hr = plmq->HrNotify(&aqstat, FALSE);
pmsgref->RetryOnDelete();
InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingLocal);
}
//
// Make sure we release the local link if we got it
//
if (plmq)
plmq->Release();
TraceFunctLeave();
}
//---[ CAQSvrInst::fRouteAndQueueMsg ]-----------------------------------------
//
//
// Description:
// Add a Categorized Message to the CMT to be queue for delivery
// Parameters:
// IN pIMailMsgProperties Msg to routing and queue for delivery
// Returns:
// TRUE if message has been successfully routed and queued for delivery
// (or if errors have been handled internally)
// FALSE if message needs to be requeued for a later retry
//
//-----------------------------------------------------------------------------
BOOL CAQSvrInst::fRouteAndQueueMsg(IN IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::fRouteAndQueueMsg");
HRESULT hr = S_OK;
HRESULT hrTmp = S_OK;
DWORD cDomains = 0; //number of domains message will be deliver to
DWORD cQueues = 0; //number of queues for the message
DWORD i = 0; //loop counter
DWORD cLocalRecips = 0;
DWORD cRemoteRecips = 0;
DWORD dwDMTVersion = 0;
CMsgRef *pmsgref = NULL;
CDestMsgQueue **rgpdmq = NULL;
BOOL fLocked = FALSE;
BOOL fRoutingLock = FALSE;
BOOL fLocal = FALSE;
BOOL fRemote = FALSE;
BOOL fOnDMQ = FALSE;
BOOL fDMTLocked = FALSE;
BOOL fKeepTrying = TRUE;
BOOL fGotMsgType = FALSE;
BOOL fReturn = TRUE;
DWORD dwMessageType = 0;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
IMailMsgRecipients *pIRecipList = NULL;
IMessageRouter *pIMessageRouter = NULL;
_ASSERT(CATMSGQ_SIG == m_dwSignature);
_ASSERT(pIMailMsgProperties);
if (NULL == pIMailMsgProperties)
{
hr = E_INVALIDARG;
ErrorTrace((LPARAM) this, "NULL pIMailMsgProperties");
goto Exit;
}
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to QI for IID_IMailMsgQueueMgmt");
goto Exit;
}
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients, (PVOID *) &pIRecipList);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to QI for IID_IMailMsgRecipients");
goto Exit;
}
//get a shared lock to guard against shutdown.
if (!fTryShutdownLock())
{
DebugTrace((LPARAM) pIMailMsgProperties,
"Shutdown detecting while routing a message... bailing");
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
hr = pIRecipList->DomainCount(&cDomains);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to get a domain count from the message - hr 0x08X", hr);
goto Exit;
}
if (!cDomains) //and hence no recipients
{
DebugTrace((LPARAM) pIMailMsgProperties,
"No domains on message - turfing message");
//This could be a completely valid case (like an empty DL)
//In this case we just turf the message and act as if everything is fine
DecMsgsInSystem(); //update our counters
HandleBadMail(pIMailMsgProperties, TRUE, NULL, AQUEUE_E_NO_RECIPIENTS, FALSE);
InterlockedIncrement((PLONG) &m_cBadmailNoRecipients);
//Delete the message
HrDeleteIMailMsg(pIMailMsgProperties);
hr = S_OK;
goto Exit;
}
//Check Message to see if there are unresolved recipients to NDR
hr = HrNDRUnresolvedRecipients(pIMailMsgProperties, pIRecipList);
if (FAILED(hr))
{
HandleAQFailure(AQ_FAILURE_CANNOT_NDR_UNRESOLVED_RECIPS, hr, pIMailMsgProperties);
ErrorTrace((LPARAM) this, "ERROR: Unable to NDR message - hr 0x%08X", hr);
//just drop message for now... we cannot let it continue until this succeeds
hr = S_OK;
goto Exit;
}
if (S_FALSE == hr)
{
//There is no work to be done for this message - delete it
HrDeleteIMailMsg(pIMailMsgProperties);
DebugTrace((LPARAM) pIMailMsgProperties,
"INFO: Deleting message after NDRing all unresolved recips");
hr = S_OK;
DecMsgsInSystem(); //update our counters
goto Exit;
}
rgpdmq = (CDestMsgQueue **) pvMalloc(cDomains * sizeof(CDestMsgQueue *));
if (NULL == rgpdmq)
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to alloc array of %d Queues", cDomains);
hr = E_OUTOFMEMORY;
goto Exit;
}
RoutingShareLock();
fRoutingLock = TRUE;
hr = HrTriggerGetMessageRouter(pIMailMsgProperties, &pIMessageRouter);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to get message router - HR 0x%08X", hr);
goto Exit;
}
hr = pIMessageRouter->GetMessageType(pIMailMsgProperties, &dwMessageType);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to get message type - HR 0x%08X", hr);
goto Exit;
}
//We own a reference to the message type now
fGotMsgType = TRUE;
pmsgref = new((DWORD) cDomains) CMsgRef(cDomains, pIMailMsgQueueMgmt, pIMailMsgProperties,
this, dwMessageType, pIMessageRouter->GetTransportSinkID());
if (NULL == pmsgref)
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"Unable to allocate CMsgRef for %d domains", cDomains);
hr = E_OUTOFMEMORY;
goto Exit;
}
//Loop until we get consistant info on where to queue message (based on
//DMT version number).
while (fKeepTrying)
{
dwDMTVersion = m_dmt.dwGetDMTVersion();
hr = pmsgref->HrInitialize(pIRecipList, pIMessageRouter, dwMessageType,
&cLocalRecips, &cRemoteRecips, &cQueues, rgpdmq);
if (FAILED(hr))
{
if (HRESULT_FROM_WIN32(ERROR_RETRY) == hr)
{
//Some sort of config/routing change... we need to retry the
//message
fGotMsgType = FALSE;
hr = HrReGetMessageType(pIMailMsgProperties,
pIMessageRouter, &dwMessageType);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"HrReGetMessageType failed with hr - 0x%08X", hr);
goto Exit;
}
fGotMsgType = TRUE;
}
else //It was a genuine error... bail
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"CMsgRef::HrInitialze failed with hr 0x%08X", hr);
goto Exit;
}
}
//Before Enqueuing Messages or firing off local delivery... throttle usage count
if (g_cMaxIMsgHandlesThreshold < cCountMsgsForHandleThrottling(pIMailMsgProperties))
{
DebugTrace((LPARAM) 0xC0DEC0DE, "INFO: Closing IMsg Content - %d messsages in queue", cCountMsgsForHandleThrottling(pIMailMsgProperties));
//bounce usage count off of zero
pIMailMsgQueueMgmt->ReleaseUsage();
pIMailMsgQueueMgmt->AddUsage();
}
m_dmt.AquireDMTShareLock();
if (m_dmt.dwGetDMTVersion() != dwDMTVersion)
{
DebugTrace((LPARAM) this,
"DMT version change: was %d, now %d",
dwDMTVersion, m_dmt.dwGetDMTVersion());
//DMT Version changed... that means that our queues may have been
//removed from the DMT. Time to retry
m_dmt.ReleaseDMTShareLock();
_ASSERT(fKeepTrying);
InterlockedIncrement((PLONG) &m_cDMTRetries);
fGotMsgType = FALSE;
hr = HrReGetMessageType(pIMailMsgProperties, pIMessageRouter,
&dwMessageType);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"HrReGetMessageType failed with hr - 0x%08X", hr);
goto Exit;
}
fGotMsgType = TRUE;
continue; //Try again
}
fKeepTrying = FALSE;
fDMTLocked = TRUE;
//enqueue the message reference for each destination it is going to
for (i = 0; i < cQueues; i++)
{
if (NULL != rgpdmq[i])
{
InterlockedIncrement(&m_cTotalMsgsQueued);
//enqueue message and assign message type to first enqueue
hr = rgpdmq[i]->HrEnqueueMsg(pmsgref, !fOnDMQ);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties,
"HrEnqueueMsg failed - 0x%08X", hr);
InterlockedDecrement(&m_cTotalMsgsQueued);
goto Exit;
}
fOnDMQ = TRUE;
//
// Check and see if this queue is explicitly routed remote.
// It may be a gateway delivery queue.
//
if (!fRemote)
fRemote = rgpdmq[i]->fIsRemote();
}
else
{
fLocal = TRUE;
}
}
_ASSERT(fDMTLocked);
m_dmt.ReleaseDMTShareLock();
fDMTLocked = FALSE;
if (fLocal) //kick off local delivery
{
QueueMsgForLocalDelivery(pmsgref, FALSE);
}
}
Exit:
if (fDMTLocked)
{
m_dmt.ReleaseDMTShareLock();
fDMTLocked = FALSE;
}
// Check and process any special queues in the DMT
if (fRoutingLock && fLocked)
m_dmt.ProcessSpecialLinks(m_dwDelayExpireMinutes, TRUE);
//
// Must Release IMessageRouter before starting shutdown since the
// IMessageRouter sink may have a reference to us (via
// IRouterReset)
//
if (NULL != pIMessageRouter)
{
if (!fOnDMQ && fGotMsgType) //we have a reference to this message type
{
hrTmp = pIMessageRouter->ReleaseMessageType(dwMessageType, 1);
_ASSERT(SUCCEEDED(hrTmp));
}
pIMessageRouter->Release();
}
if (fRoutingLock)
RoutingShareUnlock();
if (fLocked)
ShutdownUnlock();
if (pIMailMsgQueueMgmt)
pIMailMsgQueueMgmt->Release();
if (pIRecipList)
pIRecipList->Release();
if (NULL != rgpdmq)
FreePv(rgpdmq);
if (fRemote && pmsgref)
{
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingDelivery);
//msgref needs to decrement the remote count when it is released
pmsgref->CountMessageInRemoteTotals();
}
if (NULL != pmsgref)
{
if (FAILED(hr) && (fOnDMQ || fLocal))
{
//If we have a msgref and it has been queued, we must
//wait until all other references are released to retry it
pmsgref->RetryOnDelete();
hr = S_OK; //don't let caller retry
}
pmsgref->Release();
}
//if we did not succeed, msgs is still in pre-routing queue
if (FAILED(hr))
{
fReturn = FALSE;
//kick off retry if necessary
ScheduleInternalRetry(LI_TYPE_PENDING_ROUTING);
}
else
InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingRouting);
TRACE_COUNTERS;
SleepForPerfAnalysis(g_dwRoutingQueueSleepMilliseconds);
TraceFunctLeave();
return fReturn;
}
//---[ CAQSvrInst::HrAckMsg ]------------------------------------------------
//
//
// Description:
// Acknowledge the (un)delivery of a message. Will call the msgref AckMsg,
// which will requeue it to the appropriate queues
// Parameters:
// pMsgAck Pointer to Message Ack structure
// fLocal TRUE if this is an ack for a local delivery
// Returns:
// S_OK on success
// ERROR_INVALID_HANDLE if the context handle was invalid
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrAckMsg(IN MessageAck *pMsgAck, BOOL fLocal)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrAckMsg");
HRESULT hr = S_OK;
DWORD i = 0; //loop counter
CDeliveryContext *pdcntxt = NULL;
_ASSERT(pMsgAck);
_ASSERT(pMsgAck->pvMsgContext);
pdcntxt = (CDeliveryContext *) pMsgAck->pvMsgContext;
if ((NULL == pdcntxt) || !(pdcntxt->FVerifyHandle(pMsgAck->pIMailMsgProperties)))
{
hr = ERROR_INVALID_HANDLE;
goto Exit;
}
if (!fLocal)
{
InterlockedIncrement(&m_cMsgsAcked);
if (MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus)
InterlockedIncrement((PLONG) &m_cMsgsAckedRetry);
}
else //local
{
if (MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus)
InterlockedIncrement((PLONG) &m_cMsgsAckedRetryLocal);
}
hr = pdcntxt->HrAckMessage(pMsgAck);
if (FAILED(hr))
goto Exit;
Exit:
//clean up all the things we have used here
if (pdcntxt)
pdcntxt->Recycle();
TRACE_COUNTERS;
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::HrNotify ]------------------------------------------------------------
//
//
// Description:
// Passes notification off to Connection Mangaer
// Parameters:
//
// Returns:
//
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrNotify(IN CAQStats *paqstats, BOOL fAdd)
{
HRESULT hr = S_OK;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
hr = m_pConnMgr->HrNotify(paqstats, fAdd);
ShutdownUnlock();
Exit:
return hr;
}
//---[ CAQSvrInst::HrGetInternalDomainInfo ]----------------------------------
//
//
// Description:
// Expose ability to get internal Domain Info to internal components
// Parameters:
// IN cbDomainnameLength Length of string to search for
// IN szDomainName Domain Name to search for
// OUT ppIntDomainInfo Domain info returned (must be released)
// Returns:
// S_OK if match is found
// AQUEUE_E_INVALID_DOMAIN if no match is found
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrGetInternalDomainInfo(
IN DWORD cbDomainNameLength,
IN LPSTR szDomainName,
OUT CInternalDomainInfo **ppDomainInfo)
{
HRESULT hr = S_OK;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
_ASSERT(CMQ_INIT_DCT & m_dwInitMask);
hr = m_dct.HrGetInternalDomainInfo(cbDomainNameLength, szDomainName, ppDomainInfo);
ShutdownUnlock();
Exit:
return hr;
}
//---[ CAQSvrInst::HrGetDefaultDomainInfo ]----------------------------
//
//
// Description:
// Expose ability to get internal default Domain Info to internal components
// Parameters:
// OUT ppIntDomainInfo Domain info returned (must be released)
// Returns:
// S_OK if found
// AQUEUE_E_INVALID_DOMAIN if not found
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrGetDefaultDomainInfo(
OUT CInternalDomainInfo **ppDomainInfo)
{
HRESULT hr = S_OK;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
_ASSERT(CMQ_INIT_DCT & m_dwInitMask);
hr = m_dct.HrGetDefaultDomainInfo(ppDomainInfo);
ShutdownUnlock();
Exit:
return hr;
}
//---[ CAQSvrInst::HrGetDomainEntry ]----------------------------------------
//
//
// Description:
// Get Domain Entry
// Parameters:
// IN cbDomainnameLength Length of string to search for
// IN szDomainName Domain Name to search for
// OUT ppdentry Domain Entry for domain (from DMT)
// Returns:
// S_OK on success
// AQUEUE_E_INVALID_DOMAIN if domain is not found
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrGetDomainEntry(IN DWORD cbDomainNameLength,
IN LPSTR szDomainName,
OUT CDomainEntry **ppdentry)
{
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
_ASSERT(cbDomainNameLength);
_ASSERT(szDomainName);
_ASSERT(ppdentry);
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
hr = m_dmt.HrGetDomainEntry(cbDomainNameLength, szDomainName, ppdentry);
if (FAILED(hr))
goto Exit;
Exit:
if (fLocked)
ShutdownUnlock();
return hr;
}
//---[ CAQSvrInst::HrIterateDMTSubDomains ]----------------------------------------
//
//
// Description:
// Get Domain Entry
// Parameters:
// IN cbDomainnameLength Length of string to search for
// IN szDomainName Domain Name to search for
// IN pfn Iterator function
// IN pvcontext Context passed to each call
// Returns:
// S_OK on success
// AQUEUE_E_INVALID_DOMAIN if domain is not found
//
//--------------------------------------------------------------------------------
HRESULT CAQSvrInst::HrIterateDMTSubDomains(IN LPSTR szDomainName,
IN DWORD cbDomainNameLength,
IN DOMAIN_ITR_FN pfn,
IN PVOID pvContext)
{
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
DOMAIN_STRING strDomain;
_ASSERT(cbDomainNameLength);
_ASSERT(szDomainName);
_ASSERT(pfn);
_ASSERT(pvContext);
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
strDomain.Length = (USHORT) cbDomainNameLength;
strDomain.MaximumLength = (USHORT) cbDomainNameLength;
strDomain.Buffer = szDomainName;
hr = m_dmt.HrIterateOverSubDomains(&strDomain, pfn,pvContext);
if (FAILED(hr))
goto Exit;
Exit:
if (fLocked)
ShutdownUnlock();
return hr;
}
//---[ CAQSvrInst::HrIterateDMTSubDomains ]----------------------------------------
//
//
// Description:
// Get Domain Entry
// Parameters:
// IN cbDomainnameLength Length of string to search for
// IN szDomainName Domain Name to search for
// IN pfn Iterator function
// IN pvcontext Context passed to each call
// Returns:
// S_OK on success
// AQUEUE_E_INVALID_DOMAIN if domain is not found
//
//--------------------------------------------------------------------------------
HRESULT CAQSvrInst::HrIterateDCTSubDomains(IN LPSTR szDomainName,
IN DWORD cbDomainNameLength,
IN DOMAIN_ITR_FN pfn,
IN PVOID pvContext)
{
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
DOMAIN_STRING strDomain;
_ASSERT(cbDomainNameLength);
_ASSERT(szDomainName);
_ASSERT(pfn);
_ASSERT(pvContext);
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
strDomain.Length = (USHORT) cbDomainNameLength;
strDomain.MaximumLength = (USHORT) cbDomainNameLength;
strDomain.Buffer = szDomainName;
hr = m_dct.HrIterateOverSubDomains(&strDomain, pfn,pvContext);
if (FAILED(hr))
goto Exit;
Exit:
if (fLocked)
ShutdownUnlock();
return hr;
}
//---[ CAQSvrInst::HrTriggerGetMessageRouter ]--------------------------------------
//
//
// Description:
// Wrapper function that signals the MAIL_TRANSPORT_ON_GET_ROUTER_EVENT
// Parameters:
// IN pIMailMsgProperties - IMailMsgProperties to get
// OUT pIMessageRouter
//
// Returns:
//
// History:
// 5/20/98 - MikeSwa Created
// jstamerj 1998/07/10 18:30:41: Implemented Server event
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrTriggerGetMessageRouter(
IN IMailMsgProperties *pIMailMsgProperties,
OUT IMessageRouter **ppIMessageRouter)
{
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrTriggerGetMessageRouter");
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
_ASSERT(ppIMessageRouter);
_ASSERT(pIMailMsgProperties);
_ASSERT(m_pIMessageRouterDefault);
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
if(m_pISMTPServer) {
EVENTPARAMS_ROUTER EventParams;
//
// Initialiez EventParams
//
EventParams.dwVirtualServerID = m_dwServerInstance;
EventParams.pIMailMsgProperties = pIMailMsgProperties;
EventParams.pIMessageRouter = NULL;
EventParams.pIRouterReset = m_pIRouterReset;
EventParams.pIRoutingEngineDefault = this;
hr = TriggerServerEvent(
SMTP_MAILTRANSPORT_GET_ROUTER_FOR_MESSAGE_EVENT,
&EventParams);
if(SUCCEEDED(hr)) {
if(EventParams.pIMessageRouter) {
//
// The implementor of GetMessageRouter returned
// IMessageRouter with a refcount of one for us
//
*ppIMessageRouter = EventParams.pIMessageRouter;
} else {
//
// The server event succeeded, but no sink supplied an
// IMessageRouter (including default functionality)
//
hr = E_FAIL;
}
}
} else {
ErrorTrace((LPARAM)this, "Unable to trigger event to GetMessageRouter; using default");
//
// Try calling our default (builtin) GetMessageRouter
//
hr = GetMessageRouter(
pIMailMsgProperties, //IN IMsg
NULL, //IN pIMessageRouter (Current)
ppIMessageRouter); //OUT ppIMessageRouter (New)
}
Exit:
if (fLocked)
ShutdownUnlock();
TraceFunctLeaveEx((LPARAM)this);
return hr;
}
//---[ CAQSvrInst::HrTriggerLogEvent ]-----------------------------------------
//
//
// Description:
// Wrapper function that signals the SMTP_LOG_EVENT event
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrTriggerLogEvent(
IN DWORD idMessage,
IN WORD idCategory,
IN WORD cSubstrings,
IN LPCSTR *rgszSubstrings,
IN WORD wType,
IN DWORD errCode,
IN WORD iDebugLevel,
IN LPCSTR szKey,
IN DWORD dwOptions,
IN DWORD iMessageString,
IN HMODULE hModule)
{
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrTriggerLogEvent");
HRESULT hr = S_OK;
if (m_pISMTPServerEx) {
hr = m_pISMTPServerEx->TriggerLogEvent(
idMessage,
idCategory,
cSubstrings,
rgszSubstrings,
wType,
errCode,
iDebugLevel,
szKey,
dwOptions,
iMessageString,
hModule);
} else {
//
// If we do not have at least W2K SP2... we will not have this
// interface.
//
ErrorTrace((LPARAM) this,
"Need W2KSP2: Unable to log event %d with erCode %d");
}
TraceFunctLeaveEx((LPARAM)this);
return hr;
}
//---[ CAQSvrInst::HrTriggerInitRouter ]---------------------------------------
//
//
// Description:
// Wrapper function that signals the MAIL_TRANSPORT_ON_GET_ROUTER_EVENT
// but only has it create a new router object
// Parameters:
// none
//
// Returns:
//
// History:
// 5/20/98 - MikeSwa Created
// jstamerj 1998/07/10 18:30:41: Implemented Server event
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrTriggerInitRouter() {
TraceFunctEnter("CAQSvrInst::HrTriggerInitRouter");
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
if (m_pISMTPServer) {
EVENTPARAMS_ROUTER EventParams;
//
// Initialiez EventParams
//
EventParams.dwVirtualServerID = m_dwServerInstance;
EventParams.pIMailMsgProperties = NULL;
EventParams.pIMessageRouter = NULL;
EventParams.pIRouterReset = m_pIRouterReset;
EventParams.pIRoutingEngineDefault = NULL;
hr = TriggerServerEvent(
SMTP_MAILTRANSPORT_GET_ROUTER_FOR_MESSAGE_EVENT,
&EventParams);
} else {
hr = S_OK;
}
Exit:
if (fLocked)
ShutdownUnlock();
TraceFunctLeaveEx((LPARAM)this);
return hr;
}
//---[ CAQSvrInst::QueryInterface ]------------------------------------------
//
//
// Description:
// QueryInterface for IAdvQueue
// Parameters:
//
// Returns:
// S_OK on success
//
// Notes:
// This implementation makes it possible for any server component to get
// the IAdvQueueConfig interface.
//
// History:
// 7/29/98 - MikeSwa Modified (added IAdvQueueDomainType)
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
HRESULT hr = S_OK;
if (!ppvObj)
{
hr = E_INVALIDARG;
goto Exit;
}
if (IID_IUnknown == riid)
{
*ppvObj = static_cast<IAdvQueue *>(this);
}
else if (IID_IAdvQueue == riid)
{
*ppvObj = static_cast<IAdvQueue *>(this);
}
else if (IID_IAdvQueueConfig == riid)
{
*ppvObj = static_cast<IAdvQueueConfig *>(this);
}
else if (IID_IAdvQueueDomainType == riid)
{
*ppvObj = static_cast<IAdvQueueDomainType *>(this);
}
else if (IID_IAdvQueueAdmin == riid)
{
*ppvObj = static_cast<IAdvQueueAdmin *>(this);
}
else if (IID_IMailTransportRouterSetLinkState == riid)
{
*ppvObj = static_cast<IMailTransportRouterSetLinkState *>(this);
}
else if (IID_IAQServerEvent == riid)
{
*ppvObj = static_cast<IAQServerEvent*>(this);
}
else
{
*ppvObj = NULL;
hr = E_NOINTERFACE;
goto Exit;
}
static_cast<IUnknown *>(*ppvObj)->AddRef();
Exit:
return hr;
}
//---[ CAQSvrInst::SubmitMessage ]---------------------------------------------
//
//
// Description:
// External function to submit messages for delivery
// Parameters:
// pIMailMsgProperties Msg to submit for delivery
// Returns:
// S_OK always
// History:
// 10/7/1999 - MikeSwa Moved from inline function
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::SubmitMessage(IN IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SubmitMessage");
HRESULT hr = S_OK;
if (NULL == pIMailMsgProperties)
{
ErrorTrace((LPARAM)NULL,
"SubmitMessage called with NULL pIMailMsgProperties");
return E_INVALIDARG;
}
InterlockedIncrement((PLONG) &m_cTotalExternalMsgsSubmitted);
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingSubmit);
// Before we queue this message, stamp the message submission time
hr = HrSetSubmissionTimeIfNecessary(pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to stamp submission time on message - hr 0x%08x", hr);
goto Error_Exit;
}
hr = m_asyncqPreSubmissionQueue.HrQueueRequest(
pIMailMsgProperties, FALSE,
cCountMsgsForHandleThrottling(pIMailMsgProperties));
if (FAILED(hr))
{
goto Error_Exit;
}
Exit:
TraceFunctLeave();
return S_OK;
Error_Exit:
InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingSubmit);
HandleAQFailure(AQ_FAILURE_INTERNAL_ASYNCQ, hr, pIMailMsgProperties);
goto Exit;
}
//---[ CAQSvrInst::HrInternalSubmitMessage ]-----------------------------------
//
//
// Description:
// Implements IAdvQueue::SubmitMessage
// Parameters:
// pIMailMsgProperties... Messaage to queue
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrInternalSubmitMessage(
IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties,
"CAQSvrInst::SubmitMessage");
_ASSERT(CATMSGQ_SIG == m_dwSignature);
HRESULT hr = S_OK;
DWORD dwMsgStatus = MP_STATUS_SUCCESS;
EVENTPARAMS_SUBMISSION Params;
FILETIME ftDeferred;
DWORD cbProp = 0;
DWORD dwContext = 0;
_ASSERT(pIMailMsgProperties);
if (NULL == pIMailMsgProperties)
{
ErrorTrace((LPARAM)NULL,
"SubmitMessage called with NULL pIMailMsgProperties");
return E_INVALIDARG;
}
//Check and see if we need to request a retry for failed msgs
m_fmq.StartProcessingIfNecessary();
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_DEFERRED_DELIVERY_FILETIME,
sizeof(FILETIME), &cbProp,
(BYTE *) &ftDeferred);
if (SUCCEEDED(hr) && !fInPast(&ftDeferred, &dwContext))
{
//Defer delivery until a later time if deferred delivery time is
//present, and in the past
hr = S_OK;
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingDeferredDelivery);
m_defq.Enqueue(pIMailMsgProperties, &ftDeferred);
goto Exit;
}
// Set submission time for this message
hr = HrSetSubmissionTimeIfNecessary(pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to stamp submission time on message - hr 0x%08x",
hr);
return hr;
}
InterlockedIncrement((PLONG) &m_cTotalMsgsSubmitted);
//
// Set the message status (if currently unset)
//
hr = pIMailMsgProperties->GetDWORD(
IMMPID_MP_MESSAGE_STATUS,
&dwMsgStatus);
if( (hr == MAILMSG_E_PROPNOTFOUND) ||
(g_fResetMessageStatus) )
{
//
// Initialize the message status
//
hr = pIMailMsgProperties->PutDWORD(
IMMPID_MP_MESSAGE_STATUS,
MP_STATUS_SUCCESS);
dwMsgStatus = MP_STATUS_SUCCESS;
}
//
//$$TODO: Jump from here to whatever state dwMsgStatus indicates
//
MSG_TRACK_INFO msgTrackInfo;
ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
msgTrackInfo.dwEventId = MTE_BEGIN_SUBMIT_MESSAGE;
m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
//
// AddRef this object here; release in completion
//
AddRef();
Params.pIMailMsgProperties = pIMailMsgProperties;
Params.pfnCompletion = MailTransport_Completion_SubmitMessage;
Params.pCCatMsgQueue = this;
pIMailMsgProperties->AddRef();
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingSubmitEvent);
TRACE_COUNTERS;
//
// Call server event if we can and if dwMsgStatus does not
// indicate the mssage has already been submitted
//
if(SUCCEEDED(hr) &&
(m_pISMTPServer) &&
(dwMsgStatus < MP_STATUS_SUBMITTED))
{
hr = TriggerServerEvent(
SMTP_MAILTRANSPORT_SUBMISSION_EVENT,
&Params);
DebugTrace((LPARAM)pIMailMsgProperties,
"TriggerServerEvent returned hr %08lx", hr);
}
//
// If TriggerServerEvent returned an error OR m_pISMTPServer is
// null, or the message was already submitted, call the event
// completion routine directly
//
if((m_pISMTPServer == NULL) ||
FAILED(hr) ||
(dwMsgStatus >= MP_STATUS_SUBMITTED))
{
DebugTrace((LPARAM)this, "Skipping the submission event");
// Call the SEO Dispatcher completion routine directly so we
// don't loose this mail...
hr = SubmissionEventCompletion(S_OK, &Params);
}
//
// SEO dispatcher will call the completion routine
// (MailTransport_Completion_SubmitMessage) regardless wether or
// not all the sinks work synchronously or async. Because of
// this, this function is now done.
//
Exit:
TraceFunctLeaveEx((LPARAM) pIMailMsgProperties);
return hr;
}
//---[ CAQSvrInst::HandleFailedMessage ]--------------------------------------
//
//
// Description:
// Handles a failed message from SMTP... usually by NDRing the message or
// by treating the message as badmail.
//
// NOTE: Message or input file will be deleted by this operation.
// Parameters:
// pIMailMsgProperties MailMsg that needs to be handles
// fUseIMailMsgProperties Use the IMailMsg if set, else use the szFilename,
// szFileName use the filename if no msg
// dwFailureReasons One of the failure reasons described in aqueue.idl
// hrFailureCode Additional information that describes a failure
// code encountered by SMTP.
// Returns:
// S_OK on success
// E_INVALIDARG if pIMailMsgProperties is NULL
// History:
// 7/28/98 - MikeSwa Created
// 10/14/98 - MikeSwa Added filename string for badmail
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::HandleFailedMessage(
IN IMailMsgProperties *pIMailMsgProperties,
IN BOOL fUseIMailMsgProperties,
IN LPSTR szFileName,
IN DWORD dwFailureReason,
IN HRESULT hrFailureCode)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleFailedMessage");
HRESULT hr = S_OK;
HRESULT hrBadMail = hrFailureCode;
DWORD iCurrentDomain = 0;
DWORD cDomains = 0;
IMailMsgRecipients *pIMailMsgRecipients = NULL;
CDSNParams dsnparams;
BOOL fNDR = TRUE; //FALSE -> Badmail handling
SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
dsnparams.dwStartDomain = 0;
dsnparams.dwDSNActions = DSN_ACTION_FAILURE_ALL;
dsnparams.pIMailMsgProperties = pIMailMsgProperties;
dsnparams.hrStatus = hrFailureCode;
MSG_TRACK_INFO msgTrackInfo;
ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
msgTrackInfo.dwEventId = MTE_AQ_FAILED_MESSAGE;
msgTrackInfo.dwRcptReportStatus = dwFailureReason;
m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
//
// Switch over the various general failure reasons and handle them as
// appropriate.
//
switch(dwFailureReason)
{
case MESSAGE_FAILURE_HOP_COUNT_EXCEEDED:
//
// Attempt to NDR
//
_ASSERT(pIMailMsgProperties);
dsnparams.hrStatus = AQUEUE_E_MAX_HOP_COUNT_EXCEEDED;
fNDR = TRUE;
break;
case MESSAGE_FAILURE_GENERAL:
//
// Attempt to NDR
//
fNDR = TRUE;
break;
case MESSAGE_FAILURE_CAT:
//
// Attempt to NDR... set DSN context to CAT
//
dsnparams.dwDSNActions |= DSN_ACTION_CONTEXT_CAT;
fNDR = TRUE;
break;
case MESSAGE_FAILURE_BAD_PICKUP_DIR_FILE:
//
// Badmail, since we do not have the P1 information needed to badmail
//
_ASSERT(szFileName);
hrBadMail = AQUEUE_E_PICKUP_DIR;
fNDR = FALSE; //This should be handled as badmail
break;
default:
_ASSERT(0 && "Unhandled failed msg case!");
}
if (fNDR && pIMailMsgProperties && fUseIMailMsgProperties)
{
hr = HrLinkAllDomains(pIMailMsgProperties);
if (FAILED(hr))
goto Exit;
//Fire DSN Generation event
hr = HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
if (FAILED(hr))
{
HandleBadMail(pIMailMsgProperties, fUseIMailMsgProperties,
szFileName, hrBadMail, FALSE);
if (dwFailureReason == MESSAGE_FAILURE_GENERAL) {
InterlockedIncrement((PLONG) &m_cBadmailFailureGeneral);
} else {
_ASSERT(dwFailureReason == MESSAGE_FAILURE_HOP_COUNT_EXCEEDED);
InterlockedIncrement((PLONG) &m_cBadmailHopCountExceeded);
}
hr = S_OK; //handled error internally
ErrorTrace((LPARAM) this, "ERROR: Unable to NDR failed mail - hr 0x%08X", hr);
goto Exit;
}
}
else
{
//Handle as badmail
HandleBadMail(pIMailMsgProperties, fUseIMailMsgProperties,
szFileName, hrBadMail, FALSE);
InterlockedIncrement((PLONG) &m_cBadmailBadPickupFile);
_ASSERT(dwFailureReason == MESSAGE_FAILURE_BAD_PICKUP_DIR_FILE);
}
if ( fUseIMailMsgProperties && pIMailMsgProperties)
{
//Now that we are done... delete mailmsg from system
hr = HrDeleteIMailMsg(pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "ERROR: Unable to delete message hr0x%08X", hr);
//message was actually NDR'd/bad mailed correctly
hr = S_OK;
}
}
Exit:
if (pIMailMsgRecipients)
pIMailMsgRecipients->Release();
TraceFunctLeave();
return hr;
}
//+------------------------------------------------------------
//
// Function: MailTransport_Completion_SubmitMessage
//
// Synopsis: SEO will call this routine after all sinks for
// SubmitMessage have been handeled
//
// Arguments:
// pvContext: Context passed into TriggerServerEvent
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 980609 16:13:40: Created.
//
//-------------------------------------------------------------
HRESULT MailTransport_Completion_SubmitMessage(
HRESULT hrStatus,
PVOID pvContext)
{
TraceFunctEnter("MailTransport_Completion_SubmitMessage");
PEVENTPARAMS_SUBMISSION pParams = (PEVENTPARAMS_SUBMISSION) pvContext;
CAQSvrInst *paqinst = (CAQSvrInst *) pParams->pCCatMsgQueue;
TraceFunctLeave();
return paqinst->SubmissionEventCompletion(
hrStatus,
pParams);
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::SubmissionEventCompletion
//
// Synopsis: Completion routine called when the submission event is
// done.
//
// Arguments:
// hrStatus: Status of server event
// pParams: Context passed into TriggerServereEvent
//
// Returns:
// Nothing
//
// History:
// jstamerj 980610 12:26:18: Created.
//
//-------------------------------------------------------------
HRESULT CAQSvrInst::SubmissionEventCompletion(
HRESULT hrStatus,
PEVENTPARAMS_SUBMISSION pParams)
{
TraceFunctEnterEx((LPARAM)pParams->pIMailMsgProperties,
"CAQSvrInst::SubmissionEventCompletion");
_ASSERT(pParams);
HRESULT hr;
DebugTrace((LPARAM)pParams->pIMailMsgProperties,
"Status of event completion: %08lx", hrStatus);
InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingSubmitEvent);
//
// Update the message status
//
hr = SetNextMsgStatus(MP_STATUS_SUBMITTED, pParams->pIMailMsgProperties);
if (hr == S_OK) //anything else implies that the message has been handled
{
// Only trigger the precat event if message was not turfed.
TriggerPreCategorizeEvent(pParams->pIMailMsgProperties);
}
//
// Release refernce added in SubmitMessage
//
Release();
pParams->pIMailMsgProperties->Release();
//
// pParams is part of a larger allocation that will be released by
// SEO Dispatcher code
//
TraceFunctLeave();
return S_OK;
}
//---[ CAQSvrInst::SubmitMessageToCategorizer ]-------------------------------------------
//
//
// Description:
// Implements IAdvQueue::SubmitMessageToCategorizer
// Parameters:
// pIMailMsgProperties... Messaage to queue
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::SubmitMessageToCategorizer(
IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "CAQSvrInst::SubmitMessageToCategorizer");
HRESULT hr = S_OK;
HRESULT hrTmp = S_OK;
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
BOOL fLocked = FALSE;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
MSG_TRACK_INFO msgTrackInfo;
ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
msgTrackInfo.dwEventId = MTE_SUBMIT_MESSAGE_TO_CAT;
m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
fLocked = TRUE;
cIncMsgsInSystem();
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingCat);
TRACE_COUNTERS;
hr = m_asyncqPreCatQueue.HrQueueRequest(pIMailMsgProperties, FALSE,
cCountMsgsForHandleThrottling(pIMailMsgProperties));
if (FAILED(hr))
{
HandleAQFailure(AQ_FAILURE_PRECAT_RETRY, E_FAIL, pIMailMsgProperties);
goto Exit;
}
Exit:
if(fLocked)
{
ShutdownUnlock();
}
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::SetNextMsgStatus ]------------------------------------------
//
//
// Description:
// Used by the event glue code to set the next message status. Will turf
// or badmail a message if the status indicates that is the requested
// action.
// Parameters:
// IN dwCurrentStatus The current status (according to *current*
// place in event pipeline). Valid values are
// MP_STATUS_SUBMITTED
// MP_STATUS_CATEGORIZED
// IN pIMailMsgProperties The message
// OUT pdwNewStatus The new status
// Returns:
// S_OK Success
// S_FALSE Success, but message has been handled.
// History:
// 11/17/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::SetNextMsgStatus(
IN DWORD dwCurrentStatus,
IN IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SetNextMsgStatus");
HRESULT hr = S_OK;
DWORD dwActualStatus = 0;
DWORD dwNewStatus = 0;
BOOL fHandled = FALSE;
_ASSERT(pIMailMsgProperties);
_ASSERT((MP_STATUS_SUBMITTED == dwCurrentStatus) || (MP_STATUS_CATEGORIZED == dwCurrentStatus));
hr = pIMailMsgProperties->GetDWORD(IMMPID_MP_MESSAGE_STATUS, &dwActualStatus);
if (FAILED(hr))
dwActualStatus = dwCurrentStatus;
if (MP_STATUS_SUCCESS == dwActualStatus)
dwActualStatus = dwCurrentStatus;
switch(dwActualStatus)
{
case MP_STATUS_BAD_MAIL:
HandleBadMail(pIMailMsgProperties, TRUE, NULL, E_FAIL, FALSE);
InterlockedIncrement((PLONG) &m_cBadmailEvent);
//OK... now continue as if message was aborted
case MP_STATUS_ABORT_DELIVERY:
fHandled = TRUE;
HrDeleteIMailMsg(pIMailMsgProperties);
break;
case MP_STATUS_ABANDON_DELIVERY:
//In this case, we will leave the message in the queue directory
//until restart & reset the state so it goes through the entire
//pipeline. The idea is that someone can write a sink to detect
//a non-supported state (like CAT disabled in an Exchange install)
//that will abandon delivery of the messages and log an event.
//The admin can fix the problem and restart smtpsvc. Once
//the service is restarted... the messages are magically submitted
//and re-categorized.
fHandled = TRUE;
pIMailMsgProperties->PutDWORD(IMMPID_MP_MESSAGE_STATUS,
MP_STATUS_SUCCESS);
break;
case MP_STATUS_CATEGORIZED:
//Don't change status from categorized to something else
dwNewStatus = dwActualStatus;
DebugTrace((LPARAM) this, "Message 0x%x has already been categorized",
pIMailMsgProperties);
break;
default: //simply move on to the next expected status
dwNewStatus = dwCurrentStatus;
}
if (!fHandled)
{
pIMailMsgProperties->PutDWORD(IMMPID_MP_MESSAGE_STATUS, dwNewStatus);
//callers will not be able to do anything about a failure to write status
hr = S_OK;
}
else
{
DecMsgsInSystem();
hr = S_FALSE;
}
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::fPreCatQueueCompletion ]-----------------------------------
//
//
// Description:
// Completion routine for Pre-Categorization queue
// Parameters:
// pIMailMsgProperties - MailMsg to give to categorization
// Returns:
// TRUE if successful
// FALSE if message needs to be re-requeue
// History:
// 7/17/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CAQSvrInst::fPreCatQueueCompletion(IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::fPreCatQueueCompletion");
_ASSERT(CATMSGQ_SIG == m_dwSignature);
HRESULT hr = S_OK;
HRESULT hrCatCompletion;
IUnknown *pIUnknown = NULL;
BOOL fRet = TRUE;
BOOL fLocked = FALSE;
if (!fTryShutdownLock())
{
hr = S_OK; //we cannot retry on shutdown
goto Exit;
}
fLocked = TRUE;
hr = pIMailMsgProperties->QueryInterface(IID_IUnknown, (PVOID *) &pIUnknown);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IUknown Failed!");
_ASSERT(pIUnknown);
InterlockedIncrement((PLONG) &m_cCatMsgCalled);
m_asyncqPreCatQueue.IncPendingAsyncCompletions();
hr = CatMsg(m_hCat, pIUnknown,(PFNCAT_COMPLETION)CAQSvrInst::CatCompletion,
(LPVOID) this);
if (FAILED(hr))
{
if(hr == CAT_E_RETRY)
{
//
// Return false so that this message will be re-queued
//
fRet = FALSE;
InterlockedDecrement((PLONG) &m_cCatMsgCalled);
m_asyncqPreCatQueue.DecPendingAsyncCompletions();
//
// Schedule a time to retry the messages
//
ScheduleInternalRetry(LI_TYPE_PENDING_CAT);
}
else
{
//
// Return true since this is not a retryable error
// Call CatCompletion to handle the non-retryable error (log an event, etc)
//
hrCatCompletion = CatCompletion(
hr, // hrCatResult
(LPVOID) this, // pContext
pIUnknown, // pIMsg
NULL); // rgpIMsg
_ASSERT(SUCCEEDED(hrCatCompletion));
}
}
Exit:
if (pIUnknown)
pIUnknown->Release();
if(fLocked)
{
ShutdownUnlock();
}
SleepForPerfAnalysis(g_dwCatQueueSleepMilliseconds);
TraceFunctLeave();
return fRet;
}
//---[ CAQSvrInst::SetConfigInfo ]--------------------------------------------
//
//
// Description:
// Implements IAdvQueueConfig::SetConfigInfo
// Parameters:
// IN pAQConfigInfo Ptr to config info structure
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::SetConfigInfo(IN AQConfigInfo *pAQConfigInfo)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SetConfigInfo");
HRESULT hr = S_OK;
if (!pAQConfigInfo)
{
hr = E_INVALIDARG;
goto Exit;
}
//check version of structure
if (!pAQConfigInfo->cbVersion)
{
hr = E_INVALIDARG;
goto Exit;
}
//we must be setting something
if (!(pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_ALL))
{
hr = E_INVALIDARG;
goto Exit;
}
m_slPrivateData.ExclusiveLock();
//Retry related config data
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
MEMBER_OK(pAQConfigInfo, dwFirstRetrySeconds))
{
m_dwFirstTierRetrySeconds = pAQConfigInfo->dwFirstRetrySeconds;
}
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_EXPIRE_DELAY &&
MEMBER_OK(pAQConfigInfo, dwDelayExpireMinutes))
{
m_dwDelayExpireMinutes = pAQConfigInfo->dwDelayExpireMinutes;
if (m_dwDelayExpireMinutes == 0) {
//Default to g_dwRetriesBeforeDelay* retry interval
m_dwDelayExpireMinutes =
g_dwRetriesBeforeDelay*m_dwFirstTierRetrySeconds/60;
}
}
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_EXPIRE_NDR &&
MEMBER_OK(pAQConfigInfo, dwNDRExpireMinutes))
{
m_dwNDRExpireMinutes = pAQConfigInfo->dwNDRExpireMinutes;
if (m_dwNDRExpireMinutes == 0) {
//Default to g_dwDelayIntervalsBeforeNDR* delay expiration
m_dwNDRExpireMinutes =
g_dwDelayIntervalsBeforeNDR*m_dwDelayExpireMinutes;
}
}
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_LOCAL_EXPIRE_DELAY &&
MEMBER_OK(pAQConfigInfo, dwLocalDelayExpireMinutes))
{
DWORD dwOldLocalDelayExpire = m_dwLocalDelayExpireMinutes;
m_dwLocalDelayExpireMinutes = pAQConfigInfo->dwLocalDelayExpireMinutes;
if (m_dwLocalDelayExpireMinutes == 0) {
//Default to g_dwRetriesBeforeDelay* retry interval
m_dwLocalDelayExpireMinutes =
g_dwRetriesBeforeDelay*m_dwFirstTierRetrySeconds/60;
}
}
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_LOCAL_EXPIRE_NDR &&
MEMBER_OK(pAQConfigInfo, dwLocalNDRExpireMinutes))
{
m_dwLocalNDRExpireMinutes = pAQConfigInfo->dwLocalNDRExpireMinutes;
if (m_dwLocalNDRExpireMinutes == 0) {
//Default to g_dwDelayIntervalsBeforeNDR* delay expiration
m_dwLocalNDRExpireMinutes =
g_dwDelayIntervalsBeforeNDR*m_dwLocalDelayExpireMinutes;
}
}
//Handle default local domain
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_DEFAULT_DOMAIN &&
MEMBER_OK(pAQConfigInfo, szDefaultLocalDomain))
{
hr = HrUpdateRefCountedString(&m_prstrDefaultDomain,
pAQConfigInfo->szDefaultLocalDomain);
}
//Handle Server FQDN
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_SERVER_FQDN &&
MEMBER_OK(pAQConfigInfo, szServerFQDN))
{
hr = HrUpdateRefCountedString(&m_prstrServerFQDN,
pAQConfigInfo->szServerFQDN);
}
//Handle Copy NDR To Address
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_SEND_DSN_TO &&
MEMBER_OK(pAQConfigInfo, szSendCopyOfNDRToAddress))
{
hr = HrUpdateRefCountedString(&m_prstrCopyNDRTo,
pAQConfigInfo->szSendCopyOfNDRToAddress);
}
//Handle BadMail config
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_BADMAIL_DIR &&
MEMBER_OK(pAQConfigInfo, szBadMailDir))
{
hr = HrUpdateRefCountedString(&m_prstrBadMailDir,
pAQConfigInfo->szBadMailDir);
}
//Get DSN options
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_USE_DSN_OPTIONS &&
MEMBER_OK(pAQConfigInfo, dwDSNOptions))
{
m_dwDSNOptions = pAQConfigInfo->dwDSNOptions;
}
//Get Default DSN Language
if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_USE_DSN_LANGUAGE &&
MEMBER_OK(pAQConfigInfo, dwDSNLanguageID))
{
m_dwDSNLanguageID = pAQConfigInfo->dwDSNLanguageID;
}
m_slPrivateData.ExclusiveUnlock();
m_pConnMgr->UpdateConfigData(pAQConfigInfo);
if (INVALID_HANDLE_VALUE != m_hCat)
{
HRESULT hrTmp = CatChangeConfig(m_hCat, pAQConfigInfo, m_pISMTPServer, (IAdvQueueDomainType *) this);
if (SUCCEEDED(hr))
hr = hrTmp;
}
Exit:
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::SetDomainInfo ]-------------------------------------------
//
//
// Description:
// Implements IAdvQueueConfig::SetDomainInfo
// Parameters:
// IN pDomainInfo Per domain config info to store
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::SetDomainInfo(IN DomainInfo *pDomainInfo)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SetDomainInfo");
HRESULT hr = S_OK;
CInternalDomainInfo *pIntDomainInfo = NULL;
BOOL fLocked = FALSE;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
pIntDomainInfo = new CInternalDomainInfo(m_dct.dwGetCurrentVersion());
if (!pIntDomainInfo)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
//Create internal Domain Info struct
hr = pIntDomainInfo->HrInit(pDomainInfo);
if (FAILED(hr))
goto Exit;
hr = m_dct.HrSetInternalDomainInfo(pIntDomainInfo);
if (FAILED(hr))
goto Exit;
DebugTrace((LPARAM) this, "INFO: Setting domain info flags 0x%08X for domain %s",
pDomainInfo->dwDomainInfoFlags, pDomainInfo->szDomainName);
Exit:
if (fLocked)
ShutdownUnlock();
if (pIntDomainInfo)
pIntDomainInfo->Release();
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::GetDomainInfo ]-------------------------------------------
//
//
// Description:
// Implements IAdvQueue::GetDomainInfo... returns information about a
// requested domain. To keep from leaking memory, all calls must be paired
// with a call to ReleaseDomainInfo. Will handle wildcard matches
// Parameters:
// IN cbDomainNameLength Length of domain name string
// IN szDomainName Domain Name to look for
// IN OUT pDomainInfo Ptr to Domain info structure to fill
// OUT ppvDomainContext Ptr to Domain context used to release mem
// Returns:
// S_OK on success
// History:
// 7/29/98 - MikeSwa Modified (fixed leak of domain info struct)
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::GetDomainInfo(
IN DWORD cbDomainNameLength,
IN CHAR szDomainName[],
IN OUT DomainInfo *pDomainInfo,
OUT DWORD **ppvDomainContext)
{
HRESULT hr = S_OK;
CInternalDomainInfo *pIntDomainInfo = NULL;
BOOL fLocked = FALSE;
if (!cbDomainNameLength || !szDomainName || !pDomainInfo || !ppvDomainContext)
{
hr = E_INVALIDARG;
goto Exit;
}
_ASSERT(pDomainInfo->cbVersion >= sizeof(DomainInfo));
*ppvDomainContext = NULL;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
hr = m_dct.HrGetInternalDomainInfo(cbDomainNameLength, szDomainName, &pIntDomainInfo);
if (FAILED(hr))
goto Exit;
_ASSERT(pIntDomainInfo);
//copy domain info struct
memcpy(pDomainInfo, &(pIntDomainInfo->m_DomainInfo), sizeof(DomainInfo));
*ppvDomainContext = (DWORD *) pIntDomainInfo;
goto Exit;
Exit:
if (fLocked)
ShutdownUnlock();
return hr;
}
//---[ CAQSvrInst::ReleaseDomainInfo ]---------------------------------------
//
//
// Description:
// Implements IAdvQueueConfig ReleaseDomainInfo... releases data
// associated with the DomainInfo struct returned by GetDomainInfo.
// Parameters:
// IN pvDomainContext Context passed
// Returns:
// S_OK on success
// E_INVALIDARG if pvDomainContext is NULL
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::ReleaseDomainInfo(IN DWORD *pvDomainContext)
{
HRESULT hr = S_OK;
CInternalDomainInfo *pIntDomainInfo = (CInternalDomainInfo *) pvDomainContext;
if (!pIntDomainInfo)
{
hr = E_INVALIDARG;
goto Exit;
}
pIntDomainInfo->Release();
Exit:
return hr;
}
//---[ CAQSvrInst::GetPerfCounters ]---------------------------------------
//
//
// Description:
// Method to retrieve AQ perf counters.
// Parameters:
// OUT pAQPerfCounters Struct to return counters in.
// OUT pCatPerfCouneters Struct to return counters in. (optinal)
// Returns:
// S_OK on success
// E_INVALIDARG if pAQPerfCounters is NULL
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::GetPerfCounters(
OUT AQPerfCounters *pAQPerfCounters,
OUT PCATPERFBLOCK pCatPerfCounters)
{
HRESULT hr = S_OK;
if (!pAQPerfCounters)
return( E_INVALIDARG );
_ASSERT((sizeof(AQPerfCounters) == pAQPerfCounters->cbVersion) && "aqueue/smtpsvc dll version mismatch");
if (sizeof(AQPerfCounters) != pAQPerfCounters->cbVersion)
return( E_INVALIDARG );
pAQPerfCounters->cMsgsDeliveredLocal = m_cMsgsDeliveredLocal;
pAQPerfCounters->cCurrentMsgsPendingCat = m_cCurrentMsgsPendingCat;
pAQPerfCounters->cCurrentMsgsPendingRemoteDelivery = m_cCurrentMsgsPendingDelivery;
pAQPerfCounters->cCurrentMsgsPendingLocalDelivery = m_cCurrentMsgsPendingLocal;
pAQPerfCounters->cCurrentQueueMsgInstances = m_cCurrentQueueMsgInstances;
pAQPerfCounters->cTotalMsgRemoteSendRetries = m_cMsgsAckedRetry;
pAQPerfCounters->cTotalMsgLocalRetries = m_cMsgsAckedRetryLocal;
pAQPerfCounters->cCurrentMsgsPendingLocalRetry = m_cCurrentMsgsPendingLocalRetry;
//DSN counters
pAQPerfCounters->cNDRsGenerated = m_cNDRs;
pAQPerfCounters->cDelayedDSNsGenerated = m_cDelayedDSNs;
pAQPerfCounters->cDeliveredDSNsGenerated = m_cDeliveredDSNs;
pAQPerfCounters->cRelayedDSNsGenerated = m_cRelayedDSNs;
pAQPerfCounters->cExpandedDSNsGenerated = m_cExpandedDSNs;
pAQPerfCounters->cTotalMsgsTURNETRN = m_cTotalMsgsTURNETRNDelivered;
//Queue/Link related counters
pAQPerfCounters->cCurrentRemoteDestQueues = m_cCurrentRemoteDestQueues;
pAQPerfCounters->cCurrentRemoteNextHopLinks = m_cCurrentRemoteNextHops;
pAQPerfCounters->cTotalMsgsBadmailNoRecipients = m_cBadmailNoRecipients;
pAQPerfCounters->cTotalMsgsBadmailHopCountExceeded = m_cBadmailHopCountExceeded;
pAQPerfCounters->cTotalMsgsBadmailFailureGeneral = m_cBadmailFailureGeneral;
pAQPerfCounters->cTotalMsgsBadmailBadPickupFile = m_cBadmailBadPickupFile;
pAQPerfCounters->cTotalMsgsBadmailEvent = m_cBadmailEvent;
pAQPerfCounters->cTotalMsgsBadmailNdrOfDsn = m_cBadmailNdrOfDsn;
pAQPerfCounters->cCurrentMsgsPendingRouting = m_cCurrentMsgsPendingRouting;
pAQPerfCounters->cTotalDSNFailures = m_cTotalDSNFailures;
pAQPerfCounters->cCurrentMsgsInLocalDelivery = m_cCurrentMsgsInLocalDelivery;
//
// The m_cTotalMsgsSubmitted counter counts the number of times
// HrInternalSubmit msg has been called. This does not include the
// presubmission queue, so me need to manually add this count in.
//
pAQPerfCounters->cTotalMsgsSubmitted = m_cTotalMsgsSubmitted +
m_cCurrentMsgsPendingSubmit;
if (fTryShutdownLock()) {
pAQPerfCounters->cCurrentMsgsPendingUnreachableLink =
m_dmt.GetCurrentlyUnreachableTotalMsgCount();
ShutdownUnlock();
}
//For now, these counters will be calculated by walking the DMT (same
//function that determines msgs pending retry).
pAQPerfCounters->cCurrentRemoteNextHopLinksEnabled = 0;
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingRetry = 0;
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingScheduling = 0;
pAQPerfCounters->cCurrentRemoteNextHopLinksPendingTURNETRN = 0;
pAQPerfCounters->cCurrentRemoteNextHopLinksFrozenByAdmin = 0;
//Get Retry remote retry and DSN counters
pAQPerfCounters->cCurrentMsgsPendingRemoteRetry = 0;
if (fTryShutdownLock())
{
hr = m_dmt.HrIterateOverSubDomains(NULL, CalcDMTPerfCountersIteratorFn,
pAQPerfCounters);
//will not generate transient errors (we expect success or an empty table
_ASSERT(SUCCEEDED(hr) || (DOMHASH_E_NO_SUCH_DOMAIN == hr));
if((m_hCat != INVALID_HANDLE_VALUE) && (pCatPerfCounters))
hr = CatGetPerfCounters(m_hCat, pCatPerfCounters);
_ASSERT(SUCCEEDED(hr));
ShutdownUnlock();
}
//save values in CAQSvrInst, so we can dump them in the debugger
m_cCurrentRemoteNextHopsEnabled = pAQPerfCounters->cCurrentRemoteNextHopLinksEnabled;
m_cCurrentRemoteNextHopsPendingRetry = pAQPerfCounters->cCurrentRemoteNextHopLinksPendingRetry;
m_cCurrentRemoteNextHopsPendingSchedule = pAQPerfCounters->cCurrentRemoteNextHopLinksPendingScheduling;
m_cCurrentRemoteNextHopsFrozenByAdmin = pAQPerfCounters->cCurrentRemoteNextHopLinksFrozenByAdmin;
return( S_OK );
}
//---[ CAQSvrInst::ResetPerfCounters ]---------------------------------------
//
//
// Description:
// Method to reset AQ perf counters to 0.
// Parameters:
// None
// Returns:
// S_OK on success
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::ResetPerfCounters()
{
m_cTotalMsgsQueued = 0;
m_cTotalMsgsSubmitted = 0;
m_cMsgsAcked = 0;
m_cMsgsAckedRetry = 0;
m_cMsgsDeliveredLocal = 0;
m_cMsgsAckedRetryLocal = 0;
m_cTotalMsgsTURNETRNDelivered = 0;
//clear DSN counters
m_cDelayedDSNs = 0;
m_cNDRs = 0;
m_cDeliveredDSNs = 0;
m_cRelayedDSNs = 0;
m_cExpandedDSNs = 0;
return( S_OK );
}
//---[ CAQSvrInst::StartConfigUpdate() ]---------------------------------------
//
//
// Description:
// Implements IAQConfig::StartConfigUpdate() which is used to signal that
// all of the domain information is about to be updated.
// Parameters:
// -
// Returns:
// S_OK on success
// AQUEUE_E_SHUTDOWN on shutdown
// History:
// 9/29/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::StartConfigUpdate()
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::StartConfigUpdate");
HRESULT hr = S_OK;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
}
else
{
m_dct.StartConfigUpdate();
ShutdownUnlock();
}
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::FinishConfigUpdate ]----------------------------------------
//
//
// Description:
// Implements IAQConfig::FinishConfigUpdate() which is used to signal that
// signal that all of the domain information has been updated. This will
// cause us to walk through the DomainConfigTable and remove any domain
// config info that has not been updated (ie - a domain that has been
// deleted).
// Parameters:
// -
// Returns:
// S_OK on success
// AQUEUE_E_SHUTDOWN if called while shutdown is in progress.
// History:
// 9/29/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::FinishConfigUpdate()
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::FinishConfigUpdate");
HRESULT hr = S_OK;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
}
else
{
m_dct.FinishConfigUpdate();
//Reroute everything
//$$REVIEW - should we re-route on metabase changes?
ResetRoutes(RESET_NEXT_HOPS);
//Important configuration data may have changed... kick connmgr
m_pConnMgr->KickConnections();
ShutdownUnlock();
}
TraceFunctLeave();
return hr;
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::TriggerPostCategorizeEvent
//
// Synopsis: Triggers post categorization event
//
// Arguments:
// pIMsg: MailMsg for event or NULL
// rgpIMsg: NULL or a null terminated array of mailmsg pointers
//
// NOTE: pIMsg or rgpIMsg must be NULL, but neither can be null
// (exclusive OR)
//
// Returns:
// -
//
// History:
// jstamerj 980616 20:43:08: Created.
// 8/25/98 - MikeSwa Modified - removed return code
//
//-------------------------------------------------------------
void CAQSvrInst::TriggerPostCategorizeEvent(
IUnknown *pIMsg,
IUnknown **rgpIMsg)
{
TraceFunctEnterEx((LPARAM)this,
"CAQSvrInst::TriggerPostCategorizeEvent");
HRESULT hr = S_OK;
HRESULT hrTmp = S_OK;
IMailMsgProperties *pIMailMsgProperties = NULL;
if(pIMsg)
{
hr = TriggerPostCategorizeEventOneMsg(pIMsg);
if (FAILED(hr))
{
hrTmp = pIMsg->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hrTmp) && "Could not QI for IMailMsgProperties");
if (FAILED(hrTmp))
LogAQEvent(AQ_FAILURE_POSTCAT_EVENT, NULL, NULL, NULL);
else
{
HandleAQFailure(AQ_FAILURE_POSTCAT_EVENT, hr,
pIMailMsgProperties);
pIMailMsgProperties->Release();
pIMailMsgProperties = NULL;
}
DecMsgsInSystem(FALSE, FALSE);
hr = S_OK;
}
}
else
{
_ASSERT(rgpIMsg);
IUnknown **ppIMsgCurrent = rgpIMsg;
DecMsgsInSystem(FALSE, FALSE);
while(SUCCEEDED(hr) && (*ppIMsgCurrent))
{
hr = TriggerPostCategorizeEventOneMsg(
*ppIMsgCurrent);
ppIMsgCurrent++;
if (FAILED(hr))
{
hrTmp = (*ppIMsgCurrent)->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hrTmp) && "Could not QI for IMailMsgProperties");
if (FAILED(hrTmp))
LogAQEvent(AQ_FAILURE_POSTCAT_EVENT, NULL, NULL, NULL);
else
{
HandleAQFailure(AQ_FAILURE_POSTCAT_EVENT, hr,
pIMailMsgProperties);
pIMailMsgProperties->Release();
pIMailMsgProperties = NULL;
}
hr = S_OK;
}
else
{
cIncMsgsInSystem();
}
}
}
TraceFunctLeaveEx((LPARAM)this);
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::TriggerPostCategorizeEventOneMsg
//
// Synopsis: Triggers ONE server event for one mailmsg
//
// Arguments:
// pIMsg - mailmsg
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 980616 21:26:30: Created.
//
//-------------------------------------------------------------
HRESULT CAQSvrInst::TriggerPostCategorizeEventOneMsg(
IUnknown *pIMsg)
{
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::TriggerPostCategorizeEventOneMsg");
HRESULT hr;
//
// trigger one event
// this is an async event
//
EVENTPARAMS_POSTCATEGORIZE Params;
// Setup pParams
hr = pIMsg->QueryInterface(IID_IMailMsgProperties,
(PVOID *)&(Params.pIMailMsgProperties));
if(FAILED(hr)) {
ErrorTrace((LPARAM)this, "QI failed with error %08lx",
hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
}
Params.pfnCompletion = MailTransport_Completion_PostCategorization;
Params.pCCatMsgQueue = (PVOID) this;
//
// Addref here, release in completion
//
AddRef();
//
// keep a count of messages in the post-cat event
//
InterlockedIncrement((LPLONG) &m_cCurrentMsgsPendingPostCatEvent);
if(m_pISMTPServer) {
hr = TriggerServerEvent(
SMTP_MAILTRANSPORT_POSTCATEGORIZE_EVENT,
&Params);
DebugTrace((LPARAM)this, "TriggerServerEvent returned hr %08lx", hr);
}
if((m_pISMTPServer == NULL) || (FAILED(hr))) {
ErrorTrace((LPARAM)this,
"Unable to dispatch server event; calling completion routine directly");
//
// Call completion routine directly
//
TraceFunctLeaveEx((LPARAM)this);
return PostCategorizationEventCompletion(S_OK, &Params);
}
TraceFunctLeaveEx((LPARAM)this);
return S_OK;
}
//+------------------------------------------------------------
//
// Function: MailTransport_Completion_PostCategorization
//
// Synopsis: SEO will call this routine after all sinks for
// OnPostCategoriztion have been handeled
//
// Arguments:
// pvContext: Context passed into TriggerServerEvent
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 980609 16:13:40: Created.
//
//-------------------------------------------------------------
HRESULT MailTransport_Completion_PostCategorization(
HRESULT hrStatus,
PVOID pvContext)
{
TraceFunctEnter("MailTransport_Completion_PostCategorization");
PEVENTPARAMS_POSTCATEGORIZE pParams = (PEVENTPARAMS_POSTCATEGORIZE) pvContext;
CAQSvrInst *paqinst = (CAQSvrInst *) pParams->pCCatMsgQueue;
TraceFunctLeave();
return paqinst->PostCategorizationEventCompletion(
hrStatus,
pParams);
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::PostCategorizationEventCompletion
//
// Synopsis: Called on the completion side of OnPostCategorization
//
// Arguments:
// hrStatus: status of server event
// pParams: context structure passed into TriggerServerEvent
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 980616 21:33:05: Created.
//
//-------------------------------------------------------------
HRESULT CAQSvrInst::PostCategorizationEventCompletion(
HRESULT hrStatus,
PEVENTPARAMS_POSTCATEGORIZE pParams)
{
TraceFunctEnterEx((LPARAM)this,
"CAQSvrInst::PostCategorizationEventCompletion");
DebugTrace((LPARAM)this, "hrStatus is %08lx", hrStatus);
HRESULT hr;
//
// Decrease count of msgs in post-cat event
//
InterlockedDecrement((LPLONG) &m_cCurrentMsgsPendingPostCatEvent);
hr = SetNextMsgStatus(MP_STATUS_CATEGORIZED, pParams->pIMailMsgProperties);
//See if this message has been "handled"
if (S_FALSE == hr)
{
//Message has been "handled"... do not try to route it
hr = S_OK;
}
else
{
//Increment counters and put msg into the pre-routing queue
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingRouting);
hr = m_asyncqPreRoutingQueue.HrQueueRequest(pParams->pIMailMsgProperties,
FALSE, cCountMsgsForHandleThrottling(pParams->pIMailMsgProperties));
if (FAILED(hr))
{
HandleAQFailure(AQ_FAILURE_PREROUTING_FAILED, hr, pParams->pIMailMsgProperties);
ErrorTrace((LPARAM)this, "fRouteAndQueueMsg failed with hr %08lx", hr);
DecMsgsInSystem(FALSE, FALSE);
//don't passback shutdown errors in completions routines
if (AQUEUE_E_SHUTDOWN == hr)
hr = S_OK;
}
}
//
// Release mailmsg reference added in TriggerPostCategorizerEventOneMsg
//
pParams->pIMailMsgProperties->Release();
//
// Release reference to this object added in
// TriggerPostCategorizerEventOneMsg
//
Release();
TraceFunctLeaveEx((LPARAM)this);
return S_OK; //we should always handle failures internally here
}
//---[ CatCompletion ]---------------------------------------------------------
//
//
// Description:
// Message Categoriztion Completion function
// Parameters:
// hrCatResult HRESULT of categorization attempt
// pContext Context as passed into MsgCat
// pIMsg Single categorized IMsg (if not bifurcated)
// rgpIMsg NULL terminated array of IMsg's (bifurcated)
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::CatCompletion(HRESULT hrCatResult, PVOID pContext,
IUnknown *pIMsg,
IUnknown **rgpIMsg)
{
TraceFunctEnterEx((LPARAM) pIMsg, "CatCompletion");
HRESULT hr = S_OK;
CAQSvrInst *paqinst = (CAQSvrInst *) pContext;
IMailMsgProperties *pIMailMsg = NULL;
IMailMsgQueueMgmt *pIMailMsgQM = NULL;
_ASSERT(paqinst);
_ASSERT(CATMSGQ_SIG == paqinst->m_dwSignature);
//Increment count of times CatCompletion called
InterlockedIncrement((PLONG) &(paqinst->m_cCatCompletionCalled));
paqinst->m_asyncqPreCatQueue.DecPendingAsyncCompletions();
//make sure Cat is returning an HRESULT
_ASSERT(!hrCatResult || (hrCatResult & 0xFFFF0000));
if (SUCCEEDED(hrCatResult))
{
//
// Kick off post categorize event
//
InterlockedDecrement((PLONG) &(paqinst->m_cCurrentMsgsPendingCat));
paqinst->TriggerPostCategorizeEvent(pIMsg, rgpIMsg);
}
else if (FAILED(hrCatResult) &&
(CAT_E_RETRY == hrCatResult))
{
//MsgCat has some re-tryable error...
//stick it back in the queue and retry later
DebugTrace((LPARAM) paqinst, "INFO: MsgCat had tmp failure - hr 0x%08X", hr);
//
// Adjust counters... they we be adjusted correctly per msg in
// HandleCatRetryOneMessage
//
InterlockedDecrement((PLONG) &(paqinst->m_cCurrentMsgsPendingCat));
paqinst->DecMsgsInSystem(FALSE, FALSE);
if(pIMsg)
{
paqinst->HandleCatRetryOneMessage(pIMsg);
}
else
{
_ASSERT(rgpIMsg);
IUnknown **ppIMsgCurrent = rgpIMsg;
while(*ppIMsgCurrent)
{
paqinst->HandleCatRetryOneMessage(*ppIMsgCurrent);
ppIMsgCurrent++;
}
}
}
else
{
_ASSERT(pIMsg && rgpIMsg == NULL && "Message bifurcated inspite of non-retryable cat error");
paqinst->HandleCatFailure(pIMsg, hrCatResult);
} // Non retryable error
TraceFunctLeaveEx((LPARAM)paqinst);
return S_OK; //all errors should be handled internally
}
//---[ CAQSvrInst::HandleCatRetryOneMessage ]----------------------------------
//
//
// Description:
// Handles cat retry for a single message
// Parameters:
// pIUnknown IUnknown for the message to retry
// Returns:
// -
// History:
// 4/13/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::HandleCatRetryOneMessage(IUnknown *pIUnknown)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleCatRetryOneMessage");
IMailMsgProperties *pIMailMsgProperties = NULL;
HRESULT hr = S_OK;
hr = pIUnknown->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgProperties FAILED");
if (FAILED(hr))
goto Exit;
//
// Check and see if the message is still valid
//
if (!fShouldRetryMessage(pIMailMsgProperties))
goto Exit;
//
// Queue it to the pre-cat queue
//
hr = m_asyncqPreCatQueue.HrQueueRequest(pIMailMsgProperties,
TRUE, cCountMsgsForHandleThrottling(pIMailMsgProperties));
if (FAILED(hr))
{
HandleAQFailure(AQ_FAILURE_PRECAT_RETRY, hr, pIMailMsgProperties);
goto Exit;
}
//
// Adjust counters as appropriate
//
InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingCat);
cIncMsgsInSystem();
//
// Kick off cat retry if needed
//
ScheduleInternalRetry(LI_TYPE_PENDING_CAT);
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
TraceFunctLeave();
}
//---[ CAQSvrInst::HandleCatFailure ]------------------------------------------
//
//
// Description:
// Handles the details of post cat DSN generation. Will put the
// message in the failed queue if DSN generation fails
// Parameters:
// pIUnknown IUnkown for mailmsg
// hrCatResult Error code returned by cat
// Returns:
// -
// History:
// 11/11/1999 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::HandleCatFailure(IUnknown *pIUnknown, HRESULT hrCatResult)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleCatFailure");
HRESULT hr = S_OK;
IMailMsgProperties *pIMailMsgProperties = NULL;
BOOL fHasShutdownLock = FALSE;
ErrorTrace((LPARAM) this,
"ERROR: MsgCat failed, will try to NDR message - hr 0x%08X",
hrCatResult);
InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingCat);
DecMsgsInSystem(FALSE, FALSE);
const char *rgszStrings[1] = { NULL };
if(!pIUnknown)
goto Exit;
//If we are shutting down, this error could be caused by a shutdown being
//signaled. If this is the case, we do not want to log an error or
//generate an NDR.
if (!fTryShutdownLock())
goto Exit;
fHasShutdownLock = TRUE;
HrTriggerLogEvent(
AQUEUE_CAT_FAILED, // Message ID
TRAN_CAT_QUEUE_ENGINE, // Category
1, // Word count of substring
rgszStrings, // Substring
EVENTLOG_WARNING_TYPE, // Type of the message
hrCatResult, // error code
LOGEVENT_LEVEL_MEDIUM, // Logging level
"phatq", // key to this event
LOGEVENT_FLAG_PERIODIC, // Logging option
0, // index of format message string in rgszStrings
GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
);
hr = pIUnknown->QueryInterface(IID_IMailMsgProperties,
(void **) &pIMailMsgProperties);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgProperties FAILED");
if (FAILED(hr))
goto Exit;
// we ignore errors on this since it is only to help debug
// cat failures
pIMailMsgProperties->PutDWORD(IMMPID_MP_HR_CAT_STATUS,
hrCatResult);
hr = HandleFailedMessage(pIMailMsgProperties,
TRUE,
NULL,
MESSAGE_FAILURE_CAT,
hrCatResult);
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
if (fHasShutdownLock)
ShutdownUnlock();
TraceFunctLeave();
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::ResetRoutes
//
// Synopsis: This is a sink callback function; sinks will call this
// function when they wish to reset next hop routes or message types.
//
// Arguments:
// dwResetType: Must be either RESET_NEXT_HOPS or RESET_MESSAGE_TYPES
//
// Returns:
// S_OK: Success
// E_INVALIDARG: bogus dwResetType
//
// History:
// jstamerj 1998/07/10 19:27:45: Created.
// 3/9/99 - MikeSwa Added async reset
//
//-------------------------------------------------------------
STDMETHODIMP CAQSvrInst::ResetRoutes(
IN DWORD dwResetType)
{
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::ResetRoutes");
HRESULT hr = S_OK;
InterlockedIncrement((PLONG) &m_cTotalResetRoutes);
if(dwResetType == RESET_NEXT_HOPS) {
DebugTrace((LPARAM)this, "ResetNextHops called");
if (1 == InterlockedIncrement((PLONG) &m_cCurrentPendingResetRoutes))
{
DebugTrace((LPARAM) this, "Adding ResetRoutes operation to work queue");
AddRef(); //released in completion function
hr = HrQueueWorkItem(this, CAQSvrInst::fResetRoutesNextHopCompletion);
//Failure will still call completion function, so we should not release
}
else
{
DebugTrace((LPARAM) this, "Other ResetRoutes pending... only one pending allowed");
InterlockedDecrement((PLONG) &m_cCurrentPendingResetRoutes);
}
} else if(dwResetType == RESET_MESSAGE_TYPES) {
DebugTrace((LPARAM)this, "ResetMessageTypes called");
//$$TODO: Reset message types
} else {
ErrorTrace((LPARAM)this, "ResetRoutes called with bogus dwResetType %08lx",
dwResetType);
hr = E_INVALIDARG;
}
return hr;
}
//---[ CAQSvrInst::LogResetRouteEvent ]----------------------------------------
//
//
// Description:
// Log statistics on resetroute
// Parameters:
// dwObtainLock time spend on obtaining exclusive lock
// dwWaitLock time spend on waiting for the lock
// dwQueue queue length at the moment
// History:
// 11/10/2000 haozhang created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::LogResetRouteEvent( DWORD dwObtainLock,
DWORD dwWaitLock,
DWORD dwQueue)
{
LPSTR lpstr[3];
char subStrings[3][13];
sprintf (subStrings[0],"%d",dwObtainLock);
sprintf (subStrings[1],"%d",dwWaitLock);
sprintf (subStrings[2],"%d",dwQueue);
lpstr[0] = subStrings[0];
lpstr[1] = subStrings[1];
lpstr[2] = subStrings[2];
HrTriggerLogEvent(
AQUEUE_RESETROUTE_DIAGNOSTIC, // Message ID
TRAN_CAT_QUEUE_ENGINE, // Category ID
3, // Word count of substring
(LPCSTR *) lpstr, // Substring
EVENTLOG_INFORMATION_TYPE, // Type of the message
0, // No error code
LOGEVENT_LEVEL_MEDIUM, // Debug level
NULL, // Key to identify this event
LOGEVENT_FLAG_ALWAYS
);
}
//---[ CAQSvrInst::fResetRoutesNextHopCompletion ]-----------------------------
//
//
// Description:
// Completion function that handles async reset routes
// Parameters:
// pvThis Ptr to CAQSvrInst
// dwStatus Status returned by
// Returns:
// TRUE always
// History:
// 3/9/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CAQSvrInst::fResetRoutesNextHopCompletion(PVOID pvThis, DWORD dwStatus)
{
TraceFunctEnterEx((LPARAM) pvThis, "CAQSvrInst::fResetRoutesNextHopCompletion");
CAQSvrInst *paqinst = (CAQSvrInst *) pvThis;
DWORD cCurrentPendingResetRoutes = 0;
DWORD dwPreLock;
DWORD dwObtainLock;
DWORD dwReleaseLock;
DWORD dwQueue;
HRESULT hr = S_OK;
_ASSERT(paqinst);
if (ASYNC_WORK_QUEUE_NORMAL == dwStatus)
{
if (paqinst && paqinst->fTryShutdownLock())
{
DebugTrace((LPARAM) paqinst, "Rerouting domains");
dwPreLock = GetTickCount();
paqinst->m_slPrivateData.ExclusiveLock();
dwObtainLock = GetTickCount();
dwQueue = paqinst->m_cCurrentRemoteDestQueues;
//Drop pending reset routes count here. We should do it after
//we grab the lock to prevent too many threads from
//trying to grab it exclusively. We also need to do it
//before we actual update any routing info in case a ResetRoutes
//is requested midway through this update.
cCurrentPendingResetRoutes = InterlockedDecrement((PLONG)
&(paqinst->m_cCurrentPendingResetRoutes));
//Make sure the count hasn't gone negative
_ASSERT(cCurrentPendingResetRoutes < 0xFFFFFF00);
//With the lock held, call HrBeginRerouteDomains. This function
//will flag a reroute in progress and will move all domains to
//the currently unreachable queue.
hr = paqinst->m_dmt.HrBeginRerouteDomains();
paqinst->m_slPrivateData.ExclusiveUnlock();
dwReleaseLock = GetTickCount();
// If the first part failed, we don't do the second part
if(SUCCEEDED(hr))
{
//Now, having released the lock, call HrCompleteRerouteDomains.
//This function will reroute the contents of the currently
//unreachable queue and will then unflag the reroute in progress.
paqinst->m_dmt.HrCompleteRerouteDomains();
}
//If things have been re-routing to a special link... we should
//process them as well.
paqinst->m_dmt.ProcessSpecialLinks(paqinst->m_dwDelayExpireMinutes,
FALSE);
paqinst->ShutdownUnlock();
//
// Log Event of ResetRoute
//
paqinst->LogResetRouteEvent(
dwObtainLock - dwPreLock, // time to obtain the exclusive lock
dwReleaseLock - dwObtainLock, // time waiting on the lock
dwQueue // number of queues
);
}
}
else
{
if (paqinst)
{
cCurrentPendingResetRoutes = InterlockedDecrement((PLONG)
&(paqinst->m_cCurrentPendingResetRoutes));
//Make sure the count hasn't gone negative
_ASSERT(cCurrentPendingResetRoutes < 0xFFFFFF00);
}
if (ASYNC_WORK_QUEUE_FAILURE & dwStatus)
ErrorTrace((LPARAM) paqinst, "ResetRoutes completion failure");
}
if (paqinst)
paqinst->Release();
TraceFunctLeave();
return TRUE;
}
//---[ CAQSvrInst::GetDomainInfoFlags ]--------------------------------------
//
//
// Description:
// Determines if a domain is local (has DOMAIN_INFO_LOCAL_MAILBOX set)
// Parameters:
// IN szDomainName Name of domain to check for
// OUT pdwDomainInfoFlags DomainInfo flags for this domain
// Returns:
// S_OK on success
// E_INVALIDARG if szDomainName or pdwDomainInfoFlags is NULL
// History:
// 7/29/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::GetDomainInfoFlags(
IN LPSTR szDomainName,
OUT DWORD *pdwDomainInfoFlags)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::GetDomainInfoFlags");
BOOL fLocked = FALSE;
HRESULT hr = S_OK;
DWORD cbDomainName = 0;
CInternalDomainInfo *pIntDomainInfo = NULL;
ISMTPServerGetAuxDomainInfoFlags *pISMTPServerGetAuxDomainInfoFlags = NULL;
DWORD dwSinkDomainFlags = 0;
_ASSERT(pdwDomainInfoFlags && "Invalid Param");
_ASSERT(szDomainName && "Invalid Param");
if (!pdwDomainInfoFlags || !szDomainName)
{
hr = E_INVALIDARG;
goto Exit;
}
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
cbDomainName = lstrlen(szDomainName);
hr = m_dct.HrGetInternalDomainInfo(cbDomainName, szDomainName,
&pIntDomainInfo);
if (FAILED(hr))
goto Exit;
_ASSERT(pIntDomainInfo);
// Aux Domain info not found, use the config we got from our own tables
*pdwDomainInfoFlags = pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags;
// We should have gotten back domain config even if it is only
// the default config - now we need to see if we can get more
// specific data from an event sink
if (!cbDomainName || pIntDomainInfo->m_DomainInfo.szDomainName[0] == '*')
{
// QI for ISMTPServerGetAuxDomainInfoFlags interface
hr = m_pISMTPServer->QueryInterface(
IID_ISMTPServerGetAuxDomainInfoFlags,
(LPVOID *)&pISMTPServerGetAuxDomainInfoFlags);
if (FAILED(hr)) {
ErrorTrace((LPARAM) this,
"Unable to QI for ISMTPServerGetAuxDomainInfoFlags 0x%08X",hr);
// Drop this error, this isn't fatal
hr = S_OK;
goto Exit;
}
// Check for domain info
hr = pISMTPServerGetAuxDomainInfoFlags->HrTriggerGetAuxDomainInfoFlagsEvent(
szDomainName,
&dwSinkDomainFlags);
if (FAILED(hr)) {
ErrorTrace((LPARAM) this,
"Failed calling HrTriggerGetAuxDomainInfoFlags 0x%08X",hr);
// Drop this error, this isn't fatal
hr = S_OK;
goto Exit;
}
if (dwSinkDomainFlags & DOMAIN_INFO_INVALID) {
// Domain info not found from event sink
hr = S_OK;
goto Exit;
}
// Ok, we got Aux Domain info, use it
*pdwDomainInfoFlags = dwSinkDomainFlags;
}
Exit:
if (pISMTPServerGetAuxDomainInfoFlags)
pISMTPServerGetAuxDomainInfoFlags->Release();
if (pIntDomainInfo)
pIntDomainInfo->Release();
if (fLocked)
ShutdownUnlock();
TraceFunctLeave();
return hr;
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::GetMessageRouter
//
// Synopsis: Default functionality of GetMessageRouter
// If there is no current IMessageRouter, provide the
// default IMessageRouter
//
// Arguments:
// pIMailMsgProperties: MailMsg that needs a router
// pICurrentRouter: current sink provided router
// ppIMessageRouter: out param for new IMessageRouter
//
// Returns:
// S_OK: Success, provided IMessageRouter
// E_NOTIMPL: Didn't provide an IMessageRouter
//
// History:
// jstamerj 1998/07/10 19:33:41: Created.
//
//-------------------------------------------------------------
STDMETHODIMP CAQSvrInst::GetMessageRouter(
IN IMailMsgProperties *pIMailMsgProperties,
IN IMessageRouter *pICurrentMessageRouter,
OUT IMessageRouter **ppIMessageRouter)
{
_ASSERT(ppIMessageRouter);
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::GetMessageRouter");
if((pICurrentMessageRouter == NULL) &&
(m_pIMessageRouterDefault)) {
//
// Return our default IMessageRouter and AddRef for the caller
//
*ppIMessageRouter = m_pIMessageRouterDefault;
m_pIMessageRouterDefault->AddRef();
DebugTrace((LPARAM)this, "Supplying default IMessageRouter");
TraceFunctLeaveEx((LPARAM)this);
return S_OK;
} else {
TraceFunctLeaveEx((LPARAM)this);
return E_NOTIMPL;
}
}
//---[ CAQSvrInst::HrTriggerDSNGenerationEvent ]-----------------------------
//
//
// Description:
// Triggers DSN Generation event
// Parameters:
// pdsnparams A CDSNParams that will be used to trigger event
// fHasRoutingLock TRUE if routing lock is current held by this thread
// Returns:
// S_OK on success (and DSN was generated)
// S_FALSE on success, but no DSN generated
// AQUEUE_E_NOT_INITIALIZED if not initialized correctly
// History:
// 7/11/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrTriggerDSNGenerationEvent(CDSNParams *pdsnparams,
BOOL fHasRoutingLock)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrTriggerDSNGenerationEvent");
HRESULT hr = S_OK;
DWORD cCurrent = 0;
CRefCountedString *prstrDefaultDomain = NULL;
LPSTR szDefaultDomain = NULL;
CRefCountedString *prstrCopyNDRTo = NULL;
LPSTR szCopyNDRTo = NULL;
CRefCountedString *prstrFQDN = NULL;
LPSTR szFQDN = NULL;
DWORD cCurrentDSNsGenerated = 0;
DWORD cbCurrentSize = 0;
FILETIME *pftExpireTime = NULL;
FILETIME ftExpireTime;
if (!(m_dwInitMask & CMQ_INIT_DSN) || !m_pISMTPServer)
{
hr = AQUEUE_E_NOT_INITIALIZED;
goto Exit;
}
//Get config string from ref-counted objects
if (!fHasRoutingLock)
m_slPrivateData.ShareLock();
else
m_slPrivateData.AssertIsLocked();
if (m_prstrDefaultDomain)
{
prstrDefaultDomain = m_prstrDefaultDomain;
prstrDefaultDomain->AddRef();
szDefaultDomain = prstrDefaultDomain->szStr();
}
else
{
//we need to have something as our default domain
szDefaultDomain = "localhost";
}
if (m_prstrCopyNDRTo)
{
prstrCopyNDRTo = m_prstrCopyNDRTo;
prstrCopyNDRTo->AddRef();
szCopyNDRTo = prstrCopyNDRTo->szStr();
}
if (m_prstrServerFQDN)
{
prstrFQDN = m_prstrServerFQDN;
prstrFQDN->AddRef();
szFQDN = prstrFQDN->szStr();
}
if (!fHasRoutingLock)
m_slPrivateData.ShareUnlock();
//
// Get the expire time
//
hr = pdsnparams->pIMailMsgProperties->GetProperty(
IMMPID_MP_EXPIRE_NDR,
sizeof(FILETIME),
&cbCurrentSize,
(BYTE *) &ftExpireTime);
if (SUCCEEDED(hr))
{
_ASSERT(sizeof(FILETIME) == cbCurrentSize);
pftExpireTime = &ftExpireTime;
}
else if (MAILMSG_E_PROPNOTFOUND == hr)
{
//
// Calculate the expire time based on the arrival time
//
hr = pdsnparams->pIMailMsgProperties->GetProperty(
IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME),
&cbCurrentSize,
(BYTE *) &ftExpireTime);
if(SUCCEEDED(hr))
{
CalcExpireTimeNDR(ftExpireTime, FALSE, &ftExpireTime);
pftExpireTime = &ftExpireTime;
}
else if(hr == MAILMSG_E_PROPNOTFOUND)
hr = S_OK;
else
goto Exit;
}
else
goto Exit;
//
// Set the CDSNParam's paqinst pointer so that
// CDSNParams::HrSubmitDSN call call back into this CAQSvrInst
// object.
//
pdsnparams->paqinst = this;
hr = m_dsnsink.GenerateDSN(
this,
m_dwServerInstance,
m_pISMTPServer,
pdsnparams->pIMailMsgProperties,
pdsnparams->dwStartDomain,
pdsnparams->dwDSNActions,
pdsnparams->dwRFC821Status,
pdsnparams->hrStatus,
szDefaultDomain,
szFQDN,
(CHAR *) DEFAULT_MTA_TYPE,
pdsnparams->szDebugContext,
m_dwDSNLanguageID,
m_dwDSNOptions,
szCopyNDRTo,
pftExpireTime,
pdsnparams,
g_dwMaxDSNSize);
if (SUCCEEDED(hr))
{
if(pdsnparams->dwDSNTypesGenerated)
{
// A DSN was generated
hr = S_OK;
}
else
{
// No DSN was generated
hr = S_FALSE;
}
}
else if (AQUEUE_E_NDR_OF_DSN == hr)
{
hr = S_FALSE; //report as no DSN generated
//original message is badmail
HandleBadMail(pdsnparams->pIMailMsgProperties, TRUE, NULL,
AQUEUE_E_NDR_OF_DSN, fHasRoutingLock);
InterlockedIncrement((PLONG) &m_cBadmailNdrOfDsn);
}
else
{
//bail out on failure
InterlockedIncrement((PLONG) &m_cTotalDSNFailures);
//
// Check to see if the message has been deleted... store driver
// has been gone away.
//
if (!fShouldRetryMessage(pdsnparams->pIMailMsgProperties, FALSE))
{
DebugTrace((LPARAM) this, "Msg no longer valid... abandoning");
hr = S_FALSE;
}
goto Exit;
}
Exit:
if (prstrDefaultDomain)
prstrDefaultDomain->Release();
if (prstrCopyNDRTo)
prstrCopyNDRTo->Release();
if (prstrFQDN)
prstrFQDN->Release();
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::HrNDRUnresolvedRecipients ]-------------------------------
//
//
// Description:
// NDR any unresolved recipients for a given IMailMsgProperties. Also
// generates a expanded DSNs
// Parameters:
// IN pIMailMsgProperties IMailMsgProperties to generate NDR for
// IN pIMailMsgRecipients Recipients interface for message
// Returns:
// S_OK on success and message should continue through transport
// S_FALSE on success, but message should not be queued for delivery
// History:
// 7/21/98 - MikeSwa Created
// 10/14/98 - MikeSwa Modified to use common utility functions
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrNDRUnresolvedRecipients(
IMailMsgProperties *pIMailMsgProperties,
IMailMsgRecipients *pIMailMsgRecipients)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrNDRUnresolvedRecipients");
HRESULT hr = S_OK;
HRESULT hrCat = S_OK; //cat HRESULT
DWORD cbProp = 0;
DWORD iCurrentDomain = 0;
DWORD cRecips = 0;
_ASSERT(pIMailMsgProperties);
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_HR_CAT_STATUS, sizeof(HRESULT),
&cbProp, (BYTE *) &hrCat);
if (FAILED(hr))
{
if (MAILMSG_E_PROPNOTFOUND == hr) //no result... don't generate DSN
hr = S_OK; //not really an error
goto Exit;
}
if (CAT_W_SOME_UNDELIVERABLE_MSGS == hrCat)
{
//There was an error resolving recipients
//We need to NDR all recipients with hard errors (like RP_UNRESOLVED)
//and expand any recipient marked expanded.
CDSNParams dsnparams;
dsnparams.dwStartDomain = 0;
dsnparams.dwDSNActions = DSN_ACTION_FAILURE | DSN_ACTION_EXPANDED;
dsnparams.pIMailMsgProperties = pIMailMsgProperties;
dsnparams.hrStatus = CAT_W_SOME_UNDELIVERABLE_MSGS;
hr = HrLinkAllDomains(pIMailMsgProperties);
if (FAILED(hr))
goto Exit;
//Fire DSN Generation event
SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
hr = HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
if (FAILED(hr))
goto Exit;
//Check to see how many recipients have been NDRd
hr = pIMailMsgRecipients->Count(&cRecips);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: IMailMsgRecipients::Count() FAILED - hr 0x%08X", hr);
goto Exit;
}
//If all recipients have been handled... return S_FALSE
if (dsnparams.cRecips == cRecips)
{
hr = S_FALSE;
}
else
{
hr = S_OK;
}
}
else
{
hr = S_OK;
}
Exit:
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::fPreLocalDeliveryQueueCompletion ]------------------------
//
//
// Description:
// Completion function for PerLocal delivery queue
// Parameters:
// pmsgref - Msgref to attempt delivery for
// Returns:
// TRUE If Delivery attempt was handled (delivered or NDR'd)
// FALSE If MsgRef needs to be requeued
// History:
// 7/17/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CAQSvrInst::fPreLocalDeliveryQueueCompletion(CMsgRef *pmsgref)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::fPreLocalDeliveryQueueCompletion");
HRESULT hr = S_OK;
BOOL fMsgHandled = TRUE;
BOOL fLocked = FALSE; //TRUE if locked for shutdown
DWORD cRecips = 0;
DWORD *rgdwRecips= 0;
CAQStats aqstat;
CLinkMsgQueue *plmq = NULL;
BOOL fReleaseLDNotify = FALSE;
CAQLocalDeliveryNotify *pLDNotify = NULL;
BOOL fUpdateCounters = TRUE;
if (NULL == m_pISMTPServer) {
ErrorTrace((LPARAM) this,
"ERROR: Local Delivery not configured properly");
goto Exit;
}
if (NULL == pmsgref) {
ErrorTrace((LPARAM) this,
"ERROR: Local Delivery not configured properly, msgref=NULL");
goto Exit;
}
if (!fTryShutdownLock()) {
goto Exit;
}
pLDNotify = new CAQLocalDeliveryNotify(this, pmsgref);
if (!pLDNotify) {
ErrorTrace((LPARAM) this,
"ERROR: new CAQLocalDeliveryNotify failed");
fMsgHandled = FALSE;
goto Exit;
}
fReleaseLDNotify = TRUE;
fLocked = TRUE;
if (pmsgref->fIsMsgFrozen())
{
//Message is frozen... requeue message
fMsgHandled = FALSE;
goto Exit;
}
hr = m_dmt.HrPrepareForLocalDelivery(pmsgref,
FALSE,
pLDNotify->pdcntxtGetDeliveryContext(),
&cRecips,
&rgdwRecips);
if (FAILED(hr))
{
if ((AQUEUE_E_MESSAGE_HANDLED != hr) && (AQUEUE_E_MESSAGE_PENDING != hr))
{
//message will be retried when last reference is released.
pmsgref->RetryOnDelete();
ErrorTrace((LPARAM) this, "ERROR: HrPrepareLocalDelivery FAILED - hr 0x%08X", hr);
}
fMsgHandled = TRUE;
hr = S_OK;
goto Exit;
}
//Increase Ref count for message ref (as if it was actually queued)
pmsgref->AddRef();
//Send off for local delivery
InterlockedIncrement((PLONG) &m_cCurrentMsgsInLocalDelivery);
MSG_TRACK_INFO msgTrackInfo;
ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
msgTrackInfo.dwEventId = MTE_LOCAL_DELIVERY;
msgTrackInfo.cRcpts = cRecips;
m_pISMTPServer->WriteLog(&msgTrackInfo,
pLDNotify->pimsgGetIMsg(),
NULL,
NULL);
// from here on we will end up calling LDCompletion, and they will
// release it
fReleaseLDNotify = FALSE;
m_asyncqPreLocalDeliveryQueue.IncPendingAsyncCompletions();
// we need to hold a reference on pLDNotify to be able to call
// into fNotCalledCompletion
pLDNotify->AddRef();
fUpdateCounters = FALSE;
if (m_pISMTPServerAsync) {
hr = m_pISMTPServerAsync->TriggerLocalDeliveryAsync(
pLDNotify->pimsgGetIMsg(),
cRecips,
rgdwRecips,
pLDNotify);
} else {
hr = m_pISMTPServer->TriggerLocalDelivery(
pLDNotify->pimsgGetIMsg(),
cRecips,
rgdwRecips);
}
// if we get back any error code besides MAILMSG_S_PENDING then
// the dispatcher completed sync. We need to see if the
// completion function was called, and if not then we need to call
// it ourselves.
if (hr != MAILMSG_S_PENDING && pLDNotify->fNotCalledCompletion()) {
LDCompletion(hr, this, pmsgref, pLDNotify);
}
// after this point we can't call pLDNotify
pLDNotify->Release();
Exit:
if (fUpdateCounters && fMsgHandled) {
UpdateLDCounters(pmsgref);
}
if (fReleaseLDNotify) {
pLDNotify->Release();
}
if (fLocked)
ShutdownUnlock();
TraceFunctLeave();
return fMsgHandled;
}
//---[ CAQSvrInst::LDCompletion ]----------------------------------------------
//
//
// Description:
// Completion function local delivery
// Parameters:
// hrLDResult - local delivery status
// pContext - this
// pmsgref - msgref that we were delivering.
// History:
// 10/30/2000 - AWetmore Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::LDCompletion(HRESULT hr,
PVOID pContext,
CMsgRef *pmsgref,
CAQLocalDeliveryNotify *pLDNotify)
{
TraceFunctEnter("CAQSvrInst::LDCompletion");
BOOL fMsgHandled = TRUE;
MessageAck *pMsgAck = pLDNotify->pmsgackGetMsgAck();
CAQSvrInst *pThis = (CAQSvrInst *) pContext;
InterlockedDecrement((PLONG) &(pThis->m_cCurrentMsgsInLocalDelivery));
pThis->m_asyncqPreLocalDeliveryQueue.DecPendingAsyncCompletions();
if (FAILED(hr)) {
//We will need to handle in one of 2 ways:
// - set fMsgHandled to FALSE (on STOREDRV_E_RETRY)
// - NDR the message (on other errors)
if (STOREDRV_E_RETRY == hr) {
//try... try again
DebugTrace((LPARAM) pmsgref, "INFO: Msg queued for local retry");
fMsgHandled = FALSE;
pMsgAck->dwMsgStatus = MESSAGE_STATUS_RETRY;
pThis->ScheduleInternalRetry(LI_TYPE_LOCAL_DELIVERY);
} else {
ErrorTrace((LPARAM) pmsgref, "ERROR: Local delivery failed. - hr 0x%08X", hr);
pMsgAck->dwMsgStatus = MESSAGE_STATUS_NDR_ALL;
}
} else {
InterlockedIncrement(&(pThis->m_cMsgsDeliveredLocal));
}
pMsgAck->pIMailMsgProperties = pLDNotify->pimsgGetIMsg();
pMsgAck->pvMsgContext = (DWORD *) pLDNotify->pdcntxtGetDeliveryContext();
pMsgAck->dwMsgStatus |= MESSAGE_STATUS_LOCAL_DELIVERY;
//
// Make sure we should retry the message. We want to do this before we
// ACK the message so that we do not reopen the P1 stream if we *are*
// retrying it.
//
if (!fMsgHandled)
fMsgHandled = !pmsgref->fShouldRetry();
hr = pThis->HrAckMsg(pMsgAck, TRUE);
if (FAILED(hr)) {
ErrorTrace((LPARAM) pThis, "ERROR: Local MsgAck failed - hr 0x%08X", hr);
goto Exit;
}
Exit:
if (fMsgHandled) //we aren't retrying message
{
pThis->UpdateLDCounters(pmsgref);
} else {
// retry this message
pThis->HandleLocalRetry(pLDNotify->pmsgrefGetMsgRef());
}
// clean up after ourselves
pLDNotify->Release();
SleepForPerfAnalysis(g_dwLocalQueueSleepMilliseconds);
TraceFunctLeave();
}
//---[ CAQSvrInst::UpdateLDCounters ]------------------------------------------
//
//
// Description:
// Update the local delivery queue counters
// Parameters:
// pmsgref - msgref that we were delivering.
// History:
// 05/11/2001 - AWetmore Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::UpdateLDCounters(CMsgRef *pmsgref) {
TraceFunctEnter("CAQSvrInst::UpdateLDCounters");
CAQStats aqstat;
CLinkMsgQueue *plmq = NULL;
HRESULT hr;
InterlockedDecrement((PLONG) &(m_cCurrentMsgsPendingLocal));
//
// Update stats for the local link
//
pmsgref->GetStatsForMsg(&aqstat);
plmq = m_dmt.plmqGetLocalLink();
if (plmq)
{
hr = plmq->HrNotify(&aqstat, FALSE);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"HrNotify failed... local stats innaccurate 0x%08X", hr);
hr = S_OK;
}
plmq->Release();
plmq = NULL;
}
TraceFunctLeave();
}
//---[ CAQSvrInst::HandleLocalRetry ]------------------------------------------
//
//
// Description:
// Handles LD retry for a single message
// Parameters:
// pIUnknown IUnknown for the message to retry
// Returns:
// -
// History:
// 10/30/2000 - AWetmore Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::HandleLocalRetry(CMsgRef *pmsgref)
{
TraceFunctEnter("CAQSvrInst::HandleLocalRetry");
IMailMsgProperties *pIMailMsgProperties = pmsgref->pimsgGetIMsg();
HRESULT hr = S_OK;
if (pIMailMsgProperties == NULL) {
_ASSERT(pIMailMsgProperties && "pimsgGetIMsg() failed!!");
hr = E_POINTER;
goto Exit;
}
//
// Check and see if the message is still valid
//
if (!pmsgref->fShouldRetry())
goto Exit;
//
// Queue it to the local delivery queue
//
hr = m_asyncqPreLocalDeliveryQueue.HrQueueRequest(pmsgref, TRUE);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Enqueue to local delivery queue failed, 0x%08X", hr);
pmsgref->RetryOnDelete();
goto Exit;
}
Exit:
if (pIMailMsgProperties)
pIMailMsgProperties->Release();
TraceFunctLeave();
}
//---[ CAQSvrInst::HrSetSubmissionTimeIfNecessary ]----------------------------
//
//
// Description:
// Sets the submission time on the message if it is not already set.
//
// Parameters:
// IN pIMailMsgProperties message to stamp
// Returns:
// S_OK on success
// History:
// 8/13/98 - MikeSwa Created
// 10/9/98 - MikeSwa - Changed behavior to that any pre-existing
// properties will be maintained.
// 5/16/2001 - dbraun changed to only set submission time
// (was SetMessageExpiry)
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::HrSetSubmissionTimeIfNecessary(IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrSetSubmissionTimeIfNecessary");
HRESULT hr = S_OK;
DWORD dwTimeContext = 0;
DWORD cbProp = 0;
FILETIME ftSubmitTime;
_ASSERT(pIMailMsgProperties);
//Set arrival time
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME), &cbProp, (BYTE *) &ftSubmitTime);
if (MAILMSG_E_PROPNOTFOUND == hr)
{
//Prop not set... we can set it
m_qtTime.GetExpireTime(0, &ftSubmitTime, &dwTimeContext);
hr = pIMailMsgProperties->PutProperty(IMMPID_MP_ARRIVAL_FILETIME,
sizeof(FILETIME), (BYTE *) &ftSubmitTime);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "ERROR: Unable to write arrival time to msg");
goto Exit;
}
}
else if (FAILED(hr))
{
goto Exit;
}
Exit:
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::CalcExpireTimeNDR ]-----------------------------------------
//
//
// Description:
// Calculates the message's NDR expire time
//
// Parameters:
// IN ftSubmission time the message was submitted
// IN fLocal Bool TRUE if we want local time,
// otherwise returns remote
// OUT pftExpire Used to return expire time
// Returns:
// -
// History:
// 5/16/2001 - dbraun created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::CalcExpireTimeNDR(FILETIME ftSubmission, BOOL fLocal, FILETIME *pftExpire)
{
if (fLocal)
m_qtTime.GetExpireTime(ftSubmission, m_dwLocalNDRExpireMinutes, pftExpire);
else
m_qtTime.GetExpireTime(ftSubmission, m_dwNDRExpireMinutes, pftExpire);
}
//---[ CAQSvrInst::CalcExpireTimeDelay ]---------------------------------------
//
//
// Description:
// Calculates the message's delay expire time
//
// Parameters:
// IN ftSubmission time the message was submitted
// IN fLocal Bool TRUE if we want local time,
// otherwise returns remote
// OUT pftExpire Used to return expire time
// Returns:
// -
// History:
// 5/16/2001 - dbraun created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::CalcExpireTimeDelay(FILETIME ftSubmission, BOOL fLocal, FILETIME *pftExpire)
{
if (fLocal)
m_qtTime.GetExpireTime(ftSubmission, m_dwLocalDelayExpireMinutes, pftExpire);
else
m_qtTime.GetExpireTime(ftSubmission, m_dwDelayExpireMinutes, pftExpire);
}
//---[ CAQSvrInst::AsyncQueueRetry ]-----------------------------------------
//
//
// Description:
// Restarts an async queue after a failure.
// Parameters:
// dwQueueID Tells which queue to kick
// PRELOCAL_QUEUE_ID Retries pre-local queue
// PRECAT_QUEUE_ID Retries pre-cat queue
// PREROUTING_QUEUE_ID Retries pre-routing queue
// PRESUBMIT_QUEUE_ID Retries pre-submit queue
// Returns:
// -
// History:
// 8/17/98 - MikeSwa Created
// 3/3/2000 - MikeSwa Modified to add presubmit queue
//
//-----------------------------------------------------------------------------
void CAQSvrInst::AsyncQueueRetry(DWORD dwQueueID)
{
_ASSERT(CATMSGQ_SIG == m_dwSignature);
if (fTryShutdownLock())
{
if (PRELOCAL_QUEUE_ID == dwQueueID)
{
InterlockedDecrement((PLONG) &m_cLocalRetriesPending);
m_asyncqPreLocalDeliveryQueue.StartRetry();
}
else if (PRECAT_QUEUE_ID == dwQueueID)
{
InterlockedDecrement((PLONG) &m_cCatRetriesPending);
m_asyncqPreCatQueue.StartRetry();
}
else if (PREROUTING_QUEUE_ID == dwQueueID)
{
InterlockedDecrement((PLONG) &m_cRoutingRetriesPending);
m_asyncqPreRoutingQueue.StartRetry();
}
else if (PRESUBMIT_QUEUE_ID == dwQueueID)
{
InterlockedDecrement((PLONG) &m_cSubmitRetriesPending);
m_asyncqPreSubmissionQueue.StartRetry();
}
else
{
_ASSERT(0 && "Invalid Queue ID");
}
ShutdownUnlock();
}
}
//---[ HrCreateBadMailPropertyFile ]------------------------------------------
//
//
// Description:
// Creates a property stream for the given message. The property
// stream file is given name the .BDP extension.
// Parameters:
// szDestFileBase The filename of the actual badmail file
// pIMailMsgProperties The original message that is being badmailed
// (may be NULL if it is a pickup dir file)
// Returns:
// S_OK on success
// E_POINTER if szDestFileBase is NULL
// History:
// 8/17/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrCreateBadMailPropertyFile(LPSTR szDestFileBase,
IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) NULL, "HrCreateBadMailPropertyFile");
HRESULT hr = S_OK;
CHAR szOldExt[] = "123";
CHAR szNewExt[] = "BDP";
LPSTR szBadMailFileNameExt = NULL;
DWORD cbBadMailFileName = 0;
BOOL fShouldRestoreExtension = FALSE;
CFilePropertyStream fstrm;
IMailMsgBind *pIMailMsgBind = NULL;
IMailMsgPropertyStream *pIMailMsgPropertyStream = NULL;
_ASSERT(szDestFileBase);
if (!szDestFileBase)
{
hr = E_POINTER;
ErrorTrace((LPARAM) NULL, "Error NULL badmail filename passed in");
goto Exit;
}
if (!pIMailMsgProperties) //no-op
goto Exit;
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgBind,
(void **) &pIMailMsgBind);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"Unable to QI for IMailMsgBind - 0x%08X", hr);
goto Exit;
}
_ASSERT(pIMailMsgBind);
//Create the filename & file
cbBadMailFileName = strlen(szDestFileBase);
_ASSERT(cbBadMailFileName > 4); //must at least have . extenstion
szBadMailFileNameExt = szDestFileBase + cbBadMailFileName-3;
//szBadMailFileNameExt now points to the first character of the 3 char ext
_ASSERT('.' == *(szBadMailFileNameExt-1));
_ASSERT(sizeof(szNewExt) == sizeof(szOldExt));
memcpy(szOldExt, szBadMailFileNameExt, sizeof(szOldExt));
memcpy(szBadMailFileNameExt, szNewExt, sizeof(szNewExt));
fShouldRestoreExtension = TRUE;
hr = fstrm.HrInitialize(szDestFileBase);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"Unable to create badmail property stream - 0x%08X", hr);
goto Exit;
}
hr = fstrm.QueryInterface(IID_IMailMsgPropertyStream,
(void **) &pIMailMsgPropertyStream);
_ASSERT(SUCCEEDED(hr)); //we control this totally
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"Unable to QI for IID_IMailMsgPropertyStream - 0x%08X", hr);
goto Exit;
}
hr = pIMailMsgBind->GetProperties(pIMailMsgPropertyStream,
MAILMSG_GETPROPS_COMPLETE, NULL);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL, "GetProperties failed with 0x%08X", hr);
goto Exit;
}
Exit:
if (fShouldRestoreExtension)
{
_ASSERT(szBadMailFileNameExt);
memcpy(szBadMailFileNameExt, szOldExt, sizeof(szOldExt));
}
if (pIMailMsgBind)
pIMailMsgBind->Release();
if (pIMailMsgPropertyStream)
pIMailMsgPropertyStream->Release();
TraceFunctLeave();
return hr;
}
//---[ HrCreateBadMailReasonFile ]---------------------------------------------
//
//
// Description:
// Creates a file in the badmail directory that expains why the given
// message was badmailed, and dump the sender and recipient as well. Uses
// the extension BMR (BadMailReason) to differentiate from the content.
// Parameters:
// szDestFileBase The filename of the actual badmail file
// hrReason The reason the badmail is being created
// pIMailMsgProperties The original message that is being badmailed
// (may be NULL if it is a pickup dir file)
// Returns:
// S_OK on success
// E_POINTER if szDestFileBase is NULL
// History:
// 8/16/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT HrCreateBadMailReasonFile(IN LPSTR szDestFileBase,
IN HRESULT hrReason,
IN IMailMsgProperties *pIMailMsgProperties)
{
TraceFunctEnterEx((LPARAM) NULL, "HrCreateBadMailReasonFile");
HRESULT hr = S_OK;
HRESULT hrErrorLogged = hrReason;
WCHAR wszBadmailReason[1000] = L""; //Localized hrReason string
WCHAR wszReasonBuffer[2000] = L"";
WCHAR wszErrorCode[] = L"0x12345678 ";
WCHAR wszErrorCodeMessage[200] = L"";
CHAR szPropBuffer[1000] = "";
DWORD dwErr = 0;
BOOL fWriteBadmailReason = FALSE;
BOOL fShouldRestoreExtension = FALSE;
DWORD cReasonBuffer = 0;
LPSTR szBadMailFileNameExt = NULL;
CHAR szOldExt[] = "123";
CHAR szNewExt[] = "BDR";
DWORD cbBadMailFileName = 0;
HANDLE hBadMailFile = NULL;
DWORD cbBytesWritten = 0;
DWORD dwFacility = 0;
LPWSTR rgwszArgList[32];
const WCHAR wcszBlankLine[] = L"\r\n";
IMailMsgRecipients *pIMailMsgRecipients = NULL;
DWORD cRecips = 0;
DWORD iCurrentRecip = 0;
_ASSERT(szDestFileBase);
if (!szDestFileBase)
{
ErrorTrace((LPARAM) NULL, "Invalid destination file for badmail");
hr = E_POINTER;
goto Exit;
}
if (!g_hAQInstance)
{
_ASSERT(g_hAQInstance && "This should always be set in DLL main");
ErrorTrace((LPARAM) NULL, "Error, g_hAQInstance is NULL");
hr = E_FAIL;
goto Exit;
}
//Create the filename & file
cbBadMailFileName = strlen(szDestFileBase);
_ASSERT(cbBadMailFileName > 4); //must at least have . extenstion
szBadMailFileNameExt = szDestFileBase + cbBadMailFileName-3;
//szBadMailFileNameExt now points to the first character of the 3 char ext
_ASSERT('.' == *(szBadMailFileNameExt-1));
_ASSERT(sizeof(szNewExt) == sizeof(szOldExt));
memcpy(szOldExt, szBadMailFileNameExt, sizeof(szOldExt));
memcpy(szBadMailFileNameExt, szNewExt, sizeof(szNewExt));
fShouldRestoreExtension = TRUE;
hBadMailFile = CreateFile(szDestFileBase,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE ==hBadMailFile )
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace((LPARAM) NULL,
"Unable to create badmail reason file - err 0x%08X - file %s",
hr, szDestFileBase);
goto Exit;
}
//Figure out the reason for the failure
if (SUCCEEDED(hrErrorLogged))
{
//someone is being lazy about setting the error reason
_ASSERT(0 && "No badmail reason given");
ErrorTrace((LPARAM) NULL, "Non-failing badmail HRESULT given 0x%08X",
hrErrorLogged);
//Substitute a generic error so we don't have something obnoxious like
//"The operation completed succesfully" appear in the badmail file
hrErrorLogged = E_FAIL;
}
//Write the error code in "0x00000000" format
wsprintfW(wszErrorCode, L"0x%08X", hrErrorLogged);
dwFacility = ((0x0FFF0000 & hrErrorLogged) >> 16);
//If it is not ours... then "un-HRESULT" it
if (dwFacility != FACILITY_ITF)
hrErrorLogged &= 0x0000FFFF;
dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
g_hAQInstance,
hrErrorLogged,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
wszBadmailReason,
sizeof(wszBadmailReason)/sizeof(WCHAR), NULL);
if (!dwErr)
{
//We should fall back on a numeric error that we were given
ErrorTrace((LPARAM) NULL,
"Error: unable to format badmail message 0x%08X, error is %d",
hrErrorLogged, GetLastError());
wcscpy(wszBadmailReason, wszErrorCode);
}
else
{
//Get rid of trailing newline
cReasonBuffer = wcslen(wszBadmailReason);
cReasonBuffer--;
while(iswspace(wszBadmailReason[cReasonBuffer]))
{
wszBadmailReason[cReasonBuffer] = '\0';
cReasonBuffer--;
}
cReasonBuffer = 0;
}
ErrorTrace((LPARAM) NULL,
"Generating badmail because: %S", wszBadmailReason);
rgwszArgList[0] = wszBadmailReason;
rgwszArgList[1] = NULL;
dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
g_hAQInstance,
PHATQ_BADMAIL_REASON,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
wszReasonBuffer,
sizeof(wszReasonBuffer)/sizeof(WCHAR),
(va_list *) rgwszArgList);
if (!dwErr)
{
ErrorTrace((LPARAM) NULL,
"Error: unable to format PHATQ_BADMAIL_REASON, error is %d",
GetLastError());
hr = HRESULT_FROM_WIN32(GetLastError());
wcscpy(wszReasonBuffer, wszBadmailReason);
}
cReasonBuffer = wcslen(wszReasonBuffer);
if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace((LPARAM) NULL,
"Error writing to badmail reason file - erro 0x%08X - file %s",
hr, szDestFileBase);
goto Exit;
}
//Write the actual error code in 0x00000000 form so tools can parse it out
rgwszArgList[0] = wszErrorCode;
rgwszArgList[1] = NULL;
wcscpy(wszReasonBuffer, wcszBlankLine);
dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
g_hAQInstance,
PHATQ_BADMAIL_ERROR_CODE,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
wszReasonBuffer+(sizeof(wcszBlankLine)-1)/sizeof(WCHAR),
(sizeof(wszReasonBuffer)-sizeof(wcszBlankLine))/sizeof(WCHAR),
(va_list *) rgwszArgList);
wcscat(wszReasonBuffer, wcszBlankLine);
cReasonBuffer = wcslen(wszReasonBuffer);
if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace((LPARAM) NULL,
"Error writing to badmail reason file - erro 0x%08X - file %s",
hr, szDestFileBase);
goto Exit;
}
//All the rest requries access to an actual message... if we don't
//have one, bail
if (!pIMailMsgProperties)
goto Exit;
//Write Sender of message
hr = pIMailMsgProperties->GetStringA(IMMPID_MP_SENDER_ADDRESS_SMTP,
sizeof(szPropBuffer), szPropBuffer);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"ERROR: Unable to get sender of IMailMsg 0x%08X",
pIMailMsgProperties);
hr = S_OK; //just don't display sender
}
else
{
rgwszArgList[0] = (LPWSTR) szPropBuffer;
rgwszArgList[1] = NULL;
wcscpy(wszReasonBuffer, wcszBlankLine);
dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
g_hAQInstance,
PHATQ_BADMAIL_SENDER,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
wszReasonBuffer+(sizeof(wcszBlankLine)-1)/sizeof(WCHAR),
(sizeof(wszReasonBuffer)-sizeof(wcszBlankLine))/sizeof(WCHAR),
(va_list *) rgwszArgList);
wcscat(wszReasonBuffer, wcszBlankLine);
cReasonBuffer = wcslen(wszReasonBuffer);
if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace((LPARAM) NULL,
"Error writing to badmail reason file - erro 0x%08X - file %s",
hr, szDestFileBase);
goto Exit;
}
}
//Write Message recipients
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
(PVOID *) &pIMailMsgRecipients);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"Unable to query interface for recip interface - 0x%08X", hr);
goto Exit;
}
hr = pIMailMsgRecipients->Count(&cRecips);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"Unable to get recipient count - 0x%08X", hr);
goto Exit;
}
//If we don't have any recipients, bail
if (!cRecips)
goto Exit;
//Write the localized text
wcscpy(wszReasonBuffer, wcszBlankLine);
dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
g_hAQInstance,
PHATQ_BADMAIL_RECIPIENTS,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
wszReasonBuffer+(sizeof(wcszBlankLine)-1)/sizeof(WCHAR),
(sizeof(wszReasonBuffer)-sizeof(wcszBlankLine))/sizeof(WCHAR),
NULL);
cReasonBuffer = wcslen(wszReasonBuffer);
if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace((LPARAM) NULL,
"Error writing to badmail reason file - erro 0x%08X - file %s",
hr, szDestFileBase);
goto Exit;
}
//Loop over SMTP recips and dump them in the file
for (iCurrentRecip = 0; iCurrentRecip < cRecips; iCurrentRecip++)
{
hr = pIMailMsgRecipients->GetStringA(iCurrentRecip,
IMMPID_RP_ADDRESS_SMTP, sizeof(szPropBuffer), szPropBuffer);
if (FAILED(hr))
{
ErrorTrace((LPARAM) NULL,
"Unable to get SMTP address for recip %d - 0x%08X",
iCurrentRecip, hr);
hr = S_OK;
continue;
}
cReasonBuffer = wsprintfW(wszReasonBuffer, L"\t%S%s",
szPropBuffer, wcszBlankLine);
if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
ErrorTrace((LPARAM) NULL,
"Error writing to badmail reason file - error 0x%08X - file %s",
hr, szDestFileBase);
goto Exit;
}
}
Exit:
if (fShouldRestoreExtension)
{
_ASSERT(szBadMailFileNameExt);
memcpy(szBadMailFileNameExt, szOldExt, sizeof(szOldExt));
}
if (pIMailMsgRecipients)
pIMailMsgRecipients->Release();
if (hBadMailFile != INVALID_HANDLE_VALUE)
_VERIFY(CloseHandle(hBadMailFile));
TraceFunctLeave();
return hr;
}
//---[ CAQSvrInst::HandleBadMail ]---------------------------------------------
//
//
// Description:
// Handles mail that needs to be placed in the badmail directory (or
// equivalent).
// Parameters:
// IN pIMailMsgProperties that needs to be badmail'd
// IN fUseIMailMsgPropeties -- use IMAilMsgProps if set else use szFilename
// IN szFileName Name of badmail file (if no msg can be supplied)
// IN hrReason - HRESULT (defined in aqerr) that describes reason
// Eventually, we may log this information to the badmail
// file (or recipient)
// IN fHasRoutingLock - TRUE if this thread holds routing lock
// Returns:
// -
// History:
// 10/8/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::HandleBadMail(IN IMailMsgProperties *pIMailMsgProperties,
IN BOOL fUseIMailMsgProperties,
IN LPSTR szOriginalFileName,
IN HRESULT hrReason,
IN BOOL fHasRoutingLock)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleBadMail");
HRESULT hr = S_OK;
LPSTR szFullPathName = NULL;
LPSTR szFileName = NULL;
BOOL fDataLocked = FALSE;
BOOL fDone = TRUE;
HANDLE hFile = INVALID_HANDLE_VALUE;
PFIO_CONTEXT pFIOContext = NULL;
FILETIME ftCurrent;
CRefCountedString *prstrBadMailDir = NULL;
MSG_TRACK_INFO msgTrackInfo;
ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
msgTrackInfo.dwEventId = MTE_BADMAIL;
m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
InterlockedIncrement((PLONG) &m_cTotalMsgsBadmailed);
if (!fHasRoutingLock)
{
m_slPrivateData.ShareLock();
fDataLocked = TRUE;
}
else
{
m_slPrivateData.AssertIsLocked();
}
if (m_prstrBadMailDir)
{
prstrBadMailDir = m_prstrBadMailDir;
prstrBadMailDir->AddRef();
}
else
{
LogAQEvent(AQUEUE_E_NO_BADMAIL_DIR, NULL, pIMailMsgProperties, NULL);
goto Exit;
}
if (fDataLocked)
{
m_slPrivateData.ShareUnlock();
fDataLocked = FALSE;
}
szFullPathName = (LPSTR) pvMalloc(sizeof(CHAR) *
(UNIQUEUE_FILENAME_BUFFER_SIZE +
prstrBadMailDir->cbStrlen()));
if (!szFullPathName)
{
LogAQEvent(AQUEUE_E_BADMAIL, NULL, pIMailMsgProperties, NULL);
goto Exit;
}
memcpy(szFullPathName, prstrBadMailDir->szStr(),
prstrBadMailDir->cbStrlen());
if (szFullPathName[prstrBadMailDir->cbStrlen()-1] != '\\')
{
_ASSERT(0 && "Malformed badmail config");
LogAQEvent(AQUEUE_E_NO_BADMAIL_DIR, NULL, pIMailMsgProperties, NULL);
goto Exit;
}
szFileName = szFullPathName + prstrBadMailDir->cbStrlen();
//If we have a msg use it
if (pIMailMsgProperties && fUseIMailMsgProperties)
{
//Loop while trying to generate a unique file name
do
{
fDone = TRUE;
GetExpireTime(0, &ftCurrent, NULL);
GetUniqueFileName(&ftCurrent, szFileName, "BAD");
//Create file and write MsgContent to it
hFile = CreateFile( szFullPathName,
GENERIC_WRITE,
0,
NULL,
CREATE_NEW,
FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
if (ERROR_ALREADY_EXISTS == GetLastError())
{
//Try a new file name
fDone = FALSE;
continue;
}
//Other we are hosed... log an event
LogAQEvent(AQUEUE_E_BADMAIL, NULL, pIMailMsgProperties, NULL);
goto Exit;
}
_ASSERT(hFile); //should return INVALID_HANDLE_VALUE on failure
} while (!fDone);
if (hFile != INVALID_HANDLE_VALUE)
pFIOContext = AssociateFile(hFile);
if (!pFIOContext ||
FAILED(pIMailMsgProperties->CopyContentToFile(pFIOContext, NULL)))
{
//Copy failed log event
LogAQEvent(AQUEUE_E_BADMAIL, NULL, pIMailMsgProperties, NULL);
}
}
else if (szOriginalFileName)
{
//Otherwise (no msg)... just do a movefile
_ASSERT(szFullPathName[prstrBadMailDir->cbStrlen()-1] == '\\');
szFullPathName[prstrBadMailDir->cbStrlen()-1] = '\0';
if (!MoveFileEx(szOriginalFileName, szFullPathName,
MOVEFILE_COPY_ALLOWED))
{
//MoveFile failed... try renaming with a unique file name
szFullPathName[prstrBadMailDir->cbStrlen()-1] = '\\';
GetExpireTime(0, &ftCurrent, NULL);
GetUniqueFileName(&ftCurrent, szFileName, "BAD");
if (rename(szOriginalFileName, szFullPathName))
LogAQEvent(AQUEUE_E_BADMAIL, NULL, NULL, szOriginalFileName);
}
}
hr = HrCreateBadMailReasonFile(szFullPathName, hrReason, pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to make badmail reason file - hr 0x%08X", hr);
}
hr = HrCreateBadMailPropertyFile(szFullPathName, pIMailMsgProperties);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Unable to make badmail property file - hr 0x%08X", hr);
}
Exit:
if (fDataLocked)
m_slPrivateData.ShareUnlock();
if (prstrBadMailDir)
prstrBadMailDir->Release();
if (szFullPathName)
FreePv(szFullPathName);
if (NULL != pFIOContext)
ReleaseContext(pFIOContext);
TraceFunctLeave();
}
//---[ HandleAQFailure ]--------------------------------------------------------
//
//
// Description:
// Function to handle AQ failures that would result in loss of data
// or messages if unhandled. Meant to be a substitute for
// _ASSERT(SUCCEEDED(hr)).
//
// Note: Msgs are still Turfed for M2
// Parameters:
// eaqfFailureSituation Enum that describes the failure situation
// as well as what the context is.
// hr HRSULT that triggered failure condition
// pIMailMsgProperties MailMsgProperties
// Returns:
// -
// History:
// 8/25/98 - MikeSwa Created
// 10/8/98 - MikeSwa Moved to CAQSvrInst
//
//-----------------------------------------------------------------------------
void CAQSvrInst::HandleAQFailure(eAQFailure eaqfFailureSituation,
HRESULT hr,
IMailMsgProperties *pIMailMsgProperties)
{
_ASSERT(eaqfFailureSituation < AQ_FAILURE_NUM_SITUATIONS);
InterlockedIncrement((PLONG) &g_cTotalAQFailures);
InterlockedIncrement((PLONG) &(g_rgcAQFailures[eaqfFailureSituation]));
BOOL fCanRetry = fShouldRetryMessage(pIMailMsgProperties);
MSG_TRACK_INFO msgTrackInfo;
ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
msgTrackInfo.dwEventId = MTE_AQ_FAILURE;
m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
switch(eaqfFailureSituation)
{
case(AQ_FAILURE_CANNOT_NDR_UNRESOLVED_RECIPS):
LogAQEvent(AQUEUE_E_DSN_FAILURE, NULL, pIMailMsgProperties, NULL);
//drop through to default case
default:
//
// Throw the message in the last-ditch retry queue if the following are true:
// - We had a failure
// - We are not shutting down
// - We can retry the message (e.g., it has not been deleted)
//
if (FAILED(hr) && (AQUEUE_E_SHUTDOWN != hr) && fCanRetry)
{
InterlockedIncrement((PLONG) &m_cCurrentResourceFailedMsgsPendingRetry);
m_fmq.HandleFailedMailMsg(pIMailMsgProperties);
}
}
}
//---[ CAQSvrInst::LogAQEvent ]------------------------------------------------
//
//
// Description:
// General event logging mechanism for AQ
// Parameters:
// hrEventReason AQUEUE HRESULT describing event
// pmsgref CMsgRef of msg for event (can be NULL)
// pIMailMsgProperties pIMailMsgProperties for event (can be NULL)
// szFileName Filename if no msgs provided (can be NULL)
// Returns:
// -
// History:
// 10/9/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CAQSvrInst::LogAQEvent(HRESULT hrEventReason, CMsgRef *pmsgref,
IMailMsgProperties *pIMailMsgProperties,
LPSTR szFileName)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::LogAQEvent");
switch (hrEventReason)
{
//$$TODO - Add actual event callouts here
case (S_OK): //Added to remove switch compile warnings
default:
ErrorTrace((LPARAM) this, "EVENT: Generic AQueue event - 0x%08X", hrEventReason);
}
TraceFunctLeave();
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::TriggerPreCategorizeEvent
//
// Synopsis: Fire the pre-cat server event
//
// Arguments:
// pIMailMsgProperties: the IMailMsgProperties interface of the mailmsg
//
// Returns: NOTHING
//
// History:
// jstamerj 1998/11/24 20:07:58: Created.
//
//-------------------------------------------------------------
VOID CAQSvrInst::TriggerPreCategorizeEvent(
IN IMailMsgProperties *pIMailMsgProperties)
{
HRESULT hr;
EVENTPARAMS_PRECATEGORIZE Params;
TraceFunctEnterEx((LPARAM)pIMailMsgProperties,
"CAQSvrInst::TriggerPreCategorizeEvent");
_ASSERT(pIMailMsgProperties);
Params.pfnCompletion = MailTransport_Completion_PreCategorization;
Params.pCCatMsgQueue = (PVOID) this;
Params.pIMailMsgProperties = pIMailMsgProperties;
//
// Addref here, release in completion
//
pIMailMsgProperties->AddRef();
AddRef();
//
// keep a count of messages in the pre-cat event
//
InterlockedIncrement((LPLONG) &m_cCurrentMsgsPendingPreCatEvent);
if(m_pISMTPServer) {
hr = TriggerServerEvent(
SMTP_MAILTRANSPORT_PRECATEGORIZE_EVENT,
&Params);
DebugTrace((LPARAM)this, "TriggerServerEvent returned hr %08lx", hr);
}
if((m_pISMTPServer == NULL) || (FAILED(hr))) {
ErrorTrace((LPARAM)this,
"Unable to dispatch server event; calling completion routine directly");
DebugTrace((LPARAM)this, "hr is %08lx", hr);
//
// Call completion routine directly
//
_VERIFY(SUCCEEDED(PreCatEventCompletion(S_OK, &Params)));
}
TraceFunctLeaveEx((LPARAM)this);
}
//+------------------------------------------------------------
//
// Function: CAQSvrInst::PreCatEventCompletion
//
// Synopsis: Called by SEO upon completipon of the precat event
//
// Arguments:
// pIMailMsgProperties: the IMailMsgProperties interface of the mailmsg
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 1998/11/24 20:17:44: Created.
//
//-------------------------------------------------------------
HRESULT CAQSvrInst::PreCatEventCompletion(
IN HRESULT hrStatus,
IN PEVENTPARAMS_PRECATEGORIZE pParams)
{
HRESULT hr;
_ASSERT(pParams);
_ASSERT(pParams->pIMailMsgProperties);
TraceFunctEnterEx((LPARAM)pParams->pIMailMsgProperties,
"CAQSvrInst::PreCatEventCompletion");
DebugTrace((LPARAM)pParams->pIMailMsgProperties, "hrStatus is %08lx", hrStatus);
//
// Decrease count of msgs in pre-cat event
//
InterlockedDecrement((LPLONG) &m_cCurrentMsgsPendingPreCatEvent);
//
// Update the message status and check for abort/badmail
//
hr = SetNextMsgStatus(MP_STATUS_SUBMITTED, pParams->pIMailMsgProperties);
if (hr == S_OK) //anything else implies that the message has been handled
{
//Only submit to categorizer if things message was not turfed.
hr = SubmitMessageToCategorizer(pParams->pIMailMsgProperties);
if(FAILED(hr))
{
_ASSERT((hr == AQUEUE_E_SHUTDOWN) && "SubmitMessageToCategorizer failed.");
ErrorTrace((LPARAM)pParams->pIMailMsgProperties,
"SubmitMessageToCategorizer returned hr %08lx",
hr);
}
}
//
// Release references added in TriggerPreCategorizeEvent
//
pParams->pIMailMsgProperties->Release();
Release();
TraceFunctLeaveEx((LPARAM)pParams->pIMailMsgProperties);
return S_OK;
}
//+------------------------------------------------------------
//
// Function: MailTransport_Completion_PreCategorization
//
// Synopsis: SEO will call this routine after all sinks for
// OnPreCategoriztion have been handeled
//
// Arguments:
// pvContext: Context passed into TriggerServerEvent
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 1998/11/24 20:26:51: Created
//
//-------------------------------------------------------------
HRESULT MailTransport_Completion_PreCategorization(
HRESULT hrStatus,
PVOID pvContext)
{
TraceFunctEnter("MailTransport_Completion_PreCategorization");
PEVENTPARAMS_PRECATEGORIZE pParams = (PEVENTPARAMS_PRECATEGORIZE) pvContext;
CAQSvrInst *paqinst = (CAQSvrInst *) pParams->pCCatMsgQueue;
TraceFunctLeave();
return paqinst->PreCatEventCompletion(
hrStatus,
pParams);
}
//---[ CAQSvrInst::SetCallbackTime ]-------------------------------------------
//
//
// Description:
// Set a callback time based on a number of minutes.
// Parameters:
// IN pCallbackFn Ptr to a callback function
// IN pvContext Context pass to callback function
// IN dwCallbackMinutes Minutes to wait before calling callback
// function.
// Returns:
//
// History:
// 12/29/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::SetCallbackTime(IN PSRVFN pCallbackFn,
IN PVOID pvContext,
IN DWORD dwCallbackMinutes)
{
HRESULT hr = S_OK;
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
_ASSERT(m_pConnMgr);
if (m_pConnMgr)
hr = m_pConnMgr->SetCallbackTime(pCallbackFn, pvContext,
dwCallbackMinutes);
ShutdownUnlock();
Exit:
return hr;
}
//---[ CAQSvrInst::SetCallbackTime ]-------------------------------------------
//
//
// Description:
// Set a callback time based on a filetime.
// Parameters:
//
// Returns:
//
// History:
// 12/29/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CAQSvrInst::SetCallbackTime(IN PSRVFN pCallbackFn,
IN PVOID pvContext,
IN FILETIME *pft)
{
HRESULT hr = S_OK;
DWORD dwCallbackMinutes = 0;
DWORD dwTimeContext = 0;
FILETIME ftCurrentTime;
LARGE_INTEGER *pLargeIntCurrentTime = (LARGE_INTEGER *) &ftCurrentTime;
LARGE_INTEGER *pLargeIntCallbackTime = (LARGE_INTEGER *) pft;
_ASSERT(pCallbackFn);
_ASSERT(pvContext);
_ASSERT(pft);
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
if (!fInPast(pft, &dwTimeContext))
{
//Get current time using previous context (so current time is the same)
GetExpireTime(0, &ftCurrentTime, &dwTimeContext);
//the current time must be less than the callback time
_ASSERT(pLargeIntCurrentTime->QuadPart < pLargeIntCallbackTime->QuadPart);
pLargeIntCurrentTime->QuadPart = pLargeIntCallbackTime->QuadPart -
pLargeIntCurrentTime->QuadPart;
pLargeIntCurrentTime->QuadPart /= (LONGLONG) 600000000;
//If the callback time is > 2 billion minutes... I'd
//like to know about it in debug builds
_ASSERT(!pLargeIntCurrentTime->HighPart);
dwCallbackMinutes = pLargeIntCurrentTime->LowPart;
//The only current application is for deferred delivery... I would like
//to see the internal test situations that result in a deferred delivery
_ASSERT(dwCallbackMinutes < (60*24*7));
//
// If we have rounded down to 0 minutes, we should call back in 1
// otherwise we can end up in a tight loop. If the call merely wants
// another thread, they should use the AsyncWorkQueue
//
if (!dwCallbackMinutes)
dwCallbackMinutes = 1;
}
else
{
//If in past... callback as soon as possible, but don't use this thread, in
//case there are locking complications (CShareLockNH is non-reentrant).
dwCallbackMinutes = 1;
}
_ASSERT(m_pConnMgr);
if (m_pConnMgr)
hr = m_pConnMgr->SetCallbackTime(pCallbackFn, pvContext,
dwCallbackMinutes);
ShutdownUnlock();
Exit:
return hr;
}
//---[ CAQSvrInst::SetLinkState ]----------------------------------------------
//
//
// Description:
// Implements IMailTransportRouterSetLinkState::SetLinkState
// Parameters:
// IN szLinkDomainName The Domain Name of the link (next hop)
// IN guidRouterGUID The GUID ID of the router
// IN dwScheduleID The schedule ID link
// IN szConnectorName The connector name given by the router
// IN dwFlagsToSet Link State Flags to set
// IN dwFlagsToUnset Link State Flags to unset
// IN pftNextScheduledConnection Next scheduled connection time.
// Returns:
// S_OK on success
// E_INVALIDARG if szLinkDomainName is NULL
// AQUEUE_E_SHUTDOWN if shutting down.
// History:
// 1/9/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CAQSvrInst::SetLinkState(
IN LPSTR szLinkDomainName,
IN GUID guidRouterGUID,
IN DWORD dwScheduleID,
IN LPSTR szConnectorName,
IN DWORD dwSetLinkState,
IN DWORD dwUnsetLinkState,
IN FILETIME *pftNextScheduledConnection,
IN IMessageRouter *pMessageRouter)
{
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
DWORD cbLinkDomainName = 0;
CDomainEntry *pdentry = NULL;
CLinkMsgQueue *plmq = NULL;
CAQScheduleID aqsched(guidRouterGUID, dwScheduleID);
BOOL fRemoveOwnedSchedule = TRUE;
if (!szLinkDomainName)
{
hr = E_INVALIDARG;
goto Exit;
}
if (!fTryShutdownLock())
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
fLocked = TRUE;
cbLinkDomainName = lstrlen(szLinkDomainName);
// see if they want to create a new link
if (dwSetLinkState & LINK_STATE_CREATE_IF_NECESSARY) {
// creating a link requires a pmessagerouter
if (pMessageRouter == NULL) {
hr = E_POINTER;
} else {
LinkFlags lf;
if (dwSetLinkState & LINK_STATE_TYPE_INTERNAL_SMTP) {
lf = eLinkFlagsInternalSMTPLinkInfo;
} else {
_ASSERT(dwSetLinkState & LINK_STATE_TYPE_EXTERNAL_SMTP);
lf = eLinkFlagsExternalSMTPLinkInfo;
}
dwSetLinkState &=
~(LINK_STATE_TYPE_INTERNAL_SMTP |
LINK_STATE_TYPE_EXTERNAL_SMTP);
// get the link, and create it if it doesn't exist and they want to
// have a new link created
hr = m_dmt.HrGetOrCreateLink(szLinkDomainName,
cbLinkDomainName,
dwScheduleID,
szConnectorName,
pMessageRouter,
TRUE,
lf,
&plmq,
&fRemoveOwnedSchedule);
}
} else {
cbLinkDomainName = lstrlen(szLinkDomainName);
hr = HrGetDomainEntry(cbLinkDomainName, szLinkDomainName, &pdentry);
if (SUCCEEDED(hr))
hr = pdentry->HrGetLinkMsgQueue(&aqsched, &plmq);
}
if (FAILED(hr))
goto Exit;
// this bit is only used above, so remove it
dwSetLinkState &= ~LINK_STATE_CREATE_IF_NECESSARY;
dwUnsetLinkState &= ~LINK_STATE_CREATE_IF_NECESSARY;
_ASSERT(plmq);
//
// If this operation is dis-allowing scheduled connections... we should
// record when the next scheduled attempt will be. We should also do
// this before we modify the link state, so that the queue admin does
// not display a scheduled queue without a next connection time.
//
if (pftNextScheduledConnection &&
(pftNextScheduledConnection->dwLowDateTime ||
pftNextScheduledConnection->dwHighDateTime))
{
plmq->SetNextScheduledConnection(pftNextScheduledConnection);
}
//filter out the reserved bits for this "public" API
plmq->dwModifyLinkState(~LINK_STATE_RESERVED & dwSetLinkState,
~LINK_STATE_RESERVED & dwUnsetLinkState);
// schedule a callback if one was requested
if (pftNextScheduledConnection->dwLowDateTime != 0 ||
pftNextScheduledConnection->dwHighDateTime != 0)
{
//callback with next attempt
plmq->AddRef(); //Addref self as context
hr = SetCallbackTime(
CLinkMsgQueue::ScheduledCallback,
plmq,
pftNextScheduledConnection);
if (FAILED(hr))
plmq->Release(); //callback will not happen... release context
}
Exit:
if (fLocked)
ShutdownUnlock();
if (pdentry)
pdentry->Release();
if (plmq)
plmq->Release();
//
// If we have not passed ownership of the shedule ID to a link,
// then we are responsible for releasing it.
//
if (fRemoveOwnedSchedule) {
IMessageRouterLinkStateNotification *pILinkStateNotify = NULL;
HRESULT hrLinkStateNotify =
pMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
(VOID **) &pILinkStateNotify);
_ASSERT( SUCCEEDED( hrLinkStateNotify));
FILETIME ftNotUsed = {0,0};
DWORD dwSetNotUsed = LINK_STATE_NO_ACTION;
DWORD dwUnsetNotUsed = LINK_STATE_NO_ACTION;
hrLinkStateNotify =
pILinkStateNotify->LinkStateNotify(
szLinkDomainName,
guidRouterGUID,
dwScheduleID,
szConnectorName,
LINK_STATE_LINK_NO_LONGER_USED,
0, //consecutive failures
&ftNotUsed,
&dwSetNotUsed,
&dwUnsetNotUsed);
_ASSERT( SUCCEEDED( hrLinkStateNotify));
if ( NULL != pILinkStateNotify) {
pILinkStateNotify->Release();
}
}
return hr;
}
//---[ CAQSvrInst::prstrGetDefaultDomain ]-------------------------------------
//
//
// Description:
// Returns the ref-counted string for the default domain
// Parameters:
// -
// Returns:
// See above
// History:
// 2/23/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CRefCountedString *CAQSvrInst::prstrGetDefaultDomain()
{
CRefCountedString *prstrDefaultDomain = NULL;
m_slPrivateData.ShareLock();
if (m_prstrDefaultDomain)
m_prstrDefaultDomain->AddRef();
prstrDefaultDomain = m_prstrDefaultDomain;
m_slPrivateData.ShareUnlock();
return prstrDefaultDomain;
}
//+------------------------------------------------------------
//
// Function: ScheduleInternalRetry
//
// Synopsis: Schedule categorizer retry if necessary
//
// Arguments: None
//
// Returns: Nothing
//
// History:
// jstamerj 2000/06/08 17:31:30: Created.
// 1/16/2001 - MikeSwa Modified to handle all internal retries
//
//-------------------------------------------------------------
VOID CAQSvrInst::ScheduleInternalRetry(DWORD dwLinkType)
{
TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::ScheduleInternalRetry");
DWORD *pcRetriesPending = NULL;
PSRVFN pCallbackFn = NULL;
DWORD cCallbackMinutes = 0;
FILETIME ftCallback = {0,0};
BOOL fHasLock = FALSE;
CQueueAdminRetryNotify *pqapiret = NULL;
//
// First try to get the shutdown lock before accessing
// anything that may go away during deinitialization (like m_pConnMgr)
//
if (!fTryShutdownLock())
goto Exit;
fHasLock = TRUE;
switch (dwLinkType) {
case LI_TYPE_PENDING_ROUTING:
pcRetriesPending = &m_cRoutingRetriesPending;
pCallbackFn = RoutingRetry;
cCallbackMinutes = g_cRoutingRetryMinutes;
pqapiret = (CQueueAdminRetryNotify *) m_dmt.pmmaqGetPreRouting();
break;
case LI_TYPE_LOCAL_DELIVERY:
pcRetriesPending = &m_cLocalRetriesPending;
pCallbackFn = LocalDeliveryRetry;
cCallbackMinutes = g_cLocalRetryMinutes;
pqapiret = (CQueueAdminRetryNotify *) m_dmt.plmqGetLocalLink();
break;
case LI_TYPE_PENDING_CAT:
pcRetriesPending = &m_cCatRetriesPending;
pCallbackFn = CatRetry;
cCallbackMinutes = g_cCatRetryMinutes;
pqapiret = (CQueueAdminRetryNotify *) m_dmt.pmmaqGetPreCategorized();
break;
case LI_TYPE_PENDING_SUBMIT:
pcRetriesPending = &m_cSubmitRetriesPending;
pCallbackFn = SubmitRetry;
cCallbackMinutes = g_cSubmissionRetryMinutes;
pqapiret = (CQueueAdminRetryNotify *) m_dmt.pmmaqGetPreSubmission();
break;
default:
//
// Someone has called with a bogus callback
//
_ASSERT(0 && "Unspecified callback for internal retry");
ErrorTrace((LPARAM) this,
"Unspecified callback for internal retry 0x%08X", dwLinkType);
goto Exit;
}
_ASSERT(pcRetriesPending);
if (pcRetriesPending && !*pcRetriesPending)
{
_ASSERT(pCallbackFn);
_ASSERT(cCallbackMinutes);
//
// Say that we are requesting a callback
//
InterlockedIncrement((PLONG) pcRetriesPending);
m_pConnMgr->SetCallbackTime(pCallbackFn, this, cCallbackMinutes);
//
// Get the expire time... so we can update our retry time
//
m_qtTime.GetExpireTime(cCallbackMinutes, &ftCallback, NULL);
//
// Update retry time
//
if (pqapiret)
pqapiret->SetNextRetry(&ftCallback);
}
Exit:
if (pqapiret)
pqapiret->Release();
if (fHasLock)
ShutdownUnlock();
TraceFunctLeave();
} // CAQSvrInst::ScheduleInternalRetry
//+------------------------------------------------------------
//
// Function: CAQSvrInst::HrAllocBoundMessage
//
// Synopsis:
// Allocates a bound message
//
// Arguments:
// ppMsg: Out param for Allocated mailmsg
// phContent: Out param for content handle. Handle is managed by mailmsg
//
// Returns:
// S_OK: Success
// E_FAIL: No ISMTPServer is available
// error from SMTP
//
// History:
// jstamerj 2001/05/11 15:39:16: Created.
//
//-------------------------------------------------------------
HRESULT CAQSvrInst::HrAllocBoundMessage(
OUT IMailMsgProperties **ppMsg,
OUT PFIO_CONTEXT *phContent)
{
HRESULT hr = S_OK;
ISMTPServerInternal *pISMTPInternal = NULL;
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrAllocBoundMessage");
if(m_pISMTPServer == NULL)
{
hr = E_FAIL;
goto CLEANUP;
}
hr = m_pISMTPServer->QueryInterface(
IID_ISMTPServerInternal,
(LPVOID *) &pISMTPInternal);
if(FAILED(hr))
goto CLEANUP;
hr = pISMTPInternal->AllocBoundMessage(
ppMsg,
phContent);
CLEANUP:
if(pISMTPInternal)
pISMTPInternal->Release();
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CAQSvrInst::HrAllocBoundMessage
//+------------------------------------------------------------
//
// Function: CAQSvrInst::HrSubmitDSN
//
// Synopsis: Acceps a message submitted by a DSN sink
//
// Arguments:
// pIMsgOrig: Original message (for which a DSN is being generated)
// dwDSNAction: Indicates the type of DSN
// cRecipsDSNd: Number of recipients in the DSN.
// pDSNMsg: The DSN mailmsg object
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/12/08 13:42:17: Created.
//
//-------------------------------------------------------------
HRESULT CAQSvrInst::HrSubmitDSN(
CDSNParams *pdsnparams,
DWORD dwDSNAction,
DWORD cRecipsDSNd,
IMailMsgProperties *pDSNMsg)
{
HRESULT hr = S_OK;
DWORD cCurrent = 0;
TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrSubmitDSN");
DebugTrace((LPARAM)this, "dwDSNAction: %08lx", dwDSNAction);
DebugTrace((LPARAM)this, "cRecipsDSNd: %ld", cRecipsDSNd);
if(pDSNMsg == NULL)
{
hr = E_INVALIDARG;
goto CLEANUP;
}
if ((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL) & dwDSNAction)
{
MSG_TRACK_INFO msgTrackInfo;
cCurrent = InterlockedIncrement((PLONG) &m_cNDRs);
DebugTrace((LPARAM) this, "INFO: NDR Generated - total %d", cCurrent);
ZeroMemory(&msgTrackInfo, sizeof(MSG_TRACK_INFO));
msgTrackInfo.dwEventId = MTE_NDR_ALL;
msgTrackInfo.pszPartnerName = "aqueue";
msgTrackInfo.dwRcptReportStatus = MP_STATUS_ABORT_DELIVERY;
m_pISMTPServer->WriteLog(&msgTrackInfo,
pdsnparams->pIMailMsgProperties,
NULL,
NULL);
}
if (DSN_ACTION_DELAYED & dwDSNAction)
{
cCurrent = InterlockedIncrement((PLONG) &m_cDelayedDSNs);
DebugTrace((LPARAM) this, "INFO: Delayed DSN Generated - total %d", cCurrent);
}
if (DSN_ACTION_RELAYED & dwDSNAction)
{
cCurrent = InterlockedIncrement((PLONG) &m_cRelayedDSNs);
DebugTrace((LPARAM) this, "INFO: Relayed DSN Generated - total %d", cCurrent);
}
if (DSN_ACTION_DELIVERED & dwDSNAction)
{
cCurrent = InterlockedIncrement((PLONG) &m_cDeliveredDSNs);
DebugTrace((LPARAM) this, "INFO: Delivery DSN Generated - total %d", cCurrent);
}
if (DSN_ACTION_EXPANDED & dwDSNAction)
{
cCurrent = InterlockedIncrement((PLONG) &m_cExpandedDSNs);
DebugTrace((LPARAM) this, "INFO: Expanded DSN Generated - total %d", cCurrent);
}
//Queue request to post DSN generation queue
hr = m_asyncqPostDSNQueue.HrQueueRequest(
pDSNMsg,
FALSE,
cCountMsgsForHandleThrottling(pDSNMsg));
if (SUCCEEDED(hr))
hr = S_OK;
pdsnparams->dwDSNTypesGenerated |= dwDSNAction;
pdsnparams->cRecips += cRecipsDSNd;
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CAQSvrInst::HrSubmitDSN