// --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);
}