SMHOOF.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:40k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*
  2.  *  S M H O O F . C
  3.  *
  4.  *  Sample mail handling hook
  5.  *  Out of office management
  6.  *
  7.  *  Copyright 1992-95 Microsoft Corporation.  All Rights Reserved.
  8.  */
  9. #include "_pch.h"
  10. #include <mapiutil.h>
  11. #include <cindex.h>
  12. #include <limits.h>
  13. #ifdef _WIN32
  14. #define szPlatform "32"
  15. #else
  16. #define szPlatform
  17. #endif
  18. enum { ipOofRId, ipOofREid, cpOofMax };
  19. enum { icrgFrom, icrgSubmit, icrgTo, icrgCC, icrgSubj, icrgImp, icrgSen, ccrgMax };
  20. enum
  21. {
  22.     ipRespSen,
  23.     ipRespConvKey,
  24.     ipRespConvIdx,
  25.     ipRespConvTopic,
  26.     ipRespReportTag,
  27.     ipRespOrigAuthEid,
  28.     ipRespOrigAuthorName,
  29.     ipRespOrigAuthorSKey,
  30.     ipRespOrigSubmitTime,
  31.     ipRespPriority,
  32.     ipRespImportance,
  33.     ipRespSubject,
  34.     ipRespSubjectPrefix,
  35.     ipRespDelAfterSub,
  36.     ipRespMessageClass,
  37.     ipRespMessageFlags,
  38.     cpTargetResponseMax
  39. };
  40. enum
  41. {
  42.     ipRespRecipName,
  43.     ipRespRecipAdrType,
  44.     ipRespRecipEmail,
  45.     ipRespRecipType,
  46.     ipRespRecipEid,
  47.     ipRespRecipSKey,
  48.     cpTargetRecipMax
  49. };
  50. enum
  51. {
  52.     ipMsgClass,
  53.     ipMsgFlags,
  54.     ipRecipMe,
  55.     ipNSubj,
  56.     ipSndrEid,
  57.     ipSndrNm,
  58.     ipSndrType,
  59.     ipSndrEmail,
  60.     ipSndrSKey,
  61.     ipConvIndex,
  62.     ipConvTopic,
  63.     ipConvKey,
  64.     ipOrigPriority,
  65.     ipReportTag,
  66.     ipOrigAuthorEid,
  67.     ipOrigAuthorName,
  68.     ipOrigAuthorSKey,
  69.     ipOrigSubmitTime,
  70.     ipSentRepName,
  71.     ipSentRepType,
  72.     ipSentRepEmail,
  73.     ipSentRepSKey,
  74.     ipStoreSupport,
  75.     ipSubmitTime,
  76.     ipDisplayTo,
  77.     ipDisplayCc,
  78.     ipOofSubj,
  79.     ipImportance,
  80.     ipSensitivity,
  81.     cpResponseMax
  82. };
  83. const static SizedSPropTagArray (cpResponseMax, sptResponse) =
  84. {
  85.     cpResponseMax,
  86.     {
  87.         PR_MESSAGE_CLASS,
  88.         PR_MESSAGE_FLAGS,
  89.         PR_MESSAGE_RECIP_ME,
  90.         PR_NORMALIZED_SUBJECT,
  91.         PR_SENDER_ENTRYID,
  92.         PR_SENDER_NAME,
  93.         PR_SENDER_ADDRTYPE,
  94.         PR_SENDER_EMAIL_ADDRESS,
  95.         PR_SENDER_SEARCH_KEY,
  96.         PR_CONVERSATION_INDEX,
  97.         PR_CONVERSATION_TOPIC,
  98.         PR_CONVERSATION_KEY,
  99.         PR_PRIORITY,
  100.         PR_REPORT_TAG,
  101.         PR_ORIGINAL_AUTHOR_ENTRYID,
  102.         PR_ORIGINAL_AUTHOR_NAME,
  103.         PR_ORIGINAL_AUTHOR_SEARCH_KEY,
  104.         PR_ORIGINAL_SUBMIT_TIME,
  105.         PR_SENT_REPRESENTING_NAME,
  106.         PR_SENT_REPRESENTING_ADDRTYPE,
  107.         PR_SENT_REPRESENTING_EMAIL_ADDRESS,
  108.         PR_SENT_REPRESENTING_SEARCH_KEY,
  109.         PR_STORE_SUPPORT_MASK,
  110.         PR_CLIENT_SUBMIT_TIME,
  111.         PR_DISPLAY_TO,                  
  112.         PR_DISPLAY_CC,
  113.         PR_SUBJECT,
  114.         PR_IMPORTANCE,
  115.         PR_SENSITIVITY,
  116.     }
  117. };
  118. enum { ipDispNm, ipAdrTyp, ipEmail, ipSKey, cpUserMax };
  119. const static SizedSPropTagArray (cpUserMax, sptUser) =
  120. {
  121.     cpUserMax,
  122.     {
  123.         PR_DISPLAY_NAME,
  124.         PR_ADDRTYPE,
  125.         PR_EMAIL_ADDRESS,
  126.         PR_SEARCH_KEY,
  127.     }
  128. };
  129. enum { ropOof, ropForward, ropReply };
  130. static const LPTSTR rgszSubjPrfx[] =
  131. {
  132.     "OOF: ",
  133.     "FW: ",
  134.     "RE: "
  135. };
  136. enum
  137. {
  138.     ivHdrSndrName,
  139.     ivHdrSndrType,
  140.     ivHdrSndrEmail,
  141.     ivHdrSentRepName,
  142.     ivHdrSentRepType,
  143.     ivHdrSentRepEmail,
  144.     ivHdrSubmitTime,
  145.     ivDisplayTo,
  146.     ivDisplayCc,
  147.     ivSubject,
  148.     ivImportance,
  149.     ivSensitivity,
  150.     cvHeader
  151. };
  152. static const SizedSPropTagArray (cvHeader, sptHeader) = 
  153. {
  154.     cvHeader,
  155.     {
  156.         PR_SENDER_NAME,
  157.         PR_SENDER_ADDRTYPE,
  158.         PR_SENDER_EMAIL_ADDRESS,
  159.         PR_SENT_REPRESENTING_NAME,
  160.         PR_SENT_REPRESENTING_ADDRTYPE,
  161.         PR_SENT_REPRESENTING_EMAIL_ADDRESS,
  162.         PR_CLIENT_SUBMIT_TIME,
  163.         PR_DISPLAY_TO,                  
  164.         PR_DISPLAY_CC,
  165.         PR_SUBJECT,
  166.         PR_IMPORTANCE,
  167.         PR_SENSITIVITY
  168.     }
  169. };
  170. static const SizedSPropTagArray (1, sptForward) =
  171. {
  172.     1,
  173.     {
  174.         PR_MESSAGE_ATTACHMENTS,
  175.     }
  176. };
  177. enum { ipAttPos, ipAttNum, ipAttMeth, ipAttName, cpTaggingMax };
  178. static const SizedSPropTagArray (cpTaggingMax, sptTagging) =
  179. {
  180.     cpTaggingMax,
  181.     {
  182.         PR_RENDERING_POSITION,
  183.         PR_ATTACH_NUM,
  184.         PR_ATTACH_METHOD,
  185.         PR_ATTACH_FILENAME
  186.     }
  187. };
  188. static const LPTSTR rgszHeaderField[] =
  189. {
  190.     "Sent:",
  191.     "To:",
  192.     "Cc:",
  193.     "Subject:",
  194.     "Importance:",
  195.     "Sensitivity:"
  196. };
  197. static const LPTSTR rgszImportance[] = 
  198. {
  199.     "Low",
  200.     "Normal",
  201.     "High"
  202. };
  203. static const LPTSTR rgszSensitivity[] =
  204. {
  205.     "Normal",
  206.     "Personal",
  207.     "Private",
  208.     "Confidential"
  209. };
  210. static const TCHAR * rgszDay[] =
  211. {
  212.     TEXT ("Sunday"),
  213.     TEXT ("Monday"),
  214.     TEXT ("Tuesday"),
  215.     TEXT ("Wednesday"),
  216.     TEXT ("Thursday"),
  217.     TEXT ("Friday"),
  218.     TEXT ("Saturday")
  219. };
  220. extern TCHAR FAR * rgtstrMonthFull[];
  221. LONG
  222. CchInsertSz (HWND hwnd, LPTSTR lpsz)
  223. {
  224.     SendMessage (hwnd, EM_REPLACESEL, 0, (LPARAM) lpsz);
  225.     return lstrlen (lpsz);
  226. }
  227. VOID
  228. FileTimeToDateTimeSz (FILETIME FAR * lpft, LPTSTR rgch, UINT cb)
  229. {
  230.     SYSTEMTIME st;
  231.     if (FileTimeToSystemTime (lpft, &st))
  232.     {
  233.         wsprintf (rgch,
  234.             "%s, %s %02d, %4d %d:%02d %s",
  235.             rgszDay[st.wDayOfWeek],
  236.             rgtstrMonthFull[st.wMonth - 1],
  237.             st.wDay,
  238.             st.wYear,
  239.             st.wHour & 12,
  240.             st.wMinute,
  241.             (st.wHour > 11) ? "PM" : "AM");
  242.     }
  243.     else
  244.         lstrcpy (rgch, "Unavailable");
  245. }
  246. HRESULT
  247. HrCopyOriginalBody (LPSMH lpsmh,
  248.     HWND hwnd,
  249.     LPMESSAGE lpmsg,
  250.     LONG FAR * lpcch)
  251. {
  252.     HRESULT hr;
  253.     CHARRANGE chrg = {0};
  254.     EDITSTREAM es = {0};
  255.     LPSTREAM lpstm = NULL;
  256.     LPSTREAM lpstmT = NULL;
  257.     *lpcch = 0;
  258.     hr = lpmsg->lpVtbl->OpenProperty (lpmsg,
  259.                                 PR_RTF_COMPRESSED,
  260.                                 &IID_IStream,
  261.                                 0, 0,
  262.                                 (LPUNKNOWN FAR *)&lpstmT);
  263.     if (!HR_FAILED (hr))
  264.     {
  265.         hr = WrapCompressedRTFStream (lpstmT, 0, &lpstm);
  266.         if (!HR_FAILED (hr))
  267.         {
  268.             es.pfnCallback = (EDITSTREAMCALLBACK)lpstm->lpVtbl->Read;
  269.             es.dwCookie = (DWORD)lpstm;
  270.             
  271.             /*  Stuff a newline into the edit
  272.              *  control such that whatever preceeds
  273.              *  the body will be separated from the
  274.              *  the text of the original message
  275.              */
  276.             SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
  277.             CchInsertSz (hwnd, "rn");
  278.             /*  Do the body now */
  279.             
  280.             SendMessage (hwnd,
  281.                 EM_STREAMIN,
  282.                 SF_RTF | SFF_SELECTION | SFF_PLAINRTF,
  283.                 (LPARAM)&es);
  284.             /*  Calculate the size of the body in characters */
  285.             
  286.             Edit_SetSel (hwnd, chrg.cpMin, INT_MAX);
  287.             SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
  288.             *lpcch = chrg.cpMax - chrg.cpMin;
  289.             /*  Reset the selection to the begining
  290.              *  of the edit control such that all
  291.              *  additions occur before the original
  292.              *  body
  293.              */
  294.             Edit_SetSel (hwnd, 0, 0);
  295.         }
  296.     }
  297.     UlRelease (lpstm);
  298.     UlRelease (lpstmT);
  299.     DebugTraceResult (HrCopyOriginalBody(), hr);
  300.     return hr;
  301. }
  302. HRESULT
  303. HrInsertOriginalHeader (LPSMH lpsmh,
  304.     HWND hwnd,
  305.     LPSPropValue lpval,
  306.     CHARFORMAT FAR * lpcf,
  307.     LONG FAR * lpcch)
  308. {
  309.     CHAR rgch[MAX_PATH];
  310.     CHARRANGE chrg = {0};
  311.     CHARRANGE chrgHdr = {0};
  312.     CHARRANGE rgchrg[ccrgMax] = {0};
  313.     LONG cp;
  314.     LPTSTR lpsz;
  315.     UINT icrg = 0;
  316.     UINT ip;
  317.     
  318.     /*  Stuff a newline into the edit
  319.      *  control such that whatever preceeds
  320.      *  the body will be separated from the
  321.      *  the text of the original message
  322.      */
  323.     SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrgHdr);
  324.     CchInsertSz (hwnd, "rn");
  325.     /*  Mark the begining of the header */
  326.     
  327.     SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
  328.     
  329.     cp = chrg.cpMin;
  330.     cp += CchInsertSz (hwnd, "rn----------rn");
  331.     /*  Insert the "From: xxxx" line */
  332.     
  333.     if ((lpval[ipSndrNm].ulPropTag == PR_SENDER_NAME) &&
  334.         (lpval[ipSndrNm].Value.LPSZ != NULL) &&
  335.         (*lpval[ipSndrNm].Value.LPSZ != 0))
  336.     {
  337.         rgchrg[icrg].cpMin = cp;
  338.         cp += CchInsertSz (hwnd, "From:");
  339.         rgchrg[icrg].cpMax = cp;
  340.         icrg++;
  341.         
  342.         cp += CchInsertSz (hwnd, "t");
  343.         cp += CchInsertSz (hwnd, lpval[ipSndrNm].Value.LPSZ);
  344.         /*  If we were representing someone else... */
  345.         if ((lpval[ipSentRepName].ulPropTag == PR_SENT_REPRESENTING_NAME) &&
  346.             (lpval[ipSentRepName].Value.LPSZ != NULL) &&
  347.             (*lpval[ipSentRepName].Value.LPSZ != 0))
  348.         {
  349.             if ((lpval[ipSndrSKey].ulPropTag != PR_SENDER_SEARCH_KEY) ||
  350.                 (lpval[ipSentRepSKey].ulPropTag != PR_SENT_REPRESENTING_SEARCH_KEY) ||
  351.                 (lpval[ipSndrSKey].Value.bin.cb != lpval[ipSentRepSKey].Value.bin.cb) ||
  352.                 memcmp (lpval[ipSndrSKey].Value.bin.lpb,
  353.                     lpval[ipSentRepSKey].Value.bin.lpb,
  354.                     lpval[ipSndrSKey].Value.bin.cb))
  355.             {
  356.                 cp += CchInsertSz (hwnd, " on behalf of ");
  357.                 cp += CchInsertSz (hwnd, lpval[ipSentRepName].Value.LPSZ);
  358.             }
  359.             cp += CchInsertSz (hwnd, TEXT("rn"));
  360.         }
  361.         /*  Insert the remaining lines */
  362.         
  363.         for (ip = ipSubmitTime; ip < cpResponseMax; ip++)
  364.         {
  365.             lpsz = NULL;
  366.             switch (PROP_TYPE (lpval[ip].ulPropTag))
  367.             {
  368.               case PT_TSTRING:
  369.                 /*  Strings are strings */
  370.                   
  371.                 lpsz = lpval[ip].Value.LPSZ;
  372.                 break;
  373.               case PT_SYSTIME:
  374.                 /*  Convertt the date to a string */
  375.                   
  376.                 FileTimeToDateTimeSz (&lpval[ip].Value.ft, rgch, sizeof(rgch));
  377.                 lpsz = rgch;
  378.                 break;
  379.               case PT_LONG:
  380.                 /*  Importance and Sensitivity use a look-up to
  381.                  *  find the proper string to insert.  If the value
  382.                  *  equates to the "normal" level of a given message,
  383.                  *  then no value is displayed.
  384.                  */
  385.                 if ((lpval[ip].ulPropTag == PR_IMPORTANCE) &&
  386.                     (lpval[ip].Value.l != IMPORTANCE_NORMAL))
  387.                     lpsz = rgszImportance[lpval[ip].Value.l];
  388.                 else if ((lpval[ip].ulPropTag == PR_SENSITIVITY) &&
  389.                     (lpval[ip].Value.l != SENSITIVITY_NONE))
  390.                     lpsz = rgszSensitivity[lpval[ip].Value.l];
  391.                 break;
  392.             }
  393.             if (lpsz && *lpsz)
  394.             {
  395.                 rgchrg[icrg].cpMin = cp;
  396.                 cp += CchInsertSz (hwnd, rgszHeaderField[ip - ipSubmitTime]);
  397.                 rgchrg[icrg].cpMax = cp;
  398.                 icrg++;
  399.                 
  400.                 cp += CchInsertSz (hwnd, "t");
  401.                 cp += CchInsertSz (hwnd, lpsz);
  402.                 cp += CchInsertSz (hwnd, TEXT("rn"));
  403.             }
  404.         }
  405.         cp += CchInsertSz (hwnd, TEXT("rn"));
  406.         /*  Ensure that the text is formated in the
  407.          *  charformat passed in.  Such that we can
  408.          *  manipulate the rest with no worries
  409.          */
  410.         chrg.cpMin += 2;
  411.         chrg.cpMax = cp;
  412.         SendMessage (hwnd, EM_EXSETSEL, 0, (LPARAM) &chrg);
  413.         SendMessage (hwnd, EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)lpcf);
  414.         /*  Run through all the field headers and make them bold */
  415.         
  416.         lpcf->cbSize = sizeof(CHARFORMAT);
  417.         lpcf->dwMask = CFM_BOLD;
  418.         lpcf->dwEffects = CFE_BOLD;
  419.         while (icrg)
  420.         {
  421.             chrg = rgchrg[--icrg];
  422.             SendMessage (hwnd, EM_EXSETSEL, 0, (LPARAM)&chrg);
  423.             SendMessage (hwnd, EM_SETCHARFORMAT, SCF_SELECTION|SCF_WORD, (LPARAM)lpcf);
  424.         }
  425.         /*  Calculate the size of the header */
  426.         
  427.         *lpcch = cp - chrgHdr.cpMin;
  428.         Edit_SetSel (hwnd, 0, 0);
  429.     }
  430.     
  431.     DebugTraceResult (HrInsertOriginalHeader(), hrSuccess);
  432.     return hrSuccess;
  433. }
  434. HRESULT
  435. HrInsertAnnotation (LPSMH lpsmh,
  436.     HWND hwnd,
  437.     UINT rop,
  438.     LPRULE lprl,
  439.     LONG FAR * lpcch)
  440. {
  441.     CHARRANGE chrg = {0};
  442.     EDITSTREAM es = {0};
  443.     LPBYTE lpb;
  444.     RTFS rtfs = {0};
  445.     ULONG cb;
  446.     
  447.     /*  Setup which annotation to use */
  448.     
  449.     if (rop != ropOof)
  450.     {
  451.         cb = lprl->cbRTF;
  452.         lpb = lprl->lpbRTF;
  453.     }
  454.     else
  455.     {
  456.         cb = lpsmh->oof.cbRTF;
  457.         lpb = lpsmh->oof.lpbRTF;
  458.     }
  459.     /*  Stream the bad boy in */
  460.     
  461.     rtfs.cb = 0;
  462.     rtfs.cbMax = cb;
  463.     rtfs.lpb = lpb;
  464.     es.pfnCallback = ReadRTFFromBuffer;
  465.     es.dwCookie = (DWORD)&rtfs;
  466.     SendMessage (hwnd,
  467.         EM_STREAMIN,
  468.         SF_RTF | SFF_SELECTION | SFF_PLAINRTF,
  469.         (LPARAM)&es);
  470.     /*  Calculate the size of what we just streamed in */
  471.     
  472.     SendMessage (hwnd, EM_EXGETSEL, 0, (LPARAM) &chrg);
  473.     *lpcch = chrg.cpMax;
  474.     DebugTraceResult (HrInsertAnnotation(), hrSuccess);
  475.     return hrSuccess;
  476. }
  477. HRESULT
  478. HrTagAttachments (LPSMH lpsmh,
  479.     HWND hwnd,
  480.     LONG dch,
  481.     LPMESSAGE lpmsg)
  482. {
  483.     HRESULT hr;
  484.     CHAR rgch[MAX_PATH] = {0};
  485.     LONG ichPos;
  486.     LPATTACH lpatt = NULL;
  487.     LPMAPITABLE lptbl = NULL;
  488.     LPMESSAGE lpmsgT = NULL;
  489.     LPSPropValue lpval = NULL;
  490.     LPSRowSet lprws = NULL;
  491.     UINT irw;
  492.     hr = lpmsg->lpVtbl->GetAttachmentTable (lpmsg, 0, &lptbl);
  493.     if (HR_FAILED (hr))
  494.         goto ret;
  495.     hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptTagging, 0);
  496.     if (HR_FAILED (hr))
  497.         goto ret;
  498.     while (TRUE)
  499.     {
  500.         hr = lptbl->lpVtbl->QueryRows (lptbl, 64, 0, &lprws);
  501.         if (HR_FAILED (hr))
  502.             goto ret;
  503.         if (lprws->cRows == 0)
  504.             break;
  505.         for (irw = 0; irw < lprws->cRows; irw++)
  506.         {
  507.             switch (lprws->aRow[irw].lpProps[ipAttMeth].Value.l)
  508.             {
  509.               case ATTACH_OLE:
  510.                 lstrcpy (rgch, "<<OLE Object: unknown>>");
  511.                 break;
  512.               case ATTACH_EMBEDDED_MSG:
  513.                 hr = lpmsg->lpVtbl->OpenAttach (lpmsg,
  514.                                         lprws->aRow[irw].lpProps[ipAttNum].Value.l,
  515.                                         NULL,
  516.                                         0,
  517.                                         &lpatt);
  518.                 if (!HR_FAILED (hr))
  519.                 {
  520.                     hr = lpatt->lpVtbl->OpenProperty (lpatt,
  521.                                         PR_ATTACH_DATA_OBJ,
  522.                                         &IID_IMessage,
  523.                                         0, 0,
  524.                                         (LPUNKNOWN FAR *)&lpmsgT);
  525.                     if (!HR_FAILED (hr))
  526.                     {
  527.                         /*  Get the subject of the embedded message
  528.                          *  as the tag identifier
  529.                          */
  530.                         hr = HrGetOneProp ((LPMAPIPROP)lpmsgT, PR_SUBJECT, &lpval);
  531.                     }
  532.                 }
  533.                 wsprintf (rgch, "<<Message: %s>>",
  534.                     HR_FAILED (hr) ? "" : lpval->Value.LPSZ);
  535.                 
  536.                 (*lpsmh->lpfnFree) (lpval);
  537.                 UlRelease (lpmsgT);
  538.                 UlRelease (lpatt);
  539.                 lpmsgT = NULL;
  540.                 lpval = NULL;
  541.                 lpatt = NULL;
  542.                 break;
  543.               default:
  544.               case ATTACH_BY_VALUE:
  545.               case ATTACH_BY_REFERENCE:
  546.                 /*  Use the filename for the attachment tag */
  547.                 wsprintf (rgch, "<<File: %s>>",
  548.                     (lprws->aRow[irw].lpProps[ipAttName].ulPropTag == PR_ATTACH_FILENAME)
  549.                           ? lprws->aRow[irw].lpProps[ipAttName].Value.LPSZ
  550.                           : "");
  551.                 break;
  552.             }
  553.             /*  Setup the selection such that we replace the attachment
  554.              *  place holder wiht the attachment tag
  555.              */
  556.             ichPos = lprws->aRow[irw].lpProps[ipAttPos].Value.l;
  557.             if (ichPos == -1)
  558.                 Edit_SetSel (hwnd, INT_MAX, INT_MAX);
  559.             else
  560.                 Edit_SetSel (hwnd, ichPos + dch - 1, ichPos + dch);
  561.             /*  Insert the tag and adjust the offset
  562.              *  of the next attachment tag posiiton.
  563.              */
  564.             dch += CchInsertSz (hwnd, rgch) - 1;
  565.             /*  Free the row data */
  566.             
  567.             (*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps);
  568.         }
  569.         
  570.         hr = hrSuccess;
  571.         (*lpsmh->lpfnFree) (lprws);
  572.         lprws = NULL;
  573.     }
  574.     (*lpsmh->lpfnFree) (lprws);
  575.     lprws = NULL;
  576. ret:
  577.     
  578.     UlRelease (lptbl);
  579.     
  580.     DebugTraceResult (HrTagAttachments(), hr);
  581.     return hr;
  582. }
  583. HRESULT
  584. HrOffsetAttachments (LPSMH lpsmh,
  585.     LONG dch,
  586.     LPMESSAGE lpmsg)
  587. {
  588.     HRESULT hr;
  589.     LPATTACH lpatt = NULL;
  590.     LPMAPITABLE lptbl = NULL;
  591.     LPSRowSet lprws = NULL;
  592.     UINT irw;
  593.     hr = lpmsg->lpVtbl->GetAttachmentTable (lpmsg, 0, &lptbl);
  594.     if (HR_FAILED (hr))
  595.         goto ret;
  596.     hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptTagging, 0);
  597.     if (HR_FAILED (hr))
  598.         goto ret;
  599.     while (TRUE)
  600.     {
  601.         hr = lptbl->lpVtbl->QueryRows (lptbl, 64, 0, &lprws);
  602.         if (HR_FAILED (hr))
  603.             goto ret;
  604.         if (lprws->cRows == 0)
  605.             break;
  606.         for (irw = 0; irw < lprws->cRows; irw++)
  607.         {
  608.             /*  If the rendering position is not -1, we
  609.              *  want to adjust the positioning by the value
  610.              *  passed in dch
  611.              */
  612.             if (lprws->aRow[irw].lpProps[ipAttPos].Value.l != -1)
  613.             {
  614.                 /*  Adjust the positioning, and set in into the attachment */
  615.                 
  616.                 lprws->aRow[irw].lpProps[ipAttPos].Value.l += dch;
  617.                 hr = lpmsg->lpVtbl->OpenAttach (lpmsg,
  618.                                             lprws->aRow[irw].lpProps[ipAttNum].Value.l,
  619.                                             NULL,
  620.                                             MAPI_MODIFY,
  621.                                             &lpatt);
  622.                 if (!HR_FAILED (hr))
  623.                 {
  624.                     hr = lpatt->lpVtbl->SetProps (lpatt,
  625.                                             1,
  626.                                             &lprws->aRow[irw].lpProps[ipAttPos],
  627.                                             NULL);
  628.                     if (!HR_FAILED (hr))
  629.                     {
  630.                         /*  Save out the new positioning */
  631.                         
  632.                         hr = lpatt->lpVtbl->SaveChanges (lpatt, 0);
  633.                         
  634.                     }
  635.                     UlRelease (lpatt);
  636.                     lpatt = NULL;
  637.                 }
  638.                 hr = hrSuccess;
  639.             }
  640.             /*  Free the row data */
  641.             
  642.             (*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps);
  643.         }
  644.         
  645.         (*lpsmh->lpfnFree) (lprws);
  646.         lprws = NULL;
  647.     }
  648.     (*lpsmh->lpfnFree) (lprws);
  649.     lprws = NULL;
  650. ret:
  651.     
  652.     UlRelease (lptbl);
  653.     
  654.     DebugTraceResult (HrOffsetAttachments(), hr);
  655.     return hr;
  656. }
  657. HRESULT
  658. HrInsertBody (LPSMH lpsmh,
  659.     HWND hwnd,
  660.     LPSPropValue lpval,
  661.     LPMESSAGE lpmsg)
  662. {
  663.     HRESULT hr = hrSuccess;
  664.     BOOL fUpdated;
  665.     EDITSTREAM es = {0};
  666.     LPSTREAM lpstm = NULL;
  667.     LPSTREAM lpstmRTF = NULL;
  668.     SPropValue val;
  669.     ULONG ulFlags = 0;
  670.     /*  Do PR_BODY iff the store is not RTF_AWARE */
  671.     
  672.     if ((lpval[ipStoreSupport].ulPropTag != PR_STORE_SUPPORT_MASK) ||
  673.         !(lpval[ipStoreSupport].Value.l & STORE_RTF_OK))
  674.     {
  675.         hr = lpmsg->lpVtbl->OpenProperty (lpmsg,
  676.                                 PR_BODY,
  677.                                 &IID_IStream,
  678.                                 0,
  679.                                 MAPI_CREATE | MAPI_MODIFY,
  680.                                 (LPUNKNOWN FAR *)&lpstm);
  681.         if (HR_FAILED (hr))
  682.             goto ret;
  683.         es.dwCookie = (DWORD)lpstm;
  684.         es.pfnCallback = (EDITSTREAMCALLBACK)lpstm->lpVtbl->Write;
  685.         SendMessage (hwnd, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
  686.         UlRelease (lpstm);
  687.         lpstm = NULL;
  688.         if (!es.dwError)
  689.             ulFlags |= RTF_SYNC_BODY_CHANGED;
  690.     }
  691.     /*  Add in PR_COMPRESSED_RTF */
  692.     
  693.     hr = lpmsg->lpVtbl->OpenProperty (lpmsg,
  694.                                 PR_RTF_COMPRESSED,
  695.                                 &IID_IStream,
  696.                                 0,
  697.                                 MAPI_CREATE | MAPI_MODIFY,
  698.                                 (LPUNKNOWN FAR *)&lpstm);
  699.     if (HR_FAILED (hr))
  700.         goto ret;
  701.     
  702.     hr = WrapCompressedRTFStream (lpstm,
  703.                 MAPI_MODIFY | (lpval[ipStoreSupport].Value.l & STORE_UNCOMPRESSED_RTF),
  704.                 &lpstmRTF);
  705.     if (HR_FAILED (hr))
  706.         goto ret;
  707.     es.dwCookie = (DWORD)lpstmRTF;
  708.     es.pfnCallback = (EDITSTREAMCALLBACK)lpstmRTF->lpVtbl->Write;
  709.     SendMessage (hwnd, EM_STREAMOUT, SF_RTF | SFF_PLAINRTF, (LPARAM)&es);
  710.     hr = lpstmRTF->lpVtbl->Commit (lpstmRTF, 0);
  711.     if (HR_FAILED (hr))
  712.         goto ret;
  713.     
  714.     if (!es.dwError)
  715.         ulFlags |= RTF_SYNC_RTF_CHANGED;
  716.     /*  Sync the RTF and the body iff the store is not RTF aware */
  717.     if ((lpval[ipStoreSupport].ulPropTag != PR_STORE_SUPPORT_MASK) ||
  718.         !(lpval[ipStoreSupport].Value.l & STORE_RTF_OK))
  719.     {
  720.         /*  We are not aware, so we better do a full sync */
  721.         
  722.         hr = RTFSync (lpmsg, ulFlags, &fUpdated);
  723.     }
  724.     else
  725.     {
  726.         /*  If we are aware, then we want to tell the
  727.          *  store that we are completely in sync.  Otherwise,
  728.          *  we could loose our attachment positioning on
  729.          *  RTF aware stores.  And that would be bad.
  730.          */
  731.         val.ulPropTag = PR_RTF_IN_SYNC;
  732.         val.Value.b = TRUE;
  733.         lpmsg->lpVtbl->SetProps (lpmsg, 1, &val, NULL);
  734.     }
  735. ret:
  736.     UlRelease (lpstm);
  737.     UlRelease (lpstmRTF);
  738.     DebugTraceResult (HrInsertBody(), hr);
  739.     return hr;
  740. }
  741. HRESULT
  742. HrBuildRecipient (LPSMH lpsmh,
  743.     LPSPropValue lpval,
  744.     LPMESSAGE lpmsg)
  745. {
  746.     SCODE sc;
  747.     HRESULT hr;
  748.     LPADRLIST lpadr = NULL;
  749.     LPMAPIPROP lpusr = NULL;
  750.     LPSPropValue lpvalUsr = NULL;
  751.     LPSPropValue rgval = NULL;
  752.     UINT cval = 0;
  753.     ULONG ulT;
  754.     /*  Open the recipient up */
  755.     
  756.     hr = lpsmh->lpsess->lpVtbl->OpenEntry (lpsmh->lpsess,
  757.                             lpval->Value.bin.cb,
  758.                             (LPENTRYID)lpval->Value.bin.lpb,
  759.                             NULL, 0,
  760.                             &ulT,
  761.                             (LPUNKNOWN FAR *)&lpusr);
  762.     if (HR_FAILED (hr))
  763.         goto ret;
  764.     /*  Get the properties we need */
  765.     
  766.     hr = lpusr->lpVtbl->GetProps (lpusr,
  767.                             (LPSPropTagArray)&sptUser,
  768.                             0,
  769.                             &ulT,
  770.                             &lpvalUsr);
  771.     if (HR_FAILED (hr))
  772.         goto ret;
  773.     /*  Allocate the adrlist */
  774.     
  775.     if (FAILED (sc = (*lpsmh->lpfnAlloc) (CbNewADRLIST (1), &lpadr)) ||
  776.         FAILED (sc = (*lpsmh->lpfnAlloc) (cpTargetRecipMax * sizeof(SPropValue), &rgval)))
  777.     {
  778.         hr = ResultFromScode (sc);
  779.         goto ret;
  780.     }
  781.     /*  Stuff the properties and add the recipient */
  782.     
  783.     rgval[cval].ulPropTag = PR_ENTRYID;
  784.     rgval[cval].Value = lpval->Value;
  785.     cval++;
  786.     rgval[cval].ulPropTag = PR_DISPLAY_NAME;
  787.     rgval[cval].Value.LPSZ = lpvalUsr[ipDispNm].Value.LPSZ;
  788.     cval++;
  789.     
  790.     if (lpvalUsr[ipAdrTyp].ulPropTag == PR_ADDRTYPE)
  791.     {
  792.         rgval[cval].ulPropTag = PR_ADDRTYPE;
  793.         rgval[cval].Value.LPSZ = lpvalUsr[ipAdrTyp].Value.LPSZ;
  794.         cval++;
  795.     }
  796.     
  797.     if (lpvalUsr[ipEmail].ulPropTag == PR_EMAIL_ADDRESS)
  798.     {
  799.         rgval[cval].ulPropTag = PR_EMAIL_ADDRESS;
  800.         rgval[cval].Value.LPSZ = lpvalUsr[ipEmail].Value.LPSZ;
  801.         cval++;
  802.     }
  803.     if (lpvalUsr[ipSKey].ulPropTag == PR_SEARCH_KEY)
  804.     {
  805.         rgval[cval].ulPropTag = PR_SEARCH_KEY;
  806.         rgval[cval].Value = lpvalUsr[ipSKey].Value;
  807.         cval++;
  808.     }
  809.     
  810.     rgval[cval].ulPropTag = PR_RECIPIENT_TYPE;
  811.     rgval[cval].Value.l = MAPI_TO;
  812.     cval++;
  813.     
  814.     lpadr->cEntries = 1;
  815.     lpadr->aEntries[0].cValues = cval;
  816.     lpadr->aEntries[0].rgPropVals = rgval;
  817.     hr = lpmsg->lpVtbl->ModifyRecipients (lpmsg, MODRECIP_ADD, lpadr);
  818.     if (HR_FAILED (hr))
  819.         goto ret;
  820. ret:
  821.     
  822.     if (lpadr)
  823.     {
  824.         (*lpsmh->lpfnFree) (lpadr->aEntries[0].rgPropVals);
  825.         (*lpsmh->lpfnFree) (lpadr);
  826.     }
  827.     (*lpsmh->lpfnFree) (lpvalUsr);
  828.     UlRelease (lpusr);
  829.     DebugTraceResult (HrBuildRecipient(), hr);
  830.     return hr;
  831. }
  832. HRESULT
  833. HrCreateResponse (LPSMH lpsmh,
  834.     LPRULE lprl,
  835.     LPMAPIFOLDER lpfldr,
  836.     LPMESSAGE lpmsgOrig,
  837.     LPSPropValue lpval,
  838.     LPMESSAGE FAR * lppmsg)
  839. {
  840.     SCODE sc;
  841.     HRESULT hr;
  842.     CHARFORMAT cf = {0};
  843.     HINSTANCE hlib = NULL;
  844.     HWND hwnd = NULL;
  845.     LONG cch = 0;
  846.     LONG cchHdr = 0;
  847.     LPBYTE lpbConvIndex = NULL;
  848.     LPMESSAGE lpmsg = NULL;
  849.     LPREOC lpreoc = NULL;
  850.     LPSPropValue rgval = NULL;
  851.     PARAFORMAT pf = {0};
  852.     TCHAR rgchClass[MAX_PATH];
  853.     TCHAR rgchSubj[MAX_PATH];
  854.     UINT rop;
  855.     ULONG cval = 0;
  856.     *lppmsg = NULL;
  857.     
  858.     /*  Calculate the response operation based on
  859.      *  the supplied rule.  If no rule is supplied
  860.      *  then the response is an out-of-office msg.
  861.      */
  862.     if (lprl)
  863.     {
  864.         Assert (lprl->ulFlags & RULE_AUTO_RESPONSE);
  865.         if (lprl->ulFlags & RULE_AUTO_FORWARD)
  866.         {
  867.             Assert (!(lprl->ulFlags & RULE_AUTO_REPLY));
  868.             rop = ropForward;
  869.         }
  870.         else
  871.         {
  872.             Assert (!(lprl->ulFlags & RULE_AUTO_FORWARD));
  873.             rop = ropReply;
  874.         }
  875.     }
  876.     else
  877.         rop = ropOof;
  878.     /*  Create the response message */
  879.     
  880.     hr = lpfldr->lpVtbl->CreateMessage (lpfldr, NULL, 0, &lpmsg);
  881.     if (HR_FAILED (hr))
  882.         goto ret;
  883.     /*  Create and initialized the RTF edit control */
  884.     hlib = LoadLibrary (RICHEDIT_LIB);
  885.     hwnd = CreateWindow (RICHEDIT_CLASS,
  886.                         "",
  887.                         WS_BORDER|ES_MULTILINE,
  888.                         CW_USEDEFAULT, CW_USEDEFAULT, INT_MAX, INT_MAX,
  889.                         NULL,
  890.                         NULL,
  891.                         lpsmh->hinst,
  892.                         NULL);
  893.     if (!hwnd)
  894.     {
  895.         hr = ResultFromScode (MAPI_E_CALL_FAILED);
  896.         goto ret;
  897.     }
  898.     /*  Create the richedit OLE callback.  If this
  899.      *  fails, it is non-fatal.  It just means that
  900.      *  any OLE objects in the annotation will not
  901.      *  be preserved in then response message.
  902.      */
  903.     if (!FAILED (ScNewRicheditCallback (NULL,
  904.                         lpsmh->lpfnAlloc,
  905.                         lpsmh->lpfnAllocMore,
  906.                         lpsmh->lpfnFree,
  907.                         &lpreoc)))
  908.     {
  909.         /*  We have an OLE callback that we need
  910.          *  to hand off to the richedit control.
  911.          *  Although, the richedit control should
  912.          *  be AddRef()ing the object, we will hold
  913.          *  our reference until we are through with
  914.          *  the edit control.
  915.          */
  916.         SendMessage (hwnd, EM_SETOLECALLBACK, 0, (LPARAM)lpreoc);
  917.     }
  918.     /*  Setup the default character format */
  919.     
  920.     cf.cbSize = sizeof(CHARFORMAT);
  921.     cf.dwMask = CFM_FACE | CFM_SIZE | CFM_COLOR | CFM_BOLD |
  922.                 CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT |
  923.                 CFM_OFFSET | CFM_CHARSET;
  924.     cf.dwEffects = CFE_AUTOCOLOR;
  925.     cf.yHeight = 160;
  926.     lstrcpy (cf.szFaceName, "MS Sans Serif");
  927.     cf.bCharSet = DEFAULT_CHARSET;
  928.     SendMessage (hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf);
  929.     /*  Copy over the original body if it makes sense to do so */
  930.     if ((rop != ropOof) && (lprl->ulFlags & RULE_AUTO_APPEND_ORIG))
  931.     {
  932.         hr = HrCopyOriginalBody (lpsmh, hwnd, lpmsgOrig, &cch);
  933.         if (HR_FAILED (hr) && (rop == ropForward))
  934.             goto ret;
  935.         
  936.         hr = hrSuccess;
  937.     }
  938.     /*  Create and insert the original message header */
  939.     hr = HrInsertOriginalHeader (lpsmh, hwnd, lpval, &cf, &cchHdr);
  940.     if (HR_FAILED (hr))
  941.         goto ret;
  942.     /*  If this response is not a forward, we will want to
  943.      *  indent the header (and maybe the body) such that they
  944.      *  are offset from the annotation.
  945.      */
  946.     if (rop != ropForward)
  947.     {
  948.         /*  Skip indenting the blankline */
  949.         
  950.         Edit_SetSel (hwnd, 2, INT_MAX);
  951.         pf.cbSize = sizeof(PARAFORMAT);
  952.         pf.dwMask = PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_ALIGNMENT |
  953.                     PFM_OFFSET | PFM_TABSTOPS | PFM_NUMBERING;
  954.         pf.dxOffset = 1440;
  955.         pf.cTabCount = 1;
  956.         pf.rgxTabs[0] = 1440;
  957.         pf.wAlignment = PFA_LEFT;
  958.         pf.dxStartIndent = 1440 / 4;
  959.         SendMessage (hwnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
  960.         Edit_SetSel (hwnd, 0, 0);
  961.     }
  962.     /*  Copy over the annotation */
  963.     hr = HrInsertAnnotation (lpsmh, hwnd, rop, lprl, &cch);
  964.     if (HR_FAILED (hr))
  965.         goto ret;
  966.     /*  Tag the attachments for replies */
  967.     if (rop == ropReply)
  968.     {
  969.         hr = HrTagAttachments (lpsmh, hwnd, cchHdr + cch + 3, lpmsgOrig);
  970.         if (HR_FAILED (hr))
  971.             goto ret;
  972.     }
  973.     /*  Allocate space for the new message properties */
  974.     sc = (*lpsmh->lpfnAlloc) (cpTargetResponseMax * sizeof(SPropValue), &rgval);
  975.     if (FAILED (sc))
  976.     {
  977.         hr = ResultFromScode (sc);
  978.         goto ret;
  979.     }
  980.     /*  Build the response subject */
  981.     lstrcpy (rgchSubj, rgszSubjPrfx[rop]);
  982.     lstrcat (rgchSubj, lpval[ipNSubj].Value.LPSZ);
  983.     rgval[cval].ulPropTag = PR_SUBJECT;
  984.     rgval[cval].Value.LPSZ = rgchSubj;
  985.     cval++;
  986.     rgval[cval].ulPropTag = PR_SUBJECT_PREFIX;
  987.     rgval[cval].Value.LPSZ = rgszSubjPrfx[rop];
  988.     cval++;
  989.         
  990.     /*  Build the response message class */
  991.     if (rop == ropOof)
  992.         wsprintf (rgchClass, "Report.%s.OOF", lpval[ipMsgClass].Value.LPSZ);
  993.     else if (rop == ropReply)
  994.         lstrcpy (rgchClass, "IPM.Note.AutoReply");
  995.     else
  996.         lstrcpy (rgchClass, lpval[ipMsgClass].Value.LPSZ);
  997.     rgval[cval].ulPropTag = PR_MESSAGE_CLASS;
  998.     rgval[cval].Value.LPSZ = rgchClass;
  999.     cval++;
  1000.     /*  Compose the set of remaining properties */
  1001.     rgval[cval].ulPropTag = PR_DELETE_AFTER_SUBMIT;
  1002.     rgval[cval].Value.b = TRUE;
  1003.     
  1004. #ifdef  DEBUG
  1005.     rgval[cval].Value.b = GetPrivateProfileInt ("SMH", "DeleteResponses", 1, "MAPIDBG.INI");
  1006. #endif
  1007.     cval++;
  1008.     /*  Tell the store that the message has not
  1009.      *  been sent.  Otherwise, since the spooler
  1010.      *  is the creater of the message, the store
  1011.      *  will treat the message as if it has been
  1012.      *  delivered into the store by a transport.
  1013.      */
  1014.     rgval[cval].ulPropTag = PR_MESSAGE_FLAGS;
  1015.     rgval[cval].Value.l = MSGFLAG_UNSENT;
  1016.     cval++;
  1017.     
  1018.     /*  Create appropriate conversation topic and indexes */
  1019.     
  1020.     if (lpval[ipConvKey].ulPropTag == PR_CONVERSATION_KEY)
  1021.         rgval[cval++] = lpval[ipConvKey];
  1022.     else
  1023.     {
  1024.         rgval[cval].ulPropTag = PR_CONVERSATION_TOPIC;
  1025.         rgval[cval].Value.LPSZ = lpval[ipNSubj].Value.LPSZ;
  1026.         cval++;
  1027.     }
  1028.     if (lpval[ipConvTopic].ulPropTag == PR_CONVERSATION_TOPIC)
  1029.         rgval[cval++] = lpval[ipConvTopic];
  1030.     else
  1031.     {
  1032.         rgval[cval].ulPropTag = PR_CONVERSATION_TOPIC;
  1033.         rgval[cval].Value.LPSZ = lpval[ipNSubj].Value.LPSZ;
  1034.         cval++;
  1035.     }
  1036.     if (lpval[ipConvIndex].ulPropTag == PR_CONVERSATION_INDEX)
  1037.     {
  1038.         sc = ScAddConversationIndex (lpval[ipConvIndex].Value.bin.cb,
  1039.                             lpval[ipConvIndex].Value.bin.lpb,
  1040.                             &rgval[cval].Value.bin.cb,
  1041.                             &lpbConvIndex);
  1042.     }
  1043.     else
  1044.     {
  1045.         sc = ScAddConversationIndex (0,
  1046.                             NULL,
  1047.                             &rgval[cval].Value.bin.cb,
  1048.                             &lpbConvIndex);
  1049.     }
  1050.     if (!FAILED (sc))
  1051.     {
  1052.         rgval[cval].ulPropTag = PR_CONVERSATION_INDEX;
  1053.         rgval[cval].Value.bin.lpb = lpbConvIndex;
  1054.         cval++;
  1055.     }
  1056.     /*  Report tag */
  1057.     
  1058.     if (lpval[ipReportTag].ulPropTag == PR_REPORT_TAG)
  1059.         rgval[cval++] = lpval[ipReportTag];
  1060.     /*  Sensitivity */
  1061.     if (lpval[ipSensitivity].ulPropTag == PR_SENSITIVITY)
  1062.         rgval[cval++] = lpval[ipSensitivity];
  1063.     /*  Build response specific properties */
  1064.     
  1065.     if (rop == ropForward)
  1066.     {
  1067.         /*  Importance */
  1068.         if (lpval[ipImportance].ulPropTag == PR_IMPORTANCE)
  1069.             rgval[cval++] = lpval[ipImportance];
  1070.         
  1071.         /*  Priority */
  1072.         if (lpval[ipOrigPriority].ulPropTag == PR_PRIORITY)
  1073.             rgval[cval++] = lpval[ipOrigPriority];
  1074.         
  1075.         /*  Original author */
  1076.         if (lpval[ipOrigAuthorName].ulPropTag == PR_ORIGINAL_AUTHOR_NAME)
  1077.             rgval[cval++] = lpval[ipOrigAuthorName];
  1078.         
  1079.         if (lpval[ipOrigAuthorEid].ulPropTag == PR_ORIGINAL_AUTHOR_ENTRYID)
  1080.             rgval[cval++] = lpval[ipOrigAuthorEid];
  1081.         
  1082.         if (lpval[ipOrigAuthorSKey].ulPropTag == PR_ORIGINAL_AUTHOR_SEARCH_KEY)
  1083.             rgval[cval++] = lpval[ipOrigAuthorSKey];
  1084.         
  1085.         /*  Original submit time */
  1086.         if (lpval[ipOrigSubmitTime].ulPropTag == PR_ORIGINAL_SUBMIT_TIME)
  1087.             rgval[cval++] = lpval[ipOrigSubmitTime];
  1088.     }
  1089.     
  1090.     /*  Set the properties */
  1091.     hr = lpmsg->lpVtbl->SetProps (lpmsg, cval, rgval, NULL);
  1092.     if (HR_FAILED (hr))
  1093.         goto ret;
  1094.     
  1095.     /*  Insert the body into the mesage directly */
  1096.     hr = HrInsertBody (lpsmh, hwnd, lpval, lpmsg);
  1097.     if (HR_FAILED (hr))
  1098.         goto ret;
  1099.     /*  Copy the attachment across in the case of a forward */
  1100.     
  1101.     if (rop == ropForward)
  1102.     {
  1103.         hr = lpmsgOrig->lpVtbl->CopyProps(lpmsgOrig,
  1104.                         (LPSPropTagArray)&sptForward,
  1105.                         0,
  1106.                         NULL,
  1107.                         (LPIID) &IID_IMessage,
  1108.                         lpmsg,
  1109.                         0,
  1110.                         NULL);
  1111.         if (HR_FAILED (hr))
  1112.             goto ret;
  1113.         hr = HrOffsetAttachments (lpsmh, cchHdr + cch + 2, lpmsg);
  1114.         if (HR_FAILED (hr))
  1115.             goto ret;
  1116.     }
  1117.     /*  Do the recipient */
  1118.     hr = HrBuildRecipient (lpsmh,
  1119.                     (rop == ropForward)
  1120.                         ? lprl->lpvalRecip
  1121.                         : &lpval[ipSndrEid],
  1122.                     lpmsg);
  1123.     if (HR_FAILED (hr))
  1124.         goto ret;
  1125. ret:
  1126.     
  1127.     if (hwnd)
  1128.         DestroyWindow (hwnd);
  1129.     
  1130.     if (HR_FAILED (hr))
  1131.     {
  1132.         UlRelease (lpmsg);
  1133.         lpmsg = NULL;
  1134.     }
  1135.     
  1136.     if (hlib)
  1137.         FreeLibrary (hlib);
  1138.     (*lpsmh->lpfnFree) (lpbConvIndex);
  1139.     (*lpsmh->lpfnFree) (rgval);
  1140.     UlRelease (lpreoc);
  1141.     
  1142.     *lppmsg = lpmsg;
  1143.     DebugTraceResult (HrCreateResponse(), hr);
  1144.     return hr;
  1145. }
  1146. BOOL
  1147. FOofRecip (LPOOF lpoof, LPSPropValue lpvalEid)
  1148. {
  1149.     BOOL fOof = TRUE;
  1150.     LPMAPITABLE lptbl = lpoof->lptbl;
  1151.     SPropValue val;
  1152.     SRestriction res;
  1153.     if (lptbl)
  1154.     {
  1155. #ifdef  DEBUG
  1156.         if (!GetPrivateProfileInt ("SMH", "OofAlways", 0, "MAPIDBG.INI"))
  1157. #endif
  1158.         {
  1159.             val.ulPropTag = PR_ENTRYID;
  1160.             val.Value = lpvalEid->Value;
  1161.             res.rt = RES_PROPERTY;
  1162.             res.res.resProperty.relop = RELOP_EQ;
  1163.             res.res.resProperty.ulPropTag = PR_ENTRYID;
  1164.             res.res.resProperty.lpProp = &val;
  1165.             fOof = HR_FAILED (lptbl->lpVtbl->FindRow (lptbl, &res, BOOKMARK_BEGINNING, 0L));
  1166.         }
  1167.     }
  1168.     return fOof;
  1169. }
  1170. HRESULT
  1171. HrRegOofRecip (LPSMH lpsmh, LPOOF lpoof, LPSPropValue lpvalEid)
  1172. {
  1173.     HRESULT hr;
  1174.     LPTABLEDATA lptad = lpoof->lptad;
  1175.     SizedSPropTagArray (cpOofMax, sptOofTbl) = {cpOofMax, { PR_ROWID, PR_ENTRYID }};
  1176.     SPropValue rgval[cpOofMax];
  1177.     SRow rw;
  1178.     if (!lptad)
  1179.     {
  1180.         //  This is the first OOF recip so we need to create the
  1181.         //  table of recips
  1182.         //
  1183.         hr = ResultFromScode (CreateTable ((LPIID)&IID_IMAPITableData,
  1184.                             lpsmh->lpfnAlloc,
  1185.                             lpsmh->lpfnAllocMore,
  1186.                             lpsmh->lpfnFree,
  1187.                             NULL,
  1188.                             TBLTYPE_DYNAMIC,
  1189.                             PR_ROWID,
  1190.                             (LPSPropTagArray)&sptOofTbl,
  1191.                             (LPTABLEDATA FAR *)&lptad));
  1192.         if (HR_FAILED (hr))
  1193.             goto ret;
  1194.         
  1195.         hr = lptad->lpVtbl->HrGetView (lptad, NULL, NULL, 0, &lpoof->lptbl);
  1196.         if (HR_FAILED (hr))
  1197.         {
  1198.             UlRelease (lptad);
  1199.             goto ret;
  1200.         }
  1201.         lpoof->lptad = lptad;
  1202.     }
  1203.     rgval[ipOofRId].ulPropTag = PR_ROWID;
  1204.     rgval[ipOofRId].Value.l = lpoof->cRecips++;
  1205.     rgval[ipOofREid].ulPropTag = PR_ENTRYID;
  1206.     rgval[ipOofREid].Value = lpvalEid->Value;
  1207.     
  1208.     rw.cValues = cpOofMax;
  1209.     rw.lpProps = rgval; 
  1210.     hr = lptad->lpVtbl->HrModifyRow (lptad, &rw);
  1211.     if (HR_FAILED (hr))
  1212.         goto ret;
  1213. ret:
  1214.     DebugTraceResult (HrRegOofRecip(), hr);
  1215.     return hr;
  1216. }
  1217. HRESULT
  1218. HrGenerateResponse (LPSMH lpsmh, LPRULE lprl, LPMAPIFOLDER lpfldr, LPMESSAGE lpmsg)
  1219. {
  1220.     HRESULT hr;
  1221.     LPSPropValue lpval = NULL;
  1222.     LPMESSAGE lpmsgRep = NULL;
  1223.     ULONG cval;
  1224.     
  1225.     hr = lpmsg->lpVtbl->SaveChanges (lpmsg, KEEP_OPEN_READONLY);
  1226.     if (HR_FAILED (hr))
  1227.         goto ret;
  1228.     
  1229.     hr = lpmsg->lpVtbl->GetProps (lpmsg, 
  1230.                             (LPSPropTagArray)&sptResponse,
  1231.                             0,
  1232.                             &cval,
  1233.                             &lpval);
  1234.     if (HR_FAILED (hr))
  1235.         goto ret;
  1236.     hr = ResultFromScode (MAPI_E_NOT_ME);
  1237.     
  1238.     if ((lpval[ipMsgClass].ulPropTag == PR_MESSAGE_CLASS) &&
  1239.         (lpval[ipMsgClass].Value.LPSZ != NULL) &&
  1240.         FLpszContainsLpsz (lpval[ipMsgClass].Value.LPSZ, "Report."))
  1241.         goto ret;
  1242.     if ((lpval[ipFlags].ulPropTag == PR_MESSAGE_FLAGS) &&
  1243.         (lpval[ipFlags].Value.l == MSGFLAG_FROMME))
  1244.         goto ret;
  1245.     /*  If there is no rule, then we must be OOF'ing */
  1246.     if (!lprl && !FOofRecip (&lpsmh->oof, &lpval[ipSndrEid]))
  1247.         goto ret;
  1248.     if ((lpval[ipRecipMe].ulPropTag == PR_MESSAGE_RECIP_ME) &&
  1249.         (lpval[ipRecipMe].Value.b))
  1250.     {
  1251.         DebugTrace ("SMH: generating response messagen");
  1252.         
  1253.         if (!lprl)
  1254.         {
  1255.             /*  Register the OOF recipient */
  1256.             
  1257.             hr = HrRegOofRecip (lpsmh, &lpsmh->oof, &lpval[ipSndrEid]);
  1258.             if (HR_FAILED (hr))
  1259.                 goto ret;
  1260.         }
  1261.         /*  Create the response and submit it */
  1262.         
  1263.         hr = HrCreateResponse (lpsmh,
  1264.                         lprl,
  1265.                         lpfldr,
  1266.                         lpmsg,
  1267.                         lpval,
  1268.                         &lpmsgRep);
  1269.         if (!HR_FAILED (hr))
  1270.             hr = lpmsgRep->lpVtbl->SubmitMessage (lpmsgRep, 0);
  1271.         
  1272.         UlRelease (lpmsgRep);
  1273.     }
  1274. ret:
  1275.     (*lpsmh->lpfnFree) (lpval);
  1276.     DebugTraceResult (HrGenerateResponse(), hr);
  1277.     return hr;
  1278. }
  1279. HRESULT
  1280. HrInitOof (LPSMH lpsmh, LPSPropValue lpvalAnno, LPSPropValue lpvalRTF)
  1281. {
  1282.     SCODE sc = MAPI_E_UNCONFIGURED;
  1283.     if (lpvalRTF->ulPropTag == PR_SMH_OOF_RTF)
  1284.     {
  1285.         if (!FAILED (sc = (*lpsmh->lpfnAlloc) (lpvalRTF->Value.bin.cb,
  1286.                                     (LPVOID FAR *)&lpsmh->oof.lpbRTF)))
  1287.         {
  1288.             lpsmh->oof.cbRTF = lpvalRTF->Value.bin.cb;
  1289.             memcpy (lpsmh->oof.lpbRTF,
  1290.                 lpvalRTF->Value.bin.lpb,
  1291.                 (UINT)lpvalRTF->Value.bin.cb);
  1292.         }
  1293.     }
  1294.     if (lpvalAnno->ulPropTag == PR_SMH_OOF_TEXT)
  1295.     {
  1296.         if (!FAILED (sc = (*lpsmh->lpfnAlloc) (lstrlen (lpvalAnno->Value.LPSZ) + sizeof(TCHAR),
  1297.                                     (LPVOID FAR *)&lpsmh->oof.lpszBody)))
  1298.             lstrcpy (lpsmh->oof.lpszBody, lpvalAnno->Value.LPSZ);
  1299.     }
  1300.     DebugTraceSc (HrInitOof(), sc);
  1301.     return ResultFromScode (sc);
  1302. }