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