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.
 
 
 
 
 
 

691 lines
25 KiB

//-----------------------------------------------------------------------------
//
//
// File: linkmsgq.h
//
// Description:
// This provides a description of one of the external interfaces provided
// by the CMT. CLinkMsgQueue provides Route factoring with an interface
// to get the next message for a given link.
//
// Owner: mikeswa
//
// Copyright (C) 1997 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#ifndef _LINKMSGQ_H_
#define _LINKMSGQ_H_
#include "cmt.h"
#include <rwnew.h>
#include <baseobj.h>
#include <aqueue.h>
#include "domain.h"
#include "aqroute.h"
#include "smproute.h"
#include <listmacr.h>
#include "qwiklist.h"
#include "dcontext.h"
#include "aqstats.h"
#include "aqnotify.h"
#include "aqadmsvr.h"
class CAQSvrInst;
class CDestMsgQueue;
class CConnMgr;
class CSMTPConn;
class CInternalDomainInfo;
#define LINK_MSGQ_SIG ' QML'
//Define private link state flags
//NOTE - Be sure to add new private flags to AssertPrivateLinkStateFlags as well
#define LINK_STATE_PRIV_CONFIG_TURN_ETRN 0x80000000
#define LINK_STATE_PRIV_ETRN_ENABLED 0x40000000
#define LINK_STATE_PRIV_TURN_ENABLED 0x20000000
#define LINK_STATE_PRIV_NO_NOTIFY 0x10000000
#define LINK_STATE_PRIV_NO_CONNECTION 0x08000000
#define LINK_STATE_PRIV_GENERATING_DSNS 0x04000000
#define LINK_STATE_PRIV_IGNORE_DELETE_IF_EMPTY 0x02000000
#define LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION 0x01000000
#define LINK_STATE_PRIV_HAVE_SENT_NO_LONGER_USED 0x00400000
#define EMPTY_LMQ_EXPIRE_TIME_MINUTES 2
//---[ enum LinkFlags ]--------------------------------------------------------
//
//
// Hungarian: lf, pfl
//
// Private link data flags
//-----------------------------------------------------------------------------
typedef enum _LinkFlags
{
eLinkFlagsClear = 0x00000000,
eLinkFlagsSentNewNotification = 0x00000001,
eLinkFlagsRouteChangePending = 0x00000002,
eLinkFlagsFileTimeSpinLock = 0x00000004,
eLinkFlagsDiagnosticSpinLock = 0x00000008,
eLinkFlagsConnectionVerifed = 0x00000010,
eLinkFlagsGetInfoFailed = 0x00000020,
eLinkFlagsAQSpecialLinkInfo = 0x00000040,
eLinkFlagsInternalSMTPLinkInfo = 0x00000080,
eLinkFlagsExternalSMTPLinkInfo = 0x00000100,
eLinkFlagsMarkedAsEmpty = 0x00000200,
eLinkFlagsInvalid = 0x80000000, //link has been tagged as invalid
} LinkFlags, *PLinkFlags;
//inline function to verify that private flags are only using reserved bits
inline void AssertPrivateLinkStateFlags()
{
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_CONFIG_TURN_ETRN));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_ETRN_ENABLED));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_TURN_ENABLED));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_NO_NOTIFY));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_NO_CONNECTION));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_GENERATING_DSNS));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_IGNORE_DELETE_IF_EMPTY));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION));
_ASSERT(!(~LINK_STATE_RESERVED & LINK_STATE_PRIV_HAVE_SENT_NO_LONGER_USED));
}
// {34E2DCCB-C91A-11d2-A6B1-00C04FA3490A}
static const GUID g_sDefaultLinkGuid =
{ 0x34e2dccb, 0xc91a, 0x11d2, { 0xa6, 0xb1, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } };
// Global count of failures : "failed to add queue to link because link was
// marked as no longer in use"
_declspec(selectany) DWORD g_cFailedToAddQueueToRemovedLink = 0;
//---[ CLinkMsgQueue ]---------------------------------------------------------
//
//
// Hungarian: linkq, plinkq
//
//
//-----------------------------------------------------------------------------
class CLinkMsgQueue :
public IQueueAdminAction,
public IQueueAdminLink,
public CBaseObject,
public IAQNotify,
public CQueueAdminRetryNotify
{
protected:
DWORD m_dwSignature;
DWORD m_dwLinkFlags; //private data
DWORD m_dwLinkStateFlags; //Link state flags (private + eLinkStateFlagsf)
CAQSvrInst *m_paqinst; //ptr to the virtual server intance object
DWORD m_cQueues; //Number of queues on Link
CQuickList m_qlstQueues;
CDomainEntry *m_pdentryLink; //Domain Entry for link
DWORD m_cConnections; //Number of current connections
DWORD m_dwRoundRobinIndex; //Used to round-robin through queues
CShareLockInst m_slConnections; //lock to access connections
CShareLockInst m_slInfo; //share lock for link info
CShareLockInst m_slQueues; //lock to access queues
DWORD m_cbSMTPDomain; //byte count of next hop domain name
LPSTR m_szSMTPDomain; //ptr to string of next hop
CInternalDomainInfo *m_pIntDomainInfo; //internal config info for domain
LONG m_lConnMgrCount; //Count used by connection manager
//
// We have 2 failure counts to keep track of the 2 types of failures.
// m_lConsecutiveConnectionFailureCount keeps track of the number consecutive
// failures to make a connection to a remote machine.
// m_lConsecutiveMessageFailureCount tracks the number of failures to actually
// send a message. They will be different if we can connect to a remote
// server but cannot (or have not) sent mail. m_lConsecutiveConnectionFailureCount
// is reported to routing, so that mail will be routed to this link, while
// m_lConsecutiveMessageFailureCount is used to determine the retry interval.
// By doing this, we can avoid resetting our retry times if we successfully
// connect, but cannot actually send a message.
//
LONG m_lConsecutiveConnectionFailureCount;
LONG m_lConsecutiveMessageFailureCount;
LPSTR m_szConnectorName;
IMessageRouterLinkStateNotification *m_pILinkStateNotify;
//Filetimes reported to queue admin
FILETIME m_ftNextRetry;
FILETIME m_ftNextScheduledCallback;
//
// Used by RemoveLinkIfEmpty to make sure that we cache links for
// a period of time after them become empty. This is only
// valid when the eLinkFlagsMarkedAsEmpty bit is set.
//
FILETIME m_ftEmptyExpireTime;
//Message statistics
CAQStats m_aqstats;
CAQScheduleID m_aqsched; //ScheduleID returned by routing
LIST_ENTRY m_liLinks; //linked list of links for this domain
LIST_ENTRY m_liConnections; //linked list of connections for this domain
//Diagnostic information returned by SMTPSVC
HRESULT m_hrDiagnosticError;
CHAR m_szDiagnosticVerb[20]; //failed protocol VERB
CHAR m_szDiagnosticResponse[100]; //response from remote server
//See comments near SetLinkType()/GetLinkType() and
//SetSupportedActions()/fActionIsSupported() functions.
DWORD m_dwSupportedActions;
DWORD m_dwLinkType;
//Gets & verifies internal domain info
HRESULT HrGetInternalInfo(OUT CInternalDomainInfo **ppIntDomainInfo);
static inline BOOL fFlagsAllowConnection(DWORD dwFlags);
HRESULT m_hrLastConnectionFailure;
void InternalUpdateFileTime(FILETIME *pftDest, FILETIME *pftSrc);
void InternalInit();
HRESULT CLinkMsgQueue::HrInternalPrepareDelivery(
IN CMsgRef *pmsgref,
IN BOOL fQueuesLocked,
IN BOOL fLocal,
IN BOOL fDelayDSN,
IN OUT CDeliveryContext *pdcntxt,
OUT DWORD *pcRecips,
OUT DWORD **prgdwRecips);
//Static callback used to restart DSN generation
static BOOL fRestartDSNGenerationIfNecessary(PVOID pvContext,
DWORD dwStatus);
public:
CLinkMsgQueue(GUID guid = g_sDefaultLinkGuid) : m_aqsched(guid, 0)
{InternalInit();};
CLinkMsgQueue(DWORD dwScheduleID,
IMessageRouter *pIMessageRouter,
IMessageRouterLinkStateNotification *pILinkStateNotify);
~CLinkMsgQueue();
void SetLinkType(DWORD dwLinkType) { m_dwLinkType = dwLinkType; }
DWORD GetLinkType() { return m_dwLinkType; }
//For some links, certain actions are not supported:
//but they use the same class (CLinkMsgQueue) as others for which
//the actions are supported. For example CurrentlyUnreachable does
//not support freeze/thaw. So we need to maintain for the currently
//unreachable object, a list of actions that are supported, so it
//does not set the flags corresponding to an unsupported action
//when that action is commanded.
void SetSupportedActions(DWORD dwSupported) { m_dwSupportedActions = dwSupported; }
DWORD fActionIsSupported(LINK_ACTION la) { return (m_dwSupportedActions & la); }
BOOL fCanSchedule() //Can this link be scheduled
{
HrGetInternalInfo(NULL); //make sure link state flags are up to date
DWORD dwFlags = m_dwLinkStateFlags;
return fFlagsAllowConnection(dwFlags);
}
BOOL fCanSendCmd() //Is this link scheduled to send command on next connection
{
//Logic :
// Every time we see this flag set, the connection that is created also will
// be used to send a command
//
DWORD dwFlags = m_dwLinkStateFlags;
return (dwFlags & LINK_STATE_CMD_ENABLED);
}
BOOL fShouldConnect(IN DWORD cMaxLinkConnections,
IN DWORD cMinMessagesPerConnection);
//returns S_OK if connection is needed, S_FALSE if not.
HRESULT HrCreateConnectionIfNeeded(IN DWORD cMaxLinkConnections,
IN DWORD cMinMessagesPerConnection,
IN DWORD cMaxMessagesPerConnection,
IN CConnMgr *pConnMgr,
OUT CSMTPConn **ppSMTPConn);
LONG IncrementConnMgrCount() {return InterlockedIncrement(&m_lConnMgrCount);}
LONG DecrementConnMgrCount() {return InterlockedDecrement(&m_lConnMgrCount);}
//
// Connection failure API. This is used by the connection manager. We
// will always return the message failure count, since this is what we
// want to pass to the retry sink. However, we will not allow the
// connection manager to reset this count since only we should during
// ack message.
//
LONG IncrementFailureCounts()
{
InterlockedIncrement(&m_lConsecutiveConnectionFailureCount);
return InterlockedIncrement(&m_lConsecutiveMessageFailureCount);
}
LONG cGetMessageFailureCount() {return m_lConsecutiveMessageFailureCount;}
void ResetConnectionFailureCount(){InterlockedExchange(&m_lConsecutiveConnectionFailureCount, 0);}
DWORD cGetConnections() {return m_cConnections;};
HRESULT HrInitialize(IN CAQSvrInst *paqinst,
IN CDomainEntry *pdentryLink,
IN DWORD cbSMTPDomain,
IN LPSTR szSMTPDomain,
IN LinkFlags lf,
IN LPSTR szConnectorName);
HRESULT HrDeinitialize();
void AddConnection(IN CSMTPConn *pSMTPConn); //Add Connection to link
void RemoveConnection(IN CSMTPConn *pSMTPConn,
IN BOOL fForceDSNGeneration);
HRESULT HrGetDomainInfo(OUT DWORD *pcbSMTPDomain,
OUT LPSTR *pszSMTPDomain,
OUT CInternalDomainInfo **ppIntDomainInfo);
HRESULT HrGetSMTPDomain(OUT DWORD *pcbSMTPDomain,
OUT LPSTR *pszSMTPDomain);
//Queue manipulation routines
HRESULT HrAddQueue(IN CDestMsgQueue *pdmqNew);
void RemoveQueue(IN CDestMsgQueue *pdmq, IN CAQStats *paqstats);
HRESULT HrGetQueueListSnapshot(CQuickList **ppql);
void RemoveLinkIfEmpty();
//Called by DMT to signal complete routing change
void RemoveAllQueues();
// Called by DMT when this link is orphaned
void RemovedFromDMT();
// This function dequeues the next available message.The message
// retrieved will be the top one approximatly ordered by quality/class
// and arrival time.
HRESULT HrGetNextMsg(
IN OUT CDeliveryContext *pdcntxt, //delivery context for connection
OUT IMailMsgProperties **ppIMailMsgProperties, //IMsg dequeued
OUT DWORD *pcIndexes, //size of array
OUT DWORD **prgdwRecipIndex); //Array of recipient indexes
//Acknowledge the message ref.
//There should be one Ack for every dequeue from a link.
HRESULT HrAckMsg(IN MessageAck *pMsgAck);
//Gets the next message ref without getting delivery context or
//preparing for delivery
HRESULT HrGetNextMsgRef(IN BOOL fRoutingLockHeld, OUT CMsgRef **ppmsgref);
//Calls CMsgRef prepare delivery for all the messages
HRESULT HrPrepareDelivery(
IN CMsgRef *pmsgref,
IN BOOL fLocal,
IN BOOL fDelayDSN,
IN OUT CDeliveryContext *pdcntxt,
OUT DWORD *pcRecips,
OUT DWORD **prgdwRecips)
{
return HrInternalPrepareDelivery(pmsgref, FALSE, fLocal, fDelayDSN,
pdcntxt, pcRecips, prgdwRecips);
}
//Recieve notifications from contained queues
HRESULT HrNotify(IN CAQStats *paqstats, BOOL fAdd);
HRESULT HrNotifyRetryStatChange(BOOL fAdd);
// Gather statistical information for link mangment
DWORD cGetTotalMsgCount() {return m_aqstats.m_cMsgs;};
DWORD cGetRetryMsgCount() {return m_aqstats.m_cRetryMsgs;};
//functions used to manipulate lists of queues
inline CAQScheduleID *paqschedGetScheduleID();
inline BOOL fIsSameScheduleID(CAQScheduleID *paqsched);
static inline CLinkMsgQueue *plmqIsSameScheduleID(
CAQScheduleID *paqsched,
PLIST_ENTRY pli);
static inline CLinkMsgQueue *plmqGetLinkMsgQueue(PLIST_ENTRY pli);
inline PLIST_ENTRY pliGetNextListEntry();
inline void InsertLinkInList(PLIST_ENTRY pliHead);
inline BOOL fRemoveLinkFromList();
DWORD dwModifyLinkState(IN DWORD dwLinkStateToSet,
IN DWORD dwLinkStateToUnset);
//Used to send notification to routing/scheduling sink
void SendLinkStateNotification();
void SendLinkStateNotificationIfNew() {
if (m_pILinkStateNotify &&
!(m_dwLinkStateFlags & LINK_STATE_PRIV_HAVE_SENT_NOTIFICATION))
SendLinkStateNotification();
}
DWORD dwGetLinkState() {return m_dwLinkStateFlags;};
void SetLastConnectionFailure(HRESULT hrLastConnectionFailure)
{m_hrLastConnectionFailure = hrLastConnectionFailure;};
inline BOOL fRPCCopyName(OUT LPWSTR *pwszLinkName);
DWORD cGetNumQueues() {return m_cQueues;};
virtual void SetNextRetry(FILETIME *pft)
{
_ASSERT(pft);
InternalUpdateFileTime(&m_ftNextRetry, pft);
};
void SetNextScheduledConnection(FILETIME *pft)
{
_ASSERT(pft);
InternalUpdateFileTime(&m_ftNextScheduledCallback, pft);
};
static void ScheduledCallback(PVOID pvContext);
void GenerateDSNsIfNecessary(BOOL fCheckIfEmpty, BOOL fMergeOnly);
void SetDiagnosticInfo(
IN HRESULT hrDiagnosticError,
IN LPCSTR szDiagnosticVerb,
IN LPCSTR szDiagnosticResponse);
void GetDiagnosticInfo(
IN LPSTR szDiagnosticVerb,
IN DWORD cDiagnosticVerb,
IN LPSTR szDiagnosticResponse,
IN DWORD cbDiagnosticResponse,
OUT HRESULT *phrDiagnosticError);
virtual BOOL fIsRemote() {return TRUE;};
//
// Returns the connector name. Used by CSMTPConn. Valid as long
// as the link is valid.
//
LPSTR szGetConnectorName() {return m_szConnectorName;};
public: //IUnknown
STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj);
STDMETHOD_(ULONG, AddRef)(void) { return CBaseObject::AddRef(); };
STDMETHOD_(ULONG, Release)(void) { return CBaseObject::Release(); };
public: //IQueueAdminAction
STDMETHOD(HrApplyQueueAdminFunction)(
IQueueAdminMessageFilter *pIQueueAdminMessageFilter);
STDMETHOD(HrApplyActionToMessage)(
IUnknown *pIUnknownMsg,
MESSAGE_ACTION ma,
PVOID pvContext,
BOOL *pfShouldDelete);
STDMETHOD_(BOOL, fMatchesID)
(QUEUELINK_ID *QueueLinkID);
STDMETHOD(QuerySupportedActions)(DWORD *pdwSupportedActions,
DWORD *pdwSupportedFilterFlags)
{
return QueryDefaultSupportedActions(pdwSupportedActions,
pdwSupportedFilterFlags);
};
public: //IQueueAdminLink
STDMETHOD(HrGetLinkInfo)(
LINK_INFO *pliLinkInfo,
HRESULT *phrLinkDiagnostic);
STDMETHOD(HrApplyActionToLink)(
LINK_ACTION la);
STDMETHOD(HrGetLinkID)(
QUEUELINK_ID *pLinkID);
STDMETHOD(HrGetNumQueues)(
DWORD *pcQueues);
STDMETHOD(HrGetQueueIDs)(
DWORD *pcQueues,
QUEUELINK_ID *rgQueues);
};
//---[ CLinkMsgQueue::paqschedGetScheduleID ]----------------------------------
//
//
// Description:
// Returns the schedule ID for this link
// Parameters:
// -
// Returns:
// ScheduleID for this link
// History:
// 6/9/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CAQScheduleID *CLinkMsgQueue::paqschedGetScheduleID()
{
return (&m_aqsched);
}
//---[ CLinkMsgQueue::fIsSameScheduleID ]--------------------------------------
//
//
// Description:
// Checks if a given schedule ID is the same as ours
// Parameters:
// paqsched - ScheduleID to check against
// Returns:
// TRUE if same schedule ID
// History:
// 6/9/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CLinkMsgQueue::fIsSameScheduleID(CAQScheduleID *paqsched)
{
return (m_aqsched.fIsEqual(paqsched));
}
//---[ CLinkMsgQueue::plmqIsSameScheduleID ]-----------------------------------
//
//
// Description:
// Gets the link if it matches the given schedule ID
// Parameters:
// paqsched - ScheduleID to check
// pli - list entry to get link for
// Returns:
// pointer to link if scheduleID matches..
// NULL otherwise
// History:
// 6/9/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CLinkMsgQueue *CLinkMsgQueue::plmqIsSameScheduleID(
CAQScheduleID *paqsched,
PLIST_ENTRY pli)
{
CLinkMsgQueue *plmq = CONTAINING_RECORD(pli, CLinkMsgQueue, m_liLinks);
_ASSERT(LINK_MSGQ_SIG == plmq->m_dwSignature);
if (!plmq->fIsSameScheduleID(paqsched))
plmq = NULL;
return plmq;
}
//---[ CLinkMsgQueue::plmqGetLinkMsgQueue ]------------------------------------
//
//
// Description:
// Returns the LinkMsgQueue associated with the given list entry
// Parameters:
// pli - List entry to get Link for
// Returns:
// pointer to link for list entry
// History:
// 6/8/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CLinkMsgQueue *CLinkMsgQueue::plmqGetLinkMsgQueue(PLIST_ENTRY pli)
{
_ASSERT(LINK_MSGQ_SIG == (CONTAINING_RECORD(pli, CLinkMsgQueue, m_liLinks))->m_dwSignature);
return (CONTAINING_RECORD(pli, CLinkMsgQueue, m_liLinks));
}
//---[ CLinkMsgQueue::InsertLinkInList ]---------------------------------------
//
//
// Description:
// Inserts link in given linked list
// Parameters:
// pliHead - Head of list to insert in
// Returns:
// -
// History:
// 6/9/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CLinkMsgQueue::InsertLinkInList(PLIST_ENTRY pliHead)
{
_ASSERT(NULL == m_liLinks.Flink);
_ASSERT(NULL == m_liLinks.Blink);
InsertHeadList(pliHead, &m_liLinks);
};
//---[ CLinkMsgQueue::fRemoveLinkFromList ]-------------------------------------
//
//
// Description:
// Remove link from link list
// Parameters:
// -
// Returns:
// -
// History:
// 6/9/98 - MikeSwa Created
// 6/11/99 - MikeSwa Modified to allow multiple calls
//
//-----------------------------------------------------------------------------
BOOL CLinkMsgQueue::fRemoveLinkFromList()
{
if (m_liLinks.Flink && m_liLinks.Blink)
{
RemoveEntryList(&m_liLinks);
m_liLinks.Flink = NULL;
m_liLinks.Blink = NULL;
return TRUE;
} else {
return FALSE;
}
};
//---[ CLinkMsgQueue::pliGetNextListEntry ]----------------------------------
//
//
// Description:
// Gets the pointer to the next list entry for this queue.
// Parameters:
// -
// Returns:
// The Flink of the queues LIST_ENTRY
// History:
// 6/16/98 - Created
//
//---------------------------------------------------------------------------
PLIST_ENTRY CLinkMsgQueue::pliGetNextListEntry()
{
return m_liLinks.Flink;
};
//---[ CLinkMsgQueue::fFlagsAllowConnection ]---------------------------------
//
//
// Description:
// Static helper function that examines if a given set of flags will
// allow a connection. Used by fCanSchedule and the linkstate debugger
// extension.
// Parameters:
// IN dwFlags Flags to check
// Returns:
// TRUE if a connection can be made, FALSE otherwise
// History:
// 9/30/98 - MikeSwa Created (separated out from fCanSchedule)
//
//-----------------------------------------------------------------------------
BOOL CLinkMsgQueue::fFlagsAllowConnection(DWORD dwFlags)
{
//Logic :
// We make a connection for a link, if the admin has not specified an override and
// one of the following conditions is met
// -the force a connection NOW flag has been set
// -the command enable flag has been set
// -the ETRN or TURN enable flag has been set
// -the retry enable as well as the schedule enable flag has been set
// (and domain is not TURN only).
//
BOOL fRet = FALSE;
if (dwFlags & LINK_STATE_ADMIN_HALT)
fRet = FALSE;
else if (dwFlags & LINK_STATE_PRIV_NO_CONNECTION)
fRet = FALSE;
else if (dwFlags & LINK_STATE_PRIV_GENERATING_DSNS)
fRet = FALSE;
else if (dwFlags & LINK_STATE_ADMIN_FORCE_CONN)
fRet = TRUE;
else if (dwFlags & LINK_STATE_PRIV_CONFIG_TURN_ETRN)
{
//Obey retry flag... even for ETRN domains
if ((dwFlags & LINK_STATE_PRIV_ETRN_ENABLED) &&
(dwFlags & LINK_STATE_RETRY_ENABLED))
fRet = TRUE;
else
fRet = FALSE;
}
else if ((dwFlags & LINK_STATE_RETRY_ENABLED) &&
(dwFlags & LINK_STATE_SCHED_ENABLED))
fRet = TRUE;
return fRet;
}
//---[ CLinkMsgQueue::fRPCCopyName ]--------------------------------------------
//
//
// Description:
// Used by Queue admin functions to copy the name of this link
// Parameters:
// IN pszLinkName UNICODE copy of name
// Returns:
// TRUE on success
// FALSE on failure
// History:
// 12/5/98 - MikeSwa Created
// 6/7/99 - MikeSwa Changed to UNICODE
//
//-----------------------------------------------------------------------------
BOOL CLinkMsgQueue::fRPCCopyName(OUT LPWSTR *pwszLinkName)
{
_ASSERT(pwszLinkName);
if (!m_cbSMTPDomain || !m_szSMTPDomain)
return FALSE;
*pwszLinkName = wszQueueAdminConvertToUnicode(m_szSMTPDomain,
m_cbSMTPDomain);
if (!pwszLinkName)
return FALSE;
return TRUE;
}
#endif // _LINKMSGQ_H_