home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / mapi / manager.sh / smhoof.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  40.1 KB  |  1,473 lines

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