// --Messages.c---------------------------------------------------------------------
//
// This module contains miscellaneous utility procedures called
// by the rest of SMBAGENT procedures to deal with
// - creating a folder in a given folder;
// - getting the sender's property values in a given message;
// - getting the recipient list from a given message;
// - etc.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------
#include "edk.h"
#include "messages.chk"
#include "smbagent.h"
#include "resource.h"
#define EDK_ERROR_MEMORY_ALLOCATION 0x00000000U
extern HANDLE hEventSource;
// Global variables
externLPMAPISESSIONlphSession;
extern LPADRBOOK lpAdrBook;
extern LPMAPIFOLDER lpTopicsFolder;
extern LPMAPIFOLDER lpNDRFolder;
extern HINSTANCE hInst;
extern TCHAR szTextBuf[];
extern TCHAR szSubjBuf[];
extern TCHAR szCmdBuf[];
externLPENTRYIDlpSMBAgentEID;
externULONGcbSMBAgentEID;
LPADRLIST lpSenderAddr = NULL;
//$--HrGetSenderProps----------------------------------------------------------
// Gets all properties of the sender's message that we use while processing it.
// The results are placed in a dynamically allocated array named lpSenderProps.
// There are defined constants that index into this array in SMBAGENT.H.
// Also transforms the sender props into a property array that can be used for
// replying to the sender.
// ----------------------------------------------------------------------------
HRESULT HrGetSenderProps(
IN LPMESSAGE lpMessage) // Ptr.to the msg.to read sender prop.from
{
HRESULT hr = NOERROR;
ULONG ulObjType = 0;
ULONG cValues = 0;
LPSPropTagArray lpSenderPTags = NULL;
ULONGfCompare= FALSE;
int ii = 0;
LPSPropValue lpRecipProps = NULL;
static const SizedSPropTagArray( 7, aptSenderProp) =
{ 7,
{
PR_SENDER_NAME, // The order of the first 5 must NOT change.
PR_SENDER_ENTRYID,
PR_SENDER_ADDRTYPE,
PR_SENDER_EMAIL_ADDRESS,
PR_MESSAGE_FLAGS, // Placeholder replaced by HrAddRecipProps().
PR_MESSAGE_CLASS,
PR_SUBJECT,
}
};
static ULONG aRecipPropTags[] =
{// This column's props will replace these properties.
PR_DISPLAY_NAME, // PR_SENDER_NAME
PR_ENTRYID, // PR_SENDER_ENTRYID
PR_ADDRTYPE, // PR_SENDER_ADDRTYPE
PR_EMAIL_ADDRESS, // PR_SENDER_EMAIL_ADDRESS
PR_RECIPIENT_TYPE, // PR_MESSAGE_FLAGS
};
int nRecipTags = ARRAY_CNT( aRecipPropTags);
DEBUGPUBLIC( "HrGetSenderProps()");
hr = CHK_HrGetSenderProps( lpMessage);
if( FAILED( hr))
RETURN( hr);
ASSERTERROR( lpSenderProps == NULL, "lpSenderProps should be NULL");
ASSERTERROR( lpSenderAddr == NULL, "lpSenderAddr should be NULL");
// Get the properties from the message originator.
hr = MAPICALL(lpMessage)->GetProps( lpMessage,
(LPSPropTagArray)&aptSenderProp,
fMapiUnicode, &cValues, &lpSenderProps);
if( hr == MAPI_W_ERRORS_RETURNED)
{
hr = HR_LOG( E_FAIL);
goto cleanup;
}
if( FAILED(hr))
goto cleanup;
// Check to make sure we got the right properties
if( cValues != 7 || lpSenderProps == NULL) {
hr = HR_LOG( E_FAIL);
goto cleanup;
}
ASSERTERROR( lpSenderProps[IDX_SENDER_NAME].ulPropTag == PR_SENDER_NAME &&
lpSenderProps[IDX_SENDER_ENTRYID].ulPropTag == PR_SENDER_ENTRYID &&
lpSenderProps[IDX_SENDER_ADDRTYPE].ulPropTag == PR_SENDER_ADDRTYPE &&
lpSenderProps[IDX_SENDER_EMAIL_ADDRESS].ulPropTag == PR_SENDER_EMAIL_ADDRESS &&
lpSenderProps[IDX_MESSAGE_FLAGS].ulPropTag == PR_MESSAGE_FLAGS &&
lpSenderProps[IDX_MESSAGE_CLASS].ulPropTag == PR_MESSAGE_CLASS &&
lpSenderProps[IDX_SUBJECT].ulPropTag == PR_SUBJECT,
"Mangled sender props detected");
// Verify that the message is NOT from the SMBAgent profile.
hr = MAPICALL( lphSession)->CompareEntryIDs( lphSession,
lpSenderProps[IDX_SENDER_ENTRYID].Value.bin.cb,
(LPENTRYID) lpSenderProps[IDX_SENDER_ENTRYID].Value.bin.lpb,
cbSMBAgentEID, lpSMBAgentEID, 0L, &fCompare);
if( FAILED( hr))
goto cleanup;
if (fCompare == TRUE)
{ // Message is from SMBAgent profile.
MODULE_WARNING( "Skipping message from SMBAgent profile");
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Create a single entry address list.
hr = HrMAPICreateSizedAddressList( 1, &lpSenderAddr);
if( FAILED( hr))
goto cleanup;
// Set the single entry to the sender properties.
hr = HrMAPISetAddressList( 0, nRecipTags, lpSenderProps, lpSenderAddr);
if( FAILED( hr))
goto cleanup;
// Get the pointer to the copy of the sender properties and transform
// them into recipient properties.
lpRecipProps = (lpSenderAddr)->aEntries[0].rgPropVals;
for( ii = 0; ii < nRecipTags; ++ii)
{
lpRecipProps[ii].ulPropTag = aRecipPropTags[ii];
lpRecipProps[ii].dwAlignPad = 0L;
}
// Set PR_RECIPIENT_TYPE's value. All other values have been set
// correctly by HrGetSenderProps().
lpRecipProps[nRecipTags - 1].Value.l = MAPI_TO;
cleanup:
// Free globals only if there was a failure.
if( FAILED(hr))
{
MAPIFREEBUFFER( lpSenderProps);
FREEPADRLIST( lpSenderAddr);
}
RETURN( hr);
}
//$--HrEmbedMsgInMsg------------------------------------------------------------
// Embed a message within a message.
// -----------------------------------------------------------------------------
HRESULT HrEmbedMsgInMsg(
IN LPMESSAGE lpNewMsg, // Ptr to msg that will contain embedded msg.
IN LPMESSAGE lpMsgToEmbed) // Ptr to msg to embed.
{
HRESULT hr = 0;
ULONG ulAttNum = 0;
LPATTACH lpNewAttach = NULL;
LPMESSAGE lpEmbeddedMsg = NULL;
LPSPropProblemArray lpProblems = NULL;
SPropValue rgPropValue[2] = {
{PR_ATTACH_METHOD, 0, { ATTACH_EMBEDDED_MSG}},
{PR_RENDERING_POSITION, 0, { 0}}
};
hr = CHK_HrEmbedMsgInMsg( lpNewMsg, lpMsgToEmbed);
if( FAILED( hr))
RETURN( hr);
// Create the attachment in the message.
hr = MAPICALL(lpNewMsg)->CreateAttach( lpNewMsg,
NULL, // IID for returned object
MAPI_DEFERRED_ERRORS,
&ulAttNum,
&lpNewAttach);
if( FAILED( hr))
goto cleanup;
ASSERT_IUNKNOWN_PTR( lpNewAttach, "INVALID lpNewAttach pointer");
// Set the attachment method and rendering position.
if( ulAttNum > 0)
rgPropValue[1].Value.ul = 400; // arbitrary value for end of message
hr = MAPICALL( lpNewAttach)->SetProps( lpNewAttach,
ARRAY_CNT( rgPropValue),
rgPropValue,
NULL);
if( FAILED( hr))
goto cleanup;
// Open the property as a message.
hr = MAPICALL( lpNewAttach)->OpenProperty( lpNewAttach,
PR_ATTACH_DATA_OBJ,
(LPIID)&IID_IMessage,
0,
MAPI_CREATE | MAPI_MODIFY | MAPI_DEFERRED_ERRORS,
(LPUNKNOWN *)&lpEmbeddedMsg);
if( FAILED( hr))
goto cleanup;
ASSERT_IUNKNOWN_PTR( lpEmbeddedMsg, "INVALID lpEmbeddedMsg pointer");
// Copy message to attached message.
hr = MAPICALL( lpMsgToEmbed)->CopyTo( lpMsgToEmbed,
0, NULL, NULL, 0, NULL,
(LPIID) &IID_IMessage,
lpEmbeddedMsg, 0,
&lpProblems);
if( FAILED( hr))
goto cleanup;
if( hr == MAPI_W_ERRORS_RETURNED)
{ // If you find that this error occurs check lpProblems to see what went wrong.
hr = HR_LOG( E_FAIL);
goto cleanup;
}
// Save changes to the embedded message.
hr = MAPICALL( lpEmbeddedMsg)->SaveChanges( lpEmbeddedMsg, 0);
if( FAILED( hr))
goto cleanup;
// Save changes to the attachment.
hr = MAPICALL( lpNewAttach)->SaveChanges( lpNewAttach, 0);
if( FAILED( hr))
goto cleanup;
cleanup:
MAPIFREEBUFFER(lpProblems);
ULRELEASE( lpEmbeddedMsg);
ULRELEASE( lpNewAttach);
RETURN( hr);
}
//$--HrCopyMessageBody---------------------------------------------------------
// Copy large (>=32K) PR_RTF_COMPRESSED body part. The property must exist.
//-----------------------------------------------------------------------------
HRESULT
HrCopyMessageBody(
IN LPMESSAGE lpSrcMsg,
IN LPMESSAGE lpDestMsg)
{
HRESULT hr = NOERROR;
STATSTGStatStg= {0};
ULARGE_INTEGERcbWritten= {0};
PVIRTUALSTREAMONPROPERTYpvsSrcMsgBody = NULL,
pvsDestMsgBody = NULL;
DEBUGPUBLIC( "HrCopyMessageBody()");
// Prepare to copy message body >= 32K
hr = HrOpenVirtualStreamOnProperty(
(LPMAPIPROP)lpSrcMsg,
PR_RTF_COMPRESSED,
0L,
&pvsSrcMsgBody);
if( FAILED( hr))
goto cleanup;
// Get size of body property.
hr = MAPICALL( pvsSrcMsgBody)->Stat( pvsSrcMsgBody,
&StatStg, 0L);
if( FAILED( hr))
goto cleanup;
// Open receiving message
hr = HrOpenVirtualStreamOnProperty(
(LPMAPIPROP)lpDestMsg,
PR_RTF_COMPRESSED,
MAPI_MODIFY | MAPI_CREATE,
&pvsDestMsgBody);
if( FAILED( hr))
goto cleanup;
// Perform complete copy
hr = MAPICALL( pvsSrcMsgBody)->CopyTo( pvsSrcMsgBody,
(LPSTREAM) pvsDestMsgBody,
StatStg.cbSize,
NULL,
&cbWritten);
if( FAILED( hr))
goto cleanup;
// Verify all bytes were written as expected
if( MAKEDWORDLONG( cbWritten.LowPart, cbWritten.HighPart) !=
MAKEDWORDLONG( StatStg.cbSize.LowPart, StatStg.cbSize.HighPart))
{
hr = HR_LOG( E_FAIL);
goto cleanup;
}
// Commit change to destination message
hr = MAPICALL( pvsDestMsgBody)->Commit( pvsDestMsgBody, 0L);
if( FAILED( hr))
goto cleanup;
cleanup:
ULRELEASE( pvsSrcMsgBody);
ULRELEASE( pvsDestMsgBody);
RETURN( hr);
}
// --------------------------------------------------------------------------
// Property arrays for HrDuplicateMessage.
// --------------------------------------------------------------------------
SizedSPropTagArray( 14, rgptPropsToCopyForKeep) =
{ 14,
{
PR_SENDER_NAME,
PR_SENDER_ENTRYID,
PR_SENDER_ADDRTYPE,
PR_SENDER_EMAIL_ADDRESS,
PR_SENT_REPRESENTING_NAME,
PR_SENT_REPRESENTING_ENTRYID,
PR_SENT_REPRESENTING_ADDRTYPE,
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_CLIENT_SUBMIT_TIME,
PR_MESSAGE_DELIVERY_TIME,
PR_IMPORTANCE,
PR_EXPIRY_TIME,
PR_MESSAGE_ATTACHMENTS,
PR_RTF_COMPRESSED
}
};
// An important thing to note is that you can not include the PR_SENDER...
// or the PR_SEND_REPRESENTING... properties when creating a message for
// to be sent. It causes problems for One Off Recipients.
SizedSPropTagArray( 6, rgptPropsToCopyForSend) =
{ 6,
{
PR_CLIENT_SUBMIT_TIME,
PR_MESSAGE_DELIVERY_TIME,
PR_IMPORTANCE,
PR_EXPIRY_TIME,
PR_MESSAGE_ATTACHMENTS,
PR_RTF_COMPRESSED
}
};
// Properties to copy when duplicating a message for keeping or sending.
LPSPropTagArray lpPropsToCopyForKeep = (LPSPropTagArray) &rgptPropsToCopyForKeep;
LPSPropTagArray lpPropsToCopyForSend = (LPSPropTagArray) &rgptPropsToCopyForSend;
//$--HrDuplicateMessage -----------------------------------------------------
// Duplicates a message by creating a new message in the given folder
// and copying all fields of the given message in the new message
// --------------------------------------------------------------------------
HRESULT HrDuplicateMessage(
IN LPMESSAGE lpSrcMsg, // pointer to the source message
IN LPMAPIFOLDER lpDestFolder, // pointer to the destination folder
IN ULONG ulFlags, // Intended to set MAPI_ASSOCIATED
IN LPSPropTagArray lpPropsToCopy, // Pass in one of the above array pointers.
OUT LPMESSAGE *lppDestMsg) // Addr of var.to contain ptr.to new msg.
{
HRESULT hr = NOERROR;
LPSPropProblemArray lpProblemArray = NULL;
DEBUGPUBLIC( "HrDuplicateMessage()");
hr = CHK_HrDuplicateMessage( lpSrcMsg, lpDestFolder, ulFlags, lppDestMsg);
if( FAILED( hr))
RETURN( hr);
// Create a new message in the given folder
hr = MAPICALL(lpDestFolder)->CreateMessage( lpDestFolder,
NULL, ulFlags | MAPI_DEFERRED_ERRORS,
lppDestMsg);
if( FAILED(hr))
goto cleanup;
// Duplicate content of source message in the new message.
hr = MAPICALL(lpSrcMsg)->CopyProps( lpSrcMsg,
lpPropsToCopy,
0,
NULL,
&IID_IMessage,
*lppDestMsg,
0,
&lpProblemArray);
if( FAILED( hr) || hr == MAPI_W_ERRORS_RETURNED)
{
if( lpProblemArray != NULL &&
lpProblemArray->cProblem == 1 &&
lpProblemArray->aProblem->ulPropTag == PR_RTF_COMPRESSED)
{ // CopyProps may fail for message bodies sized >= 32K.
hr = HrCopyMessageBody( lpSrcMsg, *lppDestMsg);
if( FAILED( hr))
goto cleanup;
}
else
{
hr = HR_LOG( E_FAIL);
goto cleanup;
}
}
cleanup:
MAPIFREEBUFFER( lpProblemArray);
RETURN( hr);
}
//$--HrSendMessage ---------------------------------------------------------
// Creates and sends an IPM message with the given Subject, Recipient list,
// text and optional attachment(s).
// -------------------------------------------------------------------------
HRESULT HrSendMessage(
IN LPADRLIST lpRecipList, // Recipient list of the message
IN LPTSTR lpszSubject, // Message Subject string
IN LPTSTR lpszText, // Message text string
IN int cNAttach, // # of attachments to be included
IN LPMESSAGE lpAtts[]) // Array of pointers to attachment messages
{
HRESULT hr = NOERROR;
int i = 0;
ULONG ulAttNum = 0;
LPMESSAGE lpNewMsg = NULL,
lpEmbeddedMsg = NULL;
LPATTACH lpNewAttach = NULL;
DEBUGPUBLIC( "HrSendMessage()");
hr = CHK_HrSendMessage( lpRecipList, lpszSubject, lpszText, cNAttach, lpAtts);
if( FAILED( hr))
RETURN( hr);
// Create a message in the NDR folder. We don't use the TOPICS folder because we
// can't create a message to send when using public topic folders.
hr = MAPICALL(lpNDRFolder)->CreateMessage( lpNDRFolder,
NULL, MAPI_DEFERRED_ERRORS, &lpNewMsg);
if( FAILED( hr))
goto cleanup;
ASSERTERROR( lpNewMsg!=NULL, "NULL lpNewMsg.");
// Set the recipient list to that which has been given.
hr = MAPICALL(lpNewMsg)->ModifyRecipients( lpNewMsg, 0L, lpRecipList);
if( FAILED( hr))
goto cleanup;
// Encapsulate property setting in block to keep the code clear.
{
SInitPropValue SPropValues[] =
{
{PR_DELETE_AFTER_SUBMIT, 0, TRUE},
{PR_MESSAGE_CLASS, 0, (ULONG)SMBAGENT_MSG_CLASS },
{PR_SUBJECT, 0, (ULONG)lpszSubject},
{PR_BODY, 0, (ULONG)lpszText},
};
hr = MAPICALL(lpNewMsg)->SetProps( lpNewMsg,
4L, (LPSPropValue) SPropValues, NULL);
if( FAILED( hr))
goto cleanup;
}
// If lpAtts!=NULL, include it in the message
for( i=0; i<cNAttach; i++)
{
hr = HrEmbedMsgInMsg( lpNewMsg, lpAtts[i]);
if( FAILED(hr))
goto cleanup;
}
// Submit the message
hr = MAPICALL(lpNewMsg)->SubmitMessage( lpNewMsg, FORCE_SUBMIT);
if( FAILED(hr))
goto cleanup;
cleanup:
ULRELEASE ( lpNewMsg);
RETURN( hr);
}
//$--HrSendErrReport ----------------------------------------------------------
// Sends an error report to the originator of the given message, including
// the given message as an attachment in the error report.
// ----------------------------------------------------------------------------
HRESULT HrSendErrReport(
IN LPMESSAGE lpMessage, //Ptr.to the message to be included in report
IN int iErrText) //Index to text string
{
HRESULT hr = NOERROR;
LPMESSAGE lpAtt = NULL;
int cStrLen = 0;
DEBUGPUBLIC( "HrSendErrReport()");
hr = CHK_HrSendErrReport( lpMessage, iErrText);
if( FAILED( hr))
RETURN( hr);
// Load the Subject & Text field strings.
cStrLen = LoadStringA( hInst, IDS_SUBJ_ERROR, (LPTSTR)&szSubjBuf, MAXSUBJLEN);
cStrLen = LoadStringA( hInst, iErrText, (LPTSTR)&szTextBuf, MAXTEXTLEN);
// Send the error report, including the given message as an attachment
lpAtt = lpMessage;
hr = HrSendMessage( lpSenderAddr, szSubjBuf, szTextBuf, 1, &lpAtt);
if( FAILED( hr));
RETURN( hr);
}
//$--HrSendConfirmReport ------------------------------------------------------
// Sends a confirmation to the given address w. reference to the given command
// ----------------------------------------------------------------------------
HRESULT HrSendConfirmReport(
IN LPMESSAGE lpMessage, // Pointer to the command message
IN int iCmdName, // Resource Id of command name
IN int iCnfText, // Index to text string
IN LPMESSAGE lpAttach)
{
HRESULT hr = NOERROR;
LPMESSAGE alpAttachments[] = {NULL, NULL};
int cNAttach = 1L, // >=1 attachment in error reports
cStrLen = 0L;
DEBUGPUBLIC( "HrSendConfirmReport()");
hr = CHK_HrSendConfirmReport( lpMessage, iCmdName, iCnfText, lpAttach);
if( FAILED( hr))
RETURN( hr);
alpAttachments[0] = lpMessage;
if( lpAttach != NULL)
{
alpAttachments[1] = lpAttach;
cNAttach = 2;
}
// Load subject & text field strings
cStrLen = LoadString( hInst, iCmdName, szSubjBuf, MAXSUBJLEN);
cStrLen = LoadString( hInst, IDS_CNF_SUBJECT, szSubjBuf + cStrLen, MAXSUBJLEN - cStrLen);
cStrLen = LoadString( hInst, iCnfText, szTextBuf, MAXTEXTLEN);
// Send the confirmation report, including the command message as an attachment
hr = HrSendMessage( lpSenderAddr, szSubjBuf, szTextBuf, cNAttach, alpAttachments);
if( FAILED( hr));
RETURN( hr);
}
// ----------------------------------------------------------------------------