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.
 
 
 
 
 
 

5615 lines
180 KiB

//-----------------------------------------------------------------------------
//
//
// File: dsnsink.cpp
//
// Description: Implementation of default DSN Generation sink
//
// Author: Mike Swafford (MikeSwa)
//
// History:
// 6/30/98 - MikeSwa Created
//
// Copyright (C) 1998 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include "precomp.h"
//
// This length is inspired by the other protocols that we deal with. The
// default address limit is 1024, but the MTA can allow 1024 + 834 for the
// OR address. We'll define out default buffer size to allow this large
// of an address.
//
#define PROP_BUFFER_SIZE 1860
#ifdef DEBUG
#define DEBUG_DO_IT(x) x
#else
#define DEBUG_DO_IT(x)
#endif //DEBUG
//min sizes for valid status strings
#define MIN_CHAR_FOR_VALID_RFC2034 10
#define MIN_CHAR_FOR_VALID_RFC821 3
#define MAX_RFC822_DATE_SIZE 35
BOOL FileTimeToLocalRFC822Date(const FILETIME & ft, char achReturn[MAX_RFC822_DATE_SIZE]);
static char *s_rgszMonth[ 12 ] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};
static char *s_rgszWeekDays[7] =
{
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
#define MAX_RFC_DOMAIN_SIZE 64
//String used in generation of MsgID
static char g_szBoundaryChars [] = "0123456789abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static LONG g_cDSNMsgID = 0;
//Address types to check for, and their corresponding address types.
const DWORD g_rgdwSenderPropIDs[] = {
IMMPID_MP_SENDER_ADDRESS_SMTP,
IMMPID_MP_SENDER_ADDRESS_X400,
IMMPID_MP_SENDER_ADDRESS_LEGACY_EX_DN,
IMMPID_MP_SENDER_ADDRESS_X500,
IMMPID_MP_SENDER_ADDRESS_OTHER};
const DWORD g_rgdwRecipPropIDs[] = {
IMMPID_RP_ADDRESS_SMTP,
IMMPID_RP_ADDRESS_X400,
IMMPID_RP_LEGACY_EX_DN,
IMMPID_RP_ADDRESS_X500,
IMMPID_RP_ADDRESS_OTHER};
const DWORD NUM_DSN_ADDRESS_PROPERTIES = 5;
const CHAR *g_rgszAddressTypes[] = {
"rfc822",
"x-x400",
"x-ex",
"x-x500",
"unknown"};
CPool CDSNPool::sm_Pool;
//---[ fLanguageAvailable ]----------------------------------------------------
//
//
// Description:
// Checks to see if resources for a given language are available.
// Parameters:
// LangId Language to check for
// Returns:
// TRUE If localized resources for requested language are available
// FALSE If resources for that language are not available.
// History:
// 10/26/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fLanguageAvailable(LANGID LangId)
{
TraceFunctEnterEx((LPARAM) LangId, "fLanguageAvailable");
HINSTANCE hModule = GetModuleHandle(DSN_RESOUCE_MODULE_NAME);
HRSRC hResInfo = NULL;
BOOL fResult = FALSE;
if (NULL == hModule)
{
_ASSERT( 0 && "Cannot get resource module handle");
return FALSE;
}
//Find handle to string table segment
hResInfo = FindResourceEx(hModule, RT_STRING,
MAKEINTRESOURCE(((WORD)((USHORT)GENERAL_SUBJECT >> 4) + 1)),
LangId);
if (NULL != hResInfo)
fResult = TRUE;
else
ErrorTrace((LPARAM) LangId, "Unable to load DSN resources for language");
TraceFunctLeave();
return fResult;
}
//---[ fIsValidMIMEBoundaryChar ]----------------------------------------------
//
//
// Description:
//
// Checks to see if the given character is a valid as described by the
// RFC2046 BNF for MIME Boundaries:
// boundary := 0*69<bchars> bcharsnospace
// bchars := bcharsnospace / " "
// bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
// "+" / "_" / "," / "-" / "." /
// "/" / ":" / "=" / "?"
// Parameters:
// ch Char to check
// Returns:
// TRUE if VALID
// FALSE otherwise
// History:
// 7/6/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fIsValidMIMEBoundaryChar(CHAR ch)
{
if (isalnum((UCHAR)ch))
return TRUE;
//check to see if it is one of the special case characters
if (('\'' == ch) || ('(' == ch) || (')' == ch) || ('+' == ch) ||
('_' == ch) || (',' == ch) || ('_' == ch) || ('.' == ch) ||
('/' == ch) || (':' == ch) || ('=' == ch) || ('?' == ch))
return TRUE;
else
return FALSE;
}
//---[ GenerateDSNMsgID ]------------------------------------------------------
//
//
// Description:
// Generates a unique MsgID string
//
// The format is:
// <random-unique-string>@<domain>
// Parameters:
// IN szDomain Domain to generate MsgID for
// IN cbDomain Domain to generate MsgID for
// IN OUT szBuffer Buffer to write MsgID in
// IN cbBuffer Size of buffer to write MsgID in
// Returns:
// TRUE on success
// FALSE otherwise
// History:
// 3/2/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fGenerateDSNMsgID(LPSTR szDomain,DWORD cbDomain,
LPSTR szBuffer, DWORD cbBuffer)
{
TraceFunctEnterEx((LPARAM) NULL, "fGenerateDSNMsgID");
_ASSERT(szDomain);
_ASSERT(cbDomain);
_ASSERT(szBuffer);
_ASSERT(cbBuffer);
// insert the leading <
if (cbBuffer >= 1) {
*szBuffer = '<';
szBuffer++;
cbBuffer--;
}
const CHAR szSampleFormat[] = "00000000@"; // sample format string
const DWORD cbMsgIdLen = 20; //default size of random string
LPSTR szStop = szBuffer + cbMsgIdLen;
LPSTR szCurrent = szBuffer;
DWORD cbCurrent = 0;
//minimize size for *internal* static buffer
_ASSERT(cbBuffer > MAX_RFC_DOMAIN_SIZE + cbMsgIdLen);
if (!szDomain || !cbDomain || !szBuffer || !cbBuffer ||
(cbBuffer <= MAX_RFC_DOMAIN_SIZE + cbMsgIdLen))
return FALSE;
//We want to figure how much room we have for random characters
//We will need to fit the domain name, the '@', and the 8 character unique
//number
// awetmore - add 1 for the trailing >
if(cbBuffer < cbDomain + cbMsgIdLen + 1)
{
//Fall through an allow for 20 characaters and part of domain name
//We want to catch this in debug builds
_ASSERT(0 && "Buffer too small for MsgID");
}
//this should have been caught in parameter checking
_ASSERT(cbBuffer > cbMsgIdLen);
szStop -= (sizeof(szSampleFormat) + 1);
while (szCurrent < szStop)
{
*szCurrent = g_szBoundaryChars[rand() % (sizeof(g_szBoundaryChars) - 1)];
szCurrent++;
}
//Add unique number
cbCurrent = sprintf(szCurrent, "%8.8x@", InterlockedIncrement(&g_cDSNMsgID));
_ASSERT(sizeof(szSampleFormat) - 1 == cbCurrent);
//Figure out how much room we have and add domain name
szCurrent += cbCurrent;
cbCurrent = (DWORD) (szCurrent-szBuffer);
//unless I've messed up the logic this is always true
_ASSERT(cbCurrent < cbBuffer);
//Add domain part to message id
strncat(szCurrent-1, szDomain, cbBuffer - cbCurrent - 1);
_ASSERT(cbCurrent + cbDomain < cbBuffer);
// Add the trailing >. we accounted for the space above check for
// cbBuffer size
strncat(szCurrent, ">", cbBuffer - cbCurrent - cbDomain - 1);
DebugTrace((LPARAM) NULL, "Generating DSN Message ID %s", szCurrent);
TraceFunctLeave();
return TRUE;
}
//---[ fIsMailMsgDSN ]---------------------------------------------------------
//
//
// Description:
// Determines if a mailmsg is a DSN.
// Parameters:
// IN pIMailMsgProperties
// Returns:
// TRUE if the orinal message is a DSN
// FALSE if it is not a DSN
// History:
// 2/11/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL fIsMailMsgDSN(IMailMsgProperties *pIMailMsgProperties)
{
CHAR szSenderBuffer[sizeof(DSN_MAIL_FROM)];
DWORD cbSender = 0;
HRESULT hr = S_OK;
BOOL fIsDSN = FALSE; //unless proven otherwise... it is not a DSN
_ASSERT(pIMailMsgProperties);
szSenderBuffer[0] = '\0';
//Get the sender of the original message
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_SENDER_ADDRESS_SMTP,
sizeof(szSenderBuffer), &cbSender, (BYTE *) szSenderBuffer);
if (SUCCEEDED(hr) &&
('\0' == szSenderBuffer[0] || !strcmp(DSN_MAIL_FROM, szSenderBuffer)))
{
//If the sender is a NULL string... or "<>"... then it is a DSN
fIsDSN = TRUE;
}
return fIsDSN;
}
#ifdef DEBUG
#define _ASSERT_RECIP_FLAGS AssertRecipFlagsFn
#define _ASSERT_MIME_BOUNDARY(szMimeBoundary) AssertMimeBoundary(szMimeBoundary)
//---[ AssertRecipFlagsFn ]----------------------------------------------------
//
//
// Description:
// ***DEBUG ONLY***
// Asserts that the recipient flags defined in mailmsgprops.h are correct
// Parameters:
// -
// Returns:
// -
// History:
// 7/2/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void AssertRecipFlagsFn()
{
DWORD i, j;
DWORD rgdwFlags[] = {RP_DSN_NOTIFY_SUCCESS, RP_DSN_NOTIFY_FAILURE,
RP_DSN_NOTIFY_DELAY, RP_DSN_NOTIFY_NEVER, RP_DELIVERED,
RP_DSN_SENT_NDR, RP_FAILED, RP_UNRESOLVED, RP_EXPANDED,
RP_DSN_SENT_DELAYED, RP_DSN_SENT_EXPANDED, RP_DSN_SENT_RELAYED,
RP_DSN_SENT_DELIVERED, RP_REMOTE_MTA_NO_DSN, RP_ERROR_CONTEXT_STORE,
RP_ERROR_CONTEXT_CAT, RP_ERROR_CONTEXT_MTA};
DWORD cFlags = sizeof(rgdwFlags)/sizeof(DWORD);
for (i = 0; i < cFlags;i ++)
{
for (j = i+1; j < cFlags; j++)
{
//make sure all have some unique bits
if (rgdwFlags[i] & rgdwFlags[j])
{
_ASSERT((rgdwFlags[i] & rgdwFlags[j]) != rgdwFlags[j]);
_ASSERT((rgdwFlags[i] & rgdwFlags[j]) != rgdwFlags[i]);
}
}
}
//Verify that handled bit is used correctly
_ASSERT(RP_HANDLED & RP_DELIVERED);
_ASSERT(RP_HANDLED & RP_DSN_SENT_NDR);
_ASSERT(RP_HANDLED & RP_FAILED);
_ASSERT(RP_HANDLED & RP_UNRESOLVED);
_ASSERT(RP_HANDLED & RP_EXPANDED);
_ASSERT(RP_HANDLED ^ RP_DELIVERED);
_ASSERT(RP_HANDLED ^ RP_DSN_SENT_NDR);
_ASSERT(RP_HANDLED ^ RP_FAILED);
_ASSERT(RP_HANDLED ^ RP_UNRESOLVED);
_ASSERT(RP_HANDLED ^ RP_EXPANDED);
//Verify that DSN-handled bit is used correctly
_ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_NDR);
_ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_EXPANDED);
_ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_RELAYED);
_ASSERT(RP_DSN_HANDLED & RP_DSN_SENT_DELIVERED);
_ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_NDR);
_ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_EXPANDED);
_ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_RELAYED);
_ASSERT(RP_DSN_HANDLED ^ RP_DSN_SENT_DELIVERED);
//Verify that general failure bit is used correctly
_ASSERT(RP_GENERAL_FAILURE & RP_FAILED);
_ASSERT(RP_GENERAL_FAILURE & RP_UNRESOLVED);
_ASSERT(RP_GENERAL_FAILURE ^ RP_FAILED);
_ASSERT(RP_GENERAL_FAILURE ^ RP_UNRESOLVED);
}
//---[ AssertMimeBoundary ]----------------------------------------------------
//
// ***DEBUG ONLY***
// Description:
// Asserts that the given MIME boundary is NULL-terminated and has only
// Valid characters
// Parameters:
// szMimeBoundary NULL-terminated MIME Boundary string
// Returns:
// -
// History:
// 7/6/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void AssertMimeBoundary(LPSTR szMimeBoundary)
{
CHAR *pcharCurrent = szMimeBoundary;
DWORD cChars = 0;
while ('\0' != *pcharCurrent)
{
cChars++;
_ASSERT(cChars <= MIME_BOUNDARY_RFC2046_LIMIT);
_ASSERT(fIsValidMIMEBoundaryChar(*pcharCurrent));
pcharCurrent++;
}
}
#else //not DEBUG
#define _ASSERT_RECIP_FLAGS()
#define _VERIFY_MARKED_RECIPS(a, b, c)
#define _ASSERT_MIME_BOUNDARY(szMimeBoundary)
#endif //DEBUG
//---[ CDSNGenerator::CDSNGenerator ]--------------------------------------
//
//
// Description:
// CDSNGenerator constructor
// Parameters:
// -
// Returns:
//
// History:
// 6/30/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CDSNGenerator::CDSNGenerator(
IUnknown *pUnk) :
m_CDefaultDSNSink(pUnk)
{
m_dwSignature = DSN_SINK_SIG;
}
//---[ CDSNGenerator::~CDSNGenerator ]-------------------------------------
//
//
// Description:
//
// Parameters:
//
// Returns:
//
// History:
// 2/11/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CDSNGenerator::~CDSNGenerator()
{
m_dwSignature = DSN_SINK_SIG_FREED;
}
//---[ CDSNGenerator::GenerateDSN ]------------------------------------------
//
//
// Description:
// Implements GenerateDSN. Generates a DSN
// IMailMsgProperties and
// Parameters:
// pIServerEvent Interface for triggering events
// dwVSID VSID for this server
// pISMTPServer Interface used to generate DSN
// pIMailMsgProperties IMailMsg to generate DSN for
// dwStartDomain Domain to start recip context
// dwDSNActions DSN action to perform
// dwRFC821Status Global RFC821 status DWORD
// hrStatus Global HRESULT status
// szDefaultDomain Default domain (used to create FROM address)
// szReportingMTA Name of MTA requesting DSN generation
// szReportingMTAType Type of MTA requestiong DSN (SMTP is "dns"
// PreferredLangId Language to generate DSN in
// dwDSNOptions Options flags as defined in aqueue.idl
// szCopyNDRTo SMTP Address to copy NDR to
// pIDSNSubmission Interface for submitting DSNs
// dwMaxDSNSize Return HDRS by default for messages
// larger than this
//
// Returns:
// S_OK on success
// AQUEUE_E_NDR_OF_DSN if attempting to NDR a DSN
// E_OUTOFMEMORY
// error from mailmsg
// History:
// 6/30/98 - MikeSwa Created
// 12/14/98 - MikeSwa Modified (Added pcIterationsLeft)
// 10/13/1999 - MikeSwa Modified (Added szDefaultDomain)
// 5/10/2001 - jstamerj Modified for server events
//
//
//-----------------------------------------------------------------------------
STDMETHODIMP CDSNGenerator::GenerateDSN(
IAQServerEvent *pIServerEvent,
DWORD dwVSID,
ISMTPServer *pISMTPServer,
IMailMsgProperties *pIMailMsgProperties,
DWORD dwStartDomain,
DWORD dwDSNActions,
DWORD dwRFC821Status,
HRESULT hrStatus,
LPSTR szDefaultDomain,
LPSTR szReportingMTA,
LPSTR szReportingMTAType,
LPSTR szDSNContext,
DWORD dwPreferredLangId,
DWORD dwDSNOptions,
LPSTR szCopyNDRTo,
FILETIME *pftExpireTime,
IDSNSubmission *pIAQDSNSubmission,
DWORD dwMaxDSNSize)
{
HRESULT hr = S_OK;
HRESULT hrReturn = S_OK;
DWORD dwCount = 0;
DWORD fBadmailMsg = FALSE;
IDSNRecipientIterator *pIRecipIter = NULL;
CDSNPool *pDSNPool = NULL;
CDefaultDSNRecipientIterator *pDefaultRecipIter = NULL;
CPostDSNHandler *pPostDSNHandler = NULL;
CMailMsgPropertyBag *pPropBag = NULL;
TraceFunctEnterEx((LPARAM) this, "CDSNGenerator::GenerateDSN");
//
// Parameter -> Property mapping tables
//
struct _tagDWORDProps
{
DWORD dwPropId;
DWORD dwValue;
} DsnDwordProps[] =
{
{ DSNPROP_DW_DSNACTIONS, dwDSNActions },
{ DSNPROP_DW_DSNOPTIONS, dwDSNOptions },
{ DSNPROP_DW_RFC821STATUS, dwRFC821Status },
{ DSNPROP_DW_HRSTATUS, (DWORD) hrStatus },
{ DSNPROP_DW_LANGID, dwPreferredLangId },
};
struct _tagStringProps
{
DWORD dwPropId;
LPSTR psz;
} DsnStringProps[] =
{
{ DSNPROP_SZ_DEFAULTDOMAIN, szDefaultDomain },
{ DSNPROP_SZ_REPORTINGMTA, szReportingMTA },
{ DSNPROP_SZ_REPORTINGMTATYPE, szReportingMTAType },
{ DSNPROP_SZ_DSNCONTEXT, szDSNContext },
{ DSNPROP_SZ_COPYNDRTO, szCopyNDRTo },
};
pDSNPool = new CDSNPool(
this,
pIServerEvent,
dwVSID,
pISMTPServer,
pIMailMsgProperties,
pIAQDSNSubmission,
&m_CDefaultDSNSink);
if(pDSNPool == NULL)
{
hr = E_OUTOFMEMORY;
goto CLEANUP;
}
pDefaultRecipIter = pDSNPool->GetDefaultIter();
pPostDSNHandler = pDSNPool->GetPostDSNHandler();
pPropBag = pDSNPool->GetDSNProperties();
pPostDSNHandler->SetPropInterface(
pPropBag);
for(dwCount = 0;
dwCount < ( sizeof(DsnDwordProps) / sizeof(DsnDwordProps[0]));
dwCount++)
{
hr = pPropBag->PutDWORD(
DsnDwordProps[dwCount].dwPropId,
DsnDwordProps[dwCount].dwValue);
if(FAILED(hr))
goto CLEANUP;
}
for(dwCount = 0;
dwCount < ( sizeof(DsnStringProps) / sizeof(DsnStringProps[0]));
dwCount++)
{
if(DsnStringProps[dwCount].psz)
{
hr = pPropBag->PutStringA(
DsnStringProps[dwCount].dwPropId,
DsnStringProps[dwCount].psz);
if(FAILED(hr))
goto CLEANUP;
}
}
//
//Set MsgExpire Time
//
if(pftExpireTime)
{
hr = pPropBag->PutProperty(
DSNPROP_FT_EXPIRETIME,
sizeof(FILETIME),
(PBYTE) pftExpireTime);
if(FAILED(hr))
goto CLEANUP;
}
//
//Set the return type
//
hr = HrSetRetType(
dwMaxDSNSize,
pIMailMsgProperties,
pPropBag);
if(FAILED(hr))
goto CLEANUP;
hr = pDefaultRecipIter->HrInit(
pIMailMsgProperties,
dwStartDomain,
dwDSNActions);
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "DefaultRecipIter.HrInit failed hr %08lx", hr);
goto CLEANUP;
}
pIRecipIter = pDefaultRecipIter;
pIRecipIter->AddRef();
//
// Trigger events
//
hr = HrTriggerGetDSNRecipientIterator(
pIServerEvent,
dwVSID,
pISMTPServer,
pIMailMsgProperties,
pPropBag,
dwStartDomain,
dwDSNActions,
&pIRecipIter);
if(FAILED(hr))
goto CLEANUP;
hr = HrTriggerGenerateDSN(
pIServerEvent,
dwVSID,
pISMTPServer,
pPostDSNHandler,
pIMailMsgProperties,
pPropBag,
pIRecipIter);
if(FAILED(hr))
goto CLEANUP;
//
// Check sink's indicated return status
//
hr = pPropBag->GetDWORD(
DSNPROP_DW_HR_RETURN_STATUS,
(DWORD *) &hrReturn);
if(hr == MAILMSG_E_PROPNOTFOUND)
{
//
// If the property is not set, this indicates no error
//
hrReturn = S_OK;
} else if(FAILED(hr))
goto CLEANUP;
DebugTrace((LPARAM)this, "Sink return status: %08lx", hrReturn);
hr = hrReturn;
if(FAILED(hr))
goto CLEANUP;
hr = pPropBag->GetBool(
DSNPROP_F_BADMAIL_MSG,
&fBadmailMsg);
if(hr == MAILMSG_E_PROPNOTFOUND)
{
fBadmailMsg = FALSE;
hr = S_OK;
}
else if(FAILED(hr))
goto CLEANUP;
if(fBadmailMsg)
hr = AQUEUE_E_NDR_OF_DSN;
CLEANUP:
//
// Sinks should not be submitting DSNs after the event has been
// completed. This is not supported and would be bad because the
// object that implements pIAQDSNSubmission is allocated on the
// stack. Release this interface pointer here.
//
if(pPostDSNHandler)
pPostDSNHandler->ReleaseAQDSNSubmission();
if(pIRecipIter)
pIRecipIter->Release();
if(pDSNPool)
pDSNPool->Release();
DebugTrace((LPARAM)this, "returning hr %08lx", hr);
TraceFunctLeave();
return SUCCEEDED(hr) ? S_OK : hr;
}
//+------------------------------------------------------------
//
// Function: CDSNGenerator::HrSetRetType
//
// Synopsis: Set the (default) return type property in the DSN
// property bag
//
// Arguments:
// dwMaxDSNSize: Messages larger than this will default to RET=HDRS
// pIMsg: Mailmsg ptr
// pDSNProps: DSN property bag
//
// Returns:
// S_OK: Success
// error from mailmsg
//
// History:
// jstamerj 2001/06/14 17:12:50: Created.
//
//-------------------------------------------------------------
HRESULT CDSNGenerator::HrSetRetType(
IN DWORD dwMaxDSNSize,
IN IMailMsgProperties *pIMsg,
IN IMailMsgPropertyBag *pDSNProps)
{
HRESULT hr = S_OK;
DWORD dwRetType = 0;
DWORD dwMsgSize = 0;
CHAR szRET[] = "FULL";
TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrSetRetType");
//
//Determine if we want to return the full message or minimal headers.
//The logic for this is:
// - Obey explicit RET (IMMPID_MP_DSN_RET_VALUE) values
// - Default to HDRS for all DSNs greater than a specified size
// - Do not set the property otherwise (let the sinks decide)
//
hr = pIMsg->GetStringA(
IMMPID_MP_DSN_RET_VALUE,
sizeof(szRET),
szRET);
if (SUCCEEDED(hr))
{
if(!_strnicmp(szRET, (char * )"FULL", 4))
dwRetType = DSN_RET_FULL;
else if (!_strnicmp(szRET, (char * )"HDRS", 4))
dwRetType = DSN_RET_HDRS;
}
else if(hr != MAILMSG_E_PROPNOTFOUND)
goto CLEANUP;
if(dwRetType)
{
DebugTrace((LPARAM)this, "DSN Return value specified: %s", szRET);
goto CLEANUP;
}
if(dwMaxDSNSize)
{
//
// Check the original message size
//
hr = pIMsg->GetDWORD(
IMMPID_MP_MSG_SIZE_HINT,
&dwMsgSize);
if(hr == MAILMSG_E_PROPNOTFOUND)
{
hr = pIMsg->GetContentSize(
&dwMsgSize,
NULL);
if(FAILED(hr))
{
//
// Assume a failure here means we don't have the resources
// to get the original message content.
// Rather than badmailing, generate a DSN with headers only
//
ErrorTrace((LPARAM)this, "GetContentSize failed hr %08lx", hr);
hr = pDSNProps->PutDWORD(
DSNPROP_DW_CONTENT_FAILURE,
hr);
if(FAILED(hr))
goto CLEANUP;
dwRetType = DSN_RET_PARTIAL_HDRS;
goto CLEANUP;
}
}
else if(FAILED(hr))
goto CLEANUP;
if(dwMsgSize > dwMaxDSNSize)
{
//
// Return a subset of the headers (so that we do not have to
// generate the original message
//
dwRetType = DSN_RET_PARTIAL_HDRS;
}
}
else
{
//
// MaxDSNSize is zero. Always default to header subset.
//
dwRetType = DSN_RET_PARTIAL_HDRS;
}
hr = S_OK;
CLEANUP:
if(dwRetType)
{
DebugTrace((LPARAM)this, "dwRetType: %08lx", dwRetType);
hr = pDSNProps->PutDWORD(
DSNPROP_DW_RET_TYPE,
dwRetType);
}
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return SUCCEEDED(hr) ? S_OK : hr;
} // CDSNGenerator::HrSetRetType
//+------------------------------------------------------------
//
// Function: CDSNGenerator::HrTriggerGetDSNRecipientIterator
//
// Synopsis: Trigger the server event
//
// Arguments: see ptntintf.idl
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/12/11 17:01:12: Created.
//
//-------------------------------------------------------------
HRESULT CDSNGenerator::HrTriggerGetDSNRecipientIterator(
IAQServerEvent *pIServerEvent,
DWORD dwVSID,
ISMTPServer *pISMTPServer,
IMailMsgProperties *pIMsg,
IMailMsgPropertyBag *pIDSNProperties,
DWORD dwStartDomain,
DWORD dwDSNActions,
IDSNRecipientIterator **ppIRecipIterator)
{
HRESULT hr = S_OK;
EVENTPARAMS_GET_DSN_RECIPIENT_ITERATOR EventParams;
TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrTriggerGetDSNRecipientIterator");
EventParams.dwVSID = dwVSID;
EventParams.pISMTPServer = pISMTPServer;
EventParams.pIMsg = pIMsg;
EventParams.pDSNProperties = pIDSNProperties;
EventParams.dwStartDomain = dwStartDomain;
EventParams.dwDSNActions = dwDSNActions;
EventParams.pRecipIter = *ppIRecipIterator;
hr = pIServerEvent->TriggerServerEvent(
SMTP_GET_DSN_RECIPIENT_ITERATOR_EVENT,
&EventParams);
if(FAILED(hr))
goto CLEANUP;
*ppIRecipIterator = EventParams.pRecipIter;
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDSNGenerator::HrTriggerGetDSNRecipientIterator
//+------------------------------------------------------------
//
// Function: CDSNGenerator::HrTriggerGenerateDSN
//
// Synopsis: Trigger the server event
//
// Arguments: See ptntintf.idl
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/12/11 17:10:26: Created.
//
//-------------------------------------------------------------
HRESULT CDSNGenerator::HrTriggerGenerateDSN(
IAQServerEvent *pIServerEvent,
DWORD dwVSID,
ISMTPServer *pISMTPServer,
IDSNSubmission *pIDSNSubmission,
IMailMsgProperties *pIMsg,
IMailMsgPropertyBag *pIDSNProperties,
IDSNRecipientIterator *pIRecipIterator)
{
HRESULT hr = S_OK;
EVENTPARAMS_GENERATE_DSN EventParams;
TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrTriggerGenerateDSN");
EventParams.dwVSID = dwVSID;
EventParams.pDefaultSink = &m_CDefaultDSNSink;
EventParams.pISMTPServer = pISMTPServer;
EventParams.pIDSNSubmission = pIDSNSubmission;
EventParams.pIMsg = pIMsg;
EventParams.pDSNProperties = pIDSNProperties;
EventParams.pRecipIter = pIRecipIterator;
hr = pIServerEvent->TriggerServerEvent(
SMTP_GENERATE_DSN_EVENT,
&EventParams);
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "HrTriggerServerEvent failed hr %08lx", hr);
goto CLEANUP;
}
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDSNGenerator::HrTriggerGenerateDSN
//+------------------------------------------------------------
//
// Function: CDSNGenerator::HrTriggerPostGenerateDSN
//
// Synopsis: Triggers the server event
//
// Arguments: See ptntintf.idl
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/12/11 17:18:17: Created.
//
//-------------------------------------------------------------
HRESULT CDSNGenerator::HrTriggerPostGenerateDSN(
IAQServerEvent *pIServerEvent,
DWORD dwVSID,
ISMTPServer *pISMTPServer,
IMailMsgProperties *pIMsgOrig,
DWORD dwDSNAction,
DWORD cRecipsDSNd,
IMailMsgProperties *pIMsgDSN,
IMailMsgPropertyBag *pIDSNProperties)
{
HRESULT hr = S_OK;
EVENTPARAMS_POST_GENERATE_DSN EventParams;
TraceFunctEnterEx((LPARAM)this, "CDSNGenerator::HrTriggerPostGenerateDSN");
EventParams.dwVSID = dwVSID;
EventParams.pISMTPServer = pISMTPServer;
EventParams.pIMsgOrig = pIMsgOrig;
EventParams.dwDSNAction = dwDSNAction;
EventParams.cRecipsDSNd = cRecipsDSNd;
EventParams.pIMsgDSN = pIMsgDSN;
EventParams.pIDSNProperties = pIDSNProperties;
hr = pIServerEvent->TriggerServerEvent(
SMTP_POST_DSN_EVENT,
&EventParams);
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "HrTriggerServerEvent failed hr %08lx", hr);
goto CLEANUP;
}
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDSNGenerator::HrTriggerPostGenerateDSN
//---[ FileTimeToLocalRFC822Date ]---------------------------------------------
//
//
// Description:
// Converts filetime to RFC822 compliant date
// Parameters:
// ft Filetime to generate date for
// achReturn Buffer for filetime
// Returns:
// BOOL - success or not
// History:
// 8/19/98 - MikeSwa Modified from various timeconv.cxx functions written
// by Lindsay Harris - lindasyh
// Carl Kadie [carlk]
//
//-----------------------------------------------------------------------------
BOOL FileTimeToLocalRFC822Date(const FILETIME & ft, char achReturn[MAX_RFC822_DATE_SIZE])
{
TraceFunctEnterEx((LPARAM)0, "FileTimeToLocalRFC822Date");
FILETIME ftLocal;
SYSTEMTIME st;
char chSign; // Sign to print.
DWORD dwResult;
int iBias; // Offset relative to GMT.
TIME_ZONE_INFORMATION tzi; // Local time zone data.
BOOL bReturn = FALSE;
dwResult = GetTimeZoneInformation( &tzi );
_ASSERT(achReturn); //real assert
achReturn[0]='\0';
if (!FileTimeToLocalFileTime(&ft, &ftLocal))
{
ErrorTrace((LPARAM)0, "FileTimeToLocalFileTime failed - %x", GetLastError());
bReturn = FALSE;
goto Exit;
}
if (!FileTimeToSystemTime(&ftLocal, &st))
{
ErrorTrace((LPARAM)0, "FileTimeToSystemTime failed - %x", GetLastError());
bReturn = FALSE;
goto Exit;
}
// Calculate the time zone offset.
iBias = tzi.Bias;
if( dwResult == TIME_ZONE_ID_DAYLIGHT )
iBias += tzi.DaylightBias;
/*
* We always want to print the sign for the time zone offset, so
* we decide what it is now and remember that when converting.
* The convention is that west of the 0 degree meridian has a
* negative offset - i.e. add the offset to GMT to get local time.
*/
if( iBias > 0 )
{
chSign = '-'; // Yes, I do mean negative.
}
else
{
iBias = -iBias;
chSign = '+';
}
/*
* No major trickery here. We have all the data, so simply
* format it according to the rules on how to do this.
*/
wsprintf( achReturn, "%s, %d %s %04d %02d:%02d:%02d %c%02d%02d",
s_rgszWeekDays[st.wDayOfWeek],
st.wDay, s_rgszMonth[ st.wMonth - 1 ],
st.wYear,
st.wHour, st.wMinute, st.wSecond, chSign,
(iBias / 60) % 100, iBias % 60 );
_ASSERT(lstrlen(achReturn) < MAX_RFC822_DATE_SIZE);
bReturn = TRUE;
Exit:
TraceFunctLeaveEx((LPARAM)0);
return bReturn;
}
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::Init
//
// Synopsis: Constructor. Initializes member variables.
//
// Arguments:
// pIMsg: Mailmsg interface
// dwStartDomain: First domain of recipient enumeration
// dwDSNActions: The DSN actions to perform
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/09 14:18:45: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNRecipientIterator::HrInit(
IN IMailMsgProperties *pIMsg,
IN DWORD dwStartDomain,
IN DWORD dwDSNActions)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::Init");
TerminateFilter();
if(m_pIRecips)
{
m_pIRecips->Release();
m_pIRecips = NULL;
}
m_dwStartDomain = dwStartDomain;
m_dwDSNActions = dwDSNActions;
hr = pIMsg->QueryInterface(
IID_IMailMsgRecipients,
(LPVOID *) &m_pIRecips);
_ASSERT(SUCCEEDED(hr));
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "QI failed %08lx", hr);
goto CLEANUP;
}
hr = HrReset();
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "HrReset failed %08lx", hr);
}
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNRecipientIterator::HrInit
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator
//
// Synopsis: Destructor. Cleanup
//
// Arguments: NONE
//
// Returns: NOTHING
//
// History:
// jstamerj 2000/11/09 18:42:32: Created.
//
//-------------------------------------------------------------
CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator()
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator");
TerminateFilter();
if(m_pIRecips)
m_pIRecips->Release();
_ASSERT(m_dwSig == RECIPITER_SIG);
m_dwSig = RECIPITER_SIG_INVALID;
TraceFunctLeaveEx((LPARAM)this);
} // CDefaultDSNRecipientIterator::~CDefaultDSNRecipientIterator
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::QueryInterface
//
// Synopsis: Return requested interface
//
// Arguments:
// riid: Interface ID
// ppvObj: Out paramter for itnerface
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/09 14:25:39: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNRecipientIterator::QueryInterface(
IN REFIID riid,
OUT LPVOID *ppvObj)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::QueryInterface");
_ASSERT(ppvObj);
*ppvObj = NULL;
if(riid == IID_IUnknown)
{
*ppvObj = (IUnknown *)this;
}
else if(riid == IID_IDSNRecipientIterator)
{
*ppvObj = (IDSNRecipientIterator *)this;
}
else
{
hr = E_NOINTERFACE;
}
if(SUCCEEDED(hr))
AddRef();
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNRecipientIterator::QueryInterface
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::HrReset
//
// Synopsis: Reset recipent iteration
//
// Arguments: None
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/09 14:32:04: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNRecipientIterator::HrReset()
{
HRESULT hr = S_OK;
DWORD dwRecipFilterMask = 0;
DWORD dwRecipFilterFlags = 0;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::HrReset");
GetFilterMaskAndFlags(
m_dwDSNActions,
&dwRecipFilterMask,
&dwRecipFilterFlags);
TerminateFilter();
hr = m_pIRecips->InitializeRecipientFilterContext(
&m_rpfctxt,
m_dwStartDomain,
dwRecipFilterFlags,
dwRecipFilterMask);
if (FAILED(hr))
goto CLEANUP;
m_fFilterInit = TRUE;
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNRecipientIterator::HrReset
//---[ CDefaultDSNRecipientIterator::GetFilterMaskAndFlags ]-------------------
//
//
// Description:
// Determines what the appropriate mask and flags for a recip serch filter
// are based on the given actions.
//
// It may not be possible to constuct a perfectly optimal search (ie Failed
// and delivered).... this function will attempt to find the "most optimal"
// search possible.
// Parameters:
// dwDSNActions Requested DSN generation operations
// pdwRecipMask Mask to pass to InitializeRecipientFilterContext
// pdwRecipFlags Flags to pass to InitializeRecipientFilterContext
// Returns: Nothing
// History:
// 7/1/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
VOID CDefaultDSNRecipientIterator::GetFilterMaskAndFlags(
IN DWORD dwDSNActions,
OUT DWORD *pdwRecipMask,
OUT DWORD *pdwRecipFlags)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNRecipientIterator::HrGetFilterMaskAndFlags");
_ASSERT(pdwRecipMask);
_ASSERT(pdwRecipFlags);
//in general we are only interested in un-DSN'd recipients
*pdwRecipFlags = 0x00000000;
*pdwRecipMask = RP_DSN_HANDLED | RP_DSN_NOTIFY_NEVER;
//Note these searches are just optimizations... so we don't look at
//recipients we don't need to. However, it may not be possible to
//limit the search precisely
if (DSN_ACTION_FAILURE == dwDSNActions)
{
//We are interested in hard failures
*pdwRecipMask |= RP_GENERAL_FAILURE;
*pdwRecipFlags |= RP_GENERAL_FAILURE;
}
if (!((DSN_ACTION_DELIVERED | DSN_ACTION_RELAYED) & dwDSNActions))
{
//are not interested in delivered
if ((DSN_ACTION_FAILURE_ALL | DSN_ACTION_DELAYED) & dwDSNActions)
{
//it is safe to check only undelivered
*pdwRecipMask |= (RP_DELIVERED ^ RP_HANDLED); //must be un-set
_ASSERT(!(*pdwRecipFlags & (RP_DELIVERED ^ RP_HANDLED)));
}
}
else
{
//$$TODO - can narrow this search more
//we are interested in delivered
if (!((DSN_ACTION_FAILURE_ALL | DSN_ACTION_FAILURE| DSN_ACTION_DELAYED)
& dwDSNActions))
{
//it is safe to check only delivered
*pdwRecipMask |= RP_DELIVERED;
*pdwRecipFlags |= RP_DELIVERED;
}
}
DebugTrace((LPARAM) this,
"DSN Action 0x%08X, Recip mask 0x%08X, Recip flags 0x%08X",
dwDSNActions, *pdwRecipMask, *pdwRecipFlags);
TraceFunctLeave();
}
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::HrGetNextRecipient
//
// Synopsis: Returns the next recipient for wich a DSN action should
// be taken.
//
// Arguments:
// piRecipient: Receives next recipient index
// pdwDSNAction: Receives DSN Action(s) that should be taken
//
// Returns:
// S_OK: Success
// HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)
// E_UNEXPECTED: Need to call HrReset first.
//
// History:
// jstamerj 2000/11/09 15:30:17: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNRecipientIterator::HrGetNextRecipient(
OUT DWORD *piRecipient,
OUT DWORD *pdwDSNAction)
{
HRESULT hr = S_OK;
DWORD iCurrentRecip = 0;
DWORD dwCurrentRecipFlags = 0;
DWORD dwCurrentDSNAction = 0;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::HrGetNextRecipient");
if((piRecipient == NULL) ||
(pdwDSNAction == NULL))
{
hr = E_INVALIDARG;
goto CLEANUP;
}
if(! m_fFilterInit)
{
hr = E_UNEXPECTED;
goto CLEANUP;
}
while(SUCCEEDED(hr) && (dwCurrentDSNAction == 0))
{
hr = m_pIRecips->GetNextRecipient(&m_rpfctxt, &iCurrentRecip);
if(FAILED(hr))
goto CLEANUP;
hr = m_pIRecips->GetDWORD(
iCurrentRecip,
IMMPID_RP_RECIPIENT_FLAGS,
&dwCurrentRecipFlags);
if(hr == MAILMSG_E_PROPNOTFOUND)
{
dwCurrentRecipFlags = 0;
}
else if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"Failure 0x%08X to get flags for recip %d",
hr, iCurrentRecip);
goto CLEANUP;
}
DebugTrace((LPARAM) this,
"Recipient %d with flags 0x%08X found",
iCurrentRecip, dwCurrentRecipFlags);
GetDSNAction(
m_dwDSNActions,
dwCurrentRecipFlags,
&dwCurrentDSNAction);
}
if(SUCCEEDED(hr))
{
*pdwDSNAction = dwCurrentDSNAction;
*piRecipient = iCurrentRecip;
}
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNRecipientIterator::HrGetNextRecipient
//---[ CDefaultDSNRecipientIterator::GetDSNAction ]-------------------------
//
//
// Description:
// Determines what DSN action needs to happen on a recipient based on
// the requested DSN actions and the recipient flags
// Parameters:
// IN dwDSNAction The requested DSN actions
// IN dwCurrentRecipFlags The flags for current recipient...
// OUT pdwCurrentDSNAction The DSN action that needs to be performed
// On this recipient (DSN_ACTION_FAILURE is
// used to denote sending a NDR)
// Returns: Nothing
// History:
// 7/2/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
VOID CDefaultDSNRecipientIterator::GetDSNAction(
IN DWORD dwDSNAction,
IN DWORD dwCurrentRecipFlags,
OUT DWORD *pdwCurrentDSNAction)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNRecipientIterator::fdwGetDSNAction");
_ASSERT(pdwCurrentDSNAction);
DWORD dwOriginalRecipFlags = dwCurrentRecipFlags;
DWORD dwRecipFlagsForAction = 0;
DWORD dwFlags = 0;
//This should never be hit because of the filter
_ASSERT(!(dwCurrentRecipFlags & (RP_DSN_HANDLED | RP_DSN_NOTIFY_NEVER)));
*pdwCurrentDSNAction = 0;
if (DSN_ACTION_FAILURE & dwDSNAction)
{
if ((RP_GENERAL_FAILURE & dwCurrentRecipFlags) &&
((RP_DSN_NOTIFY_FAILURE & dwCurrentRecipFlags) ||
(!(RP_DSN_NOTIFY_MASK & dwCurrentRecipFlags))))
{
DebugTrace((LPARAM) this, "Recipient matched for FAILURE DSN");
// Recip flags will be updated in HrNotifyActionHandled
// dwCurrentRecipFlags |= RP_DSN_SENT_NDR;
*pdwCurrentDSNAction = DSN_ACTION_FAILURE;
goto Exit;
}
}
if (DSN_ACTION_FAILURE_ALL & dwDSNAction)
{
//Fail all non-delivered that we haven't sent notifications for
if (((!((RP_DSN_HANDLED | (RP_DELIVERED ^ RP_HANDLED)) & dwCurrentRecipFlags))) &&
((RP_DSN_NOTIFY_FAILURE & dwCurrentRecipFlags) ||
(!(RP_DSN_NOTIFY_MASK & dwCurrentRecipFlags))))
{
//Don't send failures for expanded DL;s
if (RP_EXPANDED != (dwCurrentRecipFlags & RP_EXPANDED))
{
DebugTrace((LPARAM) this, "Recipient matched for FAILURE (all) DSN");
// dwCurrentRecipFlags |= RP_DSN_SENT_NDR;
*pdwCurrentDSNAction = DSN_ACTION_FAILURE_ALL;
goto Exit;
}
}
}
if (DSN_ACTION_DELAYED & dwDSNAction)
{
//send at most 1 delay DSN
//Also send only if DELAY was requested or no specific instructions were
//specified
if ((!((RP_DSN_SENT_DELAYED | RP_HANDLED) & dwCurrentRecipFlags)) &&
((RP_DSN_NOTIFY_DELAY & dwCurrentRecipFlags) ||
(!(RP_DSN_NOTIFY_MASK & dwCurrentRecipFlags))))
{
DebugTrace((LPARAM) this, "Recipient matched for DELAYED DSN");
// dwCurrentRecipFlags |= RP_DSN_SENT_DELAYED;
*pdwCurrentDSNAction = DSN_ACTION_DELAYED;
goto Exit;
}
}
if (DSN_ACTION_RELAYED & dwDSNAction)
{
//send relay if it was delivered *and* DSN not supported by remote MTA
//*and* notification of success was explicitly requested
dwFlags = (RP_DELIVERED ^ RP_HANDLED) |
RP_REMOTE_MTA_NO_DSN |
RP_DSN_NOTIFY_SUCCESS;
if ((dwFlags & dwCurrentRecipFlags) == dwFlags)
{
DebugTrace((LPARAM) this, "Recipient matched for RELAYED DSN");
// dwCurrentRecipFlags |= RP_DSN_SENT_RELAYED;
*pdwCurrentDSNAction = DSN_ACTION_RELAYED;
goto Exit;
}
}
if (DSN_ACTION_DELIVERED & dwDSNAction)
{
//send delivered if it was delivered *and* no DSN sent yet
dwFlags = (RP_DELIVERED ^ RP_HANDLED) | RP_DSN_NOTIFY_SUCCESS;
_ASSERT(!(dwCurrentRecipFlags & RP_DSN_HANDLED)); //should be filtered out
if ((dwFlags & dwCurrentRecipFlags) == dwFlags)
{
DebugTrace((LPARAM) this, "Recipient matched for SUCCESS DSN");
// dwCurrentRecipFlags |= RP_DSN_SENT_DELIVERED;
*pdwCurrentDSNAction = DSN_ACTION_DELIVERED;
goto Exit;
}
}
if (DSN_ACTION_EXPANDED & dwDSNAction)
{
//Send expanded if the recipient is marked as expanded and
//NOTIFY=SUCCESS was requested
if ((RP_EXPANDED == (dwCurrentRecipFlags & RP_EXPANDED)) &&
(dwCurrentRecipFlags & RP_DSN_NOTIFY_SUCCESS) &&
!(dwCurrentRecipFlags & RP_DSN_SENT_EXPANDED))
{
DebugTrace((LPARAM) this, "Recipient matched for EXPANDED DSN");
// dwCurrentRecipFlags |= RP_DSN_SENT_EXPANDED;
*pdwCurrentDSNAction = DSN_ACTION_EXPANDED;
goto Exit;
}
}
Exit:
GetRecipientFlagsForActions(
*pdwCurrentDSNAction,
&dwRecipFlagsForAction);
TraceFunctLeave();
}
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::HrNotifyActionHandled
//
// Synopsis: Notifies that particular DSN(s) have been generated.
// Sets recipient flags so that recipient will not be enumerated again.
//
// Arguments:
// iRecipient: Recip index
// dwDSNAction: The action(s) performed
//
// Returns:
// S_OK: Success
// error from mailmsg
//
// History:
// jstamerj 2000/11/09 18:26:50: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNRecipientIterator::HrNotifyActionHandled(
IN DWORD iRecipient,
IN DWORD dwDSNAction)
{
HRESULT hr = S_OK;
DWORD dwRecipFlags = 0;
DWORD dwNewRecipFlags = 0;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::HrNotifyActionHandled");
hr = m_pIRecips->GetDWORD(
iRecipient,
IMMPID_RP_RECIPIENT_FLAGS,
&dwRecipFlags);
if(hr == MAILMSG_E_PROPNOTFOUND)
{
dwRecipFlags = 0;
}
else if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "m_pIRecips->GetDWORD failed");
goto CLEANUP;
}
GetRecipientFlagsForActions(
dwDSNAction,
&dwNewRecipFlags);
DebugTrace((LPARAM)this, "Orig recip flags: %08lx", dwRecipFlags);
dwRecipFlags |= dwNewRecipFlags;
hr = m_pIRecips->PutDWORD(
iRecipient,
IMMPID_RP_RECIPIENT_FLAGS,
dwRecipFlags);
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "m_pIRecips->PutDWORD failed hr %08lx", hr);
goto CLEANUP;
}
DebugTrace((LPARAM)this, "New recip flags: %08lx", dwRecipFlags);
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNRecipientIterator::HrNotifyActionHandled
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::GetRecipientFlagsForActions
//
// Synopsis: Get mailmsg recipient flags corresponding to an action
//
// Arguments:
// dwDSNAction: Action in question
// pdwRecipientFlags: Recip flags
//
// Returns: Nothing
//
// History:
// jstamerj 2000/11/09 16:18:34: Created.
//
//-------------------------------------------------------------
VOID CDefaultDSNRecipientIterator::GetRecipientFlagsForActions(
IN DWORD dwDSNAction,
OUT DWORD *pdwRecipientFlags)
{
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::GetRecipientFlagsForActions");
*pdwRecipientFlags = 0;
if(dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
{
*pdwRecipientFlags |= RP_DSN_SENT_NDR;
}
if(dwDSNAction & DSN_ACTION_DELAYED)
{
*pdwRecipientFlags |= RP_DSN_SENT_DELAYED;
}
if(dwDSNAction & DSN_ACTION_RELAYED)
{
*pdwRecipientFlags |= RP_DSN_SENT_RELAYED;
}
if(dwDSNAction & DSN_ACTION_DELIVERED)
{
*pdwRecipientFlags |= RP_DSN_SENT_DELIVERED;
}
if(dwDSNAction & DSN_ACTION_EXPANDED)
{
*pdwRecipientFlags |= RP_DSN_SENT_EXPANDED;
}
DebugTrace((LPARAM)this, "dwDSNAction: %08lx", dwDSNAction);
DebugTrace((LPARAM)this, "*pdwRecipientFlags: %08lx", *pdwRecipientFlags);
TraceFunctLeaveEx((LPARAM)this);
} // CDefaultDSNRecipientIterator::GetRecipientFlagsForActions
//+------------------------------------------------------------
//
// Function: CDefaultDSNRecipientIterator::TermianteFilter
//
// Synopsis: Terminate the mailmsg filter
//
// Arguments: None
//
// Returns: Nothing
//
// History:
// jstamerj 2000/11/14 14:14:59: Created.
//
//-------------------------------------------------------------
VOID CDefaultDSNRecipientIterator::TerminateFilter()
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNRecipientIterator::TermianteFilter");
if(m_fFilterInit)
{
//recycle context
m_fFilterInit = FALSE;
hr = m_pIRecips->TerminateRecipientFilterContext(&m_rpfctxt);
_ASSERT(SUCCEEDED(hr) && "TerminateRecipientFilterContext FAILED!!!!");
}
TraceFunctLeaveEx((LPARAM)this);
} // CDefaultDSNRecipientIterator::TermianteFilter
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::CDefaultDSNSink
//
// Synopsis: Constructor; initialize member data
//
// Arguments: None
//
// Returns: Nothing
//
// History:
// jstamerj 2000/12/04 17:41:35: Created.
//
//-------------------------------------------------------------
CDefaultDSNSink::CDefaultDSNSink(
IUnknown *pUnk)
{
FILETIME ftStartTime;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::CDefaultDSNSink");
m_dwSig = SIGNATURE_CDEFAULTDSNSINK;
m_pUnk = pUnk;
_ASSERT_RECIP_FLAGS();
m_fInit = FALSE;
m_cDSNsRequested = 0;
//Init string for MIME headers
GetSystemTimeAsFileTime(&ftStartTime);
wsprintf(m_szPerInstanceMimeBoundary, "%08X%08X",
ftStartTime.dwHighDateTime, ftStartTime.dwLowDateTime);
TraceFunctLeaveEx((LPARAM)this);
} // CDefaultDSNSink::CDefaultDSNSink
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::QueryInterface
//
// Synopsis: Return a requested interface
//
// Arguments:
// riid: Interface ID
// ppvObj: Return place for interface
//
// Returns:
// S_OK: Success
// E_NOINTERFACE: Not a supported interface
//
// History:
// jstamerj 2000/12/08 20:05:46: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNSink::QueryInterface(
REFIID riid,
LPVOID *ppvObj)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::QueryInterface");
*ppvObj = NULL;
if(riid == IID_IUnknown)
{
*ppvObj = (IUnknown *)this;
}
else if(riid == IID_IDSNGenerationSink)
{
*ppvObj = (IDSNGenerationSink *)this;
}
else
{
hr = E_NOINTERFACE;
}
if(SUCCEEDED(hr))
AddRef();
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNSink::QueryInterface
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::OnSyncSinkInit
//
// Synopsis: Initialize the sink.
//
// Arguments:
// dwVSID: Virtual server ID
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/14 13:58:32: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNSink::OnSyncSinkInit(
IN DWORD dwVSID)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::OnSyncSinkInit");
DebugTrace((LPARAM)this, "VSID: %d", dwVSID);
m_dwVSID = dwVSID;
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNSink::OnSyncSinkInit
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::OnSyncGetDSNRecipientIterator
//
// Synopsis: Not implemented
//
// Arguments: see smtpevent.idl
//
// Returns:
// S_OK: Success
// E_OUTOFMEMORY
//
// History:
// jstamerj 2000/11/14 14:00:00: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNSink::OnSyncGetDSNRecipientIterator(
IN ISMTPServer *pISMTPServer,
IN IMailMsgProperties *pIMsg,
IN IMailMsgPropertyBag *pDSNProperties,
IN DWORD dwStartDomain,
IN DWORD dwDSNActions,
IN IDSNRecipientIterator *pRecipIterIN,
OUT IDSNRecipientIterator **ppRecipIterOUT)
{
return E_NOTIMPL;
} // CDefaultDSNSink::OnSyncGetDSNRecipientIterator
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::OnSyncGenerateDSN
//
// Synopsis: Implements the default DSN generation sink
//
// Arguments: see smtpevent.idl
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/14 14:30:45: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNSink::OnSyncGenerateDSN(
IN ISMTPServer *pISMTPServer,
IN IDSNSubmission *pIDSNSubmission,
IN IMailMsgProperties *pIMsg,
IN IMailMsgPropertyBag *pDSNProperties,
IN IDSNRecipientIterator *pRecipIter)
{
HRESULT hr = S_OK;
DWORD dwCount = 0;
//
// Initialize parameters to default values
//
DWORD dwDSNActions = 0;
DWORD dwDSNOptions = 0;
DWORD dwRFC821Status = 0;
HRESULT hrStatus = S_OK;
DWORD dwPreferredLangId = 0;
LPSTR szDefaultDomain = NULL;
LPSTR szReportingMTA = NULL;
LPSTR szReportingMTAType = NULL;
LPSTR szDSNContext = NULL;
LPSTR szCopyNDRTo = NULL;
LPSTR szTopCustomText = NULL;
LPSTR szBottomCustomText = NULL;
LPWSTR wszTopCustomText = NULL;
LPWSTR wszBottomCustomText = NULL;
DWORD cIterationsLeft = 0;
IMailMsgProperties *pDSNMsg = NULL;
DWORD cbCurrentSize = 0; //used to get size of returned property
FILETIME ftExpireTime;
FILETIME *pftExpireTime = NULL;
DWORD dwDSNRetType = 0;
HRESULT hrContentFailure = S_OK;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::OnSyncGenerateDSN");
struct _tagDWORDProps
{
DWORD dwPropId;
DWORD *pdwValue;
} DsnDwordProps[] =
{
{ DSNPROP_DW_DSNACTIONS, & dwDSNActions },
{ DSNPROP_DW_DSNOPTIONS, & dwDSNOptions },
{ DSNPROP_DW_RFC821STATUS, & dwRFC821Status },
{ DSNPROP_DW_HRSTATUS, (DWORD *) & hrStatus },
{ DSNPROP_DW_LANGID, & dwPreferredLangId },
{ DSNPROP_DW_RET_TYPE, & dwDSNRetType },
{ DSNPROP_DW_CONTENT_FAILURE, (DWORD *) & hrContentFailure },
};
struct _tagStringProps
{
DWORD dwPropId;
LPSTR *ppsz;
} DsnStringProps[] =
{
{ DSNPROP_SZ_DEFAULTDOMAIN, & szDefaultDomain },
{ DSNPROP_SZ_REPORTINGMTA, & szReportingMTA },
{ DSNPROP_SZ_REPORTINGMTATYPE, & szReportingMTAType },
{ DSNPROP_SZ_DSNCONTEXT, & szDSNContext },
{ DSNPROP_SZ_COPYNDRTO, & szCopyNDRTo },
{ DSNPROP_SZ_HR_TOP_CUSTOM_TEXT_A, & szTopCustomText },
{ DSNPROP_SZ_HR_BOTTOM_CUSTOM_TEXT_A, & szBottomCustomText },
};
struct _tagWideStringProps
{
DWORD dwPropId;
LPWSTR *ppwsz;
} DsnWideStringProps[] =
{
{ DSNPROP_SZ_HR_TOP_CUSTOM_TEXT_W, & wszTopCustomText },
{ DSNPROP_SZ_HR_BOTTOM_CUSTOM_TEXT_W, & wszBottomCustomText },
};
//
// Get DWORDs
//
for(dwCount = 0;
dwCount < ( sizeof(DsnDwordProps) / sizeof(DsnDwordProps[0]));
dwCount++)
{
hr = pDSNProperties->GetDWORD(
DsnDwordProps[dwCount].dwPropId,
DsnDwordProps[dwCount].pdwValue);
if(FAILED(hr) && (hr != MAILMSG_E_PROPNOTFOUND))
goto CLEANUP;
}
//
// Get Strings
//
for(dwCount = 0;
dwCount < ( sizeof(DsnStringProps) / sizeof(DsnStringProps[0]));
dwCount++)
{
BYTE bStupid = 0;
DWORD dwcb = 0;
hr = pDSNProperties->GetProperty(
DsnStringProps[dwCount].dwPropId,
0, // Length
&dwcb, // pcbLength
&bStupid);
if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
{
*(DsnStringProps[dwCount].ppsz) = new CHAR[dwcb+1];
if( (*(DsnStringProps[dwCount].ppsz)) == NULL)
{
hr = E_OUTOFMEMORY;
goto CLEANUP;
}
hr = pDSNProperties->GetStringA(
DsnStringProps[dwCount].dwPropId,
dwcb+1,
*(DsnStringProps[dwCount].ppsz));
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "GetStringA failed hr %08lx", hr);
goto CLEANUP;
}
}
else if(FAILED(hr) && (hr != MAILMSG_E_PROPNOTFOUND))
{
ErrorTrace((LPARAM)this, "GetProperty failed hr %08lx", hr);
goto CLEANUP;
}
}
//
// Get Wide Strings
//
for(dwCount = 0;
dwCount < ( sizeof(DsnWideStringProps) / sizeof(DsnWideStringProps[0]));
dwCount++)
{
BYTE bStupid = 0;
DWORD dwcb = 0;
hr = pDSNProperties->GetProperty(
DsnWideStringProps[dwCount].dwPropId,
0, // Length
&dwcb, // pcbLength
&bStupid);
if(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
{
*(DsnWideStringProps[dwCount].ppwsz) = new WCHAR[(dwcb / sizeof(WCHAR))+1];
if( (*(DsnWideStringProps[dwCount].ppwsz)) == NULL)
{
hr = E_OUTOFMEMORY;
goto CLEANUP;
}
hr = pDSNProperties->GetStringW(
DsnWideStringProps[dwCount].dwPropId,
(dwcb / sizeof(WCHAR))+1,
*(DsnWideStringProps[dwCount].ppwsz));
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "GetStringA failed hr %08lx", hr);
goto CLEANUP;
}
}
else if(FAILED(hr) && (hr != MAILMSG_E_PROPNOTFOUND))
{
ErrorTrace((LPARAM)this, "GetProperty failed hr %08lx", hr);
goto CLEANUP;
}
}
//Get MsgExpire Time
//Write DSN_RP_HEADER_RETRY_UNTIL using expire FILETIME
hr = pDSNProperties->GetProperty(
DSNPROP_FT_EXPIRETIME,
sizeof(FILETIME),
&cbCurrentSize,
(BYTE *) &ftExpireTime);
if (SUCCEEDED(hr))
{
_ASSERT(sizeof(FILETIME) == cbCurrentSize);
if (sizeof(FILETIME) == cbCurrentSize)
pftExpireTime = &ftExpireTime;
}
else if (MAILMSG_E_PROPNOTFOUND == hr)
hr = S_OK;
else
goto CLEANUP;
do
{
DWORD dwDSNActionsGenerated = 0;
DWORD cRecipsDSNd = 0;
hr = HrGenerateDSNInternal(
pISMTPServer,
pIMsg,
pRecipIter,
pIDSNSubmission,
dwDSNActions,
dwRFC821Status,
hrStatus,
szDefaultDomain,
szDefaultDomain ? lstrlen(szDefaultDomain) : 0,
szReportingMTA,
szReportingMTA ? lstrlen(szReportingMTA) : 0,
szReportingMTAType,
szReportingMTAType ? lstrlen(szReportingMTAType) : 0,
szDSNContext,
szDSNContext ? lstrlen(szDSNContext) : 0,
dwPreferredLangId,
dwDSNOptions,
szCopyNDRTo,
szCopyNDRTo ? lstrlen(szCopyNDRTo) : 0,
pftExpireTime,
szTopCustomText,
wszTopCustomText,
szBottomCustomText,
wszBottomCustomText,
&pDSNMsg,
&dwDSNActionsGenerated,
&cRecipsDSNd,
&cIterationsLeft,
dwDSNRetType,
hrContentFailure);
if(FAILED(hr))
{
_ASSERT(pDSNMsg == NULL);
ErrorTrace((LPARAM)this, "HrGenerateDSNInternal failed hr %08lx", hr);
goto CLEANUP;
}
else if(pDSNMsg)
{
//
// Submit the DSN to the system
//
hr = pIDSNSubmission->HrSubmitDSN(
dwDSNActionsGenerated,
cRecipsDSNd,
pDSNMsg);
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "HrSubmitDSN failed hr %08lx", hr);
goto CLEANUP;
}
pDSNMsg->Release();
pDSNMsg = NULL;
}
} while(SUCCEEDED(hr) && cIterationsLeft);
if(hr == AQUEUE_E_NDR_OF_DSN)
{
DebugTrace((LPARAM)this, "NDR of DSN; setting badmail flag");
hr = pDSNProperties->PutBool(
DSNPROP_F_BADMAIL_MSG,
TRUE);
}
CLEANUP:
for(dwCount = 0;
dwCount < ( sizeof(DsnStringProps) / sizeof(DsnStringProps[0]));
dwCount++)
{
if(*(DsnStringProps[dwCount].ppsz))
{
delete [] (*(DsnStringProps[dwCount].ppsz));
*(DsnStringProps[dwCount].ppsz) = NULL;
}
}
for(dwCount = 0;
dwCount < ( sizeof(DsnWideStringProps) / sizeof(DsnWideStringProps[0]));
dwCount++)
{
if(*(DsnWideStringProps[dwCount].ppwsz))
{
delete [] (*(DsnWideStringProps[dwCount].ppwsz));
*(DsnWideStringProps[dwCount].ppwsz) = NULL;
}
}
if(pDSNMsg)
pDSNMsg->Release();
//
// Return failures in a property
//
if(FAILED(hr))
{
HRESULT hrProp;
hrProp = pDSNProperties->PutDWORD(
DSNPROP_DW_HR_RETURN_STATUS,
hr);
_ASSERT(SUCCEEDED(hrProp));
ErrorTrace((LPARAM)this, "Unable to generate DSN: %08lx", hr);
//
// Return S_OK to dispatcher
//
hr = S_OK;
}
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return SUCCEEDED(hr) ? S_OK : hr;
} // CDefaultDSNSink::OnSyncGenerateDSN
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::HrGenerateDSNInternal
//
// Synopsis: Generates one DSN message
//
// Arguments:
// pISMTPServer Interface used to generate DSN
// pIMailMsgProperties IMailMsg to generate DSN for
// pIRecipIter Controls which recipients to DSN
// dwDSNActions DSN Actions requested
// dwRFC821Status Global RFC821 status DWORD
// hrStatus Global HRESULT status
// szDefaultDomain Default domain (used to create FROM address)
// cbDefaultDomain string length of szDefaultDomain
// szReportingMTA Name of MTA requesting DSN generation
// cbReportingMTA string length of szReportingMTA
// szReportingMTAType Type of MTA requestiong DSN (SMTP is "dns"
// cbReportingMTAType string length of szReportingMTAType
// PreferredLangId Language to generate DSN in
// dwDSNOptions Options flags as defined in aqueue.idl
// szCopyNDRTo SMTP Address to copy NDR to
// cbCopyNDRTo string lengtt of szCopyNDRTo
// ppIMailMsgPeropertiesDSN Generated DSN.
// pdwDSNTypesGenerated Describes the type(s) of DSN's generated
// pcRecipsDSNd # of recipients that were DSN'd for this message
// pcIterationsLeft # of times remaining that this function needs
// to be called to generate all requested DSNs.
// First-time caller should initialize to
// zero
// dwDSNRetType Return type for DSN
// hrContentFailure Value for X-Content-Failure header
//
// Returns:
// S_OK on success
// AQUEUE_E_NDR_OF_DSN if attempting to NDR a DSN
// History:
// 6/30/98 - MikeSwa Created
// 12/14/98 - MikeSwa Modified (Added pcIterationsLeft)
// 10/13/1999 - MikeSwa Modified (Added szDefaultDomain)
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/16 10:53:30: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNSink::HrGenerateDSNInternal(
ISMTPServer *pISMTPServer,
IMailMsgProperties *pIMailMsgProperties,
IDSNRecipientIterator *pIRecipIter,
IDSNSubmission *pIDSNSubmission,
DWORD dwDSNActions,
DWORD dwRFC821Status,
HRESULT hrStatus,
LPSTR szDefaultDomain,
DWORD cbDefaultDomain,
LPSTR szReportingMTA,
DWORD cbReportingMTA,
LPSTR szReportingMTAType,
DWORD cbReportingMTAType,
LPSTR szDSNContext,
DWORD cbDSNContext,
DWORD dwPreferredLangId,
DWORD dwDSNOptions,
LPSTR szCopyNDRTo,
DWORD cbCopyNDRTo,
FILETIME *pftExpireTime,
LPSTR szHRTopCustomText,
LPWSTR wszHRTopCustomText,
LPSTR szHRBottomCustomText,
LPWSTR wszHRBottomCustomText,
IMailMsgProperties **ppIMailMsgPropertiesDSN,
DWORD *pdwDSNTypesGenerated,
DWORD *pcRecipsDSNd,
DWORD *pcIterationsLeft,
DWORD dwDSNRetTypeIN,
HRESULT hrContentFailure)
{
HRESULT hr = S_OK;
HRESULT hrTmp = S_OK;
DWORD iCurrentRecip = 0;
DWORD dwCurrentDSNAction = 0;
DWORD dwDSNActionsNeeded = 0; //the type of DSNs that will actually be sent
IMailMsgRecipients *pIMailMsgRecipients = NULL;
IMailMsgProperties *pIMailMsgPropertiesDSN = NULL;
PFIO_CONTEXT pDSNBody = NULL;
CDSNBuffer dsnbuff;
CHAR szMimeBoundary[MIME_BOUNDARY_SIZE];
DWORD cbMimeBoundary = 0;
CHAR szExpireTimeBuffer[MAX_RFC822_DATE_SIZE];
LPSTR szExpireTime = NULL; //will point to szExpireTimeBuffer if found
DWORD cbExpireTime = 0;
DWORD iRecip = 0;
DWORD dwDSNAction = 0;
DWORD dwDSNRetType = dwDSNRetTypeIN;
DWORD dwOrigMsgSize = 0;
HRESULT hrContent = hrContentFailure;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::HrGenerateDSNInternal");
_ASSERT(ppIMailMsgPropertiesDSN);
_ASSERT(pISMTPServer);
_ASSERT(pIMailMsgProperties);
_ASSERT(pdwDSNTypesGenerated);
_ASSERT(pcRecipsDSNd);
_ASSERT(pcIterationsLeft);
*pcRecipsDSNd = 0;
*ppIMailMsgPropertiesDSN = NULL;
*pdwDSNTypesGenerated = 0;
GetCurrentMimeBoundary(szReportingMTA, cbReportingMTA, szMimeBoundary, &cbMimeBoundary);
//Get Recipients interface
hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
(PVOID *) &pIMailMsgRecipients);
if (FAILED(hr))
goto Exit;
hr = pIRecipIter->HrReset();
if (FAILED(hr))
goto Exit;
//Loop over recipients to make sure we can need to allocate a message
hr = pIRecipIter->HrGetNextRecipient(
&iRecip,
&dwDSNAction);
while (SUCCEEDED(hr))
{
DebugTrace((LPARAM) this,
"Recipient %d with DSN Action 0x%08X found",
iRecip, dwDSNAction);
//keep track of the types of DSN's we will be generating
dwDSNActionsNeeded |= dwDSNAction;
hr = pIRecipIter->HrGetNextRecipient(
&iRecip,
&dwDSNAction);
}
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
hr = S_OK; //we just reached the end of the context
else if (FAILED(hr))
ErrorTrace((LPARAM) this, "GetNextRecipient failed with 0x%08X",hr);
if (dwDSNActionsNeeded == 0)
{
DebugTrace((LPARAM) this,
"Do not need to generate a 0x%08X DSN",
dwDSNActions, pIMailMsgProperties);
*pcIterationsLeft = 0;
goto Exit; //don't create a message object if we don't have to
}
//Check if message is a DSN (we will not genrate a DSN of a DSN)
//This must be checked after we run through the recipients, because
//we need to check them to keep from badmailing this message
//multiple times.
if (fIsMailMsgDSN(pIMailMsgProperties))
{
DebugTrace((LPARAM) pIMailMsgProperties, "Message is a DSN");
*pcIterationsLeft = 0;
if (dwDSNActions & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
{
//NDR of DSN... return special error code
hr = AQUEUE_E_NDR_OF_DSN;
//mark all the appropriate recipient flags so we don't
//generate 2 badmails
HrMarkAllRecipFlags(
dwDSNActions,
pIRecipIter);
}
goto Exit;
}
//if we can generate a failure DSN and the orginal request was for
//fail *all* make sure dwDSNActionNeeded reflects this
if ((DSN_ACTION_FAILURE & dwDSNActionsNeeded) &&
(DSN_ACTION_FAILURE_ALL & dwDSNActions))
dwDSNActionsNeeded |= DSN_ACTION_FAILURE_ALL;
GetCurrentIterationsDSNAction(&dwDSNActionsNeeded, pcIterationsLeft);
if (!dwDSNActionsNeeded)
{
*pcIterationsLeft = 0;
goto Exit; //don't create a message object if we don't have to
}
hr = pIDSNSubmission->HrAllocBoundMessage(
&pIMailMsgPropertiesDSN,
&pDSNBody);
if (FAILED(hr))
goto Exit;
//workaround to handle AllocBoundMessage on shutdown
if (NULL == pDSNBody)
{
hr = HRESULT_FROM_WIN32(ERROR_NO_SYSTEM_RESOURCES);
ErrorTrace((LPARAM) this, "ERROR: AllocBoundMessage failed silently");
goto Exit;
}
//Associate file handle with CDSNBuffer
hr = dsnbuff.HrInitialize(pDSNBody);
if (FAILED(hr))
goto Exit;
//Get MsgExpire Time
//Write DSN_RP_HEADER_RETRY_UNTIL using expire FILETIME
if (pftExpireTime)
{
//convert to internet standard
if (FileTimeToLocalRFC822Date(*pftExpireTime, szExpireTimeBuffer))
{
szExpireTime = szExpireTimeBuffer;
cbExpireTime = lstrlen(szExpireTime);
}
}
//
// If the RET type has not yet been specified, default to:
// FULL for Failure/Delay DSNs
// HDRS for other types of DSNs (Expanded/Delivered/Relayed)
//
if(dwDSNRetType == 0)
{
if ((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL | DSN_ACTION_DELAYED)
& dwDSNActionsNeeded)
dwDSNRetType = DSN_RET_FULL;
else
dwDSNRetType = DSN_RET_HDRS;
}
//
// If we're going to need the original content, get the size now.
//
if(SUCCEEDED(hrContent) && (dwDSNRetType != DSN_RET_PARTIAL_HDRS))
{
//Get the content size
hrContent = pIMailMsgProperties->GetContentSize(&dwOrigMsgSize, NULL);
}
//
// If we received EFNF on obtaining content size, we should simply skip this message
// without generating an NDR.
//
if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hrContent) {
DebugTrace((LPARAM)this, "GetContentSize failed with EFNF so not generating NDR or badmail.");
*pcIterationsLeft = 0;
hr = S_OK;
goto Exit;
}
if (FAILED(hrContent))
{
//
// Rather than badmailing, generate a DSN with only a header subset
//
ErrorTrace((LPARAM)this, "GetContentSize failed hr %08lx", hr);
dwDSNRetType = DSN_RET_PARTIAL_HDRS;
}
hr = HrWriteDSNP1AndP2Headers(dwDSNActionsNeeded,
pIMailMsgProperties, pIMailMsgPropertiesDSN,
&dsnbuff, szDefaultDomain, cbDefaultDomain,
szReportingMTA, cbReportingMTA,
szDSNContext, cbDSNContext,
szCopyNDRTo, hrStatus,
szMimeBoundary, cbMimeBoundary, dwDSNOptions,
hrContent);
if (FAILED(hr))
goto Exit;
hr = HrWriteDSNHumanReadable(pIMailMsgPropertiesDSN, pIMailMsgRecipients,
pIRecipIter,
dwDSNActionsNeeded,
&dsnbuff, dwPreferredLangId,
szMimeBoundary, cbMimeBoundary, hrStatus,
szHRTopCustomText, wszHRTopCustomText,
szHRBottomCustomText, wszHRBottomCustomText);
if (FAILED(hr))
goto Exit;
hr = HrWriteDSNReportPerMsgProperties(pIMailMsgProperties,
&dsnbuff, szReportingMTA, cbReportingMTA,
szMimeBoundary, cbMimeBoundary);
if (FAILED(hr))
goto Exit;
//recycle context again (may be used during generation of human readable)
hr = pIRecipIter->HrReset();
if (FAILED(hr))
goto Exit;
//$$REVIEW - Do we need to keep an "undo" list... or perhaps reverse
//engineer what the previous value was in case of a failure
hr = pIRecipIter->HrGetNextRecipient(&iCurrentRecip, &dwCurrentDSNAction);
while (SUCCEEDED(hr))
{
if(dwDSNActionsNeeded & dwCurrentDSNAction)
{
*pdwDSNTypesGenerated |= (dwCurrentDSNAction & DSN_ACTION_TYPE_MASK);
(*pcRecipsDSNd)++;
hr = HrWriteDSNReportPreRecipientProperties(
pIMailMsgRecipients, &dsnbuff,
iCurrentRecip, szExpireTime, cbExpireTime,
dwCurrentDSNAction, dwRFC821Status, hrStatus);
if (FAILED(hr))
goto Exit;
hr = pIRecipIter->HrNotifyActionHandled(
iCurrentRecip,
dwCurrentDSNAction);
_ASSERT(SUCCEEDED(hr) && "HrNotifyActionHandled failed on 2nd pass");
if (FAILED(hr))
goto Exit;
// DSN Logging
hr = HrLogDSNGenerationEvent(
pISMTPServer,
pIMailMsgProperties,
pIMailMsgRecipients,
iCurrentRecip,
dwCurrentDSNAction,
dwRFC821Status,
hrStatus);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "Failed to log DSN generation with 0x%08X",hr);
hr = S_OK; // We can accept this failure ...
}
}
hr = pIRecipIter->HrGetNextRecipient(&iCurrentRecip, &dwCurrentDSNAction);
}
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
hr = S_OK;
if (0 == (*pcRecipsDSNd))
goto Exit; //no work to do
hr = HrWriteDSNClosingAndOriginalMessage(pIMailMsgProperties,
pIMailMsgPropertiesDSN, &dsnbuff, pDSNBody,
dwDSNActionsNeeded, szMimeBoundary, cbMimeBoundary,
dwDSNRetType, dwOrigMsgSize);
if (FAILED(hr))
goto Exit;
hr = pIMailMsgPropertiesDSN->Commit(NULL);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pIMailMsgProperties, "ERROR: IMailMsg::Commit failed - hr 0x%08X", hr);
goto Exit;
}
*ppIMailMsgPropertiesDSN = pIMailMsgPropertiesDSN;
pIMailMsgPropertiesDSN = NULL;
Exit:
if (pIMailMsgRecipients)
{
pIMailMsgRecipients->Release();
}
if (pIMailMsgPropertiesDSN)
{
IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
//if non-NULL, then we should not be returning any value
_ASSERT(NULL == *ppIMailMsgPropertiesDSN);
//Check for alloc bound message failure
if (HRESULT_FROM_WIN32(ERROR_NO_SYSTEM_RESOURCES) != hr)
{
if (SUCCEEDED(pIMailMsgPropertiesDSN->QueryInterface(IID_IMailMsgQueueMgmt,
(void **) &pIMailMsgQueueMgmt)))
{
_ASSERT(pIMailMsgQueueMgmt);
pIMailMsgQueueMgmt->Delete(NULL);
pIMailMsgQueueMgmt->Release();
}
}
pIMailMsgPropertiesDSN->Release();
}
if (FAILED(hr))
*pcIterationsLeft = 0;
//workaround for alloc bound message
if (HRESULT_FROM_WIN32(ERROR_NO_SYSTEM_RESOURCES) == hr)
{
hr = S_OK;
}
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNSink::HrGenerateDSNInternal
//+------------------------------------------------------------
//
// Function: CDefaultDSNSink::HrMarkAllRecipFlags
//
// Synopsis:
// Marks all recipient according to the DSN action. Used when an NDR of
// an NDR is found and we will not be generating a DSN, but need to mark
// the recips so we can not generate 2 badmail events.
//
// Arguments:
// IN DWORD dwDSNActions: Actions to mark
// IN IDSNRecipientIterator *pIRecipIter: Recipient iterator
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/11/20 17:13:12: Created.
//
//-------------------------------------------------------------
HRESULT CDefaultDSNSink::HrMarkAllRecipFlags(
IN DWORD dwDSNActions,
IN IDSNRecipientIterator *pIRecipIter)
{
HRESULT hr = S_OK;
DWORD iRecip = 0;
DWORD dwRecipDSNActions = 0;
TraceFunctEnterEx((LPARAM)this, "CDefaultDSNSink::HrMarkAllRecipFlags");
hr = pIRecipIter->HrReset();
if(FAILED(hr))
goto CLEANUP;
hr = pIRecipIter->HrGetNextRecipient(
&iRecip,
&dwRecipDSNActions);
if(FAILED(hr))
goto CLEANUP;
while(SUCCEEDED(hr))
{
hr = pIRecipIter->HrNotifyActionHandled(
iRecip,
dwDSNActions);
if(FAILED(hr))
goto CLEANUP;
hr = pIRecipIter->HrGetNextRecipient(
&iRecip,
&dwRecipDSNActions);
if(FAILED(hr))
goto CLEANUP;
}
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CDefaultDSNSink::HrMarkAllRecipFlags
//---[ CDefaultDSNSink::GetCurrentIterationsDSNAction ]------------------------
//
//
// Description:
// This function will select one of the pdwActualDSNAction to generate
// DSNs on during this call to the DSN generation sink.
// Parameters:
// IN OUT pdwActionDSNAction DSN Actions that can/will be used.
// IN OUT pcIterationsLeft Approximate # of times needed to call
// DSN generation
// Returns:
// -
// History:
// 12/14/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CDefaultDSNSink::GetCurrentIterationsDSNAction(
IN OUT DWORD *pdwActualDSNAction,
IN OUT DWORD *pcIterationsLeft)
{
_ASSERT(pdwActualDSNAction);
_ASSERT(pcIterationsLeft);
const DWORD MAX_DSN_ACTIONS = 6;
//In the following array FAILURE_ALL must come first or else we will
//generate separate failure DSNs for hard failures and undelivereds.
DWORD rgPossibleDSNActions[MAX_DSN_ACTIONS] = {DSN_ACTION_FAILURE_ALL,
DSN_ACTION_FAILURE,
DSN_ACTION_DELAYED,
DSN_ACTION_RELAYED,
DSN_ACTION_DELIVERED,
DSN_ACTION_EXPANDED};
DWORD i = 0;
DWORD iLastMatch = MAX_DSN_ACTIONS;
DWORD iFirstMatch = MAX_DSN_ACTIONS;
DWORD iStartIndex = 0;
//Since the possible DSNs to generate may change from call to call (because
//we are updating the pre-recipient flags), we need to generate and maintain
//pcIterationsLeft based on the possible Actions (which will not be changing
//from call to call).
_ASSERT(*pcIterationsLeft < MAX_DSN_ACTIONS);
//Figure out where we should start if this is not the
if (*pcIterationsLeft)
iStartIndex = MAX_DSN_ACTIONS-*pcIterationsLeft;
//Loop through possible DSN actions (that we haven't seen) and determine
//the first and last
for (i = iStartIndex; i < MAX_DSN_ACTIONS; i++)
{
if (rgPossibleDSNActions[i] & *pdwActualDSNAction)
{
iLastMatch = i;
if (MAX_DSN_ACTIONS == iFirstMatch)
iFirstMatch = i;
}
}
if (MAX_DSN_ACTIONS == iLastMatch)
{
//No matches... we are done
*pdwActualDSNAction = 0;
*pcIterationsLeft = 0;
return;
}
//If this is possible after the above check... then I've screwed up
_ASSERT(MAX_DSN_ACTIONS != iFirstMatch);
*pdwActualDSNAction = rgPossibleDSNActions[iFirstMatch];
if ((iLastMatch == iFirstMatch) ||
((rgPossibleDSNActions[iFirstMatch] == DSN_ACTION_FAILURE_ALL) &&
(rgPossibleDSNActions[iLastMatch] == DSN_ACTION_FAILURE)))
{
//This is our last time through
*pcIterationsLeft = 0;
}
else
{
*pcIterationsLeft = MAX_DSN_ACTIONS-1-iFirstMatch;
}
}
//---[ CDefaultDSNSink::HrWriteDSNP1AndP2Headers ]-----------------------------
//
//
// Description:
// Writes global DSN P1 Properties to IMailMsgProperties
// Parameters:
// dwDSNAction DSN action specified for sink
// pIMailMsgProperties Msg that DSN is being generated for
// pIMailMsgPropertiesDSN DSN being generated
// psndbuff Buffer to write to
// szDefaultDomain Default domain - used from postmaster from address
// cbDefaultDomain strlen of szDefaultDomain
// szReportingMTA Reporting MTA as passed to event sink
// cbReportingMTA strlen of szReportingMTA
// szDSNConext Debug File and line number info passed in
// cbDSNConext strlen of szDSNContext
// szCopyNDRTo SMTP Address to copy NDR to
// hrStatus Status to record in DSN context
// szMimeBoundary MIME boundary string
// cbMimeBoundary strlen of MIME boundary
// dwDSNOptions DSN Options flags
// hrContent Content result
//
// Returns:
// S_OK on success
// History:
// 7/5/98 - MikeSwa Created
// 8/14/98 - MikeSwa Modified - Added DSN context headers
// 11/9/98 - MikeSwa Added copy NDR to functionality
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteDSNP1AndP2Headers(
IN DWORD dwDSNAction,
IN IMailMsgProperties *pIMailMsgProperties,
IN IMailMsgProperties *pIMailMsgPropertiesDSN,
IN CDSNBuffer *pdsnbuff,
IN LPSTR szDefaultDomain,
IN DWORD cbDefaultDomain,
IN LPSTR szReportingMTA,
IN DWORD cbReportingMTA,
IN LPSTR szDSNContext,
IN DWORD cbDSNContext,
IN LPSTR szCopyNDRTo,
IN HRESULT hrStatus,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary,
IN DWORD dwDSNOptions,
IN HRESULT hrContent)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteDSNP1AndP2Headers");
HRESULT hr = S_OK;
BOOL bReturn = TRUE;
HRESULT hrTmp = S_OK;
CHAR szBuffer[512];
LPSTR szSender = (LPSTR) szBuffer; //tricks to avoid AV'ing in AddPrimary
IMailMsgRecipientsAdd *pIMailMsgRecipientsAdd = NULL;
IMailMsgRecipients *pIMailMsgRecipients = NULL;
DWORD dwRecipAddressProp = IMMPID_RP_ADDRESS_SMTP;
DWORD dwSMTPAddressProp = IMMPID_RP_ADDRESS_SMTP;
DWORD iCurrentAddressProp = 0;
DWORD dwDSNRecipient = 0;
DWORD cbPostMaster = 0;
CHAR szDSNAction[15];
FILETIME ftCurrentTime;
CHAR szCurrentTimeBuffer[MAX_RFC822_DATE_SIZE];
_ASSERT(pIMailMsgProperties);
_ASSERT(pIMailMsgPropertiesDSN);
_ASSERT(pdsnbuff);
szBuffer[0] = '\0';
//Get and write Message tracking properties
hr = pIMailMsgProperties->GetStringA(IMMPID_MP_SERVER_VERSION,
sizeof(szBuffer), szBuffer);
if (SUCCEEDED(hr))
{
hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_SERVER_VERSION, szBuffer);
if (FAILED(hr))
DebugTrace((LPARAM) this,
"Warning: Unable to write version to msg - 0x%08X", hr);
hr = S_OK;
}
else
{
DebugTrace((LPARAM) this,
"Warning: Unable to get server version from msg - 0x%08X", hr);
hr = S_OK; //ignore this non-fatal error
}
hr = pIMailMsgProperties->GetStringA(IMMPID_MP_SERVER_NAME,
sizeof(szBuffer), szBuffer);
if (SUCCEEDED(hr))
{
hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_SERVER_NAME, szBuffer);
if (FAILED(hr))
DebugTrace((LPARAM) this,
"Warning: Unable to write server name to msg - 0x%08X", hr);
hr = S_OK;
}
else
{
DebugTrace((LPARAM) this,
"Warning: Unable to get server name from msg - 0x%08X", hr);
hr = S_OK; //ignore this non-fatal error
}
//Set the type of message
if (dwDSNAction &
(DSN_ACTION_EXPANDED | DSN_ACTION_RELAYED | DSN_ACTION_DELIVERED)) {
hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSGCLASS,
MP_MSGCLASS_DELIVERY_REPORT);
} else if (dwDSNAction &
(DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL | DSN_ACTION_DELAYED)) {
hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSGCLASS,
MP_MSGCLASS_NONDELIVERY_REPORT);
}
if (FAILED(hr)) {
DebugTrace((LPARAM) this,
"Warning: Unable to set msg class for dsn - 0x%08X", hr);
hr = S_OK;
}
for (iCurrentAddressProp = 0;
iCurrentAddressProp < NUM_DSN_ADDRESS_PROPERTIES;
iCurrentAddressProp++)
{
szBuffer[0] = '\0';
//Get the sender of the original message
hr = pIMailMsgProperties->GetStringA(
g_rgdwSenderPropIDs[iCurrentAddressProp],
sizeof(szBuffer), szBuffer);
if (FAILED(hr) && (MAILMSG_E_PROPNOTFOUND != hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to get 0x%X sender of IMailMsg %p",
g_rgdwSenderPropIDs[iCurrentAddressProp], pIMailMsgProperties);
goto Exit;
}
//
// If we have found an address break
//
if (SUCCEEDED(hr))
break;
}
//
// If we failed to get a property... bail
//
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to get any sender of IMailMsg 0x%08X",
pIMailMsgProperties);
goto Exit;
}
//write DSN Sender (P1)
hr = pIMailMsgPropertiesDSN->PutProperty(IMMPID_MP_SENDER_ADDRESS_SMTP,
sizeof(DSN_MAIL_FROM), (BYTE *) DSN_MAIL_FROM);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to write P1 DSN sender for IMailMsg 0x%08X",
pIMailMsgProperties);
goto Exit;
}
//write DSN Recipient
hr = pIMailMsgPropertiesDSN->QueryInterface(IID_IMailMsgRecipients,
(void **) &pIMailMsgRecipients);
_ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgRecipients failed");
if (FAILED(hr))
goto Exit;
hr = pIMailMsgRecipients->AllocNewList(&pIMailMsgRecipientsAdd);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to Alloc List for DSN generation of IMailMsg 0x%08X",
pIMailMsgProperties);
goto Exit;
}
dwRecipAddressProp = g_rgdwRecipPropIDs[iCurrentAddressProp];
hr = pIMailMsgRecipientsAdd->AddPrimary(
1,
(LPCSTR *) &szSender,
&dwRecipAddressProp,
&dwDSNRecipient,
NULL,
0);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to write DSN recipient for IMailMsg 0x%p hr - 0x%08X",
pIMailMsgProperties, hr);
goto Exit;
}
//Write Address to copy NDR to (NDRs only)
if (szCopyNDRTo &&
(dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL)))
{
hr = pIMailMsgRecipientsAdd->AddPrimary(
1,
(LPCSTR *) &szCopyNDRTo,
&dwSMTPAddressProp,
&dwDSNRecipient,
NULL,
0);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this,
"ERROR: Unable to write DSN recipient for IMailMsg 0x%08X",
pIMailMsgProperties);
goto Exit;
}
}
//write P2 DSN sender
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RFC822_SENDER, sizeof(DSN_RFC822_SENDER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szDefaultDomain, cbDefaultDomain);
if (FAILED(hr))
goto Exit;
//
// If we do not have a SMTP address, write a blank BCC instead of
// a TO address (since we do not have a address we can write in the 822.
// This is similar to what we do with the pickup dir when we have no TO
// headers.
//
if (IMMPID_MP_SENDER_ADDRESS_SMTP == g_rgdwSenderPropIDs[iCurrentAddressProp])
{
//Write P2 "To:" header (using the szSender value we determined above)
hr = pdsnbuff->HrWriteBuffer((BYTE *) TO_HEADER, sizeof(TO_HEADER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szSender, lstrlen(szSender));
if (FAILED(hr))
goto Exit;
//Save recipient (original sender) for Queue Admin/Message Tracking
hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_TO_ADDRESS, szSender);
if (FAILED(hr))
goto Exit;
}
else
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) BCC_HEADER, sizeof(BCC_HEADER)-1);
if (FAILED(hr))
goto Exit;
}
//Use szBuffer to construct 822 from to set for Queue Admin/Msg Tracking
//"Postmaster@" + max of 64 characters should be less than 1/2 K!!
_ASSERT(sizeof(szBuffer) > sizeof(DSN_SENDER_ADDRESS_PREFIX) + cbReportingMTA);
memcpy(szBuffer, DSN_SENDER_ADDRESS_PREFIX, sizeof(DSN_SENDER_ADDRESS_PREFIX));
strncat(szBuffer, szDefaultDomain, sizeof(szBuffer) - sizeof(DSN_SENDER_ADDRESS_PREFIX));
hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_FROM_ADDRESS, szSender);
if (FAILED(hr))
goto Exit;
//Write P2 "Date:" header
hr = pdsnbuff->HrWriteBuffer((BYTE *) DATE_HEADER, sizeof(DATE_HEADER)-1);
if (FAILED(hr))
goto Exit;
//Get current time
GetSystemTimeAsFileTime(&ftCurrentTime);
bReturn = FileTimeToLocalRFC822Date(ftCurrentTime, szCurrentTimeBuffer);
// The only reason this function fails is a bad filetime. Since we get it from GetSystemTimeAsFileTime, there's no reason it fails.
_ASSERT(bReturn);
hr = pdsnbuff->HrWriteBuffer((BYTE *) szCurrentTimeBuffer, lstrlen(szCurrentTimeBuffer));
if (FAILED(hr))
goto Exit;
//Write the MIME header
hr = pdsnbuff->HrWriteBuffer( (BYTE *) MIME_HEADER, sizeof(MIME_HEADER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) "\"", sizeof(CHAR));
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) "\"", sizeof(CHAR));
if (FAILED(hr))
goto Exit;
//write x-DSNContext header
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CONTEXT_HEADER,
sizeof(DSN_CONTEXT_HEADER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szDSNContext, cbDSNContext);
if (FAILED(hr))
goto Exit;
wsprintf(szDSNAction, " - %08X", dwDSNAction);
hr = pdsnbuff->HrWriteBuffer((BYTE *) szDSNAction, strlen(szDSNAction));
if (FAILED(hr))
goto Exit;
wsprintf(szDSNAction, " - %08X", hrStatus);
hr = pdsnbuff->HrWriteBuffer((BYTE *) szDSNAction, strlen(szDSNAction));
if (FAILED(hr))
goto Exit;
//Get and write the message ID
if (fGenerateDSNMsgID(szReportingMTA, cbReportingMTA, szBuffer, sizeof(szBuffer)))
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) MSGID_HEADER, sizeof(MSGID_HEADER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, strlen(szBuffer));
if (FAILED(hr))
goto Exit;
hr = pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_MSG_ID,
szBuffer);
if (FAILED(hr))
goto Exit;
}
//Write the X-Content-Failure DSN
if(FAILED(hrContent))
{
CHAR szHRESULT[11];
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CONTENT_FAILURE_HEADER,
sizeof(DSN_CONTENT_FAILURE_HEADER)-1);
if(FAILED(hr))
goto Exit;
wsprintf(szHRESULT, "0x%08lx", hrContent);
hr = pdsnbuff->HrWriteBuffer((PBYTE) szHRESULT, 10);
if(FAILED(hr))
goto Exit;
}
Exit:
if (pIMailMsgRecipients)
{
if (pIMailMsgRecipientsAdd)
{
hrTmp = pIMailMsgRecipients->WriteList( pIMailMsgRecipientsAdd );
_ASSERT(SUCCEEDED(hrTmp) && "Go Get Keith");
if (FAILED(hrTmp) && SUCCEEDED(hr))
hr = hrTmp;
}
pIMailMsgRecipients->Release();
}
if (pIMailMsgRecipientsAdd)
pIMailMsgRecipientsAdd->Release();
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrWriteDSNHumanReadable ]--------------------
//
//
// Description:
// Write human readable portion of DSN (including subject header)
// Parameters:
// pIMailMsgProperties Message DSN is being generated for
// pIMailMsgREcipeints Recip Interface for Message
// prpfctxt Delivery context that DSN's are being generated for
// dwDSNActions DSN actions being taken (after looking at recips)
// So we can generate a reasonable subject
// pdsnbuff DSN Buffer to write content to
// PreferredLangId Preferred language to generate DSN in
// szMimeBoundary MIME boundary string
// cbMimeBoundary strlen of MIME boundary
// hrStatus Status to use to decide which text to display
// Returns:
// S_OK on success
// History:
// 7/5/98 - MikeSwa Created
// 12/15/98 - MikeSwa Added list of recipients & fancy human readable
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteDSNHumanReadable(
IN IMailMsgProperties *pIMailMsgPropertiesDSN,
IN IMailMsgRecipients *pIMailMsgRecipients,
IN IDSNRecipientIterator *pIRecipIter,
IN DWORD dwDSNActions,
IN CDSNBuffer *pdsnbuff,
IN DWORD dwPreferredLangId,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary,
IN HRESULT hrStatus,
IN LPSTR szHRTopCustomText,
IN LPWSTR wszHRTopCustomText,
IN LPSTR szHRBottomCustomText,
IN LPWSTR wszHRBottomCustomText)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNGenerationSink::HrWriteDSNHumanReadable");
HRESULT hr = S_OK;
DWORD dwDSNType = (dwDSNActions & DSN_ACTION_TYPE_MASK);
LANGID LangID = (LANGID) dwPreferredLangId;
CUTF7ConversionContext utf7conv(FALSE);
CUTF7ConversionContext utf7convSubject(TRUE);
BOOL fWriteRecips = TRUE;
WORD wSubjectID = GENERAL_SUBJECT;
LPWSTR wszSubject = NULL;
LPWSTR wszStop = NULL;
DWORD cbSubject = 0;
LPSTR szSubject = NULL;
LPSTR szSubjectCurrent = NULL;
if (!fLanguageAvailable(LangID))
{
//Use default of server
LangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
}
hr = pdsnbuff->HrWriteBuffer((BYTE *) SUBJECT_HEADER, sizeof(SUBJECT_HEADER)-1);
if (FAILED(hr))
goto Exit;
//Set conversion context to UTF7 for RFC1522 subject
pdsnbuff->SetConversionContext(&utf7convSubject);
//Write subject with useful info
if (((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL) & dwDSNType) == dwDSNType)
wSubjectID = FAILURE_SUBJECT;
else if (DSN_ACTION_RELAYED == dwDSNType)
wSubjectID = RELAY_SUBJECT;
else if (DSN_ACTION_DELAYED == dwDSNType)
wSubjectID = DELAY_SUBJECT;
else if (DSN_ACTION_DELIVERED == dwDSNType)
wSubjectID = DELIVERED_SUBJECT;
else if (DSN_ACTION_EXPANDED == dwDSNType)
wSubjectID = EXPANDED_SUBJECT;
hr = pdsnbuff->HrWriteResource(wSubjectID, LangID);
if (FAILED(hr))
goto Exit;
//Write *English* subject for Queue Admin/Message tracking
//Use english, becuase we return a ASCII string to queue admin
hr = pdsnbuff->HrLoadResourceString(wSubjectID,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
&wszSubject, &cbSubject);
if (FAILED(hr))
{
ErrorTrace((LPARAM) this, "Unable to get resource for english subject 0x%08X", hr);
}
else
{
//We need to convert from UNICODE to ASCII... remember resource is not
//NULL terminated
szSubject = (LPSTR) pvMalloc(cbSubject/sizeof(WCHAR) + 1);
wszStop = wszSubject + (cbSubject/sizeof(WCHAR));
if (szSubject)
{
szSubjectCurrent = szSubject;
while ((wszSubject < wszStop) && *wszSubject)
{
wctomb(szSubjectCurrent, *wszSubject);
szSubjectCurrent++;
wszSubject++;
}
*szSubjectCurrent = '\0';
pIMailMsgPropertiesDSN->PutStringA(IMMPID_MP_RFC822_MSG_SUBJECT, szSubject);
FreePv(szSubject);
}
}
pdsnbuff->ResetConversionContext();
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
//write summary saying that this is a MIME message
hr = pdsnbuff->HrWriteBuffer((BYTE *) MESSAGE_SUMMARY, sizeof(MESSAGE_SUMMARY)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
if (FAILED(hr))
goto Exit;
//Write content type
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_CONTENT_TYPE, sizeof(MIME_CONTENT_TYPE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HUMAN_READABLE_TYPE, sizeof(DSN_HUMAN_READABLE_TYPE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_MIME_CHARSET_HEADER, sizeof(DSN_MIME_CHARSET_HEADER)-1);
if (FAILED(hr))
goto Exit;
//For now... we do our encoding as UTF7.... put that as the charset
hr = pdsnbuff->HrWriteBuffer((BYTE *) UTF7_CHARSET, sizeof(UTF7_CHARSET)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
//Set conversion context to UTF7
pdsnbuff->SetConversionContext(&utf7conv);
//
// Custom header text
//
if(szHRTopCustomText)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) szHRTopCustomText, lstrlenA(szHRTopCustomText));
if(FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
}
if(wszHRTopCustomText)
{
hr = pdsnbuff->HrWriteModifiedUnicodeString(wszHRTopCustomText);
if(FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
}
hr = pdsnbuff->HrWriteResource(DSN_SUMMARY, LangID);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
//Describe the type of DSN
if (((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL) & dwDSNType) == dwDSNType)
{
//See if we have a failure-specific message
switch(hrStatus)
{
#ifdef NEVER
//CAT can generate errors other than unresolved recipeints
//We will use the generic DSN failure message rather than confuse
//recipients
case CAT_W_SOME_UNDELIVERABLE_MSGS:
hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY_MAILBOX, LangID);
break;
#endif //NEVER
case AQUEUE_E_MAX_HOP_COUNT_EXCEEDED:
hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY_HOP, LangID);
break;
case AQUEUE_E_MSG_EXPIRED:
case AQUEUE_E_HOST_NOT_RESPONDING:
case AQUEUE_E_CONNECTION_DROPPED:
hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY_EXPIRE, LangID);
break;
default:
hr = pdsnbuff->HrWriteResource(FAILURE_SUMMARY, LangID);
}
if (FAILED(hr))
goto Exit;
}
else if (DSN_ACTION_RELAYED == dwDSNType)
{
hr = pdsnbuff->HrWriteResource(RELAY_SUMMARY, LangID);
if (FAILED(hr))
goto Exit;
}
else if (DSN_ACTION_DELAYED == dwDSNType)
{
//UE want this three line warning.
hr = pdsnbuff->HrWriteResource(DELAY_WARNING, LangID);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteResource(DELAY_DO_NOT_SEND, LangID);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteResource(DELAY_SUMMARY, LangID);
if (FAILED(hr))
goto Exit;
}
else if (DSN_ACTION_DELIVERED == dwDSNType)
{
hr = pdsnbuff->HrWriteResource(DELIVERED_SUMMARY, LangID);
if (FAILED(hr))
goto Exit;
}
else if (DSN_ACTION_EXPANDED == dwDSNType)
{
hr = pdsnbuff->HrWriteResource(EXPANDED_SUMMARY, LangID);
if (FAILED(hr))
goto Exit;
}
else
{
//In retail this will cause an extra blank line to appear in the DSN,
_ASSERT(0 && "Unsupported DSN Action");
fWriteRecips = FALSE;
}
//Write a list of recipients for this DSN
if (fWriteRecips)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = HrWriteHumanReadableListOfRecips(pIMailMsgRecipients, pIRecipIter,
dwDSNType, pdsnbuff);
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
}
//
// Custom trailer text
//
if(wszHRBottomCustomText)
{
hr = pdsnbuff->HrWriteModifiedUnicodeString(wszHRBottomCustomText);
if(FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
}
if(szHRBottomCustomText)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) szHRBottomCustomText, lstrlenA(szHRBottomCustomText));
if(FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
}
//Extra space to have nicer formatting in Outlook 97.
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
//Reset resource conversion context to default
pdsnbuff->ResetConversionContext();
Exit:
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrWriteDSNReportPerMsgProperties ]-----------
//
//
// Description:
// Write the per-msg portion of the DSN Report
// Parameters:
// pIMailMsgProperties IMailMsgProperties to generate DSN for
// pdsnbuff CDSNBuffer to write content to
// szReportingMTA MTA requesting DSN
// cbReportingMTA String length of reporting MTA
// szMimeBoundary MIME boundary for this message
// cbMimeBoundary Length of MIME boundary
// Returns:
// S_OK on success
// History:
// 7/6/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteDSNReportPerMsgProperties(
IN IMailMsgProperties *pIMailMsgProperties,
IN CDSNBuffer *pdsnbuff,
IN LPSTR szReportingMTA,
IN DWORD cbReportingMTA,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary)
{
HRESULT hr = S_OK;
CHAR szPropBuffer[PROP_BUFFER_SIZE];
_ASSERT(szReportingMTA && cbReportingMTA);
//Write properly formatted MIME boundary and report type
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER,
sizeof(MIME_DELIMITER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_CONTENT_TYPE,
sizeof(MIME_CONTENT_TYPE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_MIME_TYPE, sizeof(DSN_MIME_TYPE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
//Write DSN_HEADER_ENVID if we have it
hr = pIMailMsgProperties->GetStringA(IMMPID_MP_DSN_ENVID_VALUE,
PROP_BUFFER_SIZE, szPropBuffer);
if (SUCCEEDED(hr))
{
//Prop found
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_ENVID,
sizeof(DSN_HEADER_ENVID)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szPropBuffer, lstrlen(szPropBuffer));
if (FAILED(hr))
goto Exit;
}
else
{
if (MAILMSG_E_PROPNOTFOUND == hr)
hr = S_OK;
else
goto Exit;
}
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_REPORTING_MTA,
sizeof(DSN_HEADER_REPORTING_MTA)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szReportingMTA, cbReportingMTA);
if (FAILED(hr))
goto Exit;
//Write DSN_HEADER_RECEIVED_FROM if we have it
hr = pIMailMsgProperties->GetStringA(IMMPID_MP_HELO_DOMAIN,
PROP_BUFFER_SIZE, szPropBuffer);
if (SUCCEEDED(hr))
{
//Prop found
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_RECEIVED_FROM,
sizeof(DSN_HEADER_RECEIVED_FROM)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szPropBuffer, lstrlen(szPropBuffer));
if (FAILED(hr))
goto Exit;
}
else
{
if (MAILMSG_E_PROPNOTFOUND == hr)
hr = S_OK;
else
goto Exit;
}
//Write DSN_HEADER_ARRIVAL_DATE if we have it
hr = pIMailMsgProperties->GetStringA(IMMPID_MP_ARRIVAL_TIME,
PROP_BUFFER_SIZE, szPropBuffer);
if (SUCCEEDED(hr))
{
//Prop found
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_ARRIVAL_DATE,
sizeof(DSN_HEADER_ARRIVAL_DATE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szPropBuffer, lstrlen(szPropBuffer));
if (FAILED(hr))
goto Exit;
}
else
{
if (MAILMSG_E_PROPNOTFOUND == hr)
hr = S_OK;
else
goto Exit;
}
Exit:
return hr;
}
//---[ CDefaultDSNSink::HrWriteDSNReportPreRecipientProperties ]-----
//
//
// Description:
// Write a per-recipient portion of the DSN Report
// Parameters:
// pIMailMsgRecipients IMailMsgProperties that DSN is being generated for
// pdsnbuff CDSNBuffer to write content to
// iRecip Recipient to generate report for
// szExpireTime Time (if known) when message expires
// cbExpireTime size of string
// dwDSNAction DSN Action to take for this recipient
// dwRFC821Status Global RFC821 status DWORD
// hrStatus Global HRESULT status
// Returns:
// S_OK on success
// History:
// 7/6/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteDSNReportPreRecipientProperties(
IN IMailMsgRecipients *pIMailMsgRecipients,
IN CDSNBuffer *pdsnbuff,
IN DWORD iRecip,
IN LPSTR szExpireTime,
IN DWORD cbExpireTime,
IN DWORD dwDSNAction,
IN DWORD dwRFC821Status,
IN HRESULT hrStatus)
{
HRESULT hr = S_OK;
CHAR szTempBuffer[PROP_BUFFER_SIZE * sizeof(WCHAR)]; // Multiply by sizeof(WCHAR) because
// we'll also use it for Unicode props
LPSTR szBuffer = szTempBuffer;
LPWSTR wszBuffer = (LPWSTR) szTempBuffer;
CUTF7ConversionContext utf7conv(TRUE);
CHAR szStatus[STATUS_STRING_SIZE];
BOOL fFoundDiagnostic = FALSE;
CHAR szAddressType[PROP_BUFFER_SIZE];
DWORD cbBuffer = 0;
//Write blank line between recipient reports (recip fields start with \n)
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
//Write DSN_RP_HEADER_ORCPT if we have it
hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_DSN_ORCPT_VALUE,
PROP_BUFFER_SIZE, szBuffer);
if (S_OK == hr) //prop was found
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ORCPT, sizeof(DSN_RP_HEADER_ORCPT)-1);
if (FAILED(hr))
goto Exit;
//write address value - type should be included in this property
hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
if (FAILED(hr))
goto Exit;
}
else if (FAILED(hr))
{
if (MAILMSG_E_PROPNOTFOUND == hr)
hr = S_OK;
else
goto Exit;
}
//Write DSN_RP_HEADER_FINAL_RECIP
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_FINAL_RECIP, sizeof(DSN_RP_HEADER_FINAL_RECIP)-1);
if (FAILED(hr))
goto Exit;
//Check for IMMPID_RP_DSN_PRE_CAT_ADDRESS first
hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_DSN_PRE_CAT_ADDRESS,
PROP_BUFFER_SIZE, szBuffer);
if (S_OK == hr) //prop was found
{
//write address value - type should be included in this property
hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
if (FAILED(hr))
goto Exit;
}
else //we need to use IMMPID_RP_ADDRESS_SMTP instead
{
hr = HrGetRecipAddressAndType(pIMailMsgRecipients, iRecip, PROP_BUFFER_SIZE,
szBuffer, sizeof(szAddressType), szAddressType);
if (SUCCEEDED(hr))
{
//write address type
hr = pdsnbuff->HrWriteBuffer((BYTE *) szAddressType, lstrlen(szAddressType));
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_TYPE_DELIMITER, sizeof(DSN_HEADER_TYPE_DELIMITER)-1);
if (FAILED(hr))
goto Exit;
//write address value
hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
if (FAILED(hr))
goto Exit;
}
else
{
_ASSERT(SUCCEEDED(hr) && "Recipient address *must* be present");
}
}
//Write DSN_RP_HEADER_ACTION
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION, sizeof(DSN_RP_HEADER_ACTION)-1);
if (FAILED(hr))
goto Exit;
if (dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_FAILURE,
sizeof(DSN_RP_HEADER_ACTION_FAILURE)-1);
if (FAILED(hr))
goto Exit;
}
else if (dwDSNAction & DSN_ACTION_DELAYED)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_DELAYED,
sizeof(DSN_RP_HEADER_ACTION_DELAYED)-1);
if (FAILED(hr))
goto Exit;
}
else if (dwDSNAction & DSN_ACTION_RELAYED)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_RELAYED,
sizeof(DSN_RP_HEADER_ACTION_RELAYED)-1);
if (FAILED(hr))
goto Exit;
}
else if (dwDSNAction & DSN_ACTION_DELIVERED)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_DELIVERED,
sizeof(DSN_RP_HEADER_ACTION_DELIVERED)-1);
if (FAILED(hr))
goto Exit;
}
else if (dwDSNAction & DSN_ACTION_EXPANDED)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_ACTION_EXPANDED,
sizeof(DSN_RP_HEADER_ACTION_EXPANDED)-1);
if (FAILED(hr))
goto Exit;
}
else
{
_ASSERT(0 && "No DSN Action requested");
}
//Write DSN_RP_HEADER_STATUS
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_STATUS,
sizeof(DSN_RP_HEADER_STATUS)-1);
if (FAILED(hr))
goto Exit;
//Get status code
hr = HrGetStatusCode(pIMailMsgRecipients, iRecip, dwDSNAction,
dwRFC821Status, hrStatus,
PROP_BUFFER_SIZE, szBuffer, szStatus);
if (FAILED(hr))
goto Exit;
if (S_OK == hr)
{
//found diagnostic code
fFoundDiagnostic = TRUE;
}
hr = pdsnbuff->HrWriteBuffer((BYTE *) szStatus, lstrlen(szStatus));
if (FAILED(hr))
goto Exit;
if (fFoundDiagnostic)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_DIAG_CODE,
sizeof(DSN_RP_HEADER_DIAG_CODE)-1);
if (FAILED(hr))
goto Exit;
//
// The SMTP response may be CRLF terminated, and we cannot put
// the CRLF in the DSN since CRLF is the header-separator. So we
// check for CRLF and strip it... actually since CRLF *must* be
// the last 2 bytes in the string (if present), and since CR isn't
// allowed to be part of the SMTP response, we cheat a little and
// only set the second last byte to NULL if it is CR.
//
cbBuffer = lstrlen(szBuffer);
if(szBuffer[cbBuffer-2] == '\r')
{
_ASSERT(szBuffer[cbBuffer-1] == '\n');
szBuffer[cbBuffer-2] = '\0';
cbBuffer -= 2; // We chomped the last 2 chars
}
hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, cbBuffer);
if (FAILED(hr))
goto Exit;
}
//Write DSN_RP_HEADER_RETRY_UNTIL using expire time if delay
if (szExpireTime && (DSN_ACTION_DELAYED & dwDSNAction))
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RP_HEADER_RETRY_UNTIL,
sizeof(DSN_RP_HEADER_RETRY_UNTIL)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szExpireTime, cbExpireTime);
if (FAILED(hr))
goto Exit;
}
//Write the X-Display-Name header last
hr = pIMailMsgRecipients->GetStringW(iRecip, IMMPID_RP_DISPLAY_NAME,
PROP_BUFFER_SIZE, wszBuffer);
if ( (hr == S_OK) &&
(wszBuffer[0] != L'\0') )
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADER_DISPLAY_NAME, sizeof(DSN_HEADER_DISPLAY_NAME) - 1);
if (FAILED(hr))
goto Exit;
//
// Convert the X-Display-Name from UNICODE to RFC 1522. We also replace all
// whitespace characters in the input with Unicode whitespace (0x20). See
// documentation of HrWriteModifiedUnicodeBuffer for the reasons.
//
pdsnbuff->SetConversionContext(&utf7conv);
hr = pdsnbuff->HrWriteModifiedUnicodeString(wszBuffer);
pdsnbuff->ResetConversionContext();
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
}
else
{
// Not a fatal error if there is no display name...
hr = S_OK;
}
Exit:
return hr;
}
//---[ CDefaultDSNSink::HrLogDSNGenerationEvent ]------------------------------
//
//
// Description:
// Write a per-recipient portion of the DSN Report
// Parameters:
// pIMailMsgRecipients IMailMsgProperties that DSN is being generated for
// pdsnbuff CDSNBuffer to write content to
// iRecip Recipient to generate report for
// szExpireTime Time (if known) when message expires
// cbExpireTime size of string
// dwDSNAction DSN Action to take for this recipient
// dwRFC821Status Global RFC821 status DWORD
// hrStatus Global HRESULT status
// Returns:
// S_OK on success
// History:
// 6/12/2000 - dbraun created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrLogDSNGenerationEvent(
ISMTPServer *pISMTPServer,
IMailMsgProperties *pIMailMsgProperties,
IN IMailMsgRecipients *pIMailMsgRecipients,
IN DWORD iRecip,
IN DWORD dwDSNAction,
IN DWORD dwRFC821Status,
IN HRESULT hrStatus)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrLogDSNGenerationEvent");
HRESULT hr = S_OK;
CHAR szBuffer[PROP_BUFFER_SIZE * sizeof(WCHAR)];
CHAR szDiagBuffer[PROP_BUFFER_SIZE * sizeof(WCHAR)];
CHAR szStatus[STATUS_STRING_SIZE];
CHAR szRecipient[PROP_BUFFER_SIZE * sizeof(WCHAR)];
CHAR szMessageID[PROP_BUFFER_SIZE * sizeof(WCHAR)];
CHAR szAddressType[PROP_BUFFER_SIZE];
ISMTPServerEx *pISMTPServerEx = NULL;
DWORD cbPropSize = 0;
const char *rgszSubstrings[] = {
szStatus,
szRecipient,
szMessageID,
};
_ASSERT(pISMTPServer);
// If this is not an NDR, skip it
if (!(dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL)))
goto Exit;
// See if we can QI for ISMTPServerEx
hr = pISMTPServer->QueryInterface(
IID_ISMTPServerEx,
(LPVOID *)&pISMTPServerEx);
if (FAILED(hr))
{
ErrorTrace((LPARAM) pISMTPServer,
"Unable to QI for ISMTPServerEx 0x%08X",hr);
pISMTPServerEx = NULL;
goto Exit;
}
// Get the final recipient name
//Check for IMMPID_RP_DSN_PRE_CAT_ADDRESS first
hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_DSN_PRE_CAT_ADDRESS,
PROP_BUFFER_SIZE, szRecipient);
if (S_OK != hr) // S_OK = prop was found, otherwise ...
{
//we need to use IMMPID_RP_ADDRESS_SMTP instead
hr = HrGetRecipAddressAndType(pIMailMsgRecipients, iRecip, PROP_BUFFER_SIZE,
szBuffer, sizeof(szAddressType), szAddressType);
if (SUCCEEDED(hr))
{
// Construct the address string
sprintf(szRecipient, "%s%s%s", szAddressType, DSN_HEADER_TYPE_DELIMITER, szBuffer);
}
else
{
_ASSERT(SUCCEEDED(hr) && "Recipient address *must* be present");
goto Exit;
}
}
// Get status code
hr = HrGetStatusCode(pIMailMsgRecipients, iRecip, dwDSNAction,
dwRFC821Status, hrStatus,
PROP_BUFFER_SIZE, szDiagBuffer, szStatus);
if (FAILED(hr))
goto Exit;
// Get the message ID
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_ID,
sizeof(szMessageID), &cbPropSize, (PBYTE) szMessageID);
if (FAILED(hr))
goto Exit;
// Trigger Log Event
pISMTPServerEx->TriggerLogEvent(
AQUEUE_E_NDR_GENERATED_EVENT, // Event ID
TRAN_CAT_CONNECTION_MANAGER, // Category
3, // Word count of substring
rgszSubstrings, // Substring
EVENTLOG_WARNING_TYPE, // Type of the message
hrStatus, // error code
LOGEVENT_LEVEL_MAXIMUM, // Logging level
szRecipient, // Key to identify this event
LOGEVENT_FLAG_ALWAYS, // Event logging option
0xffffffff, // format string's index in substring
GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
);
Exit:
if (pISMTPServerEx) {
pISMTPServerEx->Release();
}
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrWriteDSNClosingAndOriginalMessage ]--------
//
//
// Description:
// Writes the closing of the DSN as well as the end of the
// Parameters:
// pIMailMsgProperties IMailMsgProperties to generate DSN for
// pIMailMsgPropertiesDSN IMailMsgProperties for DSN
// pdsnbuff CDSNBuffer to write content to
// pDestFile PFIO_CONTEXT for destination file
// dwDSNAction DSN actions for this DSN
// szMimeBoundary MIME boundary for this message
// cbMimeBoundary Length of MIME boundary
// dwDSNRetTypeIN DSN return type
// dwOrigMsgSize Content size of the original message
//
// Returns:
//
// History:
// 7/6/98 - MikeSwa Created
// 1/6/2000 - MikeSwa Modified to add RET=HDRS support
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteDSNClosingAndOriginalMessage(
IN IMailMsgProperties *pIMailMsgProperties,
IN IMailMsgProperties *pIMailMsgPropertiesDSN,
IN CDSNBuffer *pdsnbuff,
IN PFIO_CONTEXT pDestFile,
IN DWORD dwDSNAction,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary,
IN DWORD dwDSNRetTypeIN,
IN DWORD dwOrigMsgSize)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteDSNClosingAndOriginalMessage");
HRESULT hr = S_OK;
DWORD dwDSNRetType = dwDSNRetTypeIN;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
if (FAILED(hr))
goto Exit;
//Write Body content type MIME_CONTENT_TYPE = rfc822
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_CONTENT_TYPE, sizeof(MIME_CONTENT_TYPE)-1);
if (FAILED(hr))
goto Exit;
if (dwDSNRetType == DSN_RET_PARTIAL_HDRS)
{
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_HEADERS_TYPE, sizeof(DSN_HEADERS_TYPE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = HrWriteOriginalMessagePartialHeaders(
pIMailMsgProperties, pIMailMsgPropertiesDSN,
pdsnbuff, pDestFile, szMimeBoundary, cbMimeBoundary);
}
else
{
//
//$$TODO: Check for DSN_RET_HDRS and implement a function that
// returns the original headers only
//
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_RFC822_TYPE, sizeof(DSN_RFC822_TYPE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = HrWriteOriginalMessageFull(
pIMailMsgProperties, pIMailMsgPropertiesDSN,
pdsnbuff, pDestFile, szMimeBoundary, cbMimeBoundary,
dwOrigMsgSize);
}
if (FAILED(hr))
goto Exit;
Exit:
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrInitialize ]-------------------------------
//
//
// Description:
// Performs initialization...
// - Sets init flag
// - Currently nothing else
// Parameters:
// -
// Returns:
// S_OK on SUCCESS
// History:
// 7/3/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrInitialize()
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrInitialize");
HRESULT hr = S_OK;
m_fInit = TRUE;
srand(GetTickCount());
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::GetCurrentMimeBoundary ]---------------------
//
//
// Description:
// Creates unique MIME-boundary for message.
//
// Format we are using for boundary is string versions of the following:
// MIME_BOUNDARY_CONSTANT
// FILETIME at start
// DWORD count of DSNs Requested
// 16 bytes of our virtual server's domain name
// Parameters:
// IN szReportingMTA reporting MTA
// IN cbReportingMTA String length of reporting MTA
// IN OUT szMimeBoundary Buffer to put boundary in (size is MIME_BOUNDARY_SIZE)
// OUT cbMimeBoundary Amount of buffer used for MIME Boundary
// Returns:
// -
// History:
// 7/6/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CDefaultDSNSink::GetCurrentMimeBoundary(
IN LPSTR szReportingMTA,
IN DWORD cbReportingMTA,
IN OUT CHAR szMimeBoundary[MIME_BOUNDARY_SIZE],
OUT DWORD *pcbMimeBoundary)
{
_ASSERT(MIME_BOUNDARY_RFC2046_LIMIT >= MIME_BOUNDARY_SIZE);
DWORD iCurrentOffset = 0;
szMimeBoundary[MIME_BOUNDARY_SIZE-1] = '\0';
CHAR *pcharCurrent = NULL;
CHAR *pcharStop = NULL;
memcpy(szMimeBoundary+iCurrentOffset, MIME_BOUNDARY_CONSTANT,
sizeof(MIME_BOUNDARY_CONSTANT)-1);
iCurrentOffset += sizeof(MIME_BOUNDARY_CONSTANT)-1;
memcpy(szMimeBoundary+iCurrentOffset, m_szPerInstanceMimeBoundary,
MIME_BOUNDARY_START_TIME_SIZE);
iCurrentOffset += MIME_BOUNDARY_START_TIME_SIZE;
wsprintf(szMimeBoundary+iCurrentOffset, "%08X",
InterlockedIncrement((PLONG) &m_cDSNsRequested));
iCurrentOffset += 8;
if (cbReportingMTA >= MIME_BOUNDARY_SIZE-iCurrentOffset)
{
memcpy(szMimeBoundary+iCurrentOffset, szReportingMTA,
MIME_BOUNDARY_SIZE-iCurrentOffset - 1);
*pcbMimeBoundary = MIME_BOUNDARY_SIZE-1;
}
else
{
memcpy(szMimeBoundary+iCurrentOffset, szReportingMTA,
cbReportingMTA);
szMimeBoundary[iCurrentOffset + cbReportingMTA] = '\0';
*pcbMimeBoundary = iCurrentOffset + cbReportingMTA;
}
//Now we need to verify that the passed in string can be part of a valid
//MIME Header
pcharStop = szMimeBoundary + *pcbMimeBoundary;
for (pcharCurrent = szMimeBoundary + iCurrentOffset;
pcharCurrent < pcharStop;
pcharCurrent++)
{
if (!fIsValidMIMEBoundaryChar(*pcharCurrent))
*pcharCurrent = '?'; //turn it into a valid character
}
_ASSERT_MIME_BOUNDARY(szMimeBoundary);
_ASSERT('\0' == szMimeBoundary[MIME_BOUNDARY_SIZE-1]);
}
//---[ CDefaultDSNSink::HrWriteOriginalMessageFull ]-----------------
//
//
// Description:
// Writes the entire original message to the DSN
// Parameters:
// pIMailMsgProperties IMailMsgProperties to generate DSN for
// pIMailMsgPropertiesDSN IMailMsgProperties for DSN
// pdsnbuff CDSNBuffer to write content to
// pDestFile PFIO_CONTEXT for destination file
// szMimeBoundary MIME boundary for this message
// cbMimeBoundary Length of MIME boundary
// dwOrigMsgSize Size of original message
//
// Returns:
// S_OK on success
// History:
// 1/6/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteOriginalMessageFull(
IN IMailMsgProperties *pIMailMsgProperties,
IN IMailMsgProperties *pIMailMsgPropertiesDSN,
IN CDSNBuffer *pdsnbuff,
IN PFIO_CONTEXT pDestFile,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary,
IN DWORD dwOrigMsgSize)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteOriginalMessageFull");
HRESULT hr = S_OK;
DWORD dwFileSize = 0;
DWORD dwDontCare = 0;
hr = pdsnbuff->HrSeekForward(dwOrigMsgSize, &dwFileSize);
if (FAILED(hr))
goto Exit;
//Set size hint property on DSN for Queue Admin/Message Tracking
hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSG_SIZE_HINT,
dwOrigMsgSize + dwFileSize);
if (FAILED(hr))
{
//We really don't care too much about a failure with this
ErrorTrace((LPARAM) this, "Error writing size hint 0x%08X", hr);
hr = S_OK;
}
//Write at end of file - *before* file handle is lost to IMailMsg,
hr = HrWriteMimeClosing(pdsnbuff, szMimeBoundary, cbMimeBoundary, &dwDontCare);
if (FAILED(hr))
goto Exit;
//write body
hr = pIMailMsgProperties->CopyContentToFileAtOffset(pDestFile, dwFileSize, NULL);
if (FAILED(hr))
goto Exit;
Exit:
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrWriteOriginalMessagePartialHeaders ]--------------
//
//
// Description:
// Writes only some headers of the original message to the DSN
// Parameters:
// pIMailMsgProperties IMailMsgProperties to generate DSN for
// pIMailMsgPropertiesDSN IMailMsgProperties for DSN
// pdsnbuff CDSNBuffer to write content to
// pDestFile PFIO_CONTEXT for destination file
// szMimeBoundary MIME boundary for this message
// cbMimeBoundary Length of MIME boundary
// Returns:
// S_OK on success
// History:
// 1/6/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteOriginalMessagePartialHeaders(
IN IMailMsgProperties *pIMailMsgProperties,
IN IMailMsgProperties *pIMailMsgPropertiesDSN,
IN CDSNBuffer *pdsnbuff,
IN PFIO_CONTEXT pDestFile,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteOriginalMessagePartialHeaders");
HRESULT hr = S_OK;
IMailMsgRecipients *pRecips = NULL;
DWORD dwFileSize = 0;
DWORD cbPropSize = 0;
CHAR szPropBuffer[1026] = "";
//Loop through the 822 properties that we care about and write them
//to the message. A truely RFC-compliant version would re-parse the
//messages... and return all the headers
//
// From header
//
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_FROM_ADDRESS,
sizeof(szPropBuffer), &cbPropSize, (PBYTE) szPropBuffer);
if (SUCCEEDED(hr))
{
hr = pdsnbuff->HrWriteBuffer((PBYTE)DSN_FROM_HEADER_NO_CRLF,
sizeof(DSN_FROM_HEADER_NO_CRLF)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
}
//
// To header
//
hr = pdsnbuff->HrWriteBuffer((PBYTE)TO_HEADER_NO_CRLF,
sizeof(TO_HEADER_NO_CRLF)-1);
if (FAILED(hr))
goto Exit;
hr = pIMailMsgProperties->GetProperty(
IMMPID_MP_RFC822_TO_ADDRESS,
sizeof(szPropBuffer),
&cbPropSize,
(PBYTE) szPropBuffer);
if(SUCCEEDED(hr))
{
hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
if (FAILED(hr))
goto Exit;
}
else
{
//
// Write the 821 recipients as 822 recipients
//
DWORD dwcRecips = 0;
DWORD dwCount = 0;
BOOL fPrintedFirstRecip = FALSE;
hr = pIMailMsgProperties->QueryInterface(
IID_IMailMsgRecipients,
(PVOID *) &pRecips);
if(FAILED(hr))
goto Exit;
hr = pRecips->Count(&dwcRecips);
if(FAILED(hr))
goto Exit;
for(dwCount = 0; dwCount < dwcRecips; dwCount++)
{
hr = pRecips->GetStringA(
dwCount, // Index
IMMPID_RP_ADDRESS_SMTP,
sizeof(szPropBuffer),
szPropBuffer);
if(SUCCEEDED(hr))
{
if(fPrintedFirstRecip)
{
hr = pdsnbuff->HrWriteBuffer(
(PBYTE) ADDRESS_SEPERATOR,
sizeof(ADDRESS_SEPERATOR)-1);
if(FAILED(hr))
goto Exit;
}
hr = pdsnbuff->HrWriteBuffer(
(PBYTE) szPropBuffer,
lstrlen(szPropBuffer));
if(FAILED(hr))
goto Exit;
fPrintedFirstRecip = TRUE;
}
}
}
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
//
// Message ID
//
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_ID,
sizeof(szPropBuffer), &cbPropSize, (PBYTE) szPropBuffer);
if (SUCCEEDED(hr))
{
hr = pdsnbuff->HrWriteBuffer((PBYTE)MSGID_HEADER_NO_CRLF,
sizeof(MSGID_HEADER_NO_CRLF)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
}
//
// Subject header
//
hr = pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_SUBJECT,
sizeof(szPropBuffer), &cbPropSize, (PBYTE)szPropBuffer);
if (SUCCEEDED(hr))
{
hr = pdsnbuff->HrWriteBuffer((PBYTE)SUBJECT_HEADER_NO_CRLF,
sizeof(SUBJECT_HEADER_NO_CRLF)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((PBYTE)szPropBuffer, cbPropSize-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
}
hr = HrWriteMimeClosing(pdsnbuff, szMimeBoundary, cbMimeBoundary, &dwFileSize);
if (FAILED(hr))
goto Exit;
//Set size hint property on DSN for Queue Admin/Message Tracking
hr = pIMailMsgPropertiesDSN->PutDWORD(IMMPID_MP_MSG_SIZE_HINT, dwFileSize);
if (FAILED(hr))
{
//We really don't care too much about a failure with this
ErrorTrace((LPARAM) this, "Error writing size hint 0x%08X", hr);
hr = S_OK;
}
Exit:
if(pRecips)
pRecips->Release();
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrWriteMimeClosing ]-------------------------
//
//
// Description:
// Write the MIME closing of the DSN after the 3rd MIME part.
// Parameters:
// pdsnbuff CDSNBuffer to write content to
// szReportingMTA MTA requesting DSN
// cbReportingMTA String length of reporting MTA
// szMimeBoundary MIME boundary for this message
// cbMimeBoundary Length of MIME boundary
// Returns:
// S_OK on success
// Failure code from CDSNBuffer on failure
// History:
// 1/6/2000 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteMimeClosing(
IN CDSNBuffer *pdsnbuff,
IN LPSTR szMimeBoundary,
IN DWORD cbMimeBoundary,
OUT DWORD *pcbDSNSize)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrWriteMimeClosing");
HRESULT hr = S_OK;
hr = pdsnbuff->HrWriteBuffer((BYTE *) BLANK_LINE, sizeof(BLANK_LINE)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szMimeBoundary, cbMimeBoundary);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) MIME_DELIMITER, sizeof(MIME_DELIMITER)-1);
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-1);
if (FAILED(hr))
goto Exit;
//flush buffers
hr = pdsnbuff->HrFlushBuffer(pcbDSNSize);
if (FAILED(hr))
goto Exit;
Exit:
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrGetStatusCode ]----------------------------
//
//
// Description:
// Determines the status code (and diagnostic code) for a recipient. Will
// check the following (in order) to determine the status code to return:
// IMMPID_RP_SMTP_STATUS_STRING (per-recipient diagnostic code)
// Combination of:
// IMMPID_RP_RECIPIENT_FLAGS (determine who set the error)
// IMMPID_RP_ERROR_CODE (per-recipient HRESULT error code)
// dwDSNAction - kind of DSN being sent
// Combination of:
// IMMPID_RP_RECIPIENT_FLAGS (determine who set the error)
// dwRFC821Status - per message status code
// dwDSNAction - kind of DSN being sent
// Combination of:
// IMMPID_RP_RECIPIENT_FLAGS (determine who set the error)
// hrStatus - per message HRESULT failure
// dwDSNAction - kind of DSN being sent
// Status codes are defined in RFC 1893 as follows:
// status-code = class "." subject "." detail
// class = "2"/"4"/"5"
// subject = 1*3digit
// detail = 1*3digit
//
// Additionally, the class of "2", "4", and "5" correspond to success,
// transient failure, and hard failure respectively
// Parameters:
// pIMailMsgRecipients IMailMsgRecipients of message being DSN'd
// iRecip The index of the recipient we are looking at
// dwDSNAction The action code returned by fdwGetDSNAction
// dwRFC821Status RFC821 Status code returned by SMTP
// hrStatus HRESULT error if SMTP status is unavailable
// cbExtendedStatus Size of buffer for diagnostic code
// szExtendedStatus Buffer for diagnostic code
// szStatus Buffer for "n.n.n" formatted status code
// Returns:
// S_OK Success - found diagnostic code as well
// S_FALSE Success - but no diagnostic code
// History:
// 7/6/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrGetStatusCode(
IN IMailMsgRecipients *pIMailMsgRecipients,
IN DWORD iRecip,
IN DWORD dwDSNAction,
IN DWORD dwRFC821Status,
IN HRESULT hrStatus,
IN DWORD cbExtendedStatus,
IN OUT LPSTR szExtendedStatus,
IN OUT CHAR szStatus[STATUS_STRING_SIZE])
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrGetStatusCode");
HRESULT hr = S_OK;
HRESULT hrPerRecipStatus = S_OK;
BOOL fFoundDiagnostic = FALSE;
BOOL fTryToFindStatusCode = FALSE;
DWORD dwRecipFlags = 0;
//Check for IMMPID_RP_SMTP_STATUS_STRING on recipient and try to get
//status code from there
hr = pIMailMsgRecipients->GetStringA(iRecip, IMMPID_RP_SMTP_STATUS_STRING,
cbExtendedStatus, szExtendedStatus);
if (SUCCEEDED(hr)) //prop was found
{
fFoundDiagnostic = TRUE;
hr = HrGetStatusFromStatus(cbExtendedStatus, szExtendedStatus,
szStatus);
if (S_OK == hr)
goto Exit;
else if (S_FALSE == hr)
hr = S_OK; //not really an error... just get code from someplace else
else
goto Exit; //other failure
}
else if (MAILMSG_E_PROPNOTFOUND == hr)
{
//Not really a hard error
_ASSERT(!fFoundDiagnostic);
hr = S_OK;
}
else
{
goto Exit;
}
//Get the recipient flags
hr = pIMailMsgRecipients->GetDWORD(iRecip, IMMPID_RP_RECIPIENT_FLAGS, &dwRecipFlags);
if(FAILED(hr))
{
ErrorTrace((LPARAM)this, "Failure %08lx to get recipient flags for recip %d", hr, iRecip);
goto Exit;
}
//Get Per Recipient HRESULT
DEBUG_DO_IT(hrPerRecipStatus = 0xFFFFFFFF);
hr = pIMailMsgRecipients->GetDWORD(iRecip, IMMPID_RP_ERROR_CODE, (DWORD *) &hrPerRecipStatus);
if (SUCCEEDED(hr))
{
_ASSERT((0xFFFFFFFF != hrPerRecipStatus) && "Property not returned by MailMsg!!!");
hr = HrGetStatusFromContext(hrPerRecipStatus, dwRecipFlags, dwDSNAction, szStatus);
if (FAILED(hr))
goto Exit;
if (lstrcmp(szStatus, DSN_STATUS_FAILED))
goto Exit;
//
// We only found a generic status code (DSN_STATUS_FAILED), see if
// the global HRESULT or RFC821 status yield anything more specific.
//
fTryToFindStatusCode = TRUE;
}
else
{
if (MAILMSG_E_PROPNOTFOUND != hr)
goto Exit; // An error occurred getting the per-recip status
//
// There is no per-recip status. Fall back to global HRESULT or RFC821
// status string to try generate a status code.
//
fTryToFindStatusCode = TRUE;
}
if (fTryToFindStatusCode)
{
//
// We either couldn't generate a status string, or the status string
// wasn't good enough, try the global HRESULT and RFC822 status to
// generate a DSN status string.
//
hr = HrGetStatusFromRFC821Status(dwRFC821Status, szStatus);
if (FAILED(hr))
goto Exit;
if (S_OK == hr) //got status code from dwRFC821Status
goto Exit;
//If all else fails Get status code using global HRESULT & context
hr = HrGetStatusFromContext(hrStatus, dwRecipFlags, dwDSNAction, szStatus);
if (FAILED(hr))
goto Exit;
}
Exit:
if (SUCCEEDED(hr))
{
if (fFoundDiagnostic)
hr = S_OK;
else
hr = S_FALSE;
}
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrGetStatusFromStatus ]----------------------
//
//
// Description:
// Parse status code from RFC2034 extended status code string
//
// If string is not a complete RFC2034 extended status string, this
// function will attempt to parse the RFC821 SMTP return code and
// turn it into an extended status string.
// Parameters:
// IN cbExtendedStatus Size of extended status buffer
// IN szExtendedStatus Extended status buffer
// IN OUT szStatus RFC1893 formatted status code
// Returns:
// S_OK on success
// S_FALSE if could not be parsed
// FAILED if other error occurs
// History:
// 7/7/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrGetStatusFromStatus(
IN DWORD cbExtendedStatus,
IN OUT LPSTR szExtendedStatus,
IN OUT CHAR szStatus[STATUS_STRING_SIZE])
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrGetStatusFromStatus");
HRESULT hr = S_OK;
DWORD dwRFC821Status = 0;
BOOL fFormattedCorrectly = FALSE;
CHAR *pchStatus = NULL;
CHAR *pchDiag = NULL; //ptr to status string supplied by SMTP
DWORD cNumDigits = 0;
int i = 0;
//copy status code from diagnostic string in to status code
pchStatus = szStatus;
pchDiag = szExtendedStatus;
//there must be at least 3 characters to attempt parsing
if (cbExtendedStatus < MIN_CHAR_FOR_VALID_RFC821)
{
hr = S_FALSE;
goto Exit;
}
//check RFC822
if (!((DSN_STATUS_CH_CLASS_SUCCEEDED == *pchDiag) ||
(DSN_STATUS_CH_CLASS_TRANSIENT == *pchDiag) ||
(DSN_STATUS_CH_CLASS_FAILED == *pchDiag)))
{
//Doesn't start with RFC822... can't be valid
hr = S_FALSE;
goto Exit;
}
//RFC2034 must have at least RFC822 + " " + "x.x.x" = 10 chanracters
if (cbExtendedStatus >= MIN_CHAR_FOR_VALID_RFC2034)
{
pchDiag += MIN_CHAR_FOR_VALID_RFC821; //format is "xxx x.x.x"
//Find first digit
while(isspace((unsigned char)*pchDiag) && pchDiag < (szExtendedStatus + cbExtendedStatus))
pchDiag++;
if ((DSN_STATUS_CH_CLASS_SUCCEEDED == *pchDiag) ||
(DSN_STATUS_CH_CLASS_TRANSIENT == *pchDiag) ||
(DSN_STATUS_CH_CLASS_FAILED == *pchDiag))
{
//copy status code class
*pchStatus = *pchDiag;
pchStatus++;
pchDiag++;
//Next character must be a DSN_STATUS_CH_DELIMITER
if (DSN_STATUS_CH_DELIMITER == *pchDiag)
{
*pchStatus = DSN_STATUS_CH_DELIMITER;
pchStatus++;
pchDiag++;
//now parse this 1*3digit "." 1*3digit part
for (i = 0; i < 3; i++)
{
*pchStatus = *pchDiag;
if (!isdigit((unsigned char)*pchDiag))
{
if (DSN_STATUS_CH_DELIMITER != *pchDiag)
{
fFormattedCorrectly = FALSE;
break;
}
//copy delimiter
*pchStatus = *pchDiag;
pchStatus++;
pchDiag++;
break;
}
pchStatus++;
pchDiag++;
fFormattedCorrectly = TRUE; //hace first digit
}
if (fFormattedCorrectly) //so far.. so good
{
fFormattedCorrectly = FALSE;
for (i = 0; i < 3; i++)
{
*pchStatus = *pchDiag;
if (!isdigit((unsigned char)*pchDiag))
{
if (!isspace((unsigned char)*pchDiag))
{
fFormattedCorrectly = FALSE;
break;
}
break;
}
pchStatus++;
pchDiag++;
fFormattedCorrectly = TRUE;
}
//If we have found a good status code... go to exit
if (fFormattedCorrectly)
{
*pchStatus = '\0'; //make sure last CHAR is a NULL
goto Exit;
}
}
}
}
}
//We haven't been able to parse the extended status code, but we
//know we have at least a valid RFC822 response string
//convert to DWORD
for (i = 0; i < MIN_CHAR_FOR_VALID_RFC821; i++)
{
dwRFC821Status *= 10;
dwRFC821Status += szExtendedStatus[i] - '0';
}
hr = HrGetStatusFromRFC821Status(dwRFC821Status, szStatus);
_ASSERT(S_OK == hr); //this cannot possibly fail
//The code *should* be valid at this point
_ASSERT((DSN_STATUS_CH_CLASS_SUCCEEDED == szStatus[0]) ||
(DSN_STATUS_CH_CLASS_TRANSIENT == szStatus[0]) ||
(DSN_STATUS_CH_CLASS_FAILED == szStatus[0]));
hr = S_OK;
Exit:
TraceFunctLeave();
return hr;
}
//---[ CDefaultDSNSink::HrGetStatusFromContext ]---------------------
//
//
// Description:
// Determine status based on supplied context information
// Parameters:
// hrRecipient HRESULT for this recipient
// dwRecipFlags Flags for this recipient
// dwDSNAction DSN Action for this recipient
// szStatus Buffer to return status in
// Returns:
// S_OK Was able to get a valid status code
// History:
// 7/7/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrGetStatusFromContext(
IN HRESULT hrRecipient,
IN DWORD dwRecipFlags,
IN DWORD dwDSNAction,
IN OUT CHAR szStatus[STATUS_STRING_SIZE])
{
HRESULT hr = S_OK;
BOOL fValidHRESULT = FALSE;
BOOL fRecipContext = FALSE;
int iStatus = 0;
int i = 0;
CHAR chStatusClass = DSN_STATUS_CH_INVALID;
CHAR rgchStatusSubject[3] = {DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID};
CHAR rgchStatusDetail[3] = {DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID, DSN_STATUS_CH_INVALID};
//check to make sure that HRESULT is set according to the type of DSN happening
if (dwDSNAction & (DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL))
{
if (FAILED(hrRecipient)) //must be a failure code
fValidHRESULT = TRUE;
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
}
else if (dwDSNAction & DSN_ACTION_DELAYED)
{
if (FAILED(hrRecipient)) //must be a failure code
fValidHRESULT = TRUE;
chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
}
else if ((dwDSNAction & DSN_ACTION_RELAYED) ||
(dwDSNAction & DSN_ACTION_DELIVERED) ||
(dwDSNAction & DSN_ACTION_EXPANDED))
{
if (SUCCEEDED(hrRecipient)) //must be a success code
fValidHRESULT = TRUE;
chStatusClass = DSN_STATUS_CH_CLASS_SUCCEEDED;
}
else
{
_ASSERT(0 && "No DSN Action specified");
}
//special case HRESULTS
if (fValidHRESULT)
{
switch (hrRecipient)
{
case CAT_E_GENERIC: // 5.1.0 - General Cat failure.
case CAT_E_BAD_RECIPIENT: //5.1.0 - general bad address error
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = '0';
goto Exit;
case CAT_E_ILLEGAL_ADDRESS: //5.1.3 - bad address syntax
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = '3';
goto Exit;
case CAT_W_SOME_UNDELIVERABLE_MSGS: //5.1.1 - recipient could not be resolved
case (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)):
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = '1';
goto Exit;
case CAT_E_MULTIPLE_MATCHES: //5.1.4 - amiguous address
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = '4';
goto Exit;
case PHATQ_E_UNKNOWN_MAILBOX_SERVER: // 5.1.6 -- no homeMDB/msExchHomeServerName
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = '6';
goto Exit;
case CAT_E_NO_SMTP_ADDRESS: //5.1.7 - missing address
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = '7';
goto Exit;
case AQUEUE_E_MAX_HOP_COUNT_EXCEEDED: //4.4.6
chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
case CAT_E_FORWARD_LOOP: //5.4.6
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
rgchStatusDetail[0] = '6';
goto Exit;
case PHATQ_E_BAD_LOCAL_DOMAIN: //5.4.8
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
rgchStatusDetail[0] = '8';
goto Exit;
case AQUEUE_E_LOOPBACK_DETECTED: //5.3.5
//server is configured to loop back on itself
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_SYSTEM;
rgchStatusDetail[0] = '5';
goto Exit;
case AQUEUE_E_MSG_EXPIRED: //4.4.7
chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
rgchStatusDetail[0] = '7';
goto Exit;
case AQUEUE_E_HOST_NOT_RESPONDING: //4.4.1
chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
rgchStatusDetail[0] = '1';
goto Exit;
case AQUEUE_E_CONNECTION_DROPPED: //4.4.2
chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_NETWORK;
rgchStatusDetail[0] = '2';
goto Exit;
case AQUEUE_E_TOO_MANY_RECIPIENTS: //5.5.3
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_PROTOCOL;
rgchStatusDetail[0] = '3';
goto Exit;
case AQUEUE_E_LOCAL_MAIL_REFUSED: //5.2.1
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_MAILBOX;
rgchStatusDetail[0] = '1';
goto Exit;
case AQUEUE_E_MESSAGE_TOO_LARGE: //5.2.3
case AQUEUE_E_LOCAL_QUOTA_EXCEEDED: //5.2.3
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_MAILBOX;
rgchStatusDetail[0] = '3';
goto Exit;
case AQUEUE_E_ACCESS_DENIED: //5.7.1
case AQUEUE_E_SENDER_ACCESS_DENIED: //5.7.1
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_POLICY;
rgchStatusDetail[0] = '1';
goto Exit;
case AQUEUE_E_NO_ROUTE: //5.4.4
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = '4';
rgchStatusDetail[0] = '4';
goto Exit;
case AQUEUE_E_QADMIN_NDR: //4.3.2
chStatusClass = DSN_STATUS_CH_CLASS_TRANSIENT;
rgchStatusSubject[0] = '3';
rgchStatusDetail[0] = '2';
goto Exit;
case AQUEUE_E_SMTP_GENERIC_ERROR: //5.4.0
chStatusClass = DSN_STATUS_CH_CLASS_FAILED;
rgchStatusSubject[0] = '4';
rgchStatusDetail[0] = '0';
goto Exit;
}
}
if ((RP_ERROR_CONTEXT_STORE | RP_ERROR_CONTEXT_CAT | RP_ERROR_CONTEXT_MTA) &
dwRecipFlags)
fRecipContext = TRUE;
//Now look at the context on recipient flags
//$$TODO - Use HRESULT's for these case as well
if ((RP_ERROR_CONTEXT_STORE & dwRecipFlags) ||
(!fRecipContext && (DSN_ACTION_CONTEXT_STORE & dwDSNAction)))
{
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_MAILBOX;
rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
}
else if ((RP_ERROR_CONTEXT_CAT & dwRecipFlags) ||
(!fRecipContext && (DSN_ACTION_CONTEXT_CAT & dwDSNAction)))
{
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_ADDRESS;
rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
}
else if ((RP_ERROR_CONTEXT_MTA & dwRecipFlags) ||
(!fRecipContext && (DSN_ACTION_CONTEXT_MTA & dwDSNAction)))
{
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_PROTOCOL;
rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
}
else
{
rgchStatusSubject[0] = DSN_STATUS_CH_SUBJECT_GENERAL;
rgchStatusDetail[0] = DSN_STATUS_CH_DETAIL_GENERAL;
}
Exit:
if (SUCCEEDED(hr))
{
//compose szStatus
_ASSERT(DSN_STATUS_CH_INVALID != chStatusClass);
_ASSERT(DSN_STATUS_CH_INVALID != rgchStatusSubject[0]);
_ASSERT(DSN_STATUS_CH_INVALID != rgchStatusDetail[0]);
szStatus[iStatus] = chStatusClass;
iStatus++;
szStatus[iStatus] = DSN_STATUS_CH_DELIMITER;
iStatus++;
for (i = 0;
(i < 3) && (DSN_STATUS_CH_INVALID != rgchStatusSubject[i]);
i++)
{
szStatus[iStatus] = rgchStatusSubject[i];
iStatus++;
}
szStatus[iStatus] = DSN_STATUS_CH_DELIMITER;
iStatus++;
for (i = 0;
(i < 3) && (DSN_STATUS_CH_INVALID != rgchStatusDetail[i]);
i++)
{
szStatus[iStatus] = rgchStatusDetail[i];
iStatus++;
}
szStatus[iStatus] = '\0';
hr = S_OK;
}
return hr;
}
//---[ CDefaultDSNSink::HrGetStatusFromRFC821Status ]----------------
//
//
// Description:
// Attempts to generate a DSN status code from a integer version of a
// RFC821 response
// Parameters:
// IN dwRFC821Status Integer version of RFC821Status
// IN OUT szStatus Buffer to write status string to
// Returns:
// S_OK if valid status that could be converted to dsn status code
// S_FALSE if status code cannot be converted
// History:
// 7/9/98 - MikeSwa Created
//
// Note:
// Eventually, there may be a way to pass extended information in the
// DWORD to this event. We *could* also encode RFC1893 (x.xxx.xxx format)
// in a DWORD (in dwRFC821Status) as follows:
//
// 0xF 0 000 000
// | | \-/ \-/
// | | | +----- detail portion of status code
// | | +--------- subject portion of status code
// | +------------ class portion of status code
// +-------------- mask to distinguish from RFC821 status code
//
// For example "2.1.256" could be encoded as 0xF2001256
//
// If we do this, we will probably need to expose public functions to
// compress/uncompress.
//
// Yet another possiblity would be to expose an HRESULT facility "RFC1893"
// Use success, warning, and failed bits to denote the class, and then
// use the error code space to encode the status codes
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrGetStatusFromRFC821Status(
IN DWORD dwRFC821Status,
IN OUT CHAR szStatus[STATUS_STRING_SIZE])
{
HRESULT hr = S_OK;
//For now, there will be a very simple implementation just converts
//to 2.0.0, 4.0.0, or 5.0.0, but this function is designed to be
//the central place that converts RFC821 status codes to DSN (RFC1893)
//status codes
_ASSERT((!dwRFC821Status) ||
(((200 <= dwRFC821Status) && (299 >= dwRFC821Status)) ||
((400 <= dwRFC821Status) && (599 >= dwRFC821Status))) &&
"Invalid Status Code");
//For now have simplistic mapping of RFC821 status codes
if ((200 <= dwRFC821Status) && (299 >= dwRFC821Status)) //200 level error
{
strcpy(szStatus, DSN_STATUS_SUCCEEDED);
}
else if ((400 <= dwRFC821Status) && (499 >= dwRFC821Status)) //400 level error
{
strcpy(szStatus, DSN_STATUS_DELAYED);
}
else if ((500 <= dwRFC821Status) && (599 >= dwRFC821Status)) //500 level error
{
strcpy(szStatus, DSN_STATUS_SMTP_PROTOCOL_ERROR);
}
else
{
hr = S_FALSE;
}
return hr;
}
//---[ CDefaultDSNSink::HrWriteHumanReadableListOfRecips ]-----------
//
//
// Description:
// Writes a list of recipients to the human readable portion
// Parameters:
// IN pIMailMsgRecipients Recipients interface
// IN prpfctxt Recipient filter context for this DSN
// IN dwDSNActionsNeeded Type of DSN that we are generating
// IN pdsnbuff DSN buffer to write DSN to
// Returns:
// S_OK on success
// History:
// 12/15/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrWriteHumanReadableListOfRecips(
IN IMailMsgRecipients *pIMailMsgRecipients,
IN IDSNRecipientIterator *pIRecipIter,
IN DWORD dwDSNActionsNeeded,
IN CDSNBuffer *pdsnbuff)
{
HRESULT hr = S_OK;
DWORD iCurrentRecip = 0;
DWORD dwCurrentRecipFlags = 0;
DWORD dwCurrentDSNAction = 0;
CHAR szBuffer[PROP_BUFFER_SIZE];
CHAR szAddressType[PROP_BUFFER_SIZE];
hr = pIRecipIter->HrReset();
if(FAILED(hr))
goto Exit;
hr = pIRecipIter->HrGetNextRecipient(
&iCurrentRecip,
&dwCurrentDSNAction);
while (SUCCEEDED(hr))
{
if(dwDSNActionsNeeded & dwCurrentDSNAction)
{
hr = HrGetRecipAddressAndType(
pIMailMsgRecipients, iCurrentRecip,
PROP_BUFFER_SIZE, szBuffer,
sizeof(szAddressType), szAddressType);
if (SUCCEEDED(hr))
{
//write address value
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_INDENT, sizeof(DSN_INDENT)-sizeof(CHAR));
if (FAILED(hr))
goto Exit;
hr = pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
if (FAILED(hr))
goto Exit;
#ifdef NEVER
//Print the recipient flags as well
wsprintf(szBuffer, " (0x%08X)", dwCurrentRecipFlags);
pdsnbuff->HrWriteBuffer((BYTE *) szBuffer, lstrlen(szBuffer));
#endif //NEVER
hr = pdsnbuff->HrWriteBuffer((BYTE *) DSN_CRLF, sizeof(DSN_CRLF)-sizeof(CHAR));
if (FAILED(hr))
goto Exit;
}
else
{
//move along... these are not the error results you are interested in.
hr = S_OK;
}
}
hr = pIRecipIter->HrGetNextRecipient(
&iCurrentRecip,
&dwCurrentDSNAction);
}
Exit:
return hr;
}
//---[ CDefaultDSNSink::HrGetRecipAddressAndType ]-------------------
//
//
// Description:
// Gets the recipient address and returns a pointer to the appropriate
// string constant for the address type.
// Parameters:
// IN pIMailMsgRecipients Ptr To recipients interface
// IN iRecip Index of the recipient of interest
// IN cbAddressBuffer Size of buffer for address
// IN OUT pbAddressBuffer Address buffer to dump address in
// IN cbAddressType Size of buffer for address type
// IN OUT pszAddressType Buffer for address type.
// Returns:
// S_OK on success
// MAILMSG_E_PROPNOTFOUND if no address properties could be found
// History:
// 12/16/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT CDefaultDSNSink::HrGetRecipAddressAndType(
IN IMailMsgRecipients *pIMailMsgRecipients,
IN DWORD iRecip,
IN DWORD cbAddressBuffer,
IN OUT LPSTR szAddressBuffer,
IN DWORD cbAddressType,
IN OUT LPSTR szAddressType)
{
TraceFunctEnterEx((LPARAM) this, "CDefaultDSNSink::HrGetRecipAddressAndType");
HRESULT hr = S_OK;
BOOL fFoundAddress = FALSE;
DWORD i = 0;
LPSTR szDelimiterLocation = NULL;
CHAR szXDash[] = "x-";
CHAR chSave = '\0';
_ASSERT(szAddressType);
_ASSERT(cbAddressType);
_ASSERT(cbAddressBuffer);
_ASSERT(szAddressBuffer);
_ASSERT(pIMailMsgRecipients);
szAddressType[0] = '\0';
szAddressBuffer[0] = '\0';
for (i = 0; i < NUM_DSN_ADDRESS_PROPERTIES; i ++)
{
hr = pIMailMsgRecipients->GetStringA(iRecip, g_rgdwRecipPropIDs[i],
cbAddressBuffer, szAddressBuffer);
if (SUCCEEDED(hr))
{
fFoundAddress = TRUE;
strncpy(szAddressType, g_rgszAddressTypes[i], cbAddressType);
break;
}
}
if (!fFoundAddress)
{
hr = MAILMSG_E_PROPNOTFOUND;
ErrorTrace((LPARAM) this,
"Unable to find recip %d address for message", iRecip);
}
else if (IMMPID_RP_ADDRESS_OTHER == g_rgdwRecipPropIDs[i])
{
//Handle special case of IMMPID_RP_ADDRESS_OTHER... we should attempt to
//parse out address from "type:address" format of IMMPID_RP_ADDRESS_OTHER
//property
szDelimiterLocation = strchr(szAddressBuffer, ':');
if (szDelimiterLocation && cbAddressType > sizeof(szXDash))
{
chSave = *szDelimiterLocation;
*szDelimiterLocation = '\0';
DebugTrace((LPARAM) this,
"Found Address type of %s", szAddressBuffer);
strncpy(szAddressType, szXDash, cbAddressType);
strncat(szAddressType, szAddressBuffer,
cbAddressType - (sizeof(szXDash)-sizeof(CHAR)));
*szDelimiterLocation = chSave;
}
else
{
ErrorTrace((LPARAM) this,
"Unable to find address type for address %s", szAddressBuffer);
}
}
DebugTrace((LPARAM) this,
"Found recipient address %s:%s for recip %d (propery %i:%x)",
szAddressType, szAddressBuffer, iRecip, i, g_rgdwRecipPropIDs[i]);
TraceFunctLeave();
return hr;
}
//+------------------------------------------------------------
//
// Function: CPostDSNHandler::CPostDSNHandler
//
// Synopsis: Constructor. Initialize member variables
//
// Arguments:
// pIUnk: IUnknown to aggregate for refcounting (NOT refcounted internally)
// pDSNGenerator: CDSNGenerator object
// pIServerEvent: Interface for triggering events
// dwVSID: Virtual server instance number
// pISMTPServer: ISMTPServer interface
// pIMsgOrig: interface to original message
// pIAQDSNSubmission: pointer to internal interface for allocing /
// submitting DSNs
// pDefaultSink: Default sink pointer
//
// Returns: Nothing
//
// History:
// jstamerj 2001/05/14 13:17:34: Created.
//
//-------------------------------------------------------------
CPostDSNHandler::CPostDSNHandler(
IN IUnknown *pUnk,
IN CDSNGenerator *pDSNGenerator,
IN IAQServerEvent *pIServerEvent,
IN DWORD dwVSID,
IN ISMTPServer *pISMTPServer,
IN IMailMsgProperties *pIMsgOrig,
IN IDSNSubmission *pIAQDSNSubmission,
IN IDSNGenerationSink *pDefaultSink)
{
TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::CPostDSNHandler");
m_dwSig = SIGNATURE_CPOSTDSNHANDLER;
m_pUnk = pUnk;
m_pIDSNProps = NULL;
m_pDSNGenerator = pDSNGenerator;
m_pIServerEvent = pIServerEvent;
m_pIServerEvent->AddRef();
m_dwVSID = dwVSID;
m_pISMTPServer = pISMTPServer;
m_pISMTPServer->AddRef();
m_pIMsgOrig = pIMsgOrig;
m_pIMsgOrig->AddRef();
m_pIAQDSNSubmission = pIAQDSNSubmission;
m_pIAQDSNSubmission->AddRef();
m_pDefaultSink = pDefaultSink;
m_pDefaultSink->AddRef();
TraceFunctLeaveEx((LPARAM)this);
} // CPostDSNHandler::CPostDSNHandler
//+------------------------------------------------------------
//
// Function: CPostDSNHandler::~CPostDSNHandler
//
// Synopsis: Desctrutor -- cleanup
//
// Arguments: NONE
//
// Returns: NOTHING
//
// History:
// jstamerj 2001/05/14 13:21:28: Created.
//
//-------------------------------------------------------------
CPostDSNHandler::~CPostDSNHandler()
{
TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::~CPostDSNHandler");
_ASSERT(m_dwSig == SIGNATURE_CPOSTDSNHANDLER);
if(m_pIServerEvent)
m_pIServerEvent->Release();
if(m_pISMTPServer)
m_pISMTPServer->Release();
if(m_pIMsgOrig)
m_pIMsgOrig->Release();
if(m_pIDSNProps)
m_pIDSNProps->Release();
if(m_pIAQDSNSubmission)
m_pIAQDSNSubmission->Release();
if(m_pDefaultSink)
m_pDefaultSink->Release();
m_dwSig = SIGNATURE_CPOSTDSNHANDLER_INVALID;
TraceFunctLeaveEx((LPARAM)this);
} // CPostDSNHandler::~CPostDSNHandler
//+------------------------------------------------------------
//
// Function: CPostDSNHandler::QueryInterface
//
// Synopsis: Return an interface to this object
//
// Arguments:
// riid: interface IID requested
// ppvObj: out param for interface
//
// Returns:
// S_OK: Success
// E_NOINTERFACE: not supported
//
// History:
// jstamerj 2000/12/08 21:26:14: Created.
//
//-------------------------------------------------------------
HRESULT CPostDSNHandler::QueryInterface(
IN REFIID riid,
OUT LPVOID *ppvObj)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::QueryInterface");
*ppvObj = NULL;
if(riid == IID_IUnknown)
{
*ppvObj = (IUnknown *)this;
}
else if(riid == IID_IDSNSubmission)
{
*ppvObj = (IDSNSubmission *)this;
}
else
{
hr = E_NOINTERFACE;
}
if(SUCCEEDED(hr))
AddRef();
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CPostDSNHandler::QueryInterface
//+------------------------------------------------------------
//
// Function: CPostDSNHandler::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
// error from SMTP
//
// History:
// jstamerj 2001/05/11 14:19:09: Created.
//
//-------------------------------------------------------------
HRESULT CPostDSNHandler::HrAllocBoundMessage(
OUT IMailMsgProperties **ppMsg,
OUT PFIO_CONTEXT *phContent)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::HrAllocBoundMessage");
if(m_pIAQDSNSubmission == NULL)
{
ErrorTrace((LPARAM)this, "PostDSNHandler called outside of event!");
hr = E_POINTER;
goto CLEANUP;
}
hr = m_pIAQDSNSubmission->HrAllocBoundMessage(
ppMsg,
phContent);
if(FAILED(hr))
goto CLEANUP;
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CPostDSNHandler::HrAllocBoundMessage
//+------------------------------------------------------------
//
// Function: CPostDSNHandler::HrSubmitDSN
//
// Synopsis: Accept a sink generated DSN
//
// Arguments:
// dwDSNAction: Type of DSN
// cRecipsDSNd: # of recipients DSNd
// pDSNMsg: The DSN mailmsg
//
// Returns:
// S_OK: Success
//
// History:
// jstamerj 2000/12/08 21:27:56: Created.
//
//-------------------------------------------------------------
HRESULT CPostDSNHandler::HrSubmitDSN(
IN DWORD dwDSNAction,
IN DWORD cRecipsDSNd,
IN IMailMsgProperties *pDSNMsg)
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)this, "CPostDSNHandler::HrSubmitDSN");
if(pDSNMsg == NULL)
{
hr = E_POINTER;
goto CLEANUP;
}
if(m_pIAQDSNSubmission == NULL)
{
ErrorTrace((LPARAM)this, "PostDSNHandler called outside of event!");
hr = E_POINTER;
goto CLEANUP;
}
//
// Trigger event
//
_ASSERT(m_pDSNGenerator);
hr = m_pDSNGenerator->HrTriggerPostGenerateDSN(
m_pIServerEvent,
m_dwVSID,
m_pISMTPServer,
m_pIMsgOrig,
dwDSNAction,
cRecipsDSNd,
pDSNMsg,
m_pIDSNProps);
if(FAILED(hr))
goto CLEANUP;
_ASSERT(m_pIAQDSNSubmission);
hr = m_pIAQDSNSubmission->HrSubmitDSN(
dwDSNAction,
cRecipsDSNd,
pDSNMsg);
if(FAILED(hr))
goto CLEANUP;
CLEANUP:
DebugTrace((LPARAM)this, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)this);
return hr;
} // CPostDSNHandler::HrSubmitDSN
//+------------------------------------------------------------
//
// Function: CDSNGenerator::HrStaticInit
//
// Synopsis: Initialize static member data
//
// Arguments: None
//
// Returns:
// S_OK: Success
// E_OUTOFMEMORY
//
// History:
// jstamerj 2001/05/11 10:59:26: Created.
//
//-------------------------------------------------------------
HRESULT CDSNGenerator::HrStaticInit()
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)0, "CDSNGenerator::HrStaticInit");
hr = CDSNPool::HrStaticInit();
if(FAILED(hr))
goto CLEANUP;
CLEANUP:
DebugTrace((LPARAM)0, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)0);
return hr;
} // CDSNGenerator::HrStaticInit
//+------------------------------------------------------------
//
// Function: CDSNGenerator::StaticDeinit
//
// Synopsis: Deinitialize static data
//
// Arguments: None
//
// Returns: Nothing
//
// History:
// jstamerj 2001/05/11 11:00:31: Created.
//
//-------------------------------------------------------------
VOID CDSNGenerator::StaticDeinit()
{
TraceFunctEnterEx((LPARAM)0, "CDSNGenerator::HrStaticDeinit");
CDSNPool::StaticDeinit();
TraceFunctLeaveEx((LPARAM)0);
} // CDSNGenerator::HrStaticDeinit
//+------------------------------------------------------------
//
// Function: CDSNPool::HrStaticInit
//
// Synopsis: Initialize static member data
//
// Arguments: None
//
// Returns:
// S_OK: Success
// E_OUTOFMEMORY
//
// History:
// jstamerj 2001/05/11 11:02:18: Created.
//
//-------------------------------------------------------------
HRESULT CDSNPool::HrStaticInit()
{
HRESULT hr = S_OK;
TraceFunctEnterEx((LPARAM)0, "CDSNPool::HrStaticInit");
//
// There can't be more than one DSNGeneration per thread, so 1000
// objects should be more than enough
//
if(!sm_Pool.ReserveMemory(1000, sizeof(CDSNPool)))
{
hr = E_OUTOFMEMORY;
goto CLEANUP;
}
CLEANUP:
DebugTrace((LPARAM)0, "returning %08lx", hr);
TraceFunctLeaveEx((LPARAM)0);
return hr;
} // CDSNPool::HrStaticInit
//+------------------------------------------------------------
//
// Function: CDSNPool::StaticDeinit
//
// Synopsis: Deinitialize static member data
//
// Arguments: None
//
// Returns: Nothing
//
// History:
// jstamerj 2001/05/11 11:37:51: Created.
//
//-------------------------------------------------------------
VOID CDSNPool::StaticDeinit()
{
TraceFunctEnterEx((LPARAM)0, "CDSNPool::StaticDeinit");
sm_Pool.ReleaseMemory();
TraceFunctLeaveEx((LPARAM)0);
} // CDSNPool::StaticDeinit