//----------------------------------------------------------------------------- // // // File: aqinst.h // // Description: // CAQSvrInst is a central dispatcher class for Advanced Queuing. It // coordinates shutdown and exposes the following COM interfaces: // - IAdvQueue // - IAdvQueueConfig // // Owner: mikeswa // // History: // 9/3/98 - MikeSwa - changed from legacy name catmsgq.h & CCatMsgQueue // // Copyright (C) 1997, 1998 Microsoft Corporation // //----------------------------------------------------------------------------- #ifndef __AQINST_H__ #define __AQINST_H__ #include "cmt.h" #include #include "baseobj.h" #include "aqueue.h" #include "domcfg.h" #include "domain.h" #include "smtpseo.h" #include "smproute.h" #include "qwiktime.h" #include "dsnsink.h" #include "asyncq.h" #include "msgrefadm.h" #include "mailadmq.h" #include "shutdown.h" #include "refstr.h" #include "msgguid.h" #include "aqdbgcnt.h" #include "aqnotify.h" #include "defdlvrq.h" #include "failmsgq.h" #include "asncwrkq.h" #include "tran_evntlog.h" #include "aqreg.h" #include "..\aqdisp\seomgr.h" //-- *** LOCKS IN AQUEUE *** -------------------------------------------------- // // NOTE: General comment on locks in aqueue. // // In general, we use CShareLockNH as our locking mechanism. These locks are // Reader/Writer locks with TryEnter semantics and the performance feature // that they use less than 1 handle per lock (~1 handle per thread). // // Shutdown is handled by using these locks. Each class that serves as an // entrypoint for external threads (CAsyncQueue & CConnMgr) inherits from // CSyncShutdown. At shutdown, this classes lock is aquired EXCLUSIVE, and // to protect operations from shutdown, a this classes lock is aquired SHARED // for the duration of the opertaion. Getting the shutdown sharelock either // success or fails without blocking (aquiring the EXCLUSIVE shutdown lock is // the only blocking call). // // The only other global lock is the virtual server instance routing lock. // This is acquired shared for all operations at the same level the exclusive // lock is aquired. This is acquired exlusively *only* for router changes // caused by IRouterReset::ResetRoutes. // // If other classes have data which needs to be protected, they will have a // m_slPrivateData sharelock. Any operation that needs to read data in a // thread-safe manner, should aquire the m_slPrivateData SHARED. Any // operation that needs to write data that is accessable by multiple threads // should aquire that object's m_slPrivateData lock EXCLUSIVE. // // Some objects (CFifoQueue for example) require more than one lock to avoid // contention. These objects will have locks that are descriptive of that // particular locks functions. CFifoQueue, for example, uses m_slHead and // m_slTail to respectively protect the head and tail of the queue. // //----------------------------------------------------------------------------- // forward declarations to avoid #include nightmares class CLinkMsgQueue; class CConnMgr; class CAQStats; class CDSNParams; class CMsgRef; #define MEMBER_OK(pStruct, Member) \ (((LONG) (pStruct)->cbVersion) >= ( ((BYTE *) &((pStruct)->Member)) - ((BYTE *) pStruct))) //For Service callback function typedef void (*PSRVFN)(PVOID); //CatMsgQueue Signature #define CATMSGQ_SIG ' QMC' //Total number of IMsgs in the system (all virtual servers) _declspec(selectany) DWORD g_cIMsgInSystem = 0; //List of virtual servers used by debugger extensions _declspec(selectany) LIST_ENTRY g_liVirtualServers = {&g_liVirtualServers, &g_liVirtualServers}; //Sharelock used to access global virtual servers _declspec(selectany) CShareLockNH *g_pslGlobals = NULL; //Setup defaults const DWORD g_cMaxConnections = 10000; //Maximum # of total connections allocated const DWORD g_cMaxLinkConnections = 10; //Maximum # of connections per link const DWORD g_cMinMessagesPerConnection = 20; //There must be this many messages //per addional connection that is //allocated for a link const DWORD g_cMaxMessagesPerConnection = 20; //We server atmost these many messages per connection const DWORD g_dwConnectionWaitMilliseconds = 3600000; const DWORD g_dwRetryThreshold = 3; // Till 3 consecutive failures we treat it as glitch; const DWORD g_dwFirstTierRetrySeconds = (15 * 60); // retry a failure in 15 minutes const DWORD g_dwSecondTierRetrySeconds = (60 * 60); // retry a failure in 60 minutes const DWORD g_dwThirdTierRetrySeconds = (12 * 60 * 60); // retry a failure in 12 hrs const DWORD g_dwFourthTierRetrySeconds = (24 * 60 * 60); // retry a failure in 24 hrs const DWORD g_dwRetriesBeforeDelay = 5; const DWORD g_dwDelayIntervalsBeforeNDR = 2; const DWORD g_dwDelayExpireMinutes = g_dwRetriesBeforeDelay*g_dwFirstTierRetrySeconds/(60); const DWORD g_dwNDRExpireMinutes = g_dwDelayIntervalsBeforeNDR*g_dwDelayExpireMinutes; // // Additional message failure codes that should move to aqueue.idl. // #define MESSAGE_FAILURE_CAT (MESSAGE_FAILURE_BAD_PICKUP_DIR_FILE+1) //---[ eAQFailure ]------------------------------------------------------------- // // // Description: // Enum used to desribe failure scenarios that will require special handling // Hungarian: // eaqf // //----------------------------------------------------------------------------- typedef enum eAQFailure_ { AQ_FAILURE_CANNOT_NDR_UNRESOLVED_RECIPS = 0, AQ_FAILURE_PREROUTING_FAILED, AQ_FAILURE_PRECAT_RETRY, AQ_FAILURE_POSTCAT_EVENT, AQ_FAILURE_NO_RESOURCES, AQ_FAILURE_NDR_OF_DSN, AQ_FAILURE_NO_RECIPS, AQ_FAILURE_PENDING_DEFERRED_DELIVERY, AQ_FAILURE_PROCESSING_DEFERRED_DELIVERY, AQ_FAILURE_MSGREF_RETRY, AQ_FAILURE_FREE_TO_RESUSE, AQ_FAILURE_INTERNAL_ASYNCQ, AQ_FAILURE_NUM_SITUATIONS //always keep this as last } eAQFailure; _declspec(selectany) DWORD g_cTotalAQFailures = 0; _declspec(selectany) DWORD g_cAQFailureSituations = AQ_FAILURE_NUM_SITUATIONS; _declspec(selectany) DWORD g_rgcAQFailures[AQ_FAILURE_NUM_SITUATIONS] = {0}; //---[ CAQSvrInst ]------------------------------------------------------------ // // // Hungarian: aqinst, paqinst // // Legacy Hungarian: cmq, pcmq (from old CCatMsgQueue object) // // Provides an interface definition for the enqueuing/acking categorized // messages Also provides an interface for creating link queues. // // Only one of these objects exist per virtual server... it is used a // co-ordinating object used to handle an orderly shutdown. // //----------------------------------------------------------------------------- class CAQSvrInst : public CBaseObject, public CSyncShutdown, public IAdvQueue, public IAdvQueueConfig, public IAdvQueueAdmin, public IMailTransportRoutingEngine, public IMailTransportRouterReset, public IAdvQueueDomainType, public IAQNotify, public IMailTransportRouterSetLinkState, public IAQServerEvent { protected: DWORD m_dwSignature; LIST_ENTRY m_liVirtualServers; DWORD m_dwServerInstance; //Virtual server instance //Useful signatures that include flavor and verision information DWORD m_cbClasses; DWORD m_dwFlavorSignature; //Total counts used for counting totals of messages that have passed //through the system. Very useful for determing which component has //dropped a message after a stress run. LONG m_cTotalMsgsQueued; //Total # of messages on dest queues (after fanout) LONG m_cMsgsAcked; //Total # of messages that have been acknowledged LONG m_cMsgsAckedRetry; //Total # of messages acked with retry all LONG m_cMsgsDeliveredLocal; //Total # of messages delivered to local store DWORD m_cMsgsAckedRetryLocal; //Total # of messages msgs that have been ack'd retry //Current system state counters DWORD m_cCurrentMsgsSubmitted; //# total msgs in system DWORD m_cCurrentMsgsPendingCat; //# Msgs that have not be categorized DWORD m_cCurrentMsgsPendingRouting; //# Msgs that have not been routed. DWORD m_cCurrentMsgsPendingDelivery; //# Msgs pending remote delivery DWORD m_cCurrentMsgsPendingLocal; //# Msgs pending local delivery DWORD m_cCurrentMsgsPendingLocalRetry; //# Msgs pending local retries DWORD m_cCurrentMsgsPendingRetry; //# Msgs with unsuccessful attempts DWORD m_cCurrentQueueMsgInstances; //# of msgs instances pending //remote deliver (>= #msgs) DWORD m_cCurrentRemoteDestQueues; //# of DestMsgQueues created DWORD m_cCurrentRemoteNextHops; //# of Next Hop links created DWORD m_cCurrentRemoteNextHopsEnabled; //# of links that can have connections DWORD m_cCurrentRemoteNextHopsPendingRetry; //# of links pending retry DWORD m_cCurrentRemoteNextHopsPendingSchedule; //# of links pending schedule DWORD m_cCurrentRemoteNextHopsFrozenByAdmin; //# of links frozen by admin DWORD m_cTotalMsgsSubmitted; //# of messages submitted to AQ DWORD m_cTotalExternalMsgsSubmitted; //# of messages submitted to AQ externally DWORD m_cCurrentMsgsPendingSubmitEvent; //# of messages in submission event DWORD m_cCurrentMsgsPendingPreCatEvent; //# of messages in PreCat event DWORD m_cCurrentMsgsPendingPostCatEvent; //# of messages in PostCat event DWORD m_cDelayedDSNs; //# of DSN's that contain action:delayed DWORD m_cNDRs; //# of DSN's that contain action:failed DWORD m_cDeliveredDSNs; //# of DSN's that contain action:delivered DWORD m_cRelayedDSNs; //# of DSN's that contain action:relayed DWORD m_cExpandedDSNs; //# of DSN's that contain action:expanded DWORD m_cDMTRetries; DWORD m_cSupersededMsgs; DWORD m_cTotalMsgsTURNETRNDelivered; DWORD m_cTotalMsgsBadmailed; DWORD m_cCatMsgCalled; DWORD m_cCatCompletionCalled; DWORD m_cBadmailNoRecipients; DWORD m_cBadmailHopCountExceeded; DWORD m_cBadmailFailureGeneral; DWORD m_cBadmailBadPickupFile; DWORD m_cBadmailEvent; DWORD m_cBadmailNdrOfDsn; DWORD m_cTotalDSNFailures; DWORD m_cCurrentMsgsInLocalDelivery; DWORD m_cTotalResetRoutes; DWORD m_cCurrentPendingResetRoutes; DWORD m_cCurrentMsgsPendingSubmit; CAQMsgGuidList m_mglSupersedeIDs; CShareLockInst m_slPrivateData; //read/write lock for global config into CDomainMappingTable m_dmt; //ptr to domain mapping table CConnMgr *m_pConnMgr; CDomainConfigTable m_dct; ISMTPServer *m_pISMTPServer; ISMTPServerEx *m_pISMTPServerEx; ISMTPServerAsync *m_pISMTPServerAsync; HANDLE m_hCat; CAQQuickTime m_qtTime; //exposes interfaces for getting expire times CDSNGenerator m_dsnsink; //Global config data DWORD m_cMinMessagesPerConnection; DWORD m_cMaxMessagesPerConnection; DWORD m_dwConnectionWaitMilliseconds; //retry related DWORD m_dwFirstTierRetrySeconds; //Threshold failure retry interval DWORD m_dwDelayExpireMinutes; DWORD m_dwNDRExpireMinutes; DWORD m_dwLocalDelayExpireMinutes; DWORD m_dwLocalNDRExpireMinutes; //Counters used to for local and cat retry DWORD m_cLocalRetriesPending; DWORD m_cCatRetriesPending; DWORD m_cRoutingRetriesPending; DWORD m_cSubmitRetriesPending; DWORD m_dwInitMask; //used to keep track of who has been init'd IMessageRouter *m_pIMessageRouterDefault; CRefCountedString *m_prstrDefaultDomain; CRefCountedString *m_prstrBadMailDir; CRefCountedString *m_prstrCopyNDRTo; CRefCountedString *m_prstrServerFQDN; //DSN Options DWORD m_dwDSNOptions; DWORD m_dwDSNLanguageID; CAsyncAdminMailMsgQueue m_asyncqPreCatQueue; CAsyncAdminMsgRefQueue m_asyncqPreLocalDeliveryQueue; CAsyncAdminMailMsgQueue m_asyncqPostDSNQueue; CAsyncAdminMailMsgQueue m_asyncqPreRoutingQueue; CAsyncAdminMailMsgQueue m_asyncqPreSubmissionQueue; CDebugCountdown m_dbgcnt; //Flags used to describe what has been initialized IMailTransportRouterReset *m_pIRouterReset; //pointer to router reset implementation //Queue and counter for deferred delivery CAQDeferredDeliveryQueue m_defq; DWORD m_cCurrentMsgsPendingDeferredDelivery; //Failed Msg Queue CFailedMsgQueue m_fmq; DWORD m_cCurrentResourceFailedMsgsPendingRetry; //Work queue used to do async work items CAsyncWorkQueue m_aqwWorkQueue; BOOL m_fMailMsgReportsNumHandles; typedef enum _eCMQInitFlags { CMQ_INIT_OK = 0x80000000, CMQ_INIT_DMT = 0x00000001, CMQ_INIT_DCT = 0x00000002, CMQ_INIT_CONMGR = 0x00000004, CMQ_INIT_LINKQ = 0x00000008, CMQ_INIT_DSN = 0x00000010, CMQ_INIT_PRECATQ = 0x00000020, CMQ_INIT_PRELOCQ = 0x00000040, CMQ_INIT_POSTDSNQ = 0x00000080, CMQ_INIT_ROUTER_RESET = 0x00000100, CMQ_INIT_ROUTINGQ = 0x00000200, CMQ_INIT_WORKQ = 0x00000400, CMQ_INIT_SUBMISSIONQ = 0x00000800, CMQ_INIT_MSGQ = 0x80000000, } eCMQInitFlags; CSMTPSeoMgr m_CSMTPSeoMgr; public: CAQSvrInst(DWORD dwServerInstance, ISMTPServer *pISMTPServer); ~CAQSvrInst(); HRESULT HrInitialize( IN LPSTR szUserName = NULL, IN LPSTR szDomainName = NULL, IN LPSTR szPassword = NULL, IN PSRVFN pServiceStatusFn = NULL, IN PVOID pvServiceContext = NULL); HRESULT HrDeinitialize(); //publicly accessable member values //MUST wrap in fTryShutdownLock - ShutdownUnlock CDomainMappingTable *pdmtGetDMT() {AssertShutdownLockAquired();return &m_dmt;}; CAQMsgGuidList *pmglGetMsgGuidList() {AssertShutdownLockAquired(); return &m_mglSupersedeIDs;}; HRESULT HrGetIConnectionManager(OUT IConnectionManager **ppIConnectionManager); //Public Methods exposed through events (or some other mechanism) // This function queues a categorized message for remote/local delivery BOOL fRouteAndQueueMsg(IN IMailMsgProperties *pIMailMsg); //Acknowledge the message ref. //There should be one Ack for every dequeue from a link. HRESULT HrAckMsg(MessageAck *pMsgAck, BOOL fLocal = FALSE); //methods to (un)map domain names to ids. HRESULT HrGetDomainMapping( IN LPSTR szDomainName, //Domain name OUT CDomainMapping *pdmap); //resulting domain mapping HRESULT HrGetDomainName( IN CDomainMapping *pdmap, //Domain mapping OUT LPSTR *pszDomainName); //resolved domain name //Pass notifications off to Connection Manager HRESULT HrNotify(IN CAQStats *paqstats, BOOL fAdd); //Expose ability to get internal Domain Info to internal components HRESULT HrGetInternalDomainInfo(IN DWORD cbDomainNameLength, IN LPSTR szDomainName, OUT CInternalDomainInfo **ppDomainInfo); HRESULT HrGetDefaultDomainInfo(OUT CInternalDomainInfo **ppDomainInfo); //Get Domain Entry from DMT HRESULT HrGetDomainEntry(IN DWORD cbDomainNameLength, IN LPSTR szDomainName, OUT CDomainEntry **ppdentry); // jstamerj 980607 21:41:25: The completion routine of the // submission event trigger HRESULT SubmissionEventCompletion( HRESULT hrStatus, PEVENTPARAMS_SUBMISSION pParams); // jstamerj 1998/11/24 19:53:24: Fire off the PreCat event VOID TriggerPreCategorizeEvent(IN IMailMsgProperties *pIMailMsgProperties); // jstamerj 1998/11/24 19:54:23: Completion routine of the pre-cat event HRESULT PreCatEventCompletion(IN HRESULT hrStatus, IN PEVENTPARAMS_PRECATEGORIZE pParams); // jstamerj 980610 12:24:29: Called from HrPreCatEventCompletion HRESULT SubmitMessageToCategorizer(IN IMailMsgProperties *pIMailMsgProperties); // jstamerj 980616 22:06:45: Called from CatCompletion void TriggerPostCategorizeEvent(IUnknown *pIMsg, IUnknown **rgpIMsg); // jstamerj 980616 22:07:18: triggers a post-cat event for one message HRESULT TriggerPostCategorizeEventOneMsg(IUnknown *pIMsg); // jstamerj 980616 22:07:54: Handles post-cat event completions HRESULT PostCategorizationEventCompletion(HRESULT hrStatus, PEVENTPARAMS_POSTCATEGORIZE pParams); // 11/17/98 - MikeSwa added for CDO badmail/abort delivery // returns S_FALSE if message has been completely handled. HRESULT SetNextMsgStatus(IN DWORD dwCurrentStatus, IN IMailMsgProperties *pIMailMsgProperties); //Called by async completion to PreCat Queue BOOL fPreCatQueueCompletion(IMailMsgProperties *pIMailMsgProperties); //Called by async completion to PreCat Queue BOOL fPreLocalDeliveryQueueCompletion(CMsgRef *pmsgref); //Used to restart async queues after failures void AsyncQueueRetry(DWORD dwQueueID); //Called to Set message submission time duing SubmitMessage and HrInternalSubmitMessage HRESULT HrSetSubmissionTimeIfNecessary(IMailMsgProperties *pIMailMsgProperties); //Called to calculate expire times for messages that are not stamped (most messages) void CalcExpireTimeNDR(FILETIME ftSubmission, BOOL fLocal, FILETIME *pftExpire); void CalcExpireTimeDelay(FILETIME ftSubmission, BOOL fLocal, FILETIME *pftExpire); //API to keep counters in sync inline DWORD cIncMsgsInSystem(); //returns total of all virtual servers inline void DecMsgsInSystem(BOOL fWasRetriedRemote = FALSE, BOOL fWasRemote = FALSE, BOOL fWasRetriedLocal = FALSE); //Called by Msgref on first message retry inline void IncRetryCount(BOOL fLocal); //Called by DestMsgQueue to describe message fanout inline void IncQueueMsgInstances(); inline void DecQueueMsgInstances(); //Used to keep track of the number of queues/next hops inline void IncDestQueueCount(); inline void DecDestQueueCount(); inline DWORD cGetDestQueueCount(); inline void IncNextHopCount(); inline void DecNextHopCount(); //Called by functions walk pre-local queue for NDRs inline void DecPendingLocal(); inline void DecPendingSubmit() {InterlockedDecrement((PLONG)&m_cCurrentMsgsPendingSubmit);}; inline void DecPendingCat() {InterlockedDecrement((PLONG)&m_cCurrentMsgsPendingCat);}; inline void DecPendingRouting() {InterlockedDecrement((PLONG)&m_cCurrentMsgsPendingRouting);}; inline void IncTURNETRNDelivered(); //aszafer 1/28/00 //used to decide start/stop throttling handles DWORD cCountMsgsForHandleThrottling(IN IMailMsgProperties *pIMailMsgProperties); //Functions to call into the specifc hash tables to iterate over subdomains // HRESULT HrIterateDMTSubDomains(IN LPSTR szDomainName, IN DWORD cbDomainNameLength, IN DOMAIN_ITR_FN pfn, IN PVOID pvContext) ; HRESULT HrIterateDCTSubDomains(IN LPSTR szDomainName, IN DWORD cbDomainNameLength, IN DOMAIN_ITR_FN pfn, IN PVOID pvContext); //Calls that allow access to time objects inline void GetExpireTime( IN DWORD cMinutesExpireTime, IN OUT FILETIME *pftExpireTime, IN OUT DWORD *pdwExpireContext); //if non-zero, will use last time inline BOOL fInPast(IN FILETIME *pftExpireTime, IN OUT DWORD *pdwExpireContext); HRESULT HrTriggerDSNGenerationEvent(CDSNParams *pdsnparams, BOOL fHasRoutingLock); HRESULT HrNDRUnresolvedRecipients(IMailMsgProperties *pIMailMsgProperties, IMailMsgRecipients *pIMailMsgRecipients); //friend functions that can be used as completion functions friend HRESULT CatCompletion(HRESULT hrCatResult, PVOID pContext, IUnknown *pIMsg, IUnknown **rgpIMsg); //Expose server start/stop hint functions inline VOID ServerStartHintFunction(); inline VOID ServerStopHintFunction(); //function used to handle badmail void HandleBadMail(IN IMailMsgProperties *pIMailMsgProperties, IN BOOL fUseIMailMsgProperties, IN LPSTR szFileName, IN HRESULT hrReason, BOOL fHasRoutingLock); //Function to handle some sort of system failure that would cause //messages/data to be lost if unhandled void HandleAQFailure(eAQFailure eaqfFailureSituation, HRESULT hr, IMailMsgProperties *pIMailMsgProperties); //Stub call for logging an event void LogAQEvent(HRESULT hrEventReason, CMsgRef *pmsgref, IMailMsgProperties *pIMailMsgProperties, LPSTR szFileName); //Routing lock should be grabbed before accessing queues (after shutdown) void RoutingShareLock() {m_slPrivateData.ShareLock();}; BOOL fTryRoutingShareLock() {return m_slPrivateData.TryShareLock();}; void RoutingShareUnlock() {m_slPrivateData.ShareUnlock();}; HRESULT SetCallbackTime(IN PSRVFN pCallbackFn, IN PVOID pvContext, IN DWORD dwCallbackMinutes); HRESULT SetCallbackTime(IN PSRVFN pCallbackFn, IN PVOID pvContext, IN FILETIME *pft); void DecPendingDeferred() {InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingDeferredDelivery);}; void DecPendingFailed() {InterlockedDecrement((PLONG) &m_cCurrentResourceFailedMsgsPendingRetry);}; void QueueMsgForLocalDelivery(CMsgRef *pmsgref, BOOL fLocalLink); HRESULT HrInternalSubmitMessage(IMailMsgProperties *pIMailMsgProperties); //Get string for default domain CRefCountedString *prstrGetDefaultDomain(); //Completion Function called by MsgCat static HRESULT CatCompletion(HRESULT hrCatResult, PVOID pContext, IUnknown *pImsg, IUnknown **rgpImsg); #define AQLD_SIG 'LDAQ' // completion object called by local delivery class CAQLocalDeliveryNotify : public IMailMsgNotify, public CBaseObject { public: CAQLocalDeliveryNotify(PVOID pContext, CMsgRef *pmsgref) { TraceFunctEnter("CAQLocalDeliveryNotify::CAQLocalDeliveryNotify"); m_hr = E_FAIL; m_fCalledCompletion = FALSE; m_pContext = pContext; _ASSERT(pmsgref); m_pmsgref = pmsgref; m_pmsgref->AddRef(); m_pIMsg = pmsgref ? pmsgref->pimsgGetIMsg() : NULL; ZeroMemory(&m_msgack, sizeof(MessageAck)); m_msgack.dwMsgStatus = MESSAGE_STATUS_ALL_DELIVERED; m_msgack.pvMsgContext = (DWORD *) &m_dcntxtLocal; DebugTrace(0, "new(this=0x%x)\n", this); } ~CAQLocalDeliveryNotify() { TraceFunctEnter("CAQLocalDeliveryNotify::~CAQLocalDeliveryNotify"); DebugTrace(0, "delete(this=0x%x)\n", this); if (m_pIMsg) { m_pIMsg->Release(); m_pIMsg = NULL; } if (m_pmsgref) { m_pmsgref->Release(); m_pmsgref = NULL; } } // allocators void *operator new(size_t stIgnored) { return s_pool.Alloc(); } void operator delete(void *p, size_t size) { s_pool.Free(p); } // IMailMsgNotify HRESULT __stdcall Notify(HRESULT hr) { m_hr = hr; CAQSvrInst::LDCompletion(m_hr, m_pContext, m_pmsgref, this); m_fCalledCompletion = TRUE; return S_OK; } // IUnknown HRESULT __stdcall QueryInterface( const IID& iid, VOID** ppv ) { if ( iid == IID_IUnknown ) { *ppv = static_cast(this); } else if ( iid == IID_IMailMsgNotify ) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast(*ppv)->AddRef(); return S_OK; } STDMETHOD_(ULONG, AddRef)(void) { TraceFunctEnter("CAQLocalDeliveryNotify::AddRef"); DebugTrace(0, "add(this=0x%x)\n", this); TraceFunctLeave(); return CBaseObject::AddRef(); }; STDMETHOD_(ULONG, Release)(void) { TraceFunctEnter("CAQLocalDeliveryNotify::Release"); DebugTrace(0, "rel(this=0x%x)\n", this); TraceFunctLeave(); return CBaseObject::Release(); }; // accessors CDeliveryContext *pdcntxtGetDeliveryContext() { return &m_dcntxtLocal; } MessageAck *pmsgackGetMsgAck() { return &m_msgack; } IMailMsgProperties *pimsgGetIMsg() { return m_pIMsg; } CMsgRef *pmsgrefGetMsgRef() { return m_pmsgref; } BOOL fNotCalledCompletion() { return !m_fCalledCompletion; } private: IMailMsgProperties *m_pIMsg; HRESULT m_hr; PVOID m_pContext; CMsgRef *m_pmsgref; CDeliveryContext m_dcntxtLocal; MessageAck m_msgack; BOOL m_fCalledCompletion; public: static CPool s_pool; }; void UpdateLDCounters(CMsgRef *pmsgref); // local delivery completion function static void LDCompletion(HRESULT hrLDResult, PVOID pContext, CMsgRef *pmsgref, CAQLocalDeliveryNotify *pLDNotify); //Handle the details of retrying after local delivery failure void HandleLocalRetry(CMsgRef *pmsgref); //Handles details of post-cat DSN generation void HandleCatFailure(IUnknown *pIUnknown, HRESULT hrCatResult); //Handle the details of retrying after cat failure void HandleCatRetryOneMessage(IUnknown *pIUnknown); HRESULT HrGetLocalQueueAdminQueue(IQueueAdminQueue **ppIQueueAdminQueue); HRESULT HrQueueFromQueueID(QUEUELINK_ID *pqlQueueId, IQueueAdminQueue **ppIQueueAdminQueue); HRESULT HrLinkFromLinkID(QUEUELINK_ID *pqlLinkID, IQueueAdminLink **ppIQueueAdminLink); BOOL fIsLocalQueueAdminAction(IQueueAdminAction *pIQueueAdminAction); inline HRESULT HrQueueWorkItem(PVOID pvData, PASYNC_WORK_QUEUE_FN pfnCompletion); static BOOL fResetRoutesNextHopCompletion(PVOID pvThis, DWORD dwStatus); static BOOL fPreSubmissionQueueCompletionWrapper( IMailMsgProperties *pIMailMsgProperties, PVOID pvContext); BOOL fShouldRetryMessage(IMailMsgProperties *pIMailMsgProperties, BOOL fShouldBounceUsageIfRetry = TRUE); VOID ScheduleInternalRetry(DWORD dwLinkType); STDMETHOD(TriggerServerEvent) ( DWORD dwEventID, PVOID pvContext) { return m_CSMTPSeoMgr.HrTriggerServerEvent(dwEventID, pvContext); } void LogResetRouteEvent( DWORD dwObainLock, DWORD dwWaitLock, DWORD dwQueue); HRESULT HrInternalQueueFromQueueID(QUEUELINK_ID *pqlQueueId, IQueueAdminQueue **ppIQueueAdminQueue); // DSN Submission methods HRESULT HrAllocBoundMessage( OUT IMailMsgProperties **ppMsg, OUT PFIO_CONTEXT *phContext); HRESULT HrSubmitDSN( IN CDSNParams *pdsnparams, IN DWORD dwDSNAction, IN DWORD cRecipsDSNd, IN IMailMsgProperties *pDSNMsg); //Routing interface used internal to AQ components public: //Fires MAIL_TRANSPORT_ON_GET_ROUTER_FOR_MESSAGE_EVENT HRESULT HrTriggerGetMessageRouter( IN IMailMsgProperties *pIMailMsg, OUT IMessageRouter **pIMessageRouter); HRESULT 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 = 0xffffffff, IN HMODULE hModule = NULL); private: HRESULT HrTriggerInitRouter(); //IUnknown public: STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj); STDMETHOD_(ULONG, AddRef)(void) {return CBaseObject::AddRef();}; STDMETHOD_(ULONG, Release)(void) {return CBaseObject::Release();}; //IAdvQueue public: STDMETHOD(SubmitMessage)(IN IMailMsgProperties *pIMailMsgProperties); STDMETHOD(HandleFailedMessage)(IN IMailMsgProperties *pIMailMsgProperties, IN BOOL fUseIMailMsgProperties, IN LPSTR szFileName, IN DWORD dwFailureReason, IN HRESULT hrFailureCode); //IAdvQueueConfig public: STDMETHOD(SetConfigInfo)(IN AQConfigInfo *pAQConfigInfo); STDMETHOD(SetDomainInfo)(IN DomainInfo *pDomainInfo); STDMETHOD(GetDomainInfo)(IN DWORD cbDomainNameLength, IN CHAR szDomainName[], IN OUT DomainInfo *pDomainInfo, OUT DWORD **ppvDomainContext); STDMETHOD(ReleaseDomainInfo)(IN DWORD *pvDomainContext); STDMETHOD(GetPerfCounters)(OUT AQPerfCounters *pAQPerfCounters, OUT CATPERFBLOCK *pCatPerfCounters); STDMETHOD(ResetPerfCounters)(); STDMETHOD(StartConfigUpdate)(); STDMETHOD(FinishConfigUpdate)(); //IMailTransportRoutingEngine public: STDMETHOD(GetMessageRouter)( IN IMailMsgProperties *pIMailMsg, IN IMessageRouter *pICurrentMessageRouter, OUT IMessageRouter **ppIMessageRouter); //IMailTransportRouterReset public: STDMETHOD(ResetRoutes)( IN DWORD dwResetType); //IAdvQueueDomainType public: STDMETHOD(GetDomainInfoFlags)( IN LPSTR szDomainName, DWORD *pdwDomainInfoFlags); // IAdvQueueAdmin public: STDMETHOD(ApplyActionToLinks)( LINK_ACTION laAction); STDMETHOD(ApplyActionToMessages)( QUEUELINK_ID *pqlQueueLinkId, MESSAGE_FILTER *pmfMessageFilter, MESSAGE_ACTION maMessageAction, DWORD *pcMsgs); STDMETHOD(GetQueueInfo)( QUEUELINK_ID *pqlQueueId, QUEUE_INFO *pqiQueueInfo); STDMETHOD(GetLinkInfo)( QUEUELINK_ID *pqlLinkId, LINK_INFO *pliLinkInfo, HRESULT *phrLinkDiagnostic); STDMETHOD(SetLinkState)( QUEUELINK_ID *pqlLinkId, LINK_ACTION la); STDMETHOD(GetLinkIDs)( DWORD *pcLinks, QUEUELINK_ID *rgLinks); STDMETHOD(GetQueueIDs)( QUEUELINK_ID *pqlLinkId, DWORD *pcQueues, QUEUELINK_ID *rgQueues); STDMETHOD(GetMessageProperties)( QUEUELINK_ID *pqlQueueLinkId, MESSAGE_ENUM_FILTER *pmfMessageEnumFilter, DWORD *pcMsgs, MESSAGE_INFO *rgMsgs); STDMETHOD(QuerySupportedActions)( QUEUELINK_ID *pqlQueueLinkId, DWORD *pdwSupportedActions, DWORD *pdwSupportedFilterFlags); public: //IMailTransportRouterSetLinkState STDMETHOD(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); }; //*** inline counter functions //---[ CAQSvrInst::cIncMsgsInSystem ]---------------------------------------- // // // Description: // Used to increment the global and virtual server msg counts. Returns // the global count for resource management purposes. // Parameters: // - // Returns: // DWORD - Global # of Msgs in system // //----------------------------------------------------------------------------- DWORD CAQSvrInst::cIncMsgsInSystem() { InterlockedIncrement((PLONG) &m_cCurrentMsgsSubmitted); return (InterlockedIncrement((PLONG) &g_cIMsgInSystem)); }; //---[ CAQSvrInst::DecMsgsInSystem ]----------------------------------------- // // // Description: // Decrements the global and virtual server message counts. Also // decrements the pending retry count if needed. // Parameters: // fWasRetriedRemote - TRUE if msg was retried remotely and retry count needs // to be decremented. // fWasRemote - TRUE if message was being delivered remotely // fWasRetriedLocal - TRUE if counted towards m_cCurrentMsgsPendingLocalRetry // Returns: // - // //----------------------------------------------------------------------------- void CAQSvrInst::DecMsgsInSystem(BOOL fWasRetriedRemote, BOOL fWasRemote, BOOL fWasRetriedLocal) { InterlockedDecrement((PLONG) &g_cIMsgInSystem); InterlockedDecrement((PLONG) &m_cCurrentMsgsSubmitted); if (fWasRetriedRemote) InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingRetry); if (fWasRemote) InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingDelivery); if (fWasRetriedLocal) InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingLocalRetry); }; //---[ CAQSvrInst::IncRetryCount ]------------------------------------------- // // // Description: // Used by MsgRef the first time a Message is ack'd with a non-success // code. // Parameters: // BOOL fLocal TRUE if message is local // Returns: // - // //----------------------------------------------------------------------------- void CAQSvrInst::IncRetryCount(BOOL fLocal) { if (fLocal) InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingLocalRetry); else InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingRetry); }; //---[ CAQSvrInst::[Inc|Dec]QueueMsgInstances ]------------------------------ // // // Description: // Increments/decrements a count of the total number of message instances // queued for remote delivery. Because a message may be put in more than // one queue, the steady state of this count will be at least as large as // the number of messages. However, this count reflects messages that // are currently on the queues and does *not* count messages that are // currently being attempted by SMTP (which m_cCurrentMsgsPendingDelivery) // *does* count. // // Used by DestMsgQueues. // Parameters: // - // Returns: // - // //----------------------------------------------------------------------------- void CAQSvrInst::IncQueueMsgInstances() { InterlockedIncrement((PLONG) &m_cCurrentQueueMsgInstances); }; void CAQSvrInst::DecQueueMsgInstances() { InterlockedDecrement((PLONG) &m_cCurrentQueueMsgInstances); }; //---[ Queue/NextHop Counter API ]--------------------------------------------- // // // Description: // Used to increment/decrement Queue and NextHop counters // Parameters: // // Returns: // // //----------------------------------------------------------------------------- void CAQSvrInst::IncDestQueueCount() { InterlockedIncrement((PLONG) &m_cCurrentRemoteDestQueues); }; void CAQSvrInst::DecDestQueueCount() { InterlockedDecrement((PLONG) &m_cCurrentRemoteDestQueues); }; DWORD CAQSvrInst::cGetDestQueueCount() { return m_cCurrentRemoteDestQueues; } void CAQSvrInst::IncNextHopCount() { InterlockedIncrement((PLONG) &m_cCurrentRemoteNextHops); }; void CAQSvrInst::DecNextHopCount() { InterlockedDecrement((PLONG) &m_cCurrentRemoteNextHops); }; //---[ CAQSvrInst::DecPendingLocal ]----------------------------------------- // // // Description: // Called by function walking pre-local delivery queue when a message // is being expired. // Parameters: // - // Returns: // - // History: // 8/14/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CAQSvrInst::DecPendingLocal() { _ASSERT(CATMSGQ_SIG == m_dwSignature); InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingLocal); }; //---[ CAQSvrInst::IncTURNETRNDelivered ]-------------------------------------- // // // Description: // Used to keep track of the # of TURN/ETRN messages delivered. // Parameters: // - // Returns: // - // History: // 10/27/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CAQSvrInst::IncTURNETRNDelivered() { InterlockedIncrement((PLONG) &m_cTotalMsgsTURNETRNDelivered); } //---[ CAQSvrInst::GetExpireTime ]------------------------------------------- // // // Description: // Get the expriation time for cMinutesExpireTime from now. // Parameters: // IN cMinutesExpireTime # of minutes in future to set time // IN OUT pftExpireTime Filetime to store new expire time // IN OUT pdwExpireContext If non-zero will use the same tick count // as previous calls (saves call to GetTickCount) // Returns: // - // History: // 7/11/98 - MikeSwa Created // //----------------------------------------------------------------------------- void CAQSvrInst::GetExpireTime( IN DWORD cMinutesExpireTime, IN OUT FILETIME *pftExpireTime, IN OUT DWORD *pdwExpireContext) { m_qtTime.GetExpireTime(cMinutesExpireTime, pftExpireTime, pdwExpireContext); } //---[ CAQSvrInst::fInPast ]------------------------------------------------- // // // Description: // Determines if a given file time has already happened // Parameters: // IN pftExpireTime FILETIME with expiration // IN OUT pdwExpireContext If non-zero will use the same tick count // as previous calls (saves call to GetTickCount) // Returns: // TRUE if expire time is in the past // FALSE if expire time is in the future // History: // 7/11/98 - MikeSwa Created // Note: // You should NOT use the same context used to get the FILETIME, because // it will always return FALSE // //----------------------------------------------------------------------------- BOOL CAQSvrInst::fInPast(IN FILETIME *pftExpireTime, IN OUT DWORD *pdwExpireContext) { return m_qtTime.fInPast(pftExpireTime, pdwExpireContext); } //---[ ServerStartHintFunction & ServerStartHintFunction ]--------------------- // // // Description: // Functions for telling the Service control manager that we are // starting/stopping the service. // // These functions are often called by functions that have been passed // the CAQSvrInst ptr as a PVOID context, so it makes sense to check // and assert on our signature here. // Parameters: // - // Returns: // - // History: // 7/22/98 - MikeSwa Created // //----------------------------------------------------------------------------- VOID CAQSvrInst::ServerStartHintFunction() { _ASSERT(CATMSGQ_SIG == m_dwSignature); if (m_pISMTPServer) m_pISMTPServer->ServerStartHintFunction(); } VOID CAQSvrInst::ServerStopHintFunction() { _ASSERT(CATMSGQ_SIG == m_dwSignature); if (fShutdownSignaled()) { m_dbgcnt.ResetCountdown(); //Only call stop hint if shutdown has been signalled if (m_pISMTPServer) m_pISMTPServer->ServerStopHintFunction(); } } //---[ CAQSvrInst::HrQueueWorkItem ]------------------------------------------- // // // Description: // Thin wrapper to queue item to async work queue // Parameters: // pvData Data to pass to completion function // pfnCompletion Completion function // Returns: // S_OK on success // failure code from CAsyncWorkQueue // History: // 3/9/99 - MikeSwa Created // 7/7/99 - MikeSwa - will work during shutdown to allow multithreaded // shutdown work. // //----------------------------------------------------------------------------- HRESULT CAQSvrInst::HrQueueWorkItem(PVOID pvData, PASYNC_WORK_QUEUE_FN pfnCompletion) { return m_aqwWorkQueue.HrQueueWorkItem(pvData, pfnCompletion); } #endif // __AQINST_H__