//----------------------------------------------------------------------------- // // // File: msgref.h // // Description: Definition of Queueing MsgRef object // // Author: mikeswa // // Copyright (C) 1997 Microsoft Corporation // //----------------------------------------------------------------------------- #ifndef _MSGREF_H_ #define _MSGREF_H_ #include "cmt.h" #include "baseobj.h" #include "bitmap.h" #include "domain.h" #include "aqueue.h" #include "aqroute.h" #include "qwiklist.h" #include "dcontext.h" #include #include "msgguid.h" #include "aqutil.h" #include "aqadmsvr.h" #include #include class CDestMsgQueue; class CAQSvrInst; class CAQStats; // {34E2DCC8-C91A-11d2-A6B1-00C04FA3490A} static const GUID IID_CMsgRef = { 0x34e2dcc8, 0xc91a, 0x11d2, { 0xa6, 0xb1, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } }; //FLAGS that say which IMsg data we care about #define MSGREF_VALID_FLAGS (eMsgSize | eMsgArriveTime | eMsgPriority) //MsgRef signature #define MSGREF_SIG 'feRM' //max number of domains for CPool allocator #define MSGREF_STANDARD_DOMAINS 12 // // Make sure the "standard" CPool size is // - large enough to accommidate any padding in the CPoolMsgRef struct // - QWORD alligned so 64-bit machines are happy // #define MSGREF_STANDARD_CPOOL_SIZE \ (((sizeof(CPoolMsgRef) - sizeof(CMsgRef) + \ CMsgRef::size(MSGREF_STANDARD_DOMAINS)) + 0x10) & ~0xF) //A Note about bitmaps //The Recips bitmap represents the responsible recipients for a destination, //or message request. 1 means that the tansport should attempt to deliver for //this connection. #ifdef DEBUG _declspec(selectany) DWORD g_cDbgMsgRefsCpoolAllocated = 0; _declspec(selectany) DWORD g_cDbgMsgRefsExchmemAllocated = 0; _declspec(selectany) DWORD g_cDbgMsgRefsCpoolFailed = 0; _declspec(selectany) DWORD g_cDbgMsgIdHashFailures = 0; _declspec(selectany) DWORD g_cDbgMsgRefsPendingRetryOnDelete = 0; #endif //DEBUG //define reserved message status codes ... should be in MESSAGE_STATUS_RESERVED #define MESSAGE_STATUS_LOCAL_DELIVERY 0x80000000 #define MESSAGE_STATUS_DROP_DIRECTORY 0x40000000 //---[ CMsgRef ]--------------------------------------------------------------- // // // Hungarian: msgref, pmsgref // // Persistable message reference object used throughout advanced queuing //----------------------------------------------------------------------------- class CMsgRef : public IUnknown, public CBaseObject { public: static CPool s_MsgRefPool; //override the new operator void * operator new (size_t stIgnored, unsigned int cDomains); //Number of domains in message void * operator new (size_t stIgnored); //should not be used void operator delete(void *p, size_t size); CMsgRef(DWORD cDomains, IMailMsgQueueMgmt *pIMailMsg, IMailMsgProperties *pIMailMsgProperties, CAQSvrInst *paqinst, DWORD dwMessageType, GUID guidMessageRouter); ~CMsgRef(); //perform initialization and determine the queues for this message. //A NULL Queue signifies local delivery HRESULT HrInitialize( IN IMailMsgRecipients *pIRecipList, //recipient interface for msg IN IMessageRouter *pIMessageRouter, //Router for this message IN DWORD dwMessageType, OUT DWORD *pcLocalRecips, OUT DWORD *pcRemoteRecips, OUT DWORD *pcQueues, //# of queues for this message OUT CDestMsgQueue **rgpdmqQueues); //array of queue ptrs //Get the effective priority of the message inline EffectivePriority PriGetPriority() {return (EffectivePriority) (MSGREF_PRI_MASK & m_dwDataFlags);}; inline IMailMsgProperties *pimsgGetIMsg() {Assert(m_pIMailMsgProperties);m_pIMailMsgProperties->AddRef();return m_pIMailMsgProperties;}; inline BOOL fIsMyMailMsg(IMailMsgProperties *pIMailMsgProperties) {return (pIMailMsgProperties == m_pIMailMsgProperties);}; //get the size of the message content inline DWORD dwGetMsgSize() {return(m_cbMsgSize);}; inline DWORD cGetNumDomains() {return(m_cDomains);}; //get the size of the class (including all extras) inline size_t size() {return (size(m_cDomains));}; //Return the delivery context needed for delivery over a given link //Do NOT free prgdwRecips... it will disappear with the AckMessage HRESULT HrPrepareDelivery( IN BOOL fLocal, //prepare for local domains as well IN BOOL fDelayDSN, //Check/Set DelayDSN bitmap IN CQuickList *pqlstQueues, //array of DestMsgQueues IN CDestMsgRetryQueue* pdmrq, //retry interface for message IN OUT CDeliveryContext *pdcntxt, //context that must be returned on Ack OUT DWORD *pcRecips, //#of recips to deliver OUT DWORD **prgdwRecips); //array of recip indexes //Acknowledge (non)delivery of a msg HRESULT HrAckMessage( IN CDeliveryContext *pdcntxt, //Delivery context of message IN MessageAck *pMsgAck); //Delivery status of message CAQMessageType *paqmtGetMessageType() {return &m_aqmtMessageType;}; //size that can be used by new operator static inline size_t size(DWORD cDomains) { return (sizeof(CMsgRef) + (cDomains-1)*sizeof(CDestMsgQueue *) + //cDomains dmq ptrs (cDomains + 3) * (CMsgBitMap::size(cDomains)) + //bitmaps (cDomains*2) * sizeof(DWORD)); }; //Send Delay or NDR DSN's if the message has expired HRESULT HrSendDelayOrNDR( IN DWORD dwDSNOptions, //Flags for DSN generation IN CQuickList *pqlstQueues, //list of DestMsgQueues IN HRESULT hrStatus, //Status to Pass to DSN generation OUT DWORD *pdwDSNFlags); //description of what the result was //bit flag return values for HrSendDelayOrNDR enum { MSGREF_DSN_SENT_NDR = 0x00000001, //Message NDR-expired and NDR was sent MSGREF_DSN_SENT_DELAY = 0x00000002, //Message Delay-expired and Delay DSN was sent MSGREF_HANDLED = 0x00000004, //Message has been completely handled MSGREF_HAS_NOT_EXPIRED = 0x00000008, //Message younger than it's exipiration dates }; //bit flag options for DSN generation enum { MSGREF_DSN_LOCAL_QUEUE = 0x00000001, //This is for a local queue MSGREF_DSN_SEND_DELAY = 0x00000002, //Allow Delay DSNs MSGREF_DSN_CHECK_IF_STALE = 0x00000004, //Force open handle to check if stale MSGREF_DSN_HAS_ROUTING_LOCK = 0x80000000, //This thread holds the routing lock }; void SupersedeMsg(); BOOL fMatchesQueueAdminFilter(CAQAdminMessageFilter *paqmf); HRESULT HrGetQueueAdminMsgInfo(MESSAGE_INFO *pMsgInfo, IQueueAdminAction *pIQueueAdminAction); HRESULT HrRemoveMessageFromQueue(CDestMsgQueue *pdmq); HRESULT HrQueueAdminNDRMessage(CDestMsgQueue *pdmq); void GlobalFreezeMessage(); void GlobalThawMessage(); BOOL fIsMsgFrozen() {return(MSGREF_MSG_FROZEN & m_dwDataFlags);}; FILETIME *pftGetAge() {return &m_ftQueueEntry;}; void RetryOnDelete(); void PrepareForShutdown() {ReleaseMailMsg(FALSE);}; //Checks if the message can be retried (the backing storage may //have been deleted). BOOL fShouldRetry(); void GetStatsForMsg(IN OUT CAQStats *paqstat); void MarkQueueAsLocal(IN CDestMsgQueue *pdmq); void CountMessageInRemoteTotals(); // // Determines if a message is a "problem" message. Currently this is based purely on the number // of failures per message, but we may wish to add more logic at a later time. // BOOL fIsProblemMsg() {return (g_cMsgFailuresBeforeMarkingMsgAsProblem && (m_cTimesRetried >= g_cMsgFailuresBeforeMarkingMsgAsProblem));}; public: //IUnknown STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj); STDMETHOD_(ULONG, AddRef)(void) {return CBaseObject::AddRef();}; STDMETHOD_(ULONG, Release)(void) {return CBaseObject::Release();}; protected: DWORD m_dwSignature; CAQSvrInst *m_paqinst; DWORD m_dwDataFlags; //private data flags DWORD m_cbMsgSize; //Size of message content in bytes FILETIME m_ftQueueEntry; //time that message was enqueued FILETIME m_ftLocalExpireDelay; FILETIME m_ftLocalExpireNDR; FILETIME m_ftRemoteExpireDelay; FILETIME m_ftRemoteExpireNDR; CAQMsgGuidListEntry *m_pmgle; DWORD m_cDomains; //number of DOMAINS this message is destined for CAQMessageType m_aqmtMessageType; //Message type IMailMsgQueueMgmt *m_pIMailMsgQM; //Reference to message Queue mgmt IMailMsgProperties *m_pIMailMsgProperties; //reference to message IMailMsgRecipients *m_pIMailMsgRecipients; DWORD m_cTimesRetried; DWORD m_dwMsgIdHash; volatile DWORD m_cInternalUsageCount; CDestMsgQueue *m_rgpdmqDomains[1]; //Actual size is m_cDomains static inline BOOL fIsStandardSize(DWORD cDomains) { return (MSGREF_STANDARD_DOMAINS >= cDomains); } HRESULT HrOneTimeInit(); HRESULT HrPrvRetryMessage(CDeliveryContext *pdcntxt, DWORD dwMsgStatus); HRESULT HrPromoteMessageStatusToMailMsg(CDeliveryContext *pdcntxt, MessageAck *pMsgAck); HRESULT HrUpdateExtendedStatus(DWORD cbCurrentStatus, LPSTR szCurrentStatus, LPSTR *pszNewStatus); //private methods to get at "hidden" data. CMsgBitMap *pmbmapGetDomainBitmap(DWORD iDomain); CMsgBitMap *pmbmapGetHandled(); CMsgBitMap *pmbmapGetPending(); CMsgBitMap *pmbmapGetDSN(); DWORD *pdwGetRecipIndexStart(); void SetRecipIndex(DWORD iDomain, DWORD iLowRecip, DWORD iHighRecip); void GetRecipIndex(DWORD iDomain, DWORD *piLowRecip, DWORD *piHighRecip); void BounceUsageCount(); static BOOL fBounceUsageCountCompletion(PVOID pvContext, DWORD dwStatus); void ReleaseAndBounceUsageOnMsgAck(DWORD dwMsgStatus); void ReleaseMailMsg(BOOL fForceRelease); void SyncBounceUsageCount(); //synchronous version of BounceUsageCount //Checks to see if the backing mailmsg has been deleted (or is about to //be deleted). BOOL fMailMsgMarkedForDeletion() {return ((MSGREF_MAILMSG_DELETE_PENDING | MSGREF_MAILMSG_DELETED) & m_dwDataFlags);}; //Marks the mailmsg for deletion. MailMsg will be deleted when the usage //count drops. void MarkMailMsgForDeletion(); //Used to make sure that calling thread is the only one that will call Delete() //on the MailMsg. Will set the MSGREF_MAILMSG_DELETED and call Delete(). //Only called in ReleaseMailMsg() and InternalReleaseUsage(). The caller is //responsible for making sure that other threads are not reading the mailmsg or //have a usage count VOID ThreadSafeMailMsgDelete(); //Internal versions of AddUsage/ReleaseUsage. Wraps the actual mailmsg calls, and //allows the CMsgRef to call delete on the MailMsg while there are still outstanding //references on it. Uses m_cInternalUsageCount to maintain a count. HRESULT InternalAddUsage(); HRESULT InternalReleaseUsage(); enum //bitmasks for private flags { MSGREF_VERSION_MASK = 0xE0000000, MSGREF_MSG_COUNTED_AS_REMOTE = 0x08000000, MSGREF_MSG_LOCAL_RETRY = 0x04000000, MSGREF_MSG_REMOTE_RETRY = 0x02000000, MSGREF_USAGE_COUNT_IN_USE = 0x01000000, MSGREF_SUPERSEDED = 0x00800000, //Msg has been superseed MSGREF_MSG_INIT = 0x00400000, //HrInitialize has been called MSGREF_MSG_FROZEN = 0x00200000, MSGREF_MSG_RETRY_ON_DELETE = 0x00100000, MSGREF_ASYNC_BOUNCE_PENDING = 0x00040000, MSGREF_MAILMSG_RELEASED = 0x00020000, MSGREF_MAILMSG_DELETE_PENDING = 0x00010000, //A delete is pending on this msg MSGREF_MAILMSG_DELETED = 0x00008000, //The backing store for the mailmsg //has been deleted. MSGREF_PRI_MASK = 0x0000000F, MSGREF_VERSION = 0x00000000, //used by allocators MSGREF_CPOOL_SIG_MASK = 0xFFFF0000, MSGREF_CPOOL_SIG = 0xC0070000, MSGREF_CPOOL_ALLOCATED = 0x00000001, MSGREF_STANDARD_SIZE = 0x00000002, }; static DWORD s_cMsgsPendingBounceUsage; //Messages that have been marked pending delete, but have not been deleted static DWORD s_cCurrentMsgsPendingDelete; //Total number of messages that have been marked pending delete static DWORD s_cTotalMsgsPendingDelete; //Total number of messages that have been deleted after being marked //for delete pending static DWORD s_cTotalMsgsDeletedAfterPendingDelete; //Total number of messages that have had ::Deleted, but are still in //memory because someone has an outstanding reference to the msgref static DWORD s_cCurrentMsgsDeletedNotReleased; }; //----------------------------------------------------------------------------- // Description: // Checks if the DSN HRESULT status is a fatal one, i.e. one for which an // NDR should be generated. //----------------------------------------------------------------------------- inline BOOL fIsFatalError(HRESULT hrStatus) { return ((AQUEUE_E_NDR_ALL == hrStatus) || (AQUEUE_E_LOOPBACK_DETECTED == hrStatus) || (AQUEUE_E_ACCESS_DENIED == hrStatus) || (AQUEUE_E_MESSAGE_TOO_LARGE == hrStatus) || (AQUEUE_E_SMTP_GENERIC_ERROR == hrStatus) || (AQUEUE_E_QADMIN_NDR == hrStatus) || (AQUEUE_E_NO_ROUTE == hrStatus)); } //---[ CPoolMsgRef ]----------------------------------------------------------- // // // Description: // Struct used as a hidden wrapper for CMsgRef allocation... used // exclusively by the CMsgRef new and delete operators // Hungarian: // cpmsgref, pcpmsgref // //----------------------------------------------------------------------------- typedef struct _CPoolMsgRef { DWORD m_dwAllocationFlags; CMsgRef m_msgref; } CPoolMsgRef; //Cannot use default CMsgRef new operator inline void * CMsgRef::operator new(size_t stIgnored) { _ASSERT(0 && "Use new that specifies # of domains"); return NULL; } inline void CMsgRef::operator delete(void *p, size_t size) { CPoolMsgRef *pcpmsgref = CONTAINING_RECORD(p, CPoolMsgRef, m_msgref); _ASSERT((pcpmsgref->m_dwAllocationFlags & MSGREF_CPOOL_SIG_MASK) == MSGREF_CPOOL_SIG); if (pcpmsgref->m_dwAllocationFlags & MSGREF_CPOOL_ALLOCATED) { s_MsgRefPool.Free((void *) pcpmsgref); } else { FreePv((void *) pcpmsgref); } } // Layout of private data bit fields // ------------------------------------- // |332|2222222221111111111987654|3210| // |109|8765432109876543210 | | // ------------------------------------- // | | | ^--- Effective routing priority (max 16) // | | | (Keep least significant so it can be // | | | used as an array index) // | | ^-------------------- General msgref flags // | ^---------------------------------- Version number //Actual data is variable-sized and extends beyond the class structure. //Use the public functions to access it. When persisting, be sure to persist //the entire thing (use size() to see how big it really is). // +----------+ // | | // | | constant-size data structure CMsgRef // | | // +----------+ // | | // | | m_cDomains CDestMsgQueue pointers - Tells which queues this // | | message is on. // +----------+ // | | Handled bitmap \ // | | Delivery pending bitmap >- bitmaps are variable sized // | | Delay DSN's sent bitmap / (up to 32 domains fit in a DWORD) // +----------+ // | | // | | m_cDomains Domain responsibility bitmaps - used with // | | the concept of "compressed" queues... not fully supported yet // +----------+ // | | // | | m_cDomains (x2) Recipient Index (start and stop... inclusive) // | | // +----------+ #endif //_MSGREF_H_