Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

432 lines
17 KiB

//-----------------------------------------------------------------------------
//
//
// 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 <mailmsg.h>
#include "msgguid.h"
#include "aqutil.h"
#include "aqadmsvr.h"
#include <aqerr.h>
#include <aqreg.h>
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_