REPORT.C

// --report.c------------------------------------------------------------------- 
//
// Module containing report functions.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------

#include "edk.h"

//$--REPORT_T-------------------------------------------------------------------
// Report type.
// -----------------------------------------------------------------------------
typedef enum __REPORT_T
{
NDR = 0, // non-delivery report
DR, // delivery report
RN, // read notification report
NRN, // non-read notification report
REPORT_LAST // all report values are less than this
} REPORT_T;

#define VALID_REPORT_T(x) (((x) >= ((ULONG)0)) && \
((x) <= ((ULONG)REPORT_LAST)))

#include "report.chk"

//$--HrEDKReportSubject---------------------------------------------------------
// Return a report subject.
// -----------------------------------------------------------------------------
static HRESULT HrEDKReportSubject( // RETURNS: return code
IN REPORT_T ReportType, // report type
IN LPTSTR lpszSubject, // pointer to subject
OUT LPTSTR *lppszReportSubject) // pointer to report subject
{
HRESULT hr = NOERROR;
ULONG cch = 0;
LPTSTR lpszOrigSubject = NULL;
LPTSTR lpszReportPrefix = NULL;
SCODE sc = 0;
ULONG cBytes = 0;

DEBUGPRIVATE("HrEDKReportSubject()\n");

hr = CHK_HrEDKReportSubject(
ReportType,
lpszSubject,
lppszReportSubject);

if(FAILED(hr))
RETURN(hr);

*lppszReportSubject = NULL;

cch = 0;

if(lpszSubject != NULL)
{
cch = lstrlen(lpszSubject);
}

if(cch == 0)
{
lpszOrigSubject = TEXT("");
}
else
{
lpszOrigSubject = lpszSubject;
}

switch(ReportType)
{
case NDR:
lpszReportPrefix = TEXT("Undeliverable: ");
break;
case DR:
lpszReportPrefix = TEXT("Delivered: ");
break;
case RN:
lpszReportPrefix = TEXT("Read: ");
break;
case NRN:
lpszReportPrefix = TEXT("Unread: ");
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cch += lstrlen(lpszReportPrefix);

cch++; // terminating NULL character

cBytes = cch * sizeof(TCHAR);

sc = MAPIAllocateBuffer(cBytes, lppszReportSubject);

if(FAILED(sc))
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}

_tcscpy(*lppszReportSubject, lpszReportPrefix);
_tcscat(*lppszReportSubject, lpszOrigSubject);

cleanup:

if(FAILED(hr))
{
MAPIFREEBUFFER(*lppszReportSubject);
}

RETURN(hr);
}

//$--HrEDKReportMessageClass----------------------------------------------------
// Return a report message class.
// -----------------------------------------------------------------------------
static HRESULT HrEDKReportMessageClass( // RETURNS: return code
IN REPORT_T ReportType, // report type
IN LPTSTR lpszMessageClass, // pointer to message class
OUT LPTSTR *lppszReportMessageClass) // pointer to report message class
{
HRESULT hr = NOERROR;
ULONG cch = 0;
LPTSTR lpszMsgClass = NULL;
LPTSTR lpszReportSuffix = NULL;
SCODE sc = 0;
ULONG cBytes = 0;

DEBUGPRIVATE("HrEDKReportMessageClass()\n");

hr = CHK_HrEDKReportMessageClass(
ReportType,
lpszMessageClass,
lppszReportMessageClass);

if(FAILED(hr))
RETURN(hr);

*lppszReportMessageClass = NULL;

cch = lstrlen(lpszMessageClass);

if(cch == 0)
{
lpszMsgClass = TEXT("IPM.NOTE");
cch = lstrlen(lpszMsgClass);
}
else
{
lpszMsgClass = lpszMessageClass;
}

cch += lstrlen(TEXT("REPORT."));

switch(ReportType)
{
case NDR:
lpszReportSuffix = TEXT(".NDR");
break;
case DR:
lpszReportSuffix = TEXT(".DR");
break;
case RN:
lpszReportSuffix = TEXT(".IPNRN");
break;
case NRN:
lpszReportSuffix = TEXT(".IPNNRN");
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cch += lstrlen(lpszReportSuffix);

cch++; // terminating NULL character

cBytes = cch * sizeof(TCHAR);

sc = MAPIAllocateBuffer(cBytes, lppszReportMessageClass);

if(FAILED(sc))
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}

_tcscpy(*lppszReportMessageClass, TEXT("REPORT."));
_tcscat(*lppszReportMessageClass, lpszMsgClass);
_tcscat(*lppszReportMessageClass, lpszReportSuffix);

cleanup:

if(FAILED(hr))
{
MAPIFREEBUFFER(*lppszReportMessageClass);
}

RETURN(hr);
}

//$--HrEDKSetReportMessageClass-------------------------------------------------
// Set the report message class.
// -----------------------------------------------------------------------------
static HRESULT HrEDKSetReportMessageClass( // RETURNS: return code
IN REPORT_T ReportType, // report type
IN LPMESSAGE lpMessage, // pointer to message
IN OUT LPMESSAGE lpReport) // pointer to report
{
HRESULT hr = NOERROR;
HRESULT hrT = 0;
ULONG cbMessageClass = 0;
LPTSTR lpszMessageClass = NULL;
LPTSTR lpszReportMessageClass = NULL;

DEBUGPRIVATE("HrEDKSetReportMessageClass()\n");

hr = CHK_HrEDKSetReportMessageClass(
ReportType,
lpMessage,
lpReport);

if(FAILED(hr))
RETURN(hr);

hr = HrMAPIGetPropString(
(LPMAPIPROP)lpMessage,
PR_MESSAGE_CLASS,
&cbMessageClass,
(LPVOID *)&lpszMessageClass);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrEDKReportMessageClass(
ReportType,
lpszMessageClass,
&lpszReportMessageClass);

MAPIFREEBUFFER(lpszMessageClass);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropString(
(LPMAPIPROP)lpReport,
PR_MESSAGE_CLASS,
(LPVOID)lpszReportMessageClass);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:

MAPIFREEBUFFER(lpszMessageClass);

MAPIFREEBUFFER(lpszReportMessageClass);

RETURN(hr);
}

//$--HrEDKEnvelopeMessageClass--------------------------------------------------
// Return an envelope message class.
// -----------------------------------------------------------------------------
static HRESULT HrEDKEnvelopeMessageClass( // RETURNS: return code
IN LPTSTR lpszMessageClass, // pointer to message class
OUT LPTSTR *lppszEnvelopeMessageClass) // pointer to envelope message class
{
HRESULT hr = NOERROR;
ULONG cch = 0;
LPTSTR lpszMsgClass = NULL;
LPTSTR lpszReportSuffix = NULL;
SCODE sc = 0;
ULONG cBytes = 0;

DEBUGPRIVATE("HrEDKEnvelopeMessageClass()\n");

hr = CHK_HrEDKEnvelopeMessageClass(
lpszMessageClass,
lppszEnvelopeMessageClass);

if(FAILED(hr))
RETURN(hr);

*lppszEnvelopeMessageClass = NULL;

cch = lstrlen(lpszMessageClass);

if(cch == 0)
{
lpszMsgClass = "IPM.NOTE";
cch = lstrlen(lpszMsgClass);
}
else
{
lpszMsgClass = lpszMessageClass;
}

cch += lstrlen(TEXT("ENVELOPE."));

cch++; // terminating NULL character

cBytes = cch * sizeof(TCHAR);

sc = MAPIAllocateBuffer(cBytes, lppszEnvelopeMessageClass);

if(FAILED(sc))
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}

_tcscpy(*lppszEnvelopeMessageClass, TEXT("ENVELOPE."));
_tcscat(*lppszEnvelopeMessageClass, lpszMsgClass);

cleanup:

if(FAILED(hr))
{
MAPIFREEBUFFER(*lppszEnvelopeMessageClass);
}

RETURN(hr);
}

//$--HrEDKCopyReportProps-------------------------------------------------------
// Copy report properties.
// -----------------------------------------------------------------------------
static HRESULT HrEDKCopyReportProps( // RETURNS: return code
IN REPORT_T ReportType, // report type
IN LPMESSAGE lpReport, // pointer to report
IN ULONG cbSubjectMtsId, // count of bytes in subject MTS-ID
IN LPBYTE lpSubjectMtsId, // pointer to subject MTS-ID
IN LPTSTR lpszReportName, // pointer to report name
IN ULONG cbReportEntryId, // count of bytes in report entry ID
IN LPENTRYID lpReportEntryId, // pointer to report entry ID
IN OUT LPMESSAGE lpReportEnvelope) // pointer to report envelope
{
HRESULT hr = NOERROR;

DEBUGPRIVATE("HrEDKCopyReportProps()\n");

hr = CHK_HrEDKCopyReportProps(
ReportType,
lpReport,
cbSubjectMtsId,
lpSubjectMtsId,
lpszReportName,
cbReportEntryId,
lpReportEntryId,
lpReportEnvelope);

if(FAILED(hr))
RETURN(hr);

hr = HrMAPIMoveOneProp(
(LPMAPIPROP)lpReport,
PR_SUBJECT,
PR_SUBJECT,
FALSE,
TRUE,
(LPMAPIPROP)lpReportEnvelope);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrEDKSetReportMessageClass(
ReportType,
lpReport,
lpReportEnvelope);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropBinary(
(LPMAPIPROP)lpReportEnvelope,
PR_MTS_SUBJECT_ID,
cbSubjectMtsId,
(LPVOID)lpSubjectMtsId);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropString(
(LPMAPIPROP)lpReportEnvelope,
PR_REPORT_DESTINATION_NAME,
(LPVOID)lpszReportName);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropBinary(
(LPMAPIPROP)lpReportEnvelope,
PR_REPORT_DESTINATION_ENTRYID,
cbReportEntryId,
(LPVOID)lpReportEntryId);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropBoolean(
(LPMAPIPROP)lpReportEnvelope,
PR_DELETE_AFTER_SUBMIT,
TRUE);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:

RETURN(hr);
}

// These strings are explicitly not localized.

static const char lpszReport[] = "REPORT.";
static const char lpszIPNRN[] = ".IPNRN";
static const char lpszIPNNRN[] = ".IPNNRN";
static const UINT cchReportClass = sizeof(lpszReport) + sizeof(lpszIPNNRN);

static const enum
{
IREPORT = 0, ICONVERSATION, ICONVERSATIONTOPIC, ICONVERSATIONIDX, ISEARCH,
IMSGCLASS, ISUBJECT, ISUBJECTPREFX, ISUBJECTNORM, ISENDERNAME, ISENDEREID,
ISENDADRTYPE, ISENDEMAIL, IREPORTNAME, IREPORTEID, IRECEIVENAME, IRECEIVEEID,
IPRIORITY, IIMPORTANCE, ISENTREPNAME, ISENTREPEID, IRCVDREPNAME, IRCVDREPEID,
IMSGDELTIME, ISUBMITTIME, IDISPTO, IDISPCC, IDISPBCC, ISENSITIVITY,
INDRREQUEST, IDRREQUEST, IRNREQUEST, RRN_PROP_MAX
};

static const enum
{
IRECIPTYPE = 0, IRECIPDISPNM, IRECIPEID, IRECIPADRTYPE, IRECIPEMAIL,
RRN_RECIP_PROP_MAX
};


//$--HrReadReceipt--------------------------------------------------------------
// Create a read/non-read notification report.
// -----------------------------------------------------------------------------
static HRESULT HrReadReceipt( // RETURNS: return code
IN ULONG ulFlags, // flags
IN LPSTR lpszReportText, // report text
IN LPSTR lpszSubjectPrefix, // subject prefix
IN LPMESSAGE lpMessage, // message
OUT LPMESSAGE lpReadReceipt) // report
{
SCODEsc= S_OK;
HRESULThr= hrSuccess;
ADRLISTAdrList = {0};
BOOLfSetError= FALSE;
BOOLfSubjectOpenProperty= FALSE;
CHARchNull= 0;
FILETIME ft = {0};
LPSPropValuelpOrigProps= NULL;
LPSPropValuelpRecipVals= NULL;
LPSTRlpszMessageClass= NULL;
LPSTRlpszReadSubject= NULL;
LPSTRlpszSubjOrig= NULL;
SPropValue rgMsgVals[ RRN_PROP_MAX ] = {0};
SYSTEMTIME st = {0};
UINTiNumProp = 0;
ULONGulSize = 0;
ULONGulT = 0;
#ifdef DEBUG
LPSPropProblemArraylpProblem= NULL;
#endif

static SizedSPropTagArray(RRN_PROP_MAX, ptagaOrigMsg) =
{
RRN_PROP_MAX,
{
PR_REPORT_TAG,
PR_CONVERSATION_KEY,
PR_CONVERSATION_TOPIC_A,
PR_CONVERSATION_INDEX,
PR_SEARCH_KEY,// must
PR_MESSAGE_CLASS_A,// must
PR_SUBJECT_A,
PR_SUBJECT_PREFIX_A,
PR_NORMALIZED_SUBJECT_A,
PR_SENDER_NAME_A,
PR_SENDER_ENTRYID,// must
PR_SENDER_ADDRTYPE,
PR_SENDER_EMAIL_ADDRESS,
PR_REPORT_NAME_A,
PR_REPORT_ENTRYID,
PR_RECEIVED_BY_NAME_A,
PR_RECEIVED_BY_ENTRYID,
PR_PRIORITY,
PR_IMPORTANCE,
PR_SENT_REPRESENTING_NAME_A,
PR_SENT_REPRESENTING_ENTRYID,
PR_RCVD_REPRESENTING_NAME_A,
PR_RCVD_REPRESENTING_ENTRYID,
PR_MESSAGE_DELIVERY_TIME,// must
PR_CLIENT_SUBMIT_TIME,
PR_DISPLAY_TO_A,
PR_DISPLAY_CC_A,
PR_DISPLAY_BCC_A,
PR_SENSITIVITY,

/*Properties from here out are just place
*holders for values we set into a message
*regardless of the value in the original
*message
*/

PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED,
PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED,
PR_READ_RECEIPT_REQUESTED
}
};

static enum
{
outIREPORTTEXT = 0, outIMESSAGECLASS, outIREPORTTIME, outIORIGAUTHOREID,
outIORIGDELIVERYTIME, outIPARENTKEY, outIDELETEAFTERSUBMIT,
outCMAXIDX
};

hr = CHK_HrReadReceipt(
ulFlags,
lpszReportText,
lpszSubjectPrefix,
lpMessage,
lpReadReceipt);

if(FAILED(hr))
RETURN(hr);

#ifdef DEBUG

// Determine if we should fill out the out the empty message.

hr = HrGetOneProp((LPMAPIPROP)lpMessage, PR_READ_RECEIPT_REQUESTED, &lpOrigProps);
if (FAILED(hr) && hr != ResultFromScode(MAPI_E_NOT_FOUND))
{
MODULE_ERROR("HrReadReceipt: Failure to get prop");
goto exit;
}

ASSERTERROR(!(GetScode(hr) == MAPI_E_NOT_FOUND || !lpOrigProps->Value.l),
"PR_READ_RECEIPT_REQUESTED Flag not found");

MAPIFreeBuffer(lpOrigProps);
lpOrigProps = NULL;
#endif

// get the props we need from the original message

hr = lpMessage->lpVtbl->GetProps(lpMessage,
(LPSPropTagArray)&ptagaOrigMsg, 0, // ansi
&ulT, &lpOrigProps);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure to get Original msg props");
fSetError = TRUE;
goto exit;
}

// if message delivery time is not found then add it

if(lpOrigProps[IMSGDELTIME].ulPropTag != PR_MESSAGE_DELIVERY_TIME)
{
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);

lpOrigProps[IMSGDELTIME].ulPropTag = PR_MESSAGE_DELIVERY_TIME;
lpOrigProps[IMSGDELTIME].Value.ft = ft;
}

// check to see if we have the "must" properties

if (lpOrigProps[ISEARCH].ulPropTag != PR_SEARCH_KEY
|| lpOrigProps[IMSGCLASS].ulPropTag != PR_MESSAGE_CLASS_A
|| lpOrigProps[ISENDEREID].ulPropTag != PR_SENDER_ENTRYID
|| lpOrigProps[IMSGDELTIME].ulPropTag != PR_MESSAGE_DELIVERY_TIME)
{
hr = HR_LOG(ResultFromScode(MAPI_E_NOT_FOUND));
MODULE_ERROR("HrReadReceipt: Required property(s) not found");
goto exit;
}

// Get the original subject

if (lpOrigProps[ISUBJECTNORM].ulPropTag == PR_NORMALIZED_SUBJECT_A)
lpszSubjOrig = lpOrigProps[ISUBJECTNORM].Value.lpszA;
else if (lpOrigProps[ISUBJECT].ulPropTag == PR_SUBJECT_A)
lpszSubjOrig = lpOrigProps[ISUBJECT].Value.lpszA;
else if (lpOrigProps[ISUBJECT].Value.err == MAPI_E_NOT_ENOUGH_MEMORY)
{
//We've got a mondo subject. Use OpenProperty...
//
LPSTREAM lpstreamSrc= NULL;
LPSTREAM lpstreamDst= NULL;
ULARGE_INTEGERuliRead = { 0 };
ULARGE_INTEGERuliWritten = { 0 };
ULARGE_INTEGERuliSize;
LARGE_INTEGERliMove;
ULONGcbWritten= 0;

// open the source Stream

hr = lpMessage->lpVtbl->OpenProperty(lpMessage, PR_SUBJECT_A,
&IID_IStream, 0, 0, (LPUNKNOWN *)&lpstreamSrc);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure calling OpenProperty on subject Src");
fSetError = TRUE;
goto openprop_cleanup;
}

// open the destinationStream

hr = lpReadReceipt->lpVtbl->OpenProperty(lpReadReceipt, PR_SUBJECT_A,
&IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY,
(LPUNKNOWN *)&lpstreamDst);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure calling OpenProperty on subject Dst");
fSetError = TRUE;
goto openprop_cleanup;
}

// Copy the Subject header to the destination

hr = lpstreamDst->lpVtbl->Write(lpstreamDst, lpszSubjectPrefix,
lstrlenA(lpszSubjectPrefix), &cbWritten);

if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: OLE IStream Write failed");
goto openprop_cleanup;
}

ASSERTERROR((LONG)cbWritten == lstrlenA(lpszSubjectPrefix), "Write failed");

// Tell OLE ISTREAM to copy all of it

uliSize.HighPart = (ULONG) -1;
uliSize.LowPart = (ULONG) -1;

hr = lpstreamSrc->lpVtbl->CopyTo(lpstreamSrc, lpstreamDst, uliSize,
&uliRead, &uliWritten);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: OLE IStream CopyTo failed");
goto openprop_cleanup;
}

ASSERTERROR(uliWritten.LowPart == uliRead.LowPart, "Write failed");

hr = lpstreamDst->lpVtbl->Commit(lpstreamDst, 0);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: OLE IStream Commit failed");
goto openprop_cleanup;
}

// Handle PR_ORIGINAL_SUBJECT

UlRelease(lpstreamDst);
lpstreamDst = NULL;

hr = lpReadReceipt->lpVtbl->OpenProperty(lpReadReceipt,
PR_ORIGINAL_SUBJECT_A, &IID_IStream, 0,
MAPI_CREATE | MAPI_MODIFY, (LPUNKNOWN *)&lpstreamDst);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure calling OpenProperty on Orig Subject Dst");
fSetError = TRUE;
goto openprop_cleanup;
}

// rewind the source

liMove.HighPart = 0;
liMove.LowPart = 0;

hr = lpstreamSrc->lpVtbl->Seek(lpstreamSrc, liMove, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure calling Seek on subject Src stream");
goto openprop_cleanup;
}

// Tell OLE ISTREAM to copy all of it

uliSize.HighPart = (ULONG) -1;
uliSize.LowPart = (ULONG) -1;

hr = lpstreamSrc->lpVtbl->CopyTo(lpstreamSrc, lpstreamDst, uliSize,
&uliRead, &uliWritten);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: OLE IStream CopyTo failed");
goto openprop_cleanup;
}

ASSERTERROR(uliWritten.LowPart == uliRead.LowPart, "Write failed");

hr = lpstreamDst->lpVtbl->Commit(lpstreamDst, 0);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: OLE IStream Commit failed");
goto openprop_cleanup;
}

fSubjectOpenProperty = TRUE;

openprop_cleanup:

UlRelease(lpstreamSrc);
UlRelease(lpstreamDst);

if (FAILED(hr))
{
goto exit;
}
}
else
lpszSubjOrig = &chNull;

if (!fSubjectOpenProperty)
{
ulSize = lstrlenA (lpszSubjOrig) + lstrlenA (lpszSubjectPrefix) + 1;
sc = MAPIAllocateBuffer(ulSize, (LPVOID *) &lpszReadSubject);
if (FAILED(sc))
{
hr = HR_LOG(ResultFromScode(sc));
MODULE_ERROR("HrReadReceipt: OOM");
goto exit;
}

wsprintfA (lpszReadSubject,
"%s%s",
lpszSubjectPrefix,
lpszSubjOrig);
}

// Generate the Report Message Class from the existing message class

ASSERTERROR(
lpOrigProps[IMSGCLASS].ulPropTag == PR_MESSAGE_CLASS_A,
"Message class is missing");

ulSize = cchReportClass + lstrlenA(lpOrigProps[IMSGCLASS].Value.lpszA) + 1;
sc = MAPIAllocateBuffer(ulSize, (LPVOID *)&lpszMessageClass);
if (FAILED(sc))
{
hr = HR_LOG(ResultFromScode(sc));
MODULE_ERROR("HrReadReceipt: OOM for RRN report message class");
goto exit;
}

wsprintfA(lpszMessageClass,
"%s%s%s",
lpszReport,
lpOrigProps[IMSGCLASS].Value.lpszA,
(ulFlags & MAPI_NON_READ) ? (LPSTR) lpszIPNNRN : (LPSTR) lpszIPNRN);

//
// Setup the "must" prop vals
//

rgMsgVals[outIREPORTTEXT].ulPropTag = PR_REPORT_TEXT_A;
rgMsgVals[outIREPORTTEXT].Value.lpszA = lpszReportText;

rgMsgVals[outIMESSAGECLASS].ulPropTag = PR_MESSAGE_CLASS_A;
rgMsgVals[outIMESSAGECLASS].Value.lpszA= lpszMessageClass;

GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);

rgMsgVals[outIREPORTTIME].ulPropTag= PR_REPORT_TIME;
rgMsgVals[outIREPORTTIME].Value.ft= ft;

// PR_ORIGINAL_AUTHOR_?? is set from PR_SENDER_??

rgMsgVals[outIORIGAUTHOREID].ulPropTag = PR_ORIGINAL_AUTHOR_ENTRYID;
rgMsgVals[outIORIGAUTHOREID].Value.bin= lpOrigProps[ISENDEREID].Value.bin;

rgMsgVals[outIORIGDELIVERYTIME].ulPropTag = PR_ORIGINAL_DELIVERY_TIME;
rgMsgVals[outIORIGDELIVERYTIME].Value.ft = lpOrigProps[IMSGDELTIME].Value.ft;

// Set PR_PARENT_KEY with PR_MESSAGE_KEY of the original message.

rgMsgVals[outIPARENTKEY].ulPropTag = PR_PARENT_KEY;
rgMsgVals[outIPARENTKEY].Value.bin= lpOrigProps[ISEARCH].Value.bin;

rgMsgVals[outIDELETEAFTERSUBMIT].ulPropTag = PR_DELETE_AFTER_SUBMIT;
rgMsgVals[outIDELETEAFTERSUBMIT].Value.b = TRUE;

iNumProp = outCMAXIDX;

//
// Setup the variable Prop Vals
//

if (lpOrigProps[ISENDERNAME].ulPropTag == PR_SENDER_NAME_A)
{
rgMsgVals[iNumProp].ulPropTag = PR_ORIGINAL_AUTHOR_NAME_A;
rgMsgVals[iNumProp].Value.lpszA= lpOrigProps[ISENDERNAME].Value.lpszA;
iNumProp++;
}

// Check to see if we handled Subject props by OpenProperty...

if (!fSubjectOpenProperty)
{
rgMsgVals[iNumProp].ulPropTag = PR_SUBJECT_A;
rgMsgVals[iNumProp].Value.lpszA = lpszReadSubject;
iNumProp++;

if (lpOrigProps[ISUBJECT].ulPropTag == PR_SUBJECT_A)
{
rgMsgVals[iNumProp].ulPropTag= PR_ORIGINAL_SUBJECT_A;
rgMsgVals[iNumProp].Value.lpszA= lpOrigProps[ISUBJECT].Value.lpszA;
iNumProp++;
}
else
{
MODULE_ERROR("HrReadReceipt: PR_SUBJECT missing");
rgMsgVals[iNumProp].ulPropTag= PR_ORIGINAL_SUBJECT_A;
rgMsgVals[iNumProp].Value.lpszA= &chNull;
iNumProp++;
}
}

//The ordering here is slightly important. PR_SUBJECT
//needs to be either first or last to aid in EMS processing
//
rgMsgVals[iNumProp].ulPropTag = PR_SUBJECT_PREFIX_A;
rgMsgVals[iNumProp].Value.lpszA = lpszSubjectPrefix;
iNumProp++;
rgMsgVals[iNumProp].ulPropTag = PR_NORMALIZED_SUBJECT_A;
rgMsgVals[iNumProp].Value.lpszA = lpszSubjOrig;
iNumProp++;

// If PR_SENT_REPRESENTING_?? exists copy to PR_RCVD_REPRESENTING_??

if (lpOrigProps[ISENTREPNAME].ulPropTag == PR_SENT_REPRESENTING_NAME_A)
{
rgMsgVals[iNumProp].ulPropTag = PR_RCVD_REPRESENTING_NAME_A;
rgMsgVals[iNumProp].Value.lpszA= lpOrigProps[ISENTREPNAME].Value.lpszA;
iNumProp++;
}

if (lpOrigProps[ISENTREPEID].ulPropTag == PR_SENT_REPRESENTING_ENTRYID)
{
rgMsgVals[iNumProp].ulPropTag = PR_RCVD_REPRESENTING_ENTRYID;
rgMsgVals[iNumProp].Value.bin= lpOrigProps[ISENTREPEID].Value.bin;
iNumProp++;
}

// If PR_RCVD_REPRESENTING_?? exists copy to PR_SENT_REPRESENTING_??

if (lpOrigProps[IRCVDREPNAME].ulPropTag == PR_RCVD_REPRESENTING_NAME_A)
{
rgMsgVals[iNumProp].ulPropTag = PR_SENT_REPRESENTING_NAME_A;
rgMsgVals[iNumProp].Value.lpszA= lpOrigProps[IRCVDREPNAME].Value.lpszA;
iNumProp++;
}

if (lpOrigProps[IRCVDREPEID].ulPropTag == PR_RCVD_REPRESENTING_ENTRYID)
{
rgMsgVals[iNumProp].ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
rgMsgVals[iNumProp].Value.bin= lpOrigProps[IRCVDREPEID].Value.bin;
iNumProp++;
}

if (lpOrigProps[ISUBMITTIME].ulPropTag == PR_CLIENT_SUBMIT_TIME)
{
rgMsgVals[iNumProp].ulPropTag = PR_ORIGINAL_SUBMIT_TIME;
rgMsgVals[iNumProp].Value.ft = lpOrigProps[ISUBMITTIME].Value.ft;
iNumProp++;
}

if (lpOrigProps[IDISPTO].ulPropTag == PR_DISPLAY_TO_A)
{
rgMsgVals[iNumProp].ulPropTag= PR_ORIGINAL_DISPLAY_TO_A;
rgMsgVals[iNumProp].Value.lpszA = lpOrigProps[IDISPTO].Value.lpszA;
iNumProp++;
}

if (lpOrigProps[IDISPCC].ulPropTag == PR_DISPLAY_CC_A)
{
rgMsgVals[iNumProp].ulPropTag= PR_ORIGINAL_DISPLAY_CC_A;
rgMsgVals[iNumProp].Value.lpszA = lpOrigProps[IDISPCC].Value.lpszA;
iNumProp++;
}

if (lpOrigProps[IDISPBCC].ulPropTag == PR_DISPLAY_BCC_A)
{
rgMsgVals[iNumProp].ulPropTag= PR_ORIGINAL_DISPLAY_BCC_A;
rgMsgVals[iNumProp].Value.lpszA = lpOrigProps[IDISPBCC].Value.lpszA;
iNumProp++;
}

if (lpOrigProps[IPRIORITY].ulPropTag == PR_PRIORITY)
rgMsgVals[iNumProp++] = lpOrigProps[IPRIORITY];

if (lpOrigProps[IIMPORTANCE].ulPropTag == PR_IMPORTANCE)
rgMsgVals[iNumProp++] = lpOrigProps[IIMPORTANCE];

if (lpOrigProps[ISENSITIVITY].ulPropTag == PR_SENSITIVITY)
rgMsgVals[iNumProp++] = lpOrigProps[ISENSITIVITY];

if (lpOrigProps[ICONVERSATION].ulPropTag == PR_CONVERSATION_KEY)
rgMsgVals[iNumProp++] = lpOrigProps[ICONVERSATION];

if (lpOrigProps[ICONVERSATIONTOPIC].ulPropTag == PR_CONVERSATION_TOPIC_A)
rgMsgVals[iNumProp++] = lpOrigProps[ICONVERSATIONTOPIC];

if (lpOrigProps[ICONVERSATIONIDX].ulPropTag == PR_CONVERSATION_INDEX)
rgMsgVals[iNumProp++] = lpOrigProps[ICONVERSATIONIDX];

if (lpOrigProps[IREPORT].ulPropTag == PR_REPORT_TAG)
rgMsgVals[iNumProp++] = lpOrigProps[IREPORT];

//Turn off any and all report requests
//
rgMsgVals[iNumProp].ulPropTag = PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED;
rgMsgVals[iNumProp].Value.b = FALSE;
iNumProp++;
rgMsgVals[iNumProp].ulPropTag = PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED;
rgMsgVals[iNumProp].Value.b = FALSE;
iNumProp++;
rgMsgVals[iNumProp].ulPropTag = PR_READ_RECEIPT_REQUESTED;
rgMsgVals[iNumProp].Value.b = FALSE;
iNumProp++;

// Fill in recipient properties

sc = MAPIAllocateBuffer(sizeof(SPropValue) * RRN_RECIP_PROP_MAX, (LPVOID *)&lpRecipVals);
if (FAILED(sc))
{
hr = HR_LOG(ResultFromScode(sc));
MODULE_ERROR("HrReadReceipt: OOM for recipient props");
goto exit;
}

// If PR_REPORT_NAME & EID exists, use that as the MAPI_TO recipient
// else use PR_SENDER_NAME & EID.

lpRecipVals[IRECIPTYPE].ulPropTag = PR_RECIPIENT_TYPE;
lpRecipVals[IRECIPTYPE].Value.l = MAPI_TO;

lpRecipVals[IRECIPDISPNM].ulPropTag= PR_DISPLAY_NAME_A;
lpRecipVals[IRECIPEID].ulPropTag= PR_ENTRYID;

if (lpOrigProps[IREPORTNAME].ulPropTag == PR_REPORT_NAME_A
&& lpOrigProps[IREPORTEID].ulPropTag == PR_REPORT_ENTRYID)
{
lpRecipVals[IRECIPDISPNM].Value.lpszA= lpOrigProps[IREPORTNAME].Value.lpszA;
lpRecipVals[IRECIPEID].Value.bin= lpOrigProps[IREPORTEID].Value.bin;
lpRecipVals[IRECIPADRTYPE].ulPropTag= PR_NULL;
lpRecipVals[IRECIPEMAIL].ulPropTag= PR_NULL;
}
else
{
lpRecipVals[IRECIPDISPNM].Value.lpszA= lpOrigProps[ISENDERNAME].Value.lpszA;
lpRecipVals[IRECIPEID].Value.bin= lpOrigProps[ISENDEREID].Value.bin;

lpRecipVals[IRECIPADRTYPE].ulPropTag= PR_ADDRTYPE_A;
lpRecipVals[IRECIPADRTYPE].Value.lpszA= lpOrigProps[ISENDADRTYPE].Value.lpszA;

lpRecipVals[IRECIPEMAIL].ulPropTag= PR_EMAIL_ADDRESS_A;
lpRecipVals[IRECIPEMAIL].Value.lpszA= lpOrigProps[ISENDEMAIL].Value.lpszA;
}

AdrList.cEntries = 1;

AdrList.aEntries[0].cValues = RRN_RECIP_PROP_MAX; 
AdrList.aEntries[0].rgPropVals = lpRecipVals;

// SetProps and ModifyRecips.

#ifdef DEBUG
hr = lpReadReceipt->lpVtbl->SetProps(lpReadReceipt,
(ULONG)iNumProp, rgMsgVals, &lpProblem);
#else
hr = lpReadReceipt->lpVtbl->SetProps(lpReadReceipt,
(ULONG)iNumProp, rgMsgVals, NULL);
#endif
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure setting a slew of props");
fSetError = TRUE;
goto exit;
}

#ifdef DEBUG
if (lpProblem)
{
MAPIFreeBuffer(lpProblem);
}
#endif

hr = lpReadReceipt->lpVtbl->ModifyRecipients(
lpReadReceipt, 0, &AdrList);
if (FAILED(hr))
{
MODULE_ERROR("HrReadReceipt: Failure modifying recipients");
fSetError = TRUE;
goto exit;
}

exit:
MAPIFreeBuffer(lpOrigProps);
MAPIFreeBuffer(lpszMessageClass);
MAPIFreeBuffer(lpszReadSubject);
MAPIFreeBuffer(AdrList.aEntries[0].rgPropVals);

return(hr);
}

//$--HrEDKCreateReadReceipt-----------------------------------------------------
// Create a RN/NRN report.
// -----------------------------------------------------------------------------
static HRESULT HrEDKCreateReadReceipt( // RETURNS: return code
IN ULONG ulFlags, // flags
IN LPMAPIFOLDER lpFolder, // pointer to folder
IN LPMESSAGE lpMessage, // pointer to message
IN LPTSTR lpszSubjectPrefix, // pointer to subject prefix
IN LPTSTR lpszReportText, // pointer to report text
OUT LPMESSAGE *lppReportEnvelope, // pointer to report envelope
OUT LPMESSAGE *lppReport) // pointer to report
{
HRESULT hr = NOERROR;
LPTSTR lpszMessageClass = NULL;
LPMESSAGE lpReportEnvelope = NULL;
ULONG ulAttachmentNum = 0;
LPATTACH lpAttach = NULL;
LPMESSAGE lpReport = NULL;
HRESULT hrT = 0;
ULONG cValues = 0;
LPSPropValue lpProps = NULL;
LPSPropValue lpPropT = NULL;

ULONG cNewProps = 0;
LPSPropValue lpNewProps = NULL;

LPMAPITABLE lpTable = NULL;
LPADRLIST lpAdrList = NULL;

LPADRLIST lpEnvelopeAdrList = NULL;

ULONG i = 0;
ULONG iRecip = 0;

SPropValue RecipNumProp[1] = {0};
SPropValue rgProps[1] = {0};

SYSTEMTIME st = {0};
FILETIME ft = {0};

SizedSPropTagArray(6,rgReportTags) =
{
6,
{
PR_SENT_REPRESENTING_NAME,
PR_SENT_REPRESENTING_ENTRYID,
PR_MESSAGE_CLASS,
PR_SUBJECT,
PR_PRIORITY,
PR_DELETE_AFTER_SUBMIT
}
};

DEBUGPRIVATE("HrEDKCreateReadReceipt()\n");

hr = CHK_HrEDKCreateReadReceipt(
ulFlags,
lpFolder,
lpMessage,
lpszSubjectPrefix,
lpszReportText,
lppReportEnvelope,
lppReport);

if(FAILED(hr))
RETURN(hr);

if(ulFlags == MAPI_NON_READ)
{
hr = HrMAPISetPropBoolean(
(LPMAPIPROP)lpMessage,
PR_NON_RECEIPT_NOTIFICATION_REQUESTED,
TRUE);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
else
{
hr = HrMAPISetPropBoolean(
(LPMAPIPROP)lpMessage,
PR_READ_RECEIPT_REQUESTED,
TRUE);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}

*lppReportEnvelope = NULL;
*lppReport = NULL;

//
// Create a message
//

hr = MAPICALL(lpFolder)->CreateMessage(
lpFolder,
NULL,
(ULONG)0,
&lpReportEnvelope);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

//
// Create an embedded message
//

hr = MAPICALL(lpReportEnvelope)->CreateAttach(
lpReportEnvelope,
NULL,
(ULONG)0,
&ulAttachmentNum,
&lpAttach);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

rgProps[0].ulPropTag = PR_ATTACH_METHOD;
rgProps[0].Value.ul = ATTACH_EMBEDDED_MSG;

hr = MAPICALL(lpAttach)->SetProps(
lpAttach,
1,
rgProps,
NULL);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPICALL(lpAttach)->OpenProperty(
lpAttach,
PR_ATTACH_DATA_OBJ,
(LPIID)&IID_IMessage,
0,
MAPI_CREATE|MAPI_MODIFY,
(LPUNKNOWN *)&lpReport);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

//--------------------------------------------------------------------------

hrT = HrReadReceipt(
ulFlags,
lpszReportText,
lpszSubjectPrefix,
lpMessage,
lpReport);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);

rgProps[0].ulPropTag = PR_CLIENT_SUBMIT_TIME;
rgProps[0].Value.ft = ft;

hrT = MAPICALL(lpReport)->SetProps(
lpReport,
1,
rgProps,
NULL);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hrT = MAPICALL(lpReport)->GetProps(
lpReport,
(SPropTagArray *)&rgReportTags,
fMapiUnicode,
&cValues,
&lpProps);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

if((lpProps[0].ulPropTag != PR_SENT_REPRESENTING_NAME) ||
(lpProps[1].ulPropTag != PR_SENT_REPRESENTING_ENTRYID) ||
(lpProps[2].ulPropTag != PR_MESSAGE_CLASS))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

lpProps[0].ulPropTag = PR_ORIGINATOR_NAME;
lpProps[1].ulPropTag = PR_ORIGINATOR_ENTRYID;

hr = HrEDKEnvelopeMessageClass(
lpProps[2].Value.LPSZ,
&lpszMessageClass);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

lpProps[2].Value.LPSZ = lpszMessageClass;

hrT = MAPICALL(lpReportEnvelope)->SetProps(
lpReportEnvelope,
6,
lpProps,
NULL);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

//
// Create recipient table for report envelope.
//

// -------------------------------------------------------------------------

//
// Get the current recipient table from the report.
//

hrT = MAPICALL(lpReport)->GetRecipientTable(lpReport, 0, &lpTable);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

//
// Query all the rows.
//

hrT = HrQueryAllRows(
lpTable,
NULL,
NULL,
NULL,
0,
&(LPSRowSet)lpAdrList);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPICreateSizedAddressList(
lpAdrList->cEntries,
&lpEnvelopeAdrList);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

for(i = 0, iRecip = 0; i < lpAdrList->cEntries; i++)
{
RecipNumProp[0].ulPropTag = PR_RECIPIENT_NUMBER;
RecipNumProp[0].Value.ul = iRecip+1;

cNewProps = 0;
lpNewProps = NULL;

hr = HrMAPIAppendSPropValues(
lpAdrList->aEntries[i].cValues,
lpAdrList->aEntries[i].rgPropVals,
1,
RecipNumProp,
&cNewProps,
&lpNewProps);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetAddressList(
iRecip,
cNewProps,
lpNewProps,
lpEnvelopeAdrList);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

MAPIFREEBUFFER(lpNewProps);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

iRecip++;
}

lpEnvelopeAdrList->cEntries = iRecip;

if(iRecip == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hrT = MAPICALL(lpReportEnvelope)->ModifyRecipients(
lpReportEnvelope,
0,
lpEnvelopeAdrList);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// -------------------------------------------------------------------------

hrT = MAPICALL(lpReport)->SaveChanges(lpReport, KEEP_OPEN_READWRITE);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hrT = MAPICALL(lpAttach)->SaveChanges(lpAttach, KEEP_OPEN_READWRITE);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Save changes to the report envelope
hrT = MAPICALL(lpReportEnvelope)->SaveChanges(lpReportEnvelope, KEEP_OPEN_READWRITE);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

*lppReportEnvelope = lpReportEnvelope;
*lppReport = lpReport;

cleanup:

MAPIFREEBUFFER(lpNewProps);

ULRELEASE(lpTable);

FREEPADRLIST(lpAdrList);

FREEPADRLIST(lpEnvelopeAdrList);

MAPIFREEBUFFER(lpszMessageClass);

ULRELEASE(lpAttach);

RETURN(hr);
}

//$--HrEDKCreateReadNotificationReport------------------------------------------
// Create a read notification report.
// -----------------------------------------------------------------------------
HRESULT HrEDKCreateReadNotificationReport( // RETURNS: return code
IN LPMAPIFOLDER lpFolder, // pointer to folder
IN LPMESSAGE lpMessage, // pointer to message
IN LPTSTR lpszReportText, // pointer to report text
OUT LPMESSAGE *lppReportEnvelope, // pointer to report envelope
OUT LPMESSAGE *lppReport) // pointer to report
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("HrEDKCreateReadNotificationReport()\n");

hr = CHK_HrEDKCreateReadNotificationReport(
lpFolder,
lpMessage,
lpszReportText,
lppReportEnvelope,
lppReport);

if(FAILED(hr))
RETURN(hr);

hr = HrEDKCreateReadReceipt(
0,
lpFolder,
lpMessage,
TEXT("Read: "),
lpszReportText,
lppReportEnvelope,
lppReport);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:

RETURN(hr);
}

//$--HrEDKCreateNonReadNotificationReport---------------------------------------
// Create a non-read notification report.
// -----------------------------------------------------------------------------
HRESULT HrEDKCreateNonReadNotificationReport( // RETURNS: return code
IN LPMAPIFOLDER lpFolder, // pointer to folder
IN LPMESSAGE lpMessage, // pointer to message
IN LPTSTR lpszReportText, // pointer to report text
IN ULONG ulNonReceiptReason, // non-receipt reason
IN ULONG ulDiscardReason, // discard reason
OUT LPMESSAGE *lppReportEnvelope, // pointer to report envelope
OUT LPMESSAGE *lppReport) // pointer to report
{
HRESULT hr = NOERROR;
HRESULT hrT = 0;

DEBUGPUBLIC("HrEDKCreateNonReadNotificationReport()\n");

hr = CHK_HrEDKCreateNonReadNotificationReport(
lpFolder,
lpMessage,
lpszReportText,
ulNonReceiptReason,
ulDiscardReason,
lppReportEnvelope,
lppReport);

if(FAILED(hr))
RETURN(hr);

hr = HrEDKCreateReadReceipt(
MAPI_NON_READ,
lpFolder,
lpMessage,
TEXT("Unread: "),
lpszReportText,
lppReportEnvelope,
lppReport);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropLong(
(LPMAPIPROP)*lppReport,
PR_DISCARD_REASON,
ulDiscardReason);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrMAPISetPropLong(
(LPMAPIPROP)*lppReport,
PR_NON_RECEIPT_REASON,
ulNonReceiptReason);

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Save changes to the report
hrT = MAPICALL(*lppReport)->SaveChanges(*lppReport, KEEP_OPEN_READWRITE);

if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:

RETURN(hr);
}