/*
* S M H O O F . C
*
* Sample mail handling hook
* Out of office management
*
* Copyright 1992-95 Microsoft Corporation. All Rights Reserved.
*/
#include "_pch.h"
#include <mapiutil.h>
#include <cindex.h>
#include <limits.h>
#ifdef _WIN32
#define szPlatform "32"
#else
#define szPlatform
#endif
enum { ipOofRId, ipOofREid, cpOofMax };
enum { icrgFrom, icrgSubmit, icrgTo, icrgCC, icrgSubj, icrgImp, icrgSen, ccrgMax };
enum
{
ipRespSen,
ipRespConvKey,
ipRespConvIdx,
ipRespConvTopic,
ipRespReportTag,
ipRespOrigAuthEid,
ipRespOrigAuthorName,
ipRespOrigAuthorSKey,
ipRespOrigSubmitTime,
ipRespPriority,
ipRespImportance,
ipRespSubject,
ipRespSubjectPrefix,
ipRespDelAfterSub,
ipRespMessageClass,
ipRespMessageFlags,
cpTargetResponseMax
};
enum
{
ipRespRecipName,
ipRespRecipAdrType,
ipRespRecipEmail,
ipRespRecipType,
ipRespRecipEid,
ipRespRecipSKey,
cpTargetRecipMax
};
enum
{
ipMsgClass,
ipMsgFlags,
ipRecipMe,
ipNSubj,
ipSndrEid,
ipSndrNm,
ipSndrType,
ipSndrEmail,
ipSndrSKey,
ipConvIndex,
ipConvTopic,
ipConvKey,
ipOrigPriority,
ipReportTag,
ipOrigAuthorEid,
ipOrigAuthorName,
ipOrigAuthorSKey,
ipOrigSubmitTime,
ipSentRepName,
ipSentRepType,
ipSentRepEmail,
ipSentRepSKey,
ipStoreSupport,
ipSubmitTime,
ipDisplayTo,
ipDisplayCc,
ipOofSubj,
ipImportance,
ipSensitivity,
cpResponseMax
};
const static SizedSPropTagArray (cpResponseMax, sptResponse) =
{
cpResponseMax,
{
PR_MESSAGE_CLASS,
PR_MESSAGE_FLAGS,
PR_MESSAGE_RECIP_ME,
PR_NORMALIZED_SUBJECT,
PR_SENDER_ENTRYID,
PR_SENDER_NAME,
PR_SENDER_ADDRTYPE,
PR_SENDER_EMAIL_ADDRESS,
PR_SENDER_SEARCH_KEY,
PR_CONVERSATION_INDEX,
PR_CONVERSATION_TOPIC,
PR_CONVERSATION_KEY,
PR_PRIORITY,
PR_REPORT_TAG,
PR_ORIGINAL_AUTHOR_ENTRYID,
PR_ORIGINAL_AUTHOR_NAME,
PR_ORIGINAL_AUTHOR_SEARCH_KEY,
PR_ORIGINAL_SUBMIT_TIME,
PR_SENT_REPRESENTING_NAME,
PR_SENT_REPRESENTING_ADDRTYPE,
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_SENT_REPRESENTING_SEARCH_KEY,
PR_STORE_SUPPORT_MASK,
PR_CLIENT_SUBMIT_TIME,
PR_DISPLAY_TO,
PR_DISPLAY_CC,
PR_SUBJECT,
PR_IMPORTANCE,
PR_SENSITIVITY,
}
};
enum { ipDispNm, ipAdrTyp, ipEmail, ipSKey, cpUserMax };
const static SizedSPropTagArray (cpUserMax, sptUser) =
{
cpUserMax,
{
PR_DISPLAY_NAME,
PR_ADDRTYPE,
PR_EMAIL_ADDRESS,
PR_SEARCH_KEY,
}
};
enum { ropOof, ropForward, ropReply };
static const LPTSTR rgszSubjPrfx[] =
{
"OOF: ",
"FW: ",
"RE: "
};
enum
{
ivHdrSndrName,
ivHdrSndrType,
ivHdrSndrEmail,
ivHdrSentRepName,
ivHdrSentRepType,
ivHdrSentRepEmail,
ivHdrSubmitTime,
ivDisplayTo,
ivDisplayCc,
ivSubject,
ivImportance,
ivSensitivity,
cvHeader
};
static const SizedSPropTagArray (cvHeader, sptHeader) =
{
cvHeader,
{
PR_SENDER_NAME,
PR_SENDER_ADDRTYPE,
PR_SENDER_EMAIL_ADDRESS,
PR_SENT_REPRESENTING_NAME,
PR_SENT_REPRESENTING_ADDRTYPE,
PR_SENT_REPRESENTING_EMAIL_ADDRESS,
PR_CLIENT_SUBMIT_TIME,
PR_DISPLAY_TO,
PR_DISPLAY_CC,
PR_SUBJECT,
PR_IMPORTANCE,
PR_SENSITIVITY
}
};
static const SizedSPropTagArray (1, sptForward) =
{
1,
{
PR_MESSAGE_ATTACHMENTS,
}
};
enum { ipAttPos, ipAttNum, ipAttMeth, ipAttName, cpTaggingMax };
static const SizedSPropTagArray (cpTaggingMax, sptTagging) =
{
cpTaggingMax,
{
PR_RENDERING_POSITION,
PR_ATTACH_NUM,
PR_ATTACH_METHOD,
PR_ATTACH_FILENAME
}
};
static const LPTSTR rgszHeaderField[] =
{
"Sent:",
"To:",
"Cc:",
"Subject:",
"Importance:",
"Sensitivity:"
};
static const LPTSTR rgszImportance[] =
{
"Low",
"Normal",
"High"
};
static const LPTSTR rgszSensitivity[] =
{
"Normal",
"Personal",
"Private",
"Confidential"
};
static const TCHAR * rgszDay[] =
{
TEXT ("Sunday"),
TEXT ("Monday"),
TEXT ("Tuesday"),
TEXT ("Wednesday"),
TEXT ("Thursday"),
TEXT ("Friday"),
TEXT ("Saturday")
};
extern TCHAR FAR * rgtstrMonthFull[];
LONG
CchInsertSz (HWND hwnd, LPTSTR lpsz)
{
SendMessage (hwnd, EM_REPLACESEL, 0, (LPARAM) lpsz);
return lstrlen (lpsz);
}
VOID
FileTimeToDateTimeSz (FILETIME FAR * lpft, LPTSTR rgch, UINT cb)
{
SYSTEMTIME st;
if (FileTimeToSystemTime (lpft, &st))
{
wsprintf (rgch,
"%s, %s %02d, %4d %d:%02d %s",
rgszDay[st.wDayOfWeek],
rgtstrMonthFull[st.wMonth - 1],
st.wDay,
st.wYear,
st.wHour & 12,
st.wMinute,
(st.wHour > 11) ? "PM" : "AM");
}
else
lstrcpy (rgch, "Unavailable");
}
HRESULT
HrCopyOriginalBody (LPSMH lpsmh,
HWND hwnd,
LPMESSAGE lpmsg,
LONG FAR * lpcch)
{
HRESULT hr;
CHARRANGE chrg = {0};
EDITSTREAM es = {0};
LPSTREAM lpstm = NULL;
LPSTREAM lpstmT = NULL;
*lpcch = 0;
hr = lpmsg->lpVtbl->OpenProperty (lpmsg,
PR_RTF_COMPRESSED,
&IID_IStream,
0, 0,
(LPUNKNOWN FAR *)&lpstmT);
if (!HR_FAILED (hr))
{
hr = WrapCompressedRTFStream (lpstmT, 0, &lpstm);
if (!HR_FAILED (hr))
{
es.pfnCallback = (EDITSTREAMCALLBACK)lpstm->lpVtbl->Read;
es.dwCookie = (DWORD)lpstm;
/* Stuff a newline into the edit
* control such that whatever preceeds
* the body will be separated from the
* the text of the original message
*/
SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
CchInsertSz (hwnd, "\r\n");
/* Do the body now */
SendMessage (hwnd,
EM_STREAMIN,
SF_RTF | SFF_SELECTION | SFF_PLAINRTF,
(LPARAM)&es);
/* Calculate the size of the body in characters */
Edit_SetSel (hwnd, chrg.cpMin, INT_MAX);
SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
*lpcch = chrg.cpMax - chrg.cpMin;
/* Reset the selection to the begining
* of the edit control such that all
* additions occur before the original
* body
*/
Edit_SetSel (hwnd, 0, 0);
}
}
UlRelease (lpstm);
UlRelease (lpstmT);
DebugTraceResult (HrCopyOriginalBody(), hr);
return hr;
}
HRESULT
HrInsertOriginalHeader (LPSMH lpsmh,
HWND hwnd,
LPSPropValue lpval,
CHARFORMAT FAR * lpcf,
LONG FAR * lpcch)
{
CHAR rgch[MAX_PATH];
CHARRANGE chrg = {0};
CHARRANGE chrgHdr = {0};
CHARRANGE rgchrg[ccrgMax] = {0};
LONG cp;
LPTSTR lpsz;
UINT icrg = 0;
UINT ip;
/* Stuff a newline into the edit
* control such that whatever preceeds
* the body will be separated from the
* the text of the original message
*/
SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrgHdr);
CchInsertSz (hwnd, "\r\n");
/* Mark the begining of the header */
SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
cp = chrg.cpMin;
cp += CchInsertSz (hwnd, "\r\n----------\r\n");
/* Insert the "From: xxxx" line */
if ((lpval[ipSndrNm].ulPropTag == PR_SENDER_NAME) &&
(lpval[ipSndrNm].Value.LPSZ != NULL) &&
(*lpval[ipSndrNm].Value.LPSZ != 0))
{
rgchrg[icrg].cpMin = cp;
cp += CchInsertSz (hwnd, "From:");
rgchrg[icrg].cpMax = cp;
icrg++;
cp += CchInsertSz (hwnd, "\t");
cp += CchInsertSz (hwnd, lpval[ipSndrNm].Value.LPSZ);
/* If we were representing someone else... */
if ((lpval[ipSentRepName].ulPropTag == PR_SENT_REPRESENTING_NAME) &&
(lpval[ipSentRepName].Value.LPSZ != NULL) &&
(*lpval[ipSentRepName].Value.LPSZ != 0))
{
if ((lpval[ipSndrSKey].ulPropTag != PR_SENDER_SEARCH_KEY) ||
(lpval[ipSentRepSKey].ulPropTag != PR_SENT_REPRESENTING_SEARCH_KEY) ||
(lpval[ipSndrSKey].Value.bin.cb != lpval[ipSentRepSKey].Value.bin.cb) ||
memcmp (lpval[ipSndrSKey].Value.bin.lpb,
lpval[ipSentRepSKey].Value.bin.lpb,
lpval[ipSndrSKey].Value.bin.cb))
{
cp += CchInsertSz (hwnd, " on behalf of ");
cp += CchInsertSz (hwnd, lpval[ipSentRepName].Value.LPSZ);
}
cp += CchInsertSz (hwnd, TEXT("\r\n"));
}
/* Insert the remaining lines */
for (ip = ipSubmitTime; ip < cpResponseMax; ip++)
{
lpsz = NULL;
switch (PROP_TYPE (lpval[ip].ulPropTag))
{
case PT_TSTRING:
/* Strings are strings */
lpsz = lpval[ip].Value.LPSZ;
break;
case PT_SYSTIME:
/* Convertt the date to a string */
FileTimeToDateTimeSz (&lpval[ip].Value.ft, rgch, sizeof(rgch));
lpsz = rgch;
break;
case PT_LONG:
/* Importance and Sensitivity use a look-up to
* find the proper string to insert. If the value
* equates to the "normal" level of a given message,
* then no value is displayed.
*/
if ((lpval[ip].ulPropTag == PR_IMPORTANCE) &&
(lpval[ip].Value.l != IMPORTANCE_NORMAL))
lpsz = rgszImportance[lpval[ip].Value.l];
else if ((lpval[ip].ulPropTag == PR_SENSITIVITY) &&
(lpval[ip].Value.l != SENSITIVITY_NONE))
lpsz = rgszSensitivity[lpval[ip].Value.l];
break;
}
if (lpsz && *lpsz)
{
rgchrg[icrg].cpMin = cp;
cp += CchInsertSz (hwnd, rgszHeaderField[ip - ipSubmitTime]);
rgchrg[icrg].cpMax = cp;
icrg++;
cp += CchInsertSz (hwnd, "\t");
cp += CchInsertSz (hwnd, lpsz);
cp += CchInsertSz (hwnd, TEXT("\r\n"));
}
}
cp += CchInsertSz (hwnd, TEXT("\r\n"));
/* Ensure that the text is formated in the
* charformat passed in. Such that we can
* manipulate the rest with no worries
*/
chrg.cpMin += 2;
chrg.cpMax = cp;
SendMessage (hwnd, EM_EXSETSEL, 0, (LPARAM) &chrg);
SendMessage (hwnd, EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)lpcf);
/* Run through all the field headers and make them bold */
lpcf->cbSize = sizeof(CHARFORMAT);
lpcf->dwMask = CFM_BOLD;
lpcf->dwEffects = CFE_BOLD;
while (icrg)
{
chrg = rgchrg[--icrg];
SendMessage (hwnd, EM_EXSETSEL, 0, (LPARAM)&chrg);
SendMessage (hwnd, EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)lpcf);
}
/* Calculate the size of the header */
*lpcch = cp - chrgHdr.cpMin;
Edit_SetSel (hwnd, 0, 0);
}
DebugTraceResult (HrInsertOriginalHeader(), hrSuccess);
return hrSuccess;
}
HRESULT
HrInsertAnnotation (LPSMH lpsmh,
HWND hwnd,
UINT rop,
LPRULE lprl,
LONG FAR * lpcch)
{
CHARRANGE chrg = {0};
EDITSTREAM es = {0};
LPBYTE lpb;
RTFS rtfs = {0};
ULONG cb;
/* Setup which annotation to use */
if (rop != ropOof)
{
cb = lprl->cbRTF;
lpb = lprl->lpbRTF;
}
else
{
cb = lpsmh->oof.cbRTF;
lpb = lpsmh->oof.lpbRTF;
}
/* Stream the bad boy in */
rtfs.cb = 0;
rtfs.cbMax = cb;
rtfs.lpb = lpb;
es.pfnCallback = ReadRTFFromBuffer;
es.dwCookie = (DWORD)&rtfs;
SendMessage (hwnd,
EM_STREAMIN,
SF_RTF | SFF_SELECTION | SFF_PLAINRTF,
(LPARAM)&es);
/* Calculate the size of what we just streamed in */
SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
*lpcch = chrg.cpMax;
DebugTraceResult (HrInsertAnnotation(), hrSuccess);
return hrSuccess;
}
HRESULT
HrTagAttachments (LPSMH lpsmh,
HWND hwnd,
LONG dch,
LPMESSAGE lpmsg)
{
HRESULT hr;
CHAR rgch[MAX_PATH] = {0};
LONG ichPos;
LPATTACH lpatt = NULL;
LPMAPITABLE lptbl = NULL;
LPMESSAGE lpmsgT = NULL;
LPSPropValue lpval = NULL;
LPSRowSet lprws = NULL;
UINT irw;
hr = lpmsg->lpVtbl->GetAttachmentTable (lpmsg, 0, &lptbl);
if (HR_FAILED (hr))
goto ret;
hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptTagging, 0);
if (HR_FAILED (hr))
goto ret;
while (TRUE)
{
hr = lptbl->lpVtbl->QueryRows (lptbl, 64, 0, &lprws);
if (HR_FAILED (hr))
goto ret;
if (lprws->cRows == 0)
break;
for (irw = 0; irw < lprws->cRows; irw++)
{
switch (lprws->aRow[irw].lpProps[ipAttMeth].Value.l)
{
case ATTACH_OLE:
lstrcpy (rgch, "<<OLE Object: unknown>>");
break;
case ATTACH_EMBEDDED_MSG:
hr = lpmsg->lpVtbl->OpenAttach (lpmsg,
lprws->aRow[irw].lpProps[ipAttNum].Value.l,
NULL,
0,
&lpatt);
if (!HR_FAILED (hr))
{
hr = lpatt->lpVtbl->OpenProperty (lpatt,
PR_ATTACH_DATA_OBJ,
&IID_IMessage,
0, 0,
(LPUNKNOWN FAR *)&lpmsgT);
if (!HR_FAILED (hr))
{
/* Get the subject of the embedded message
* as the tag identifier
*/
hr = HrGetOneProp ((LPMAPIPROP)lpmsgT, PR_SUBJECT, &lpval);
}
}
wsprintf (rgch, "<<Message: %s>>",
HR_FAILED (hr) ? "" : lpval->Value.LPSZ);
(*lpsmh->lpfnFree) (lpval);
UlRelease (lpmsgT);
UlRelease (lpatt);
lpmsgT = NULL;
lpval = NULL;
lpatt = NULL;
break;
default:
case ATTACH_BY_VALUE:
case ATTACH_BY_REFERENCE:
/* Use the filename for the attachment tag */
wsprintf (rgch, "<<File: %s>>",
(lprws->aRow[irw].lpProps[ipAttName].ulPropTag == PR_ATTACH_FILENAME)
? lprws->aRow[irw].lpProps[ipAttName].Value.LPSZ
: "");
break;
}
/* Setup the selection such that we replace the attachment
* place holder wiht the attachment tag
*/
ichPos = lprws->aRow[irw].lpProps[ipAttPos].Value.l;
if (ichPos == -1)
Edit_SetSel (hwnd, INT_MAX, INT_MAX);
else
Edit_SetSel (hwnd, ichPos + dch - 1, ichPos + dch);
/* Insert the tag and adjust the offset
* of the next attachment tag posiiton.
*/
dch += CchInsertSz (hwnd, rgch) - 1;
/* Free the row data */
(*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps);
}
hr = hrSuccess;
(*lpsmh->lpfnFree) (lprws);
lprws = NULL;
}
(*lpsmh->lpfnFree) (lprws);
lprws = NULL;
ret:
UlRelease (lptbl);
DebugTraceResult (HrTagAttachments(), hr);
return hr;
}
HRESULT
HrOffsetAttachments (LPSMH lpsmh,
LONG dch,
LPMESSAGE lpmsg)
{
HRESULT hr;
LPATTACH lpatt = NULL;
LPMAPITABLE lptbl = NULL;
LPSRowSet lprws = NULL;
UINT irw;
hr = lpmsg->lpVtbl->GetAttachmentTable (lpmsg, 0, &lptbl);
if (HR_FAILED (hr))
goto ret;
hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptTagging, 0);
if (HR_FAILED (hr))
goto ret;
while (TRUE)
{
hr = lptbl->lpVtbl->QueryRows (lptbl, 64, 0, &lprws);
if (HR_FAILED (hr))
goto ret;
if (lprws->cRows == 0)
break;
for (irw = 0; irw < lprws->cRows; irw++)
{
/* If the rendering position is not -1, we
* want to adjust the positioning by the value
* passed in dch
*/
if (lprws->aRow[irw].lpProps[ipAttPos].Value.l != -1)
{
/* Adjust the positioning, and set in into the attachment */
lprws->aRow[irw].lpProps[ipAttPos].Value.l += dch;
hr = lpmsg->lpVtbl->OpenAttach (lpmsg,
lprws->aRow[irw].lpProps[ipAttNum].Value.l,
NULL,
MAPI_MODIFY,
&lpatt);
if (!HR_FAILED (hr))
{
hr = lpatt->lpVtbl->SetProps (lpatt,
1,
&lprws->aRow[irw].lpProps[ipAttPos],
NULL);
if (!HR_FAILED (hr))
{
/* Save out the new positioning */
hr = lpatt->lpVtbl->SaveChanges (lpatt, 0);
}
UlRelease (lpatt);
lpatt = NULL;
}
hr = hrSuccess;
}
/* Free the row data */
(*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps);
}
(*lpsmh->lpfnFree) (lprws);
lprws = NULL;
}
(*lpsmh->lpfnFree) (lprws);
lprws = NULL;
ret:
UlRelease (lptbl);
DebugTraceResult (HrOffsetAttachments(), hr);
return hr;
}
HRESULT
HrInsertBody (LPSMH lpsmh,
HWND hwnd,
LPSPropValue lpval,
LPMESSAGE lpmsg)
{
HRESULT hr = hrSuccess;
BOOL fUpdated;
EDITSTREAM es = {0};
LPSTREAM lpstm = NULL;
LPSTREAM lpstmRTF = NULL;
SPropValue val;
ULONG ulFlags = 0;
/* Do PR_BODY iff the store is not RTF_AWARE */
if ((lpval[ipStoreSupport].ulPropTag != PR_STORE_SUPPORT_MASK) ||
!(lpval[ipStoreSupport].Value.l & STORE_RTF_OK))
{
hr = lpmsg->lpVtbl->OpenProperty (lpmsg,
PR_BODY,
&IID_IStream,
0,
MAPI_CREATE | MAPI_MODIFY,
(LPUNKNOWN FAR *)&lpstm);
if (HR_FAILED (hr))
goto ret;
es.dwCookie = (DWORD)lpstm;
es.pfnCallback = (EDITSTREAMCALLBACK)lpstm->lpVtbl->Write;
SendMessage (hwnd, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
UlRelease (lpstm);
lpstm = NULL;
if (!es.dwError)
ulFlags |= RTF_SYNC_BODY_CHANGED;
}
/* Add in PR_COMPRESSED_RTF */
hr = lpmsg->lpVtbl->OpenProperty (lpmsg,
PR_RTF_COMPRESSED,
&IID_IStream,
0,
MAPI_CREATE | MAPI_MODIFY,
(LPUNKNOWN FAR *)&lpstm);
if (HR_FAILED (hr))
goto ret;
hr = WrapCompressedRTFStream (lpstm,
MAPI_MODIFY | (lpval[ipStoreSupport].Value.l & STORE_UNCOMPRESSED_RTF),
&lpstmRTF);
if (HR_FAILED (hr))
goto ret;
es.dwCookie = (DWORD)lpstmRTF;
es.pfnCallback = (EDITSTREAMCALLBACK)lpstmRTF->lpVtbl->Write;
SendMessage (hwnd, EM_STREAMOUT, SF_RTF | SFF_PLAINRTF, (LPARAM)&es);
hr = lpstmRTF->lpVtbl->Commit (lpstmRTF, 0);
if (HR_FAILED (hr))
goto ret;
if (!es.dwError)
ulFlags |= RTF_SYNC_RTF_CHANGED;
/* Sync the RTF and the body iff the store is not RTF aware */
if ((lpval[ipStoreSupport].ulPropTag != PR_STORE_SUPPORT_MASK) ||
!(lpval[ipStoreSupport].Value.l & STORE_RTF_OK))
{
/* We are not aware, so we better do a full sync */
hr = RTFSync (lpmsg, ulFlags, &fUpdated);
}
else
{
/* If we are aware, then we want to tell the
* store that we are completely in sync. Otherwise,
* we could loose our attachment positioning on
* RTF aware stores. And that would be bad.
*/
val.ulPropTag = PR_RTF_IN_SYNC;
val.Value.b = TRUE;
lpmsg->lpVtbl->SetProps (lpmsg, 1, &val, NULL);
}
ret:
UlRelease (lpstm);
UlRelease (lpstmRTF);
DebugTraceResult (HrInsertBody(), hr);
return hr;
}
HRESULT
HrBuildRecipient (LPSMH lpsmh,
LPSPropValue lpval,
LPMESSAGE lpmsg)
{
SCODE sc;
HRESULT hr;
LPADRLIST lpadr = NULL;
LPMAPIPROP lpusr = NULL;
LPSPropValue lpvalUsr = NULL;
LPSPropValue rgval = NULL;
UINT cval = 0;
ULONG ulT;
/* Open the recipient up */
hr = lpsmh->lpsess->lpVtbl->OpenEntry (lpsmh->lpsess,
lpval->Value.bin.cb,
(LPENTRYID)lpval->Value.bin.lpb,
NULL, 0,
&ulT,
(LPUNKNOWN FAR *)&lpusr);
if (HR_FAILED (hr))
goto ret;
/* Get the properties we need */
hr = lpusr->lpVtbl->GetProps (lpusr,
(LPSPropTagArray)&sptUser,
0,
&ulT,
&lpvalUsr);
if (HR_FAILED (hr))
goto ret;
/* Allocate the adrlist */
if (FAILED (sc = (*lpsmh->lpfnAlloc) (CbNewADRLIST (1), &lpadr)) ||
FAILED (sc = (*lpsmh->lpfnAlloc) (cpTargetRecipMax * sizeof(SPropValue), &rgval)))
{
hr = ResultFromScode (sc);
goto ret;
}
/* Stuff the properties and add the recipient */
rgval[cval].ulPropTag = PR_ENTRYID;
rgval[cval].Value = lpval->Value;
cval++;
rgval[cval].ulPropTag = PR_DISPLAY_NAME;
rgval[cval].Value.LPSZ = lpvalUsr[ipDispNm].Value.LPSZ;
cval++;
if (lpvalUsr[ipAdrTyp].ulPropTag == PR_ADDRTYPE)
{
rgval[cval].ulPropTag = PR_ADDRTYPE;
rgval[cval].Value.LPSZ = lpvalUsr[ipAdrTyp].Value.LPSZ;
cval++;
}
if (lpvalUsr[ipEmail].ulPropTag == PR_EMAIL_ADDRESS)
{
rgval[cval].ulPropTag = PR_EMAIL_ADDRESS;
rgval[cval].Value.LPSZ = lpvalUsr[ipEmail].Value.LPSZ;
cval++;
}
if (lpvalUsr[ipSKey].ulPropTag == PR_SEARCH_KEY)
{
rgval[cval].ulPropTag = PR_SEARCH_KEY;
rgval[cval].Value = lpvalUsr[ipSKey].Value;
cval++;
}
rgval[cval].ulPropTag = PR_RECIPIENT_TYPE;
rgval[cval].Value.l = MAPI_TO;
cval++;
lpadr->cEntries = 1;
lpadr->aEntries[0].cValues = cval;
lpadr->aEntries[0].rgPropVals = rgval;
hr = lpmsg->lpVtbl->ModifyRecipients (lpmsg, MODRECIP_ADD, lpadr);
if (HR_FAILED (hr))
goto ret;
ret:
if (lpadr)
{
(*lpsmh->lpfnFree) (lpadr->aEntries[0].rgPropVals);
(*lpsmh->lpfnFree) (lpadr);
}
(*lpsmh->lpfnFree) (lpvalUsr);
UlRelease (lpusr);
DebugTraceResult (HrBuildRecipient(), hr);
return hr;
}
HRESULT
HrCreateResponse (LPSMH lpsmh,
LPRULE lprl,
LPMAPIFOLDER lpfldr,
LPMESSAGE lpmsgOrig,
LPSPropValue lpval,
LPMESSAGE FAR * lppmsg)
{
SCODE sc;
HRESULT hr;
CHARFORMAT cf = {0};
HINSTANCE hlib = NULL;
HWND hwnd = NULL;
LONG cch = 0;
LONG cchHdr = 0;
LPBYTE lpbConvIndex = NULL;
LPMESSAGE lpmsg = NULL;
LPREOC lpreoc = NULL;
LPSPropValue rgval = NULL;
PARAFORMAT pf = {0};
TCHAR rgchClass[MAX_PATH];
TCHAR rgchSubj[MAX_PATH];
UINT rop;
ULONG cval = 0;
*lppmsg = NULL;
/* Calculate the response operation based on
* the supplied rule. If no rule is supplied
* then the response is an out-of-office msg.
*/
if (lprl)
{
Assert (lprl->ulFlags & RULE_AUTO_RESPONSE);
if (lprl->ulFlags & RULE_AUTO_FORWARD)
{
Assert (!(lprl->ulFlags & RULE_AUTO_REPLY));
rop = ropForward;
}
else
{
Assert (!(lprl->ulFlags & RULE_AUTO_FORWARD));
rop = ropReply;
}
}
else
rop = ropOof;
/* Create the response message */
hr = lpfldr->lpVtbl->CreateMessage (lpfldr, NULL, 0, &lpmsg);
if (HR_FAILED (hr))
goto ret;
/* Create and initialized the RTF edit control */
hlib = LoadLibrary (RICHEDIT_LIB);
hwnd = CreateWindow (RICHEDIT_CLASS,
"",
WS_BORDER|ES_MULTILINE,
CW_USEDEFAULT, CW_USEDEFAULT, INT_MAX, INT_MAX,
NULL,
NULL,
lpsmh->hinst,
NULL);
if (!hwnd)
{
hr = ResultFromScode (MAPI_E_CALL_FAILED);
goto ret;
}
/* Create the richedit OLE callback. If this
* fails, it is non-fatal. It just means that
* any OLE objects in the annotation will not
* be preserved in then response message.
*/
if (!FAILED (ScNewRicheditCallback (NULL,
lpsmh->lpfnAlloc,
lpsmh->lpfnAllocMore,
lpsmh->lpfnFree,
&lpreoc)))
{
/* We have an OLE callback that we need
* to hand off to the richedit control.
* Although, the richedit control should
* be AddRef()ing the object, we will hold
* our reference until we are through with
* the edit control.
*/
SendMessage (hwnd, EM_SETOLECALLBACK, 0, (LPARAM)lpreoc);
}
/* Setup the default character format */
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask = CFM_FACE | CFM_SIZE | CFM_COLOR | CFM_BOLD |
CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT |
CFM_OFFSET | CFM_CHARSET;
cf.dwEffects = CFE_AUTOCOLOR;
cf.yHeight = 160;
lstrcpy (cf.szFaceName, "MS Sans Serif");
cf.bCharSet = DEFAULT_CHARSET;
SendMessage (hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf);
/* Copy over the original body if it makes sense to do so */
if ((rop != ropOof) && (lprl->ulFlags & RULE_AUTO_APPEND_ORIG))
{
hr = HrCopyOriginalBody (lpsmh, hwnd, lpmsgOrig, &cch);
if (HR_FAILED (hr) && (rop == ropForward))
goto ret;
hr = hrSuccess;
}
/* Create and insert the original message header */
hr = HrInsertOriginalHeader (lpsmh, hwnd, lpval, &cf, &cchHdr);
if (HR_FAILED (hr))
goto ret;
/* If this response is not a forward, we will want to
* indent the header (and maybe the body) such that they
* are offset from the annotation.
*/
if (rop != ropForward)
{
/* Skip indenting the blankline */
Edit_SetSel (hwnd, 2, INT_MAX);
pf.cbSize = sizeof(PARAFORMAT);
pf.dwMask = PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_ALIGNMENT |
PFM_OFFSET | PFM_TABSTOPS | PFM_NUMBERING;
pf.dxOffset = 1440;
pf.cTabCount = 1;
pf.rgxTabs[0] = 1440;
pf.wAlignment = PFA_LEFT;
pf.dxStartIndent = 1440 / 4;
SendMessage (hwnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
Edit_SetSel (hwnd, 0, 0);
}
/* Copy over the annotation */
hr = HrInsertAnnotation (lpsmh, hwnd, rop, lprl, &cch);
if (HR_FAILED (hr))
goto ret;
/* Tag the attachments for replies */
if (rop == ropReply)
{
hr = HrTagAttachments (lpsmh, hwnd, cchHdr + cch + 3, lpmsgOrig);
if (HR_FAILED (hr))
goto ret;
}
/* Allocate space for the new message properties */
sc = (*lpsmh->lpfnAlloc) (cpTargetResponseMax * sizeof(SPropValue), &rgval);
if (FAILED (sc))
{
hr = ResultFromScode (sc);
goto ret;
}
/* Build the response subject */
lstrcpy (rgchSubj, rgszSubjPrfx[rop]);
lstrcat (rgchSubj, lpval[ipNSubj].Value.LPSZ);
rgval[cval].ulPropTag = PR_SUBJECT;
rgval[cval].Value.LPSZ = rgchSubj;
cval++;
rgval[cval].ulPropTag = PR_SUBJECT_PREFIX;
rgval[cval].Value.LPSZ = rgszSubjPrfx[rop];
cval++;
/* Build the response message class */
if (rop == ropOof)
wsprintf (rgchClass, "Report.%s.OOF", lpval[ipMsgClass].Value.LPSZ);
else if (rop == ropReply)
lstrcpy (rgchClass, "IPM.Note.AutoReply");
else
lstrcpy (rgchClass, lpval[ipMsgClass].Value.LPSZ);
rgval[cval].ulPropTag = PR_MESSAGE_CLASS;
rgval[cval].Value.LPSZ = rgchClass;
cval++;
/* Compose the set of remaining properties */
rgval[cval].ulPropTag = PR_DELETE_AFTER_SUBMIT;
rgval[cval].Value.b = TRUE;
#ifdef DEBUG
rgval[cval].Value.b = GetPrivateProfileInt ("SMH", "DeleteResponses", 1, "MAPIDBG.INI");
#endif
cval++;
/* Tell the store that the message has not
* been sent. Otherwise, since the spooler
* is the creater of the message, the store
* will treat the message as if it has been
* delivered into the store by a transport.
*/
rgval[cval].ulPropTag = PR_MESSAGE_FLAGS;
rgval[cval].Value.l = MSGFLAG_UNSENT;
cval++;
/* Create appropriate conversation topic and indexes */
if (lpval[ipConvKey].ulPropTag == PR_CONVERSATION_KEY)
rgval[cval++] = lpval[ipConvKey];
else
{
rgval[cval].ulPropTag = PR_CONVERSATION_TOPIC;
rgval[cval].Value.LPSZ = lpval[ipNSubj].Value.LPSZ;
cval++;
}
if (lpval[ipConvTopic].ulPropTag == PR_CONVERSATION_TOPIC)
rgval[cval++] = lpval[ipConvTopic];
else
{
rgval[cval].ulPropTag = PR_CONVERSATION_TOPIC;
rgval[cval].Value.LPSZ = lpval[ipNSubj].Value.LPSZ;
cval++;
}
if (lpval[ipConvIndex].ulPropTag == PR_CONVERSATION_INDEX)
{
sc = ScAddConversationIndex (lpval[ipConvIndex].Value.bin.cb,
lpval[ipConvIndex].Value.bin.lpb,
&rgval[cval].Value.bin.cb,
&lpbConvIndex);
}
else
{
sc = ScAddConversationIndex (0,
NULL,
&rgval[cval].Value.bin.cb,
&lpbConvIndex);
}
if (!FAILED (sc))
{
rgval[cval].ulPropTag = PR_CONVERSATION_INDEX;
rgval[cval].Value.bin.lpb = lpbConvIndex;
cval++;
}
/* Report tag */
if (lpval[ipReportTag].ulPropTag == PR_REPORT_TAG)
rgval[cval++] = lpval[ipReportTag];
/* Sensitivity */
if (lpval[ipSensitivity].ulPropTag == PR_SENSITIVITY)
rgval[cval++] = lpval[ipSensitivity];
/* Build response specific properties */
if (rop == ropForward)
{
/* Importance */
if (lpval[ipImportance].ulPropTag == PR_IMPORTANCE)
rgval[cval++] = lpval[ipImportance];
/* Priority */
if (lpval[ipOrigPriority].ulPropTag == PR_PRIORITY)
rgval[cval++] = lpval[ipOrigPriority];
/* Original author */
if (lpval[ipOrigAuthorName].ulPropTag == PR_ORIGINAL_AUTHOR_NAME)
rgval[cval++] = lpval[ipOrigAuthorName];
if (lpval[ipOrigAuthorEid].ulPropTag == PR_ORIGINAL_AUTHOR_ENTRYID)
rgval[cval++] = lpval[ipOrigAuthorEid];
if (lpval[ipOrigAuthorSKey].ulPropTag == PR_ORIGINAL_AUTHOR_SEARCH_KEY)
rgval[cval++] = lpval[ipOrigAuthorSKey];
/* Original submit time */
if (lpval[ipOrigSubmitTime].ulPropTag == PR_ORIGINAL_SUBMIT_TIME)
rgval[cval++] = lpval[ipOrigSubmitTime];
}
/* Set the properties */
hr = lpmsg->lpVtbl->SetProps (lpmsg, cval, rgval, NULL);
if (HR_FAILED (hr))
goto ret;
/* Insert the body into the mesage directly */
hr = HrInsertBody (lpsmh, hwnd, lpval, lpmsg);
if (HR_FAILED (hr))
goto ret;
/* Copy the attachment across in the case of a forward */
if (rop == ropForward)
{
hr = lpmsgOrig->lpVtbl->CopyProps(lpmsgOrig,
(LPSPropTagArray)&sptForward,
0,
NULL,
(LPIID) &IID_IMessage,
lpmsg,
0,
NULL);
if (HR_FAILED (hr))
goto ret;
hr = HrOffsetAttachments (lpsmh, cchHdr + cch + 2, lpmsg);
if (HR_FAILED (hr))
goto ret;
}
/* Do the recipient */
hr = HrBuildRecipient (lpsmh,
(rop == ropForward)
? lprl->lpvalRecip
: &lpval[ipSndrEid],
lpmsg);
if (HR_FAILED (hr))
goto ret;
ret:
if (hwnd)
DestroyWindow (hwnd);
if (HR_FAILED (hr))
{
UlRelease (lpmsg);
lpmsg = NULL;
}
if (hlib)
FreeLibrary (hlib);
(*lpsmh->lpfnFree) (lpbConvIndex);
(*lpsmh->lpfnFree) (rgval);
UlRelease (lpreoc);
*lppmsg = lpmsg;
DebugTraceResult (HrCreateResponse(), hr);
return hr;
}
BOOL
FOofRecip (LPOOF lpoof, LPSPropValue lpvalEid)
{
BOOL fOof = TRUE;
LPMAPITABLE lptbl = lpoof->lptbl;
SPropValue val;
SRestriction res;
if (lptbl)
{
#ifdef DEBUG
if (!GetPrivateProfileInt ("SMH", "OofAlways", 0, "MAPIDBG.INI"))
#endif
{
val.ulPropTag = PR_ENTRYID;
val.Value = lpvalEid->Value;
res.rt = RES_PROPERTY;
res.res.resProperty.relop = RELOP_EQ;
res.res.resProperty.ulPropTag = PR_ENTRYID;
res.res.resProperty.lpProp = &val;
fOof = HR_FAILED (lptbl->lpVtbl->FindRow (lptbl, &res, BOOKMARK_BEGINNING, 0L));
}
}
return fOof;
}
HRESULT
HrRegOofRecip (LPSMH lpsmh, LPOOF lpoof, LPSPropValue lpvalEid)
{
HRESULT hr;
LPTABLEDATA lptad = lpoof->lptad;
SizedSPropTagArray (cpOofMax, sptOofTbl) = {cpOofMax, { PR_ROWID, PR_ENTRYID }};
SPropValue rgval[cpOofMax];
SRow rw;
if (!lptad)
{
// This is the first OOF recip so we need to create the
// table of recips
//
hr = ResultFromScode (CreateTable ((LPIID)&IID_IMAPITableData,
lpsmh->lpfnAlloc,
lpsmh->lpfnAllocMore,
lpsmh->lpfnFree,
NULL,
TBLTYPE_DYNAMIC,
PR_ROWID,
(LPSPropTagArray)&sptOofTbl,
(LPTABLEDATA FAR *)&lptad));
if (HR_FAILED (hr))
goto ret;
hr = lptad->lpVtbl->HrGetView (lptad, NULL, NULL, 0, &lpoof->lptbl);
if (HR_FAILED (hr))
{
UlRelease (lptad);
goto ret;
}
lpoof->lptad = lptad;
}
rgval[ipOofRId].ulPropTag = PR_ROWID;
rgval[ipOofRId].Value.l = lpoof->cRecips++;
rgval[ipOofREid].ulPropTag = PR_ENTRYID;
rgval[ipOofREid].Value = lpvalEid->Value;
rw.cValues = cpOofMax;
rw.lpProps = rgval;
hr = lptad->lpVtbl->HrModifyRow (lptad, &rw);
if (HR_FAILED (hr))
goto ret;
ret:
DebugTraceResult (HrRegOofRecip(), hr);
return hr;
}
HRESULT
HrGenerateResponse (LPSMH lpsmh, LPRULE lprl, LPMAPIFOLDER lpfldr, LPMESSAGE lpmsg)
{
HRESULT hr;
LPSPropValue lpval = NULL;
LPMESSAGE lpmsgRep = NULL;
ULONG cval;
hr = lpmsg->lpVtbl->SaveChanges (lpmsg, KEEP_OPEN_READONLY);
if (HR_FAILED (hr))
goto ret;
hr = lpmsg->lpVtbl->GetProps (lpmsg,
(LPSPropTagArray)&sptResponse,
0,
&cval,
&lpval);
if (HR_FAILED (hr))
goto ret;
hr = ResultFromScode (MAPI_E_NOT_ME);
if ((lpval[ipMsgClass].ulPropTag == PR_MESSAGE_CLASS) &&
(lpval[ipMsgClass].Value.LPSZ != NULL) &&
FLpszContainsLpsz (lpval[ipMsgClass].Value.LPSZ, "Report."))
goto ret;
if ((lpval[ipFlags].ulPropTag == PR_MESSAGE_FLAGS) &&
(lpval[ipFlags].Value.l == MSGFLAG_FROMME))
goto ret;
/* If there is no rule, then we must be OOF'ing */
if (!lprl && !FOofRecip (&lpsmh->oof, &lpval[ipSndrEid]))
goto ret;
if ((lpval[ipRecipMe].ulPropTag == PR_MESSAGE_RECIP_ME) &&
(lpval[ipRecipMe].Value.b))
{
DebugTrace ("SMH: generating response message\n");
if (!lprl)
{
/* Register the OOF recipient */
hr = HrRegOofRecip (lpsmh, &lpsmh->oof, &lpval[ipSndrEid]);
if (HR_FAILED (hr))
goto ret;
}
/* Create the response and submit it */
hr = HrCreateResponse (lpsmh,
lprl,
lpfldr,
lpmsg,
lpval,
&lpmsgRep);
if (!HR_FAILED (hr))
hr = lpmsgRep->lpVtbl->SubmitMessage (lpmsgRep, 0);
UlRelease (lpmsgRep);
}
ret:
(*lpsmh->lpfnFree) (lpval);
DebugTraceResult (HrGenerateResponse(), hr);
return hr;
}
HRESULT
HrInitOof (LPSMH lpsmh, LPSPropValue lpvalAnno, LPSPropValue lpvalRTF)
{
SCODE sc = MAPI_E_UNCONFIGURED;
if (lpvalRTF->ulPropTag == PR_SMH_OOF_RTF)
{
if (!FAILED (sc = (*lpsmh->lpfnAlloc) (lpvalRTF->Value.bin.cb,
(LPVOID FAR *)&lpsmh->oof.lpbRTF)))
{
lpsmh->oof.cbRTF = lpvalRTF->Value.bin.cb;
memcpy (lpsmh->oof.lpbRTF,
lpvalRTF->Value.bin.lpb,
(UINT)lpvalRTF->Value.bin.cb);
}
}
if (lpvalAnno->ulPropTag == PR_SMH_OOF_TEXT)
{
if (!FAILED (sc = (*lpsmh->lpfnAlloc) (lstrlen (lpvalAnno->Value.LPSZ) + sizeof(TCHAR),
(LPVOID FAR *)&lpsmh->oof.lpszBody)))
lstrcpy (lpsmh->oof.lpszBody, lpvalAnno->Value.LPSZ);
}
DebugTraceSc (HrInitOof(), sc);
return ResultFromScode (sc);
}