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 / peer.xp / xpqueue.c < prev    next >
C/C++ Source or Header  |  1996-04-11  |  56KB  |  1,791 lines

  1. /*
  2.  -  X P Q U E U E . C
  3.  -
  4.  *  Purpose:
  5.  *      Code to support the background queueing mechanism for the Sample
  6.  *      Transport Provider.
  7.  *      This module contains the following SPI entry points:
  8.  *
  9.  *          Idle()
  10.  *
  11.  *      Additional support functions found here:
  12.  *
  13.  *          HrBuildAdrList()
  14.  *          HrSendOneMessage()
  15.  *          HrIMsgToTextMsg()
  16.  *          HrPrepareRecipientTable()
  17.  *          HrCrackSenderEID()
  18.  *          FPropIndex()
  19.  *          FormatFileTime()
  20.  *          FreeMyAdrList()
  21.  *          ScLoadTnef()
  22.  *
  23.  *      Also present in this module is the transmit list management code and
  24.  *      all the recipient list manipulation logic.
  25.  *
  26.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  27.  */
  28.  
  29. #include "xppch.h"
  30. #include <tnef.h>
  31. #include "xpsof.h"
  32. #include "xptxtmsg.h"
  33. #include "xpresrc.h"
  34.  
  35. BOOL FIsTextizedProp(ULONG ulPropTag);
  36.  
  37. /* Generic BAD_VALUE for use in comparisons below */
  38.  
  39. #define BAD_VALUE (0xFFFFFFFF)
  40.  
  41.  
  42. /*
  43.  -  lpxpl->lpVtbl->Idle
  44.  -
  45.  *  Purpose:
  46.  *      Called by the Spooler periodically in its idle loop.
  47.  *
  48.  *      The Transport will determine if there's any incoming mail for the
  49.  *      Spooler and will TransportNotify (NOTIFY_NEWMAIL) if so.
  50.  *
  51.  *  Parameters:
  52.  *      ulFlags             Flags. None are currently defined.
  53.  *
  54.  *  Returns:
  55.  *      (HRESULT)           Errors encountered if any.
  56.  *
  57.  *  Operation:
  58.  *      If inbound operation is currently enabled, call the Poll() entry point
  59.  *      to see if there's anything there. If we find something with Poll(), then
  60.  *      SpoolerNotify(NOTIFY_NEWMAIL).
  61.  */
  62.  
  63. STDMETHODIMP
  64. XPL_Idle(LPXPL lpxpl, ULONG ulFlags)
  65. {
  66.     HRESULT hResult = 0;
  67.     LPDEFMSG lpDefMsg;
  68.  
  69.     /* Incoming messages? */
  70.  
  71.     if (lpxpl->ulTransportStatus & STATUS_INBOUND_ENABLED)
  72.     {
  73.         ULONG ulT = 0;
  74.  
  75.         if ((hResult = XPL_Poll(lpxpl, &ulT)) != 0)
  76.             goto ret;
  77.         if (ulT != 0)
  78.         {
  79.             /* SpoolerNotify() could theoretically return a nonzero
  80.             HRESULT, but it's much easier and unambiguous to ignore
  81.             that possibility and field Spooler errors elsewhere. */
  82.  
  83.             (void)lpxpl->lpMAPISup->lpVtbl->SpoolerNotify(lpxpl->lpMAPISup, NOTIFY_NEWMAIL, NULL);
  84.         }
  85.     }
  86.  
  87.     if (lpxpl->fResendDeferred)
  88.     {
  89.         while (lpxpl->lpDeferredList)
  90.         {
  91.             lpDefMsg = lpxpl->lpDeferredList;
  92.             lpxpl->lpDeferredList = lpDefMsg->lpNext;
  93.  
  94.             lpxpl->lpMAPISup->lpVtbl->SpoolerNotify(lpxpl->lpMAPISup,
  95.                 NOTIFY_SENTDEFERRED, &(lpDefMsg->sbinEIDDef));
  96.  
  97.             lpxpl->FreeBuffer(lpDefMsg);
  98.         }
  99.  
  100.         lpxpl->fResendDeferred = FALSE;
  101.     }
  102.  
  103. ret:
  104.  
  105.     DebugTraceResult(XPL_Idle, hResult);
  106.     return hResult;
  107. }
  108.  
  109. /*
  110.  -  HrBuildAdrList
  111.  -
  112.  *  Called by outbound and inbound logic.
  113.  *
  114.  *  Traverses a restricted recipient table and collates the rows into
  115.  *  a "done" adrlist structure and a "not done" adrlist structure. The
  116.  *  only distinction between done and not done is the result of a call
  117.  *  to an optional function to attempt to send the open message to
  118.  *  a particular recipient.
  119.  *
  120.  *  Obviously, if the callback isn't specified, the "done" adrlist will
  121.  *  contain all recipients that matched the restriction.
  122.  *
  123.  *  Parameters:
  124.  *      lpxpl               Session in which we're executing
  125.  *      lpMessage           Message containing the recipient table
  126.  *      lpTable             Recipient table
  127.  *      fSetResponsibility  TRUE/FALSE = Set/Clear PR_RESPONSIBILITY
  128.  *                          in good list (clear bad list always)
  129.  *      lpfnCallBack        Routine to call when trying to send
  130.  *      lppMyAdrListGood    Where to store the list of done recips
  131.  *      lppMyAdrListBad     Where to store the list of not done
  132.  *
  133.  *  Returns:
  134.  *      (HRESULT)           Errors encountered if any.
  135.  *
  136.  *
  137.  *  Operation:
  138.  *      This routine builds a pair of MYADRLISTS from the (restricted)
  139.  *      table which is passed to it.
  140.  *
  141.  *      Do a SetColumns so we know where to find PR_EMAIL_ADDRESS, PR_ROWID
  142.  *      and other necessary columns. Included in the SetColumns is
  143.  *      PR_REPORT_TEXT as a placeholder for NDR information.
  144.  *
  145.  *      Then, until we run out of data,
  146.  *
  147.  *      1) QueryRows on the input table
  148.  *      2) For each row:
  149.  *          a) If lpfnCallBack and PR_EMAIL_ADDRESS, invoke callback.
  150.  *          b) If callback wasn't defined or if successful, add row to
  151.  *             "good" list, else add to "bad" list with NDR reason
  152.  *      3) Free RowSet
  153.  *
  154.  *      The caller can tell if any recipients remain by whether the
  155.  *      "not done" set contains any elements.
  156.  *
  157.  *      The "not done" set will set PR_RESPONSIBILITY to FALSE if the caller
  158.  *      wants "done" recipients set,
  159.  */
  160.  
  161. HRESULT
  162. HrBuildAdrList(LPXPL lpxpl,
  163.     LPSPropValue lpPropArray,
  164.     LPMESSAGE lpMessage,
  165.     LPMAPITABLE lpTable,
  166.     BOOL fSetResponsibility,
  167.     LPMYCALLBACK lpfnCallBack,
  168.     LPMYADRLIST FAR * lppMyAdrListGood,
  169.     LPMYADRLIST FAR * lppMyAdrListBad)
  170. {
  171.     HRESULT hResult = hrSuccess;
  172.     SCODE sc = S_OK;
  173.     LPVOID lpvT;
  174.     TCHAR rgchBuffer[512];
  175.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  176.  
  177.     LPSRowSet lpRow = NULL;
  178.     LPMYADRLIST lpDone = NULL;
  179.     LPMYADRLIST lpNotDone = NULL;
  180.     LPSPropTagArray lpsptT = NULL;
  181.  
  182.     LPMYADRLIST FAR *lppMyAdrList;
  183.     LPSPropTagArray lpsptNew;
  184.     LPSPropValue lpspvT;
  185.  
  186.     ULONG ulT;
  187.     ULONG ulNew;
  188.     ULONG ulRow;
  189.  
  190.     /*  These are the columns I need to have in the recipient table,
  191.         even if all I get is a placeholder. If you need to add any,
  192.         bump the definition of NUM_REQUIRED_COLS, add new defines
  193.         for the new columns, and add the new column proptags to the
  194.         switch below. If the property isn't necessarily going to be
  195.         in the table, use the PR_NULL trick you see below for the
  196.         PR_REPORT_TEXT property. */
  197.  
  198.     enum enumColumns
  199.     {
  200.         COLUMN_PR_ROWID,
  201.         COLUMN_PR_EMAIL_ADDRESS,
  202.         COLUMN_PR_RESPONSIBILITY,
  203.         COLUMN_PR_REPORT_TEXT,
  204.         COLUMN_PR_RECIPIENT_TYPE,
  205.         NUM_REQUIRED_COLS
  206.     };
  207.  
  208.     Assert(lppMyAdrListGood);
  209.     Assert(lppMyAdrListBad);
  210.  
  211.     /*
  212.         Arrange the columns so that we'll have the recipient properties
  213.         we really want.
  214.  
  215.         So as not to lose any recipient properties, we need to do it in
  216.         this fashion:
  217.  
  218.         1)  QueryColumns() on the input table
  219.         2)  Build a new column set based on the properties we care
  220.             about plus all other properties of the input table
  221.         3)  SetColumns() on the input table
  222.  
  223.         The properties we care about are the following:
  224.  
  225.         1)  PR_ROWID
  226.         2)  PR_EMAIL_ADDRESS
  227.         3)  PR_RESPONSIBILITY */
  228.  
  229.     /*  Get the complete column set */
  230.  
  231.     hResult = lpTable->lpVtbl->QueryColumns(lpTable, TBL_ALL_COLUMNS, &lpsptT);
  232.  
  233.     if (hResult)
  234.     {
  235.         DebugTrace("QueryColumns failed.\n");
  236.         goto ret;
  237.     }
  238.  
  239.     /*  Gotta have a PR_ROWID. That guarantees at least one column! */
  240.  
  241.     Assert(lpsptT->cValues >= 1);
  242.  
  243.     /*  Allocate a new column set, linked to the old one for cleanup */
  244.  
  245.     ulT = CbNewSPropTagArray(NUM_REQUIRED_COLS + lpsptT->cValues);
  246.     sc = (*lpxpl->AllocateMore) (ulT, (LPVOID) lpsptT, (LPVOID) &lpsptNew);
  247.  
  248.     if (sc)
  249.     {
  250.         hResult = ResultFromScode(sc);
  251.         DebugTrace("New column set allocation failed.\n");
  252.         goto ret;
  253.     }
  254.  
  255.     /*  Fill in the new column set, required fields first in our order. */
  256.  
  257.     lpsptNew->aulPropTag[COLUMN_PR_ROWID] = PR_ROWID;
  258.     lpsptNew->aulPropTag[COLUMN_PR_EMAIL_ADDRESS] = PR_EMAIL_ADDRESS;
  259.     lpsptNew->aulPropTag[COLUMN_PR_RESPONSIBILITY] = PR_RESPONSIBILITY;
  260.     lpsptNew->aulPropTag[COLUMN_PR_REPORT_TEXT] = PR_NULL;
  261.     lpsptNew->aulPropTag[COLUMN_PR_RECIPIENT_TYPE] = PR_RECIPIENT_TYPE;
  262.     ulNew = NUM_REQUIRED_COLS;
  263.  
  264.     for (ulT = 0; ulT < lpsptT->cValues; ulT++)
  265.     {
  266.         switch (lpsptT->aulPropTag[ulT])
  267.         {
  268.         case PR_REPORT_TEXT:
  269.             lpsptNew->aulPropTag[COLUMN_PR_REPORT_TEXT] = PR_REPORT_TEXT;
  270.             break;
  271.  
  272.         case PR_ROWID:
  273.         case PR_EMAIL_ADDRESS:
  274.         case PR_RESPONSIBILITY:
  275.         case PR_RECIPIENT_TYPE:
  276.             break;
  277.  
  278.         default:
  279.             lpsptNew->aulPropTag[ulNew++] = lpsptT->aulPropTag[ulT];
  280.             break;
  281.         }
  282.     }
  283.  
  284.     lpsptNew->cValues = ulNew;
  285.  
  286.     /*  Set the new columns */
  287.  
  288.     hResult = lpTable->lpVtbl->SetColumns(lpTable, lpsptNew, 0L);
  289.  
  290.     if (hResult)
  291.     {
  292.         DebugTrace("SetColumns failed.\n");
  293.         goto ret;
  294.     }
  295.  
  296.     /*  Free both old and new PropTagArrays. */
  297.  
  298.     (*lpxpl->FreeBuffer) ((LPVOID) lpsptT);
  299.     lpsptT = NULL;
  300.  
  301.     /*  Build two big ADRLISTs from this table. Do minimal allocations so
  302.         that all the reallocation code will be properly exercised. We can
  303.         optimize later when we've tested.
  304.  
  305.         The obvious underlying assumption of this code is that it's
  306.         possible to get the entire recipient list into memory at once. */
  307.  
  308. #ifdef DEBUG
  309. #define GROW_SIZE   1       /* How much we'll add on every reallocation. */
  310. #define QUERY_SIZE  1       /* How much we'll try for on query rows */
  311. #else
  312. #define GROW_SIZE   20
  313. #define QUERY_SIZE  20
  314. #endif
  315.  
  316.     for (;;)
  317.     {
  318.         /*  Get some rows from the recipient table. */
  319.  
  320.         hResult = lpTable->lpVtbl->QueryRows(lpTable, QUERY_SIZE, 0L, &lpRow);
  321.  
  322.         if (hResult)
  323.         {
  324.             DebugTrace("QueryRows failed.\n");
  325.             goto ret;
  326.         }
  327.  
  328.         /*  Are we finished getting rows? */
  329.  
  330.         if (!lpRow || !(lpRow->cRows))
  331.         {
  332.             /*  Free the row if any */
  333.  
  334.             (*lpxpl->FreeBuffer) ((LPVOID) lpRow);
  335.             lpRow = NULL;
  336.  
  337.             /*  We're finished now, break out of the (for (;;)) loop. */
  338.  
  339.             break;
  340.         }
  341.  
  342.         /*  Work our way through the RowSet */
  343.  
  344.         for (ulRow = 0; ulRow < lpRow->cRows; ulRow++)
  345.         {
  346.             BOOL fDone = TRUE;
  347.  
  348.             Assert(lpRow->aRow[ulRow].cValues);
  349.             Assert(lpRow->aRow[ulRow].lpProps);
  350.  
  351.             lpspvT = lpRow->aRow[ulRow].lpProps;
  352.  
  353.             /* Check our .2 second timer before processing each row. */
  354.  
  355.             sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  356.  
  357.             if (sc == MAPI_W_CANCEL_MESSAGE)
  358.             {
  359.                 DebugTrace("Cancelling message delivery.\n");
  360.                 goto ret;
  361.             }
  362.  
  363.             /*  See if we need to do the callback. If so, make the call now */
  364.  
  365.             if (lpfnCallBack)
  366.             {
  367.                 Assert(lpspvT[COLUMN_PR_EMAIL_ADDRESS].ulPropTag == PR_EMAIL_ADDRESS);
  368.                 Assert(lpspvT[COLUMN_PR_RECIPIENT_TYPE].ulPropTag == PR_RECIPIENT_TYPE);
  369.  
  370.                 hResult = lpfnCallBack(lpxpl,
  371.                     lpPropArray,
  372.                     lpMessage,
  373.                     lpspvT[COLUMN_PR_RECIPIENT_TYPE].Value.ul,
  374.                     lpspvT[COLUMN_PR_EMAIL_ADDRESS].Value.LPSZ,
  375.                     &fDone);
  376.  
  377.                 if (hResult)
  378.                 {
  379.                     DebugTrace("IGNORE: Callback function  failed.\n");
  380.                     hResult = hrSuccess;
  381.                 }
  382.             }
  383.  
  384.             /*  Set the PR_RESPONSIBILITY flag to its indicated new state. */
  385.  
  386.             Assert(lpRow->aRow[ulRow].cValues > COLUMN_PR_RESPONSIBILITY);
  387.  
  388.             /*  Make the appropriate change to PR_RESPONSIBILITY. If we're
  389.                 setting it, just do it into whatever column we've set;
  390.                 is pointing at, Prop Tag and Value. If we're clearing it,
  391.                 we only need to do anything if there was a discrete column,
  392.                 and all that's needed is to set the Prop Tag to PR_NULL. */
  393.  
  394.             if (fSetResponsibility)
  395.             {
  396.                 lpspvT[COLUMN_PR_RESPONSIBILITY].ulPropTag = PR_RESPONSIBILITY;
  397.                 lpspvT[COLUMN_PR_RESPONSIBILITY].Value.b = fDone;
  398.             }
  399.             else
  400.             {
  401.                 /*  We want to clear the flag */
  402.                 lpspvT[COLUMN_PR_RESPONSIBILITY].ulPropTag = PR_NULL;
  403.             }
  404.  
  405.             /*  PR_NULL the PR_ROWID column if this will be a "bad" recipient, also
  406.                 adding NDR information to the row (in a column we set earlier) */
  407.  
  408.             if (!fDone)
  409.             {
  410.                 TCHAR szReportText[512];
  411.  
  412.                 lpspvT[COLUMN_PR_ROWID].ulPropTag = PR_NULL;
  413.  
  414.                 /*  The Spooler will default to a NDR report and will fill in all
  415.                     required properties in the StatusRecips call. The only thing
  416.                     we need to do is to fill in a specific per-recipient text
  417.                     description of the problem (it defaults too but it's good
  418.                     to have real info from the horse's mouth) */
  419.  
  420.                 LoadString(lpxpl->lpxppParent->hInst, IDS_REPORT_TEXT_MSG,
  421.                     szReportText, sizeof(szReportText));
  422.  
  423.                 wsprintf(rgchBuffer, "%s%s", szReportText,
  424.                     lpspvT[COLUMN_PR_EMAIL_ADDRESS].Value.LPSZ);
  425.  
  426.                 ulT = Cbtszsize(rgchBuffer);
  427.                 sc = lpxpl->AllocateMore(ulT, lpspvT, &lpvT);
  428.  
  429.                 if (sc)
  430.                 {
  431.                     hResult = ResultFromScode(sc);
  432.                     DebugTrace("NDR text allocation failed.\n");
  433.                     goto ret;
  434.                 }
  435.  
  436.                 /*  Memory allocated, copy the formatted string and hook it into
  437.                     the pre-allocated column. */
  438.  
  439.                 lstrcpy((LPTSTR) lpvT, rgchBuffer);
  440.                 lpspvT[COLUMN_PR_REPORT_TEXT].ulPropTag = PR_REPORT_TEXT;
  441.                 lpspvT[COLUMN_PR_REPORT_TEXT].Value.LPSZ = (LPTSTR) lpvT;
  442.             }
  443.  
  444.             /*  Now point ourselves at one of the two LPMYADRLIST pointers */
  445.  
  446.             lppMyAdrList = (fDone ? &lpDone : &lpNotDone);
  447.  
  448.             /*  If we didn't already have a LPMYADRLIST, allocate one now. */
  449.  
  450.             if (*lppMyAdrList == NULL)
  451.             {
  452.                 /*  Allocate an initial MYADRLIST structure. Then allocate
  453.                     enough memory for (GROW_SIZE) ADRENTRYs and store
  454.                     it into the structure. Initialize the members. */
  455.  
  456.                 sc = (*lpxpl->AllocateBuffer) (sizeof(MYADRLIST), &lpvT);
  457.  
  458.                 if (sc)
  459.                 {
  460.                     hResult = ResultFromScode(sc);
  461.                     DebugTrace("Initial MYADRLIST allocation failed.\n");
  462.                     goto ret;
  463.                 }
  464.  
  465.                 /*  Hook up the MYADRLIST with no entries. */
  466.  
  467.                 *lppMyAdrList = (LPMYADRLIST) lpvT;
  468.                 (*lppMyAdrList)->cMaxEntries = 0;
  469.                 (*lppMyAdrList)->lpAdrList = NULL;
  470.  
  471.                 /* Now allocate a ADRLIST with GROW_SIZE entries in it. */
  472.  
  473.                 sc = (*lpxpl->AllocateBuffer) (CbNewADRLIST(GROW_SIZE), &lpvT);
  474.  
  475.                 if (sc)
  476.                 {
  477.                     hResult = ResultFromScode(sc);
  478.                     DebugTrace("Initial ADRLIST allocation failed.\n");
  479.                     goto ret;
  480.                 }
  481.  
  482.                 /*  Now hook up this ADRLIST into the MYADRLIST. */
  483.  
  484.                 (*lppMyAdrList)->lpAdrList = (LPADRLIST) lpvT;
  485.                 (*lppMyAdrList)->cMaxEntries = GROW_SIZE;
  486.                 (*lppMyAdrList)->lpAdrList->cEntries = 0;
  487.             }
  488.  
  489.             /*  Make sure that the selected MYADRLIST has room for another row */
  490.  
  491.             ulT = (*lppMyAdrList)->lpAdrList->cEntries + 1;
  492.             Assert(ulT <= (*lppMyAdrList)->cMaxEntries + 1);
  493.  
  494.             if (ulT > (*lppMyAdrList)->cMaxEntries)
  495.             {
  496.                 LPADRLIST lpAdrListT;
  497.  
  498.                 /*  Not enough space, we need to create a new ADRLIST. */
  499.  
  500.                 ulT = CbNewADRLIST((*lppMyAdrList)->cMaxEntries + GROW_SIZE);
  501.                 sc = (*lpxpl->AllocateBuffer) (ulT, &lpvT);
  502.  
  503.                 if (sc)
  504.                 {
  505.                     hResult = ResultFromScode(sc);
  506.                     DebugTrace("Reallocation of ADRLIST failed.\n");
  507.                     goto ret;
  508.                 }
  509.  
  510.                 /*  Got it, now copy the data from the old one */
  511.  
  512.                 lpAdrListT = (LPADRLIST) lpvT;
  513.                 ulT = CbNewADRLIST((*lppMyAdrList)->cMaxEntries);
  514.  
  515.                 if (ulT)
  516.                     memcpy(lpAdrListT, (*lppMyAdrList)->lpAdrList, (UINT) ulT);
  517.  
  518.                 (*lppMyAdrList)->cMaxEntries += GROW_SIZE;
  519.  
  520.                 /*  Exchange the pointers */
  521.  
  522.                 lpvT = (LPVOID) (*lppMyAdrList)->lpAdrList;
  523.                 (*lppMyAdrList)->lpAdrList = lpAdrListT;
  524.  
  525.                 /*  Free the old memory */
  526.  
  527.                 lpxpl->FreeBuffer(lpvT);
  528.             }
  529.  
  530.             /*  We have room now so store the new ADRENTRY. As part of the
  531.                 storage, we're going to copy the SRow pointer from the SRowSet
  532.                 into the ADRENTRY. Once we've done this, we won't need the
  533.                 SRowSet any more ... and the SRow will be deallocated when
  534.                 we unwind the ADRLIST. */
  535.  
  536.             /*  Calculate location in ADRLIST for the new entry. */
  537.  
  538.             ulT = (*lppMyAdrList)->lpAdrList->cEntries++;
  539.  
  540.             /*  Copy the data from the SRowSet. */
  541.  
  542.             (*lppMyAdrList)->lpAdrList->aEntries[ulT].cValues = lpRow->aRow[ulRow].cValues;
  543.             (*lppMyAdrList)->lpAdrList->aEntries[ulT].rgPropVals = lpRow->aRow[ulRow].lpProps;
  544.  
  545.             /*  Now that we are finished with this row (eg it is in the
  546.                 adrlist) we want to disassociate it from the rowset. */
  547.  
  548.             lpRow->aRow[ulRow].lpProps = NULL;
  549.         }
  550.  
  551.         /*  We're finished with the SRowSet (since it is allocated
  552.             separately from the SRow). Deallocate it. */
  553.  
  554.         lpxpl->FreeBuffer((LPVOID) lpRow);
  555.         lpRow = NULL;
  556.  
  557.     }                           /* End of big ADRLIST builder loop */
  558.  
  559.     /*  Fall into exit with hResult set if something failed. */
  560.  
  561. ret:
  562.  
  563.     /*  Clean up RowSet if any is left */
  564.  
  565.     FreeProws(lpRow);
  566.  
  567.     /*  Clean up column stuff if any's left */
  568.  
  569.     lpxpl->FreeBuffer((LPVOID) lpsptT);
  570.  
  571.     /*  If sc is set, feed it into hResult. Afterwards, hResult is the
  572.         determinant of whether there was an error or not. */
  573.  
  574.     if (hResult)
  575.     {
  576.         /*  This memory should only be hanging around in the error case. */
  577.  
  578.         FreeMyAdrList(lpxpl, lpDone);
  579.         FreeMyAdrList(lpxpl, lpNotDone);
  580.  
  581.         *lppMyAdrListGood = NULL;
  582.         *lppMyAdrListBad = NULL;
  583.     }
  584.     else
  585.     {
  586.         /*  Success, pass the adrlists we built back to the caller */
  587.  
  588.         *lppMyAdrListGood = lpDone;
  589.         *lppMyAdrListBad = lpNotDone;
  590.     }
  591.  
  592.     DebugTraceResult(HrBuildAdrList, hResult);
  593.     return hResult;
  594. }
  595.  
  596.  
  597. /*
  598.  -  HrSendOneMessage
  599.  -
  600.  *  Purpose:
  601.  *      Called by HrBuildAdrList() to send a message to a recipient
  602.  *      that matched all of the caller's restrictions. Also called by
  603.  *      XPL_SubmitMessage() if transport is not peer-to-peer, in which
  604.  *      case the "Email address" is just our outbound directory.
  605.  *
  606.  *  Parameters:
  607.  *      lpxpl               The Transports logon object for this session
  608.  *      lpPropArray         Logon properties that have been copied from lpxpl
  609.  *      lpMessage           Message to send
  610.  *      ulRecipType         MAPI_TO, MAPI_CC, etc. If 0, not a recip.
  611.  *      lpszEmailAddress    Email address of this recipient.
  612.  *      lpfSent             Pointer to the boolean result
  613.  *                          of the operation.
  614.  *
  615.  *  Returns:
  616.  *      (HRESULT)           Set if an error encountered.
  617.  *      *lpfSent            FALSE if sending failed,
  618.  *                          TRUE if sending succeeded.
  619.  *
  620.  *  Operation:
  621.  *      Initializes result in *lpfSent to FALSE.  Open a stream interface
  622.  *      on the destination message file.  Write all the textized envelope
  623.  *      properties into the stream, then use TNEF to encapsulate the remaining
  624.  *      message properties into the stream.
  625.  */
  626.  
  627. HRESULT
  628. HrSendOneMessage(LPXPL lpxpl,
  629.     LPSPropValue lpPropArray,
  630.     LPMESSAGE lpMessage,
  631.     ULONG ulRecipType,
  632.     LPTSTR lpszEmailAddress,
  633.     BOOL FAR * lpfSent)
  634. {
  635.     TCHAR rgchOutFileName[MAX_PATH];
  636.     HRESULT hResult = 0;
  637.     SCODE sc = 0;
  638.     ULONG ulT;
  639.     ULONG cValues;
  640.     BOOL fFromMe;
  641.  
  642.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  643.     LPIID lpidMessage = (LPIID) &IID_IMessage;
  644.     SPropValue spvDestMsgProps[3];
  645.  
  646.     LPSPropProblemArray lpProblems = NULL;
  647.     LPSPropTagArray lpPropTagArray = NULL;
  648.  
  649.     LPSTnefProblemArray lptpa = NULL;
  650.  
  651.     WORD wKey = 0;
  652.     LPITNEF lpTnef = (LPITNEF) NULL;
  653.     LPSTREAM lpSof = (LPSTREAM) NULL;
  654.     LPSTREAM lpXPSof = (LPSTREAM) NULL;
  655.  
  656.     /* Assume the worst, we'll fix it later if need be */
  657.  
  658.     *lpfSent = FALSE;
  659.  
  660.     /* Build file name of outgoing message. Because loopback doesn't
  661.        always work on all platforms, we check the email address against
  662.        our own and just substitute the inbox path if it matches. */
  663.  
  664.     if (!lstrcmpi(lpszEmailAddress,
  665.             ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS, lpPropArray).Value.LPSZ))
  666.     {
  667.         DebugTrace("Email Address is mine, doing my own internal loopback\n");
  668.         lstrcpy(rgchOutFileName, ArrayIndex(PR_SAMPLE_INBOUND_DIR, lpPropArray).Value.LPSZ);
  669.         fFromMe = TRUE;
  670.     }
  671.     else
  672.     {
  673.         lstrcpy(rgchOutFileName, lpszEmailAddress);
  674.  
  675.         /*  If this is a real email address, it won't have a
  676.             trailing backslash. But if it's our outbound dir (when
  677.             we're not p2p) it wiil have one. Only add one if
  678.             it's needed. */
  679.  
  680.         ulT = lstrlen(rgchOutFileName);
  681.         Assert(ulT > 1);
  682.  
  683.         if (lstrcmp(&rgchOutFileName[ulT], TEXT("\\")))
  684.             lstrcat(rgchOutFileName, TEXT("\\"));
  685.         fFromMe = FALSE;
  686.     }
  687.  
  688.     hResult = OpenStreamOnFile(lpxpl->AllocateBuffer, lpxpl->FreeBuffer,
  689.         STGM_CREATE | STGM_READWRITE | SOF_UNIQUEFILENAME,
  690.         rgchOutFileName, TEXT("TNF"), &lpSof);
  691.     if (HR_FAILED(hResult))
  692.     {
  693.         DebugTrace("OpenStreamOnFile(%s) failed.\n",rgchOutFileName);
  694.         PrintfTransportLog(TEXT("Delivery failed in OpenStreamOnFile."));
  695.         goto ret;
  696.     }
  697.  
  698.     /* Wrap the Stream-On-File object in our buffered wrapper. */
  699.  
  700.     hResult = HrWrapStreamOnFile(lpxpl->AllocateBuffer, lpxpl->FreeBuffer,
  701.             XPSOF_READWRITE, lpSof, &lpXPSof);
  702.     if (HR_FAILED(hResult))
  703.     {
  704.         DebugTrace("HrWrapStreamOnFile() failed\n");
  705.         goto ret;
  706.     }
  707.  
  708.     /* Write all non-TNEF properties to the text file */
  709.  
  710.     hResult = HrIMsgToTextMsg(lpxpl, lpPropArray, lpMessage, lpXPSof);
  711.  
  712.     if (HR_FAILED(hResult))
  713.     {
  714.         DebugTrace("HrIMsgToTextMsg() failed.\n");
  715.         PrintfTransportLog(TEXT("Delivery failed in HrIMsgToTextMsg."));
  716.         goto ret;
  717.     }
  718.  
  719.     hResult = hrSuccess;
  720.  
  721.     /* Check our .2 second timer after writting textized properties. */
  722.  
  723.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  724.  
  725.     if (sc == MAPI_W_CANCEL_MESSAGE)
  726.     {
  727.         DebugTrace("Cancelling message delivery.\n");
  728.         goto ret;
  729.     }
  730.  
  731.     /* Open a TNEF encapsulation on the StreamOnFile interface */
  732.  
  733.     hResult = OpenTnefStream(lpMAPISup, lpXPSof, TEXT("MAPIMAIL.DAT"),
  734.             TNEF_ENCODE, lpMessage, 0x01AF, &lpTnef);
  735.  
  736.     if (HR_FAILED(hResult))
  737.     {
  738.         DebugTrace("OpenTNEFStream() failed.\n");
  739.         PrintfTransportLog(TEXT("Delivery failed in OpenTNEFStream."));
  740.         goto ret;
  741.     }
  742.  
  743.     /* Find out what properties there are, so we can exclude the
  744.        "nontransmittables" and the properties we've textized. */
  745.  
  746.     hResult = lpMessage->lpVtbl->GetPropList(lpMessage, 0, /* ansi */
  747.             &lpPropTagArray);
  748.  
  749.     if (hResult)
  750.     {
  751.         DebugTrace("GetPropList failed.\n");
  752.         goto ret;
  753.     }
  754.  
  755.     Assert(lpPropTagArray);
  756.  
  757.     /* Build a new prop tag array on the memory we just got back. This
  758.        prop tag array will only contain nontransmittable properties. */
  759.  
  760.     cValues = 0;
  761.  
  762.     for (ulT = 0; ulT < lpPropTagArray->cValues; ulT++)
  763.     {
  764.         ULONG ulPropTagT = lpPropTagArray->aulPropTag[ulT];
  765.  
  766.         /*  FIsTransmittable is a macro in the MAPI headers. Makes it
  767.             really easy to determine transmittable/not when we need to */
  768.  
  769.         if ((!FIsTransmittable(ulPropTagT) || FIsTextizedProp(ulPropTagT)) &&
  770.             (ulPropTagT != PR_MESSAGE_ATTACHMENTS))
  771.         {
  772.             lpPropTagArray->aulPropTag[cValues++] = ulPropTagT;
  773.         }
  774.     }
  775.     lpPropTagArray->cValues = cValues;
  776.  
  777.     /* Exclude selected properties from our TNEF encapsulation. */
  778.  
  779.     hResult = lpTnef->lpVtbl->AddProps(lpTnef,
  780.         TNEF_PROP_EXCLUDE, 0L, NULL, lpPropTagArray);
  781.  
  782.     if (HR_FAILED(hResult))
  783.     {
  784.         DebugTrace("AddProps failed.\n");
  785.         goto ret;
  786.     }
  787.  
  788.     /* Check our .2 second timer! */
  789.  
  790.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  791.  
  792.     if (sc == MAPI_W_CANCEL_MESSAGE)
  793.     {
  794.         DebugTrace("Cancelling message delivery.\n");
  795.         goto ret;
  796.     }
  797.  
  798.     /* If we had a recipient type, we should help the receiver side out
  799.        with some properties, as follows:
  800.  
  801.        PR_MESSAGE_TO_ME should be set TRUE if ulRecipType == MAPI_TO
  802.        PR_MESSAGE_CC_ME should be set TRUE if ulRecipType == MAPI_CC
  803.        PR_MESSAGE_RECIP_ME should be set TRUE for either of the above */
  804.  
  805.     if (ulRecipType)
  806.     {
  807.         spvDestMsgProps[0].ulPropTag = PR_MESSAGE_TO_ME;
  808.         spvDestMsgProps[0].Value.b = (ulRecipType == MAPI_TO);
  809.  
  810.         spvDestMsgProps[1].ulPropTag = PR_MESSAGE_CC_ME;
  811.         spvDestMsgProps[1].Value.b = (ulRecipType == MAPI_CC);
  812.  
  813.         spvDestMsgProps[2].ulPropTag = PR_MESSAGE_RECIP_ME;
  814.         spvDestMsgProps[2].Value.b = ((ulRecipType == MAPI_TO) || (ulRecipType == MAPI_CC));
  815.  
  816.         lpxpl->FreeBuffer(lpProblems);
  817.         lpProblems = NULL;
  818.  
  819.         hResult = lpTnef->lpVtbl->SetProps(lpTnef, 0L, 0L, 3,
  820.             spvDestMsgProps);
  821.  
  822.         if (HR_FAILED(hResult))
  823.         {
  824.             DebugTrace("SetProps failed");
  825.             goto ret;
  826.         }
  827.     }
  828.  
  829.     /* OK. All the properties are copied over. Save Changes on the
  830.        message we created. */
  831.  
  832.     hResult = lpTnef->lpVtbl->Finish(lpTnef, 0L, &wKey, &lptpa);
  833.     lpxpl->FreeBuffer(lptpa);
  834.     if (HR_FAILED(hResult))
  835.     {
  836.         DebugTrace("Finish failed.\n");
  837.         goto ret;
  838.     }
  839.  
  840.     hResult = lpXPSof->lpVtbl->Commit(lpXPSof, STGC_DEFAULT);
  841.  
  842.     if (HR_FAILED(hResult))
  843.     {
  844.         DebugTrace("Commit stream failed.\n");
  845.         goto ret;
  846.     }
  847.  
  848.     /* Check our .2 second timer! */
  849.  
  850.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  851.  
  852.     if (sc == MAPI_W_CANCEL_MESSAGE)
  853.     {
  854.         DebugTrace("Cancelling message delivery.\n");
  855.         goto ret;
  856.     }
  857.  
  858.     *lpfSent = TRUE;
  859.  
  860.     /* Log successful transmission. */
  861.  
  862.     PrintfTransportLog(TEXT("Delivery Complete: %s"), rgchOutFileName);
  863.  
  864. ret:
  865.     /* Release any open object */
  866.  
  867.     UlRelease(lpTnef);
  868.     UlRelease(lpXPSof);
  869.     UlRelease(lpSof);
  870.  
  871.     if (HR_FAILED(hResult) && lpSof)
  872.         DeleteFile(rgchOutFileName);
  873.  
  874.     /* Release the prop tag array and/or problem array if any */
  875.  
  876.     lpxpl->FreeBuffer(lpPropTagArray);
  877.  
  878.     DebugTraceResult(HrSendOneMessage, hResult);
  879.     return hResult;
  880. }
  881.  
  882.  
  883. /* Stuff to support the textized message formatting */
  884.  
  885. const static SizedSPropTagArray(4, sptMsgProps) =
  886. {
  887.     4,
  888.     {
  889.         PR_CLIENT_SUBMIT_TIME,
  890.         PR_SUBJECT,
  891.         PR_PRIORITY,
  892.         PR_BODY
  893.     }
  894. };
  895.  
  896. const static SizedSPropTagArray(5, sptRecipProps) =
  897. {
  898.     5,
  899.     {
  900.         PR_RECIPIENT_TYPE,
  901.         PR_EMAIL_ADDRESS,
  902.         PR_ADDRTYPE,
  903.         PR_DISPLAY_NAME,
  904.         PR_RESPONSIBILITY
  905.     }
  906. };
  907.  
  908. const static SizedSPropTagArray(3, sptSenderDelegate) =
  909. {
  910.     3,
  911.     {
  912.         PR_SENT_REPRESENTING_ENTRYID,
  913.         PR_SENDER_ENTRYID,
  914.         PR_REPLY_RECIPIENT_ENTRIES,
  915.     }
  916. };
  917.  
  918. TCHAR rgszTags[NUM_TAGS][MAX_TAG_LEN] =
  919. {
  920.     TEXT("Message: "),
  921.     TEXT("From: "),
  922.     TEXT("Representing: "),
  923.     TEXT("Reply To: "),
  924.     TEXT("Date: "),
  925.     TEXT("To: "),
  926.     TEXT("Cc: "),
  927.     TEXT("Bcc: "),
  928.     TEXT("Subject: "),
  929.     TEXT("Priority Urgent: "),
  930.     TEXT("Priority Normal: "),
  931.     TEXT("Priority Low: "),
  932.     TEXT("Contents: "),
  933.     TEXT("Text Item: "),
  934.     TEXT("File Item: ")
  935. };
  936.  
  937. TCHAR szCRLF[3] = {TEXT("\r\n")};
  938. TCHAR szCRLFCRLF[5] = {TEXT("\r\n\r\n")};
  939.  
  940.  
  941. /*
  942.  -  HrIMsgToTextMsg
  943.  -
  944.  *  Purpose:
  945.  *      Called by HrSendOneMessage() to write the envelope properties
  946.  *      to the destination message text file.  Uses the StreamOnFile
  947.  *      interface for all the file access.  This stream interface
  948.  *      is then passed to the TNEF encoder, with its current file
  949.  *      pointer un-modified, so the non-envelope properties can be
  950.  *      encapsulated in a TNEF encapsulation in a "File Item" section.
  951.  *
  952.  *  Parameters:
  953.  *      lpxpl               Pointer to Transport Logon object
  954.  *      lpPropArray         Copy of the sessions logon properties
  955.  *      lpMessage           Message to send
  956.  *      lpSof               Pointer to the stream interface
  957.  *
  958.  *  Returns:
  959.  *      (HRESULT)           Set if an error encountered.
  960.  *
  961.  *  Operation:
  962.  *      Call GetProps() on the message to extract PR_SUBMIT_DATE, PR_SUBJECT,
  963.  *      PR_PRIORITY, and PR_BODY.  Then call GetRecipientTable() to extract
  964.  *      the PR_DISPLAY_NAME and PR_EMAIL_ADDRESS for each type of
  965.  *      PR_RECIPIENT_TYPE.  Write all this info to the stream (after
  966.  *      formatting appropriately).
  967.  */
  968.  
  969. HRESULT
  970. HrIMsgToTextMsg(LPXPL lpxpl, LPSPropValue lpPropArray, LPMESSAGE lpMessage, LPSTREAM lpSof)
  971. {
  972.     HRESULT hr = hrSuccess;
  973.     ULONG cMsgVals = 0;
  974.     ULONG cSndrVals = 0;
  975.     LPSPropValue lpMsgProps = NULL;
  976.     LPSPropValue lpSndrProps = NULL;
  977.     LPSPropValue lpPropT = NULL;
  978.     LPMAPITABLE lpTbl = NULL;
  979.     LPSRowSet lpRows = NULL;
  980.     ULONG uli;
  981.     ULONG cbOut;
  982.     ULONG cbRead;
  983.     ULONG cbCRLF = lstrlen(szCRLF);
  984.     ULONG cbCRLFCRLF = lstrlen(szCRLFCRLF);
  985.     TCHAR szRep[128];
  986.     TCHAR szFrom[128];
  987.     TCHAR szOutBuf[128];
  988.     TCHAR rgchStrmBuf[MAX_STRM_BUF];
  989.     LPTSTR lpszT1;
  990.     LPTSTR lpszT2;
  991.     LPSTREAM lpStrm = NULL;
  992.  
  993.     /* Get the 4 Message properties to be textized */
  994.  
  995.     hr = lpMessage->lpVtbl->GetProps(lpMessage,
  996.             (LPSPropTagArray) &sptMsgProps, 0, /* ansi */
  997.             &cMsgVals, &lpMsgProps);
  998.  
  999.     if (HR_FAILED(hr) || !cMsgVals)
  1000.     {
  1001.         DebugTrace("GetProps() MsgProps failed.\n");
  1002.         goto ret;
  1003.     }
  1004.  
  1005.     /* Get the 3 Sender/Delegate properties to be textized */
  1006.  
  1007.     hr = lpMessage->lpVtbl->GetProps(lpMessage,
  1008.             (LPSPropTagArray) &sptSenderDelegate, 0, /* ansi */
  1009.             &cSndrVals, &lpSndrProps);
  1010.  
  1011.     if (HR_FAILED(hr) || !cSndrVals)
  1012.     {
  1013.         DebugTrace("GetProps()  SndrProps failed.\n");
  1014.         goto ret;
  1015.     }
  1016.  
  1017.     /* Returns the Recipient Table in a well defined state
  1018.        (i.e. minimal column set and restricted on our AddrType). */
  1019.  
  1020.     hr = HrPrepareRecipientTable(lpPropArray, lpMessage, &lpTbl);
  1021.  
  1022.     if (HR_FAILED(hr))
  1023.     {
  1024.         DebugTrace("HrPrepareRecipientTable()  failed.\n");
  1025.         goto ret;
  1026.     }
  1027.  
  1028.     /* Write Message: field */
  1029.  
  1030.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagMessage],
  1031.             lstrlen(rgszTags[tagMessage]), &cbOut), ret);
  1032.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1033.             cbCRLFCRLF, &cbOut), ret);
  1034.  
  1035.     /* Write From: and Representing: fields */
  1036.  
  1037.     /* The following code sets the Sender/Delegate */
  1038.     /* properties as follows:
  1039.  
  1040.         1) If no PR_SENT_REPRESENTING_??? in message,
  1041.             a)  If no PR_SENDER_??? in message,
  1042.                     PR_SENDER_??? = transport identities
  1043.             b)  PR_SENT_REPRESENTING_??? = PR_SENDER_???
  1044.         2) (else) If there was a PR_SENT_REPRESENTING_??? in message,
  1045.             a)  If no PR_SENDER_??? in message,
  1046.                     PR_SENDER_??? = PR_SENT_REPRESENTING_???
  1047.         3)  If no PR_REPLY_RECIPIENT_???,
  1048.                 PR_REPLY_RECIPIENT_ENTRIES = PR_SENT_REPRESENTING_ENTRYID
  1049.                 PR_REPLY_RECIPIENT_NAMES = PR_SENT_REPRESENTING_NAME
  1050.  
  1051.        This just works because HrCrackSenderEID() doesn't change the
  1052.        szFrom or szRep memory unless it is successful.  It is for this
  1053.        reason that we don't check the return from HrCrackSenderEID() */
  1054.  
  1055.     hr = hrSuccess;
  1056.     *szFrom = '\0';
  1057.     *szRep = '\0';
  1058.  
  1059.     if (lpSndrProps[0].ulPropTag == PR_SENT_REPRESENTING_ENTRYID)
  1060.         HrCrackSenderEID(lpxpl, lpSndrProps[0].Value.bin.cb,
  1061.             lpSndrProps[0].Value.bin.lpb, szRep);
  1062.  
  1063.     if (lpSndrProps[1].ulPropTag == PR_SENDER_ENTRYID)
  1064.         HrCrackSenderEID(lpxpl, lpSndrProps[1].Value.bin.cb,
  1065.             lpSndrProps[1].Value.bin.lpb, szFrom);
  1066.  
  1067.     if (!*szFrom && !*szRep)
  1068.     {
  1069.         lpszT1 = ArrayIndex(PR_SAMPLE_DISPLAY_NAME, lpPropArray).Value.LPSZ;
  1070.         lpszT2 = ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS, lpPropArray).Value.LPSZ;
  1071.         wsprintf(szFrom, TEXT("%s[%s]"), lpszT1, lpszT2);
  1072.         lstrcpy(szRep, szFrom);
  1073.     }
  1074.     else if (*szFrom && !*szRep)
  1075.     {
  1076.         lstrcpy(szRep, szFrom);
  1077.     }
  1078.     else
  1079.     {
  1080.         lstrcpy(szFrom, szRep);
  1081.     }
  1082.  
  1083.     wsprintf(szOutBuf, TEXT("%s%s"), rgszTags[tagFrom], szFrom);
  1084.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szOutBuf,
  1085.             lstrlen(szOutBuf), &cbOut), ret);
  1086.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1087.             cbCRLFCRLF, &cbOut), ret);
  1088.  
  1089.     wsprintf(szOutBuf, TEXT("%s%s"), rgszTags[tagRepresenting], szRep);
  1090.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szOutBuf,
  1091.             lstrlen(szOutBuf), &cbOut), ret);
  1092.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1093.             cbCRLFCRLF, &cbOut), ret);
  1094.  
  1095.     /* Write Reply To: fields */
  1096.  
  1097.     if (lpSndrProps[2].ulPropTag == PR_REPLY_RECIPIENT_ENTRIES)
  1098.     {
  1099.         LPFLATENTRYLIST lpList = (LPFLATENTRYLIST) lpSndrProps[2].Value.bin.lpb;
  1100.         LPBYTE lpb;
  1101.         ULONG cEntries;
  1102.  
  1103.         /* Attempt some level of validation for this property */
  1104.  
  1105.         if (!lpList
  1106.             || IsBadReadPtr(lpList, CbNewFLATENTRYLIST(0))
  1107.             || !lpList->abEntries
  1108.             || IsBadReadPtr(lpList, (UINT) CbFLATENTRYLIST(lpList))
  1109.             || !lpList->cEntries
  1110.             || (lpList->cEntries * sizeof(GUID) > lpList->cbEntries))
  1111.         {
  1112.             DebugTrace("Bad PR_REPLY_RECIPIENT_ENTRIES!\n");
  1113.             DebugTrace("Skipping the Reply To: field.\n");
  1114.         }
  1115.         else
  1116.         {
  1117.             lpb = lpList->abEntries;
  1118.             cEntries = lpList->cEntries;
  1119.  
  1120.             while (cEntries--)
  1121.             {
  1122.                 LPFLATENTRY lpEntry = (LPFLATENTRY) lpb;
  1123.                 ULONG ulSize;
  1124.  
  1125.                 if (IsBadReadPtr(lpEntry, CbNewFLATENTRY(0))
  1126.                     || IsBadReadPtr(lpEntry, (UINT) CbFLATENTRY(lpEntry)))
  1127.                 {
  1128.                     DebugTrace("Bad entry inside PR_REPLY_RECIPIENT_ENTRIES!\n");
  1129.                     break;
  1130.                 }
  1131.  
  1132.                 ulSize = lpEntry->cb;
  1133.  
  1134.                 hr = HrCrackSenderEID(lpxpl, ulSize, lpEntry->abEntry, szFrom);
  1135.  
  1136.                 if (!hr)
  1137.                 {
  1138.                     wsprintf(szOutBuf, TEXT("%s%s"),
  1139.                         rgszTags[tagReplyTo], szFrom);
  1140.                     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szOutBuf,
  1141.                             lstrlen(szOutBuf), &cbOut), ret);
  1142.                     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1143.                             cbCRLF, &cbOut), ret);
  1144.                 }
  1145.  
  1146.                 lpb += offsetof (FLATENTRY, abEntry) + ((ulSize + 3) & -4L);
  1147.             }
  1148.  
  1149.             /* Add one more CR/LF pair after Reply Recipients */
  1150.  
  1151.             TraceFailedWrite(lpSof->lpVtbl->Write(lpSof,
  1152.                     szCRLF, cbCRLF, &cbOut), ret);
  1153.         }
  1154.     }
  1155.     else
  1156.     {
  1157.         wsprintf(szOutBuf, TEXT("%s%s"), rgszTags[tagReplyTo], szFrom);
  1158.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szOutBuf,
  1159.                 lstrlen(szOutBuf), &cbOut), ret);
  1160.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1161.                 cbCRLFCRLF, &cbOut), ret);
  1162.     }
  1163.  
  1164.     /* Write Date: field */
  1165.  
  1166.     if (FPropIndex(lpMsgProps, cMsgVals, PR_CLIENT_SUBMIT_TIME, &uli))
  1167.     {
  1168.         /* Property exists in message; write it to the stream */
  1169.         FormatFileTime(&lpMsgProps[uli].Value.ft, szOutBuf);
  1170.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagDate],
  1171.                 lstrlen(rgszTags[tagDate]), &cbOut), ret);
  1172.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szOutBuf,
  1173.                 lstrlen(szOutBuf), &cbOut), ret);
  1174.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1175.                 cbCRLFCRLF, &cbOut), ret);
  1176.     }
  1177.  
  1178.     /* Write To: & Cc: fields */
  1179.  
  1180.     while (TRUE)
  1181.     {
  1182.         /* Get a row from the Recipient Table */
  1183.  
  1184.         hr = lpTbl->lpVtbl->QueryRows(lpTbl, 1, 0, &lpRows);
  1185.  
  1186.         if (hr || !lpRows || (lpRows->cRows != 1))
  1187.             break;
  1188.  
  1189.         lpPropT = lpRows->aRow[0].lpProps;
  1190.  
  1191.         /* Throw away MAPI_ORIG and P1 Recipient Types */
  1192.  
  1193.         if ((lpPropT[0].Value.l != MAPI_TO) &&
  1194.             (lpPropT[0].Value.l != MAPI_CC))
  1195.         {
  1196.             FreeProws(lpRows);
  1197.             lpRows = NULL;
  1198.             lpPropT = NULL;
  1199.             continue;
  1200.         }
  1201.  
  1202.         /* Write Recipients as:
  1203.             '{To: | Cc: } Display Name [email-address]' */
  1204.  
  1205.         Assert((lpPropT[0].Value.l == MAPI_TO) ||
  1206.             (lpPropT[0].Value.l == MAPI_CC));
  1207.  
  1208.         wsprintf(szOutBuf, TEXT("%s%s[%s]"),
  1209.             rgszTags[tagDate + lpPropT[0].Value.l],
  1210.             lpPropT[3].Value.LPSZ, lpPropT[1].Value.LPSZ);
  1211.  
  1212.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szOutBuf,
  1213.                 lstrlen(szOutBuf), &cbOut), ret);
  1214.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1215.                 cbCRLF, &cbOut), ret);
  1216.  
  1217.         /* Clean-Up */
  1218.  
  1219.         FreeProws(lpRows);
  1220.         lpRows = NULL;
  1221.         lpPropT = NULL;
  1222.     }
  1223.  
  1224.     /* Add one more CR/LF pair after recipients */
  1225.  
  1226.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF, cbCRLF, &cbOut), ret);
  1227.  
  1228.     /* Write Subject: field */
  1229.  
  1230.     if (FPropIndex(lpMsgProps, cMsgVals, PR_SUBJECT, &uli))
  1231.     {
  1232.         /* Property exists and is small enough to not require a
  1233.            stream interface to be opened on it; just write it. */
  1234.  
  1235.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagSubject],
  1236.                 lstrlen(rgszTags[tagSubject]), &cbOut), ret);
  1237.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1238.                 cbCRLF, &cbOut), ret);
  1239.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, lpMsgProps[uli].Value.LPSZ,
  1240.                 lstrlen(lpMsgProps[uli].Value.LPSZ), &cbOut), ret);
  1241.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1242.                 cbCRLFCRLF, &cbOut), ret);
  1243.     }
  1244.     else if (!(hr = lpMessage->lpVtbl->OpenProperty(lpMessage, PR_SUBJECT,
  1245.                 (LPIID) &IID_IStream, 0, 0, (LPUNKNOWN *) &lpStrm)))
  1246.     {
  1247.         /* Property exists and requires a stream interface to
  1248.            access all the data.  Copy between streams! */
  1249.  
  1250.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagSubject],
  1251.                 lstrlen(rgszTags[tagSubject]), &cbOut), ret);
  1252.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1253.                 cbCRLF, &cbOut), ret);
  1254.  
  1255.         while (TRUE)
  1256.         {
  1257.             hr = lpStrm->lpVtbl->Read(lpStrm, (LPVOID) rgchStrmBuf,
  1258.                 MAX_STRM_BUF, &cbRead);
  1259.  
  1260.             if (hr || (cbRead == 0))
  1261.                 break;          /* There's nothing to write; we're done! */
  1262.  
  1263.             TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, (LPVOID) rgchStrmBuf,
  1264.                     cbRead, &cbOut), ret);
  1265.  
  1266.             if (cbRead < MAX_STRM_BUF)
  1267.                 break;          /* We've exhausted the stream; we're done! */
  1268.         }
  1269.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1270.                 cbCRLFCRLF, &cbOut), ret);
  1271.  
  1272.         lpStrm->lpVtbl->Release(lpStrm);
  1273.         lpStrm = NULL;
  1274.     }
  1275.  
  1276.     /* Write Priority: field */
  1277.  
  1278.     if (FPropIndex(lpMsgProps, cMsgVals, PR_PRIORITY, &uli))
  1279.     {
  1280.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof,
  1281.                 rgszTags[tagPrioNormal - lpMsgProps[uli].Value.l],
  1282.                 lstrlen(rgszTags[tagPrioNormal - lpMsgProps[uli].Value.l]),
  1283.                 &cbOut), ret);
  1284.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1285.                 cbCRLFCRLF, &cbOut), ret);
  1286.     }
  1287.  
  1288.     /* Write Contents: field */
  1289.  
  1290.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagContents],
  1291.             lstrlen(rgszTags[tagContents]), &cbOut), ret);
  1292.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1293.             cbCRLFCRLF, &cbOut), ret);
  1294.  
  1295.     /* Text Item: */
  1296.  
  1297.     if (FPropIndex(lpMsgProps, cMsgVals, PR_BODY, &uli))
  1298.     {
  1299.         /* Property exists and is small enough to not require
  1300.            a stream interface to be opened on it; just copy it */
  1301.  
  1302.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagTextItem],
  1303.                 lstrlen(rgszTags[tagTextItem]), &cbOut), ret);
  1304.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1305.                 cbCRLF, &cbOut), ret);
  1306.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, lpMsgProps[uli].Value.LPSZ,
  1307.                 lstrlen(lpMsgProps[uli].Value.LPSZ), &cbOut), ret);
  1308.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1309.                 cbCRLFCRLF, &cbOut), ret);
  1310.     }
  1311.     else if (!(hr = lpMessage->lpVtbl->OpenProperty(lpMessage, PR_BODY,
  1312.                 (LPIID) &IID_IStream, 0, 0, (LPUNKNOWN *) &lpStrm)))
  1313.     {
  1314.         /* Property exists and requires a stream interface to
  1315.            access all the data.  Copy between streams! */
  1316.  
  1317.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagTextItem],
  1318.                 lstrlen(rgszTags[tagTextItem]), &cbOut), ret);
  1319.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1320.                 cbCRLF, &cbOut), ret);
  1321.  
  1322.         while (TRUE)
  1323.         {
  1324.             hr = lpStrm->lpVtbl->Read(lpStrm, (LPVOID) rgchStrmBuf,
  1325.                 MAX_STRM_BUF, &cbRead);
  1326.  
  1327.             if (hr || (cbRead == 0))
  1328.                 break;          /* There's nothing to write; we're done! */
  1329.  
  1330.             TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, (LPVOID) rgchStrmBuf,
  1331.                     cbRead, &cbOut), ret);
  1332.  
  1333.             if (cbRead < MAX_STRM_BUF)
  1334.                 break;          /* We've exhausted the stream; we're done! */
  1335.         }
  1336.         TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLFCRLF,
  1337.                 cbCRLFCRLF, &cbOut), ret);
  1338.  
  1339.         lpStrm->lpVtbl->Release(lpStrm);
  1340.         lpStrm = NULL;
  1341.     }
  1342.  
  1343.     /* File Item: */
  1344.  
  1345.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, rgszTags[tagFileItem],
  1346.             lstrlen(rgszTags[tagFileItem]), &cbOut), ret);
  1347.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, TEXT("MESSAGE.TNF"),
  1348.             lstrlen(TEXT("MESSAGE.TNF")), &cbOut), ret);
  1349.     TraceFailedWrite(lpSof->lpVtbl->Write(lpSof, szCRLF,
  1350.             cbCRLF, &cbOut), ret);
  1351.  
  1352. ret:
  1353.     lpxpl->FreeBuffer(lpMsgProps);
  1354.     lpxpl->FreeBuffer(lpSndrProps);
  1355.     FreeProws(lpRows);
  1356.  
  1357.     UlRelease(lpTbl);
  1358.  
  1359.     DebugTraceResult(HrIMsgToTextMsg(), hr);
  1360.     return hr;
  1361. }
  1362.  
  1363.  
  1364. /*
  1365.  -  HrPrepareRecipientTable
  1366.  -
  1367.  *  Purpose:
  1368.  *      Gets the Recipient Table from an IMAPIMessage and sets its
  1369.  *      columns, restricts the view, and sorts it (if sorts are
  1370.  *      supported).  All this in preparation of writing recipients
  1371.  *      to the destination message file.
  1372.  *
  1373.  *  Parameters:
  1374.  *      lpPropArray         Pointer to Transport Logon object
  1375.  *      lpMsg               Message to GetRecipientTable on
  1376.  *      lppTbl              Receives the RecipientTable
  1377.  *
  1378.  *  Returns:
  1379.  *      hr                  Indicating Success/Failure
  1380.  */
  1381.  
  1382. HRESULT
  1383. HrPrepareRecipientTable(LPSPropValue lpPropArray, LPMESSAGE lpMsg, LPMAPITABLE * lppTbl)
  1384. {
  1385.     HRESULT hr = hrSuccess;
  1386.     LPMAPITABLE lpTbl = NULL;
  1387.  
  1388.     SizedSSortOrderSet(2, rgSort) =
  1389.     {
  1390.         2, 0, 0,
  1391.         {
  1392.             PR_RECIPIENT_TYPE, TABLE_SORT_ASCEND,
  1393.                 PR_ROWID, TABLE_SORT_ASCEND
  1394.         }
  1395.     };
  1396.  
  1397.     *lppTbl = NULL;
  1398.  
  1399.     hr = lpMsg->lpVtbl->GetRecipientTable(lpMsg, 0, &lpTbl);
  1400.  
  1401.     if (HR_FAILED(hr))
  1402.     {
  1403.         DebugTrace("GetRecipientTable() failed in HrPrepareRecipientTable()");
  1404.         goto ret;
  1405.     }
  1406.  
  1407.     /* SetColumns to: PR_RECIPIENT_TYPE, PR_EMAIL_ADDRESS, PR_ADDRTYPE,
  1408.        PR_DISPLAY_NAME, and PR_RESPONSIBILITY in that order */
  1409.  
  1410.     hr = lpTbl->lpVtbl->SetColumns(lpTbl, (LPSPropTagArray) &sptRecipProps,
  1411.         TBL_BATCH);
  1412.  
  1413.     if (HR_FAILED(hr))
  1414.     {
  1415.         DebugTrace("SetColumns() failed in HrPrepareRecipientTable()");
  1416.         goto ret;
  1417.     }
  1418.  
  1419.     /* Sort by: PR_RECIPIENT_TYPE (i.e. MAPI_TO, MAPI_CC, MAPI_BCC)
  1420.             and PR_ROWID, both in acsending order */
  1421.  
  1422.     hr = lpTbl->lpVtbl->SortTable(lpTbl, (LPSSortOrderSet) &rgSort, TBL_BATCH);
  1423.  
  1424.     if (hr)
  1425.     {
  1426.         /* Don't fail the call for no support!  Just return
  1427.            the table in whatever order it may be in by default. */
  1428.  
  1429.         if (GetScode(hr) == MAPI_E_NO_SUPPORT)
  1430.             hr = 0;
  1431.         else
  1432.             DebugTrace("SortTable() failed in HrPrepareRecipientTable()");
  1433.     }
  1434.  
  1435.     /* Seek to the beginning since sorting may have moved out position */
  1436.  
  1437.     hr = lpTbl->lpVtbl->SeekRow(lpTbl, BOOKMARK_BEGINNING, 0, NULL);
  1438.  
  1439.     if (hr)
  1440.     {
  1441.         DebugTrace("SeekRow() failed in HrPrepareRecipientTable()");
  1442.     }
  1443.  
  1444. ret:
  1445.     if (HR_FAILED(hr))
  1446.     {
  1447.         UlRelease(lpTbl);
  1448.     }
  1449.     else
  1450.         *lppTbl = lpTbl;
  1451.  
  1452.     DebugTraceResult(HrPrepareRecipientTable(), hr);
  1453.     return hr;
  1454. }
  1455.  
  1456.  
  1457. /*
  1458.  -  HrCrackSenderEID
  1459.  -
  1460.  *  Purpose:
  1461.  *      Does an OpenEntry() on the EntryID and GetProps() PR_DISPLAY_NAME
  1462.  *      and PR_EMAIL_ADDRESS.  Then formats the return string like:
  1463.  *          "Display Name[email-address]"
  1464.  *
  1465.  *  Parameters:
  1466.  *      lpxpl           Pointer to Transport Logon object
  1467.  *      cb              Count of bytes in EntryID
  1468.  *      lpb             Pointer to EntryID
  1469.  *      lpsz            Receives the formatted Name/Address pair
  1470.  *
  1471.  *  Returns:
  1472.  *      hr              Indicating Success/Failure
  1473.  *
  1474.  *  Note:
  1475.  *      No parameter validation!  I assume the caller knew what was
  1476.  *      being passed in and ensured all was well.
  1477.  */
  1478.  
  1479. HRESULT
  1480. HrCrackSenderEID(LPXPL lpxpl, ULONG cb, LPBYTE lpb, LPTSTR lpsz)
  1481. {
  1482.     HRESULT hr = hrSuccess;
  1483.     ULONG ulObjType;
  1484.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  1485.     LPMAPIPROP lpMAPIProp = NULL;
  1486.     ULONG cVals = 0;
  1487.     LPSPropValue lpProps = NULL;
  1488.  
  1489.     const static SizedSPropTagArray(2, sptCracked) =
  1490.     {
  1491.         2,
  1492.         {
  1493.             PR_DISPLAY_NAME,
  1494.             PR_EMAIL_ADDRESS
  1495.         }
  1496.     };
  1497.  
  1498.     /* Open a Property Interface on this EntryID */
  1499.  
  1500.     hr = lpMAPISup->lpVtbl->OpenEntry(lpMAPISup, cb, (LPENTRYID) lpb,
  1501.         NULL, 0, &ulObjType, (LPUNKNOWN *) &lpMAPIProp);
  1502.  
  1503.     if (hr)
  1504.     {
  1505.         DebugTrace("OpenEntry() Failed in HrCrackSenderEID().\n");
  1506.         goto ret;
  1507.     }
  1508.  
  1509.     /* Get the 2 properties we need from this object */
  1510.  
  1511.     hr = lpMAPIProp->lpVtbl->GetProps(lpMAPIProp,
  1512.         (LPSPropTagArray) &sptCracked, 0, /* ansi */
  1513.         &cVals, &lpProps);
  1514.  
  1515.     if (hr || !cVals)
  1516.     {
  1517.         DebugTrace("GetProps() Failed in HrCrackSenderEID().\n");
  1518.         goto ret;
  1519.     }
  1520.  
  1521.     /* Assert that all went well so far!!! */
  1522.  
  1523.     Assert(lpProps);
  1524.     Assert(cVals == 2);
  1525.     Assert(lpProps[0].ulPropTag == PR_DISPLAY_NAME);
  1526.     Assert(lpProps[1].ulPropTag == PR_EMAIL_ADDRESS);
  1527.  
  1528.     /* Format our Name/Address pair as desired */
  1529.  
  1530.     wsprintf(lpsz, "%s[%s]", lpProps[0].Value.LPSZ, lpProps[1].Value.LPSZ);
  1531.  
  1532. ret:
  1533.     lpxpl->FreeBuffer(lpProps);
  1534.  
  1535.     UlRelease(lpMAPIProp);
  1536.  
  1537.     DebugTraceResult(HrCrackSenderEID(), hr);
  1538.     return hr;
  1539. }
  1540.  
  1541.  
  1542. /*
  1543.  -  FPropIndex
  1544.  -
  1545.  *  Purpose:
  1546.  *      Finds and returns (if it exists) the index of ulPropTag
  1547.  *      in the lpProps SPropValue array.
  1548.  *
  1549.  *  Parameters:
  1550.  *      lpProps         PropValue array to search through
  1551.  *      cVals           Count of properties in lpProps
  1552.  *      ulPropTag       PropTag to search for
  1553.  *      puli            Receives the index of ulPropTag in lpProps
  1554.  *
  1555.  *  Returns:
  1556.  *      TRUE/FALSE      TRUE if found, FALSE otherwise
  1557.  */
  1558.  
  1559. BOOL
  1560. FPropIndex(LPSPropValue lpProps, ULONG cVals, ULONG ulPropTag, ULONG * puli)
  1561. {
  1562.     Assert(lpProps);
  1563.     Assert(cVals);
  1564.     Assert(puli);
  1565.  
  1566.     while (cVals--)
  1567.     {
  1568.         if (lpProps[cVals].ulPropTag == ulPropTag)
  1569.         {
  1570.             *puli = cVals;
  1571.             return TRUE;
  1572.         }
  1573.     }
  1574.     return FALSE;
  1575. }
  1576.  
  1577.  
  1578. /*
  1579.  -  FormatFileTime
  1580.  -
  1581.  *  Purpose:
  1582.  *      Formats a Windows NT file time as a MAPI date/time string
  1583.  *      of the format:  yyyy/mm/dd hh:mm
  1584.  *
  1585.  *  Parameters:
  1586.  *      pft             Pointer to FILETIME to convert
  1587.  *      szTime          Destination string
  1588.  *
  1589.  *  Returns:
  1590.  *      void.
  1591.  */
  1592.  
  1593. void
  1594. FormatFileTime(FILETIME * pft, LPTSTR szTime)
  1595. {
  1596.     SYSTEMTIME systime;
  1597.  
  1598.     FileTimeToSystemTime(pft, &systime);
  1599.     wsprintf(szTime, "%04.4d/%02.2d/%02.2d %02.2d:%02.2d",
  1600.         systime.wYear, systime.wMonth, systime.wDay,
  1601.         systime.wHour, systime.wMinute);
  1602. }
  1603.  
  1604.  
  1605. /*
  1606.  -  FIsTextizedProp
  1607.  -
  1608.  *  Purpose:
  1609.  *      Used to determine if the property is one we wish to
  1610.  *      exclude from the TNEF encapsulation (i.e. one we've
  1611.  *      textized in the envelope of the message file).
  1612.  *
  1613.  *  Parameters:
  1614.  *      ulPropTag       PropTag to test
  1615.  *
  1616.  *  Return:
  1617.  *      BOOL            Indicating if the property is textized
  1618.  */
  1619.  
  1620. BOOL
  1621. FIsTextizedProp(ULONG ulPropTag)
  1622. {
  1623.     ULONG i;
  1624.  
  1625.     static SizedSPropTagArray(16, spta) =
  1626.     {
  1627.         16,
  1628.         {
  1629.             PR_SENDER_NAME,
  1630.             PR_SENDER_ENTRYID,
  1631.             PR_SENDER_SEARCH_KEY,
  1632.             PR_SENDER_EMAIL_ADDRESS,
  1633.             PR_SENDER_ADDRTYPE,
  1634.             PR_SENT_REPRESENTING_NAME,
  1635.             PR_SENT_REPRESENTING_ENTRYID,
  1636.             PR_SENT_REPRESENTING_SEARCH_KEY,
  1637.             PR_SENT_REPRESENTING_EMAIL_ADDRESS,
  1638.             PR_SENT_REPRESENTING_ADDRTYPE,
  1639.             PR_REPLY_RECIPIENT_ENTRIES,
  1640.             PR_REPLY_RECIPIENT_NAMES,
  1641.             PR_SUBJECT,
  1642.             PR_CLIENT_SUBMIT_TIME,
  1643.             PR_BODY,
  1644.             PR_PRIORITY
  1645.         }
  1646.     };
  1647.  
  1648.     for (i = 0; i < spta.cValues; i++)
  1649.         if (spta.aulPropTag[i] == ulPropTag)
  1650.             return TRUE;
  1651.  
  1652.     return FALSE;
  1653. }
  1654.  
  1655.  
  1656. /*
  1657.  -  FreeMyAdrList
  1658.  -
  1659.  *  Purpose:
  1660.  *      Called by anyone who winds up with a MYADRLIST structure
  1661.  *      and wants to free it.
  1662.  *
  1663.  *  Parameters:
  1664.  *      lpxpl               Session context.
  1665.  *      lpMyAdrList         Structure to free.
  1666.  *
  1667.  *  Returns:
  1668.  *      void                Data in lpMyAdrList freed.
  1669.  *
  1670.  *  Operation:
  1671.  *      Walks through adrlist and frees all the memory.
  1672.  */
  1673.  
  1674. void
  1675. FreeMyAdrList(LPXPL lpxpl, LPMYADRLIST lpMyAdrList)
  1676. {
  1677.     ULONG ulT;
  1678.     LPSPropValue lpspvT;
  1679.     LPADRLIST lpAdrListT;
  1680.  
  1681.     /*  Clean up any adrlist stuff that's lying around. */
  1682.  
  1683.     if (lpMyAdrList)
  1684.     {
  1685.         lpAdrListT = lpMyAdrList->lpAdrList;
  1686.  
  1687.         if (lpAdrListT && lpAdrListT->cEntries)
  1688.         {
  1689.             for (ulT = 0; ulT < lpAdrListT->cEntries; ulT++)
  1690.             {
  1691.                 lpspvT = (lpAdrListT->aEntries[ulT]).rgPropVals;
  1692.  
  1693.                 lpxpl->FreeBuffer((LPVOID) lpspvT);
  1694.             }
  1695.         }
  1696.         lpxpl->FreeBuffer((LPVOID) lpAdrListT);
  1697.         lpxpl->FreeBuffer((LPVOID) lpMyAdrList);
  1698.     }
  1699. }
  1700.  
  1701.  
  1702. CHAR lpszEOM[] = "\n*** End of Message ***\n";
  1703. #define cchEOM (sizeof(lpszEOM) - 1)
  1704.  
  1705. STDMETHODIMP
  1706. PreprocessMessage (LPMAPISESSION lpSession,
  1707.     LPMESSAGE lpMessage,
  1708.     LPADRBOOK lpAdrBook,
  1709.     LPMAPIFOLDER lpFolder,
  1710.     LPALLOCATEBUFFER AllocateBuffer,
  1711.     LPALLOCATEMORE AllocateMore,
  1712.     LPFREEBUFFER FreeBuffer,
  1713.     ULONG FAR *lpcOutbound,
  1714.     LPMESSAGE FAR * FAR * lpppMessage,
  1715.     LPADRLIST FAR *lppRecipList)
  1716. {
  1717.     HRESULT hr;
  1718.     LPSTREAM lpstrm = NULL;
  1719.     ULONG cb;
  1720.     LARGE_INTEGER liTo = {0};
  1721.  
  1722.     hr = lpMessage->lpVtbl->OpenProperty (lpMessage,
  1723.                                     PR_BODY_A,
  1724.                                     (LPIID)&IID_IStream,
  1725.                                     0,
  1726.                                     MAPI_MODIFY,
  1727.                                     (LPUNKNOWN FAR *)&lpstrm);
  1728.     if (!HR_FAILED (hr))
  1729.     {
  1730.         hr = lpstrm->lpVtbl->Seek (lpstrm, liTo, STREAM_SEEK_END, NULL);
  1731.         if (!HR_FAILED (hr))
  1732.         {
  1733.             hr = lpstrm->lpVtbl->Write (lpstrm, lpszEOM, cchEOM, &cb);
  1734.             if (!HR_FAILED (hr) && (cb == cchEOM))
  1735.             {
  1736.                 if (!HR_FAILED (hr = lpstrm->lpVtbl->Commit (lpstrm, 0L)))
  1737.                     hr = lpMessage->lpVtbl->SaveChanges (lpMessage, KEEP_OPEN_READWRITE);
  1738.             }
  1739.         }
  1740.         UlRelease (lpstrm);
  1741.     }
  1742.     
  1743.     *lpcOutbound = 0;
  1744.     *lpppMessage = NULL;
  1745.     
  1746.     DebugTraceResult (PreprocessMessage(), hr);
  1747.     return hr;
  1748. }
  1749.  
  1750.  
  1751. STDMETHODIMP
  1752. RemovePreprocessInfo (LPMESSAGE lpMessage)
  1753. {
  1754.     CHAR lpszBuf[sizeof(lpszEOM)] = {0};
  1755.     HRESULT hr;
  1756.     LARGE_INTEGER liTo;
  1757.     LPSTREAM lpstrm = NULL;
  1758.     ULARGE_INTEGER liSize;
  1759.     ULONG cb;
  1760.     BOOL fUpd = FALSE;
  1761.  
  1762.     hr = lpMessage->lpVtbl->OpenProperty (lpMessage,
  1763.                                     PR_BODY_A,
  1764.                                     (LPIID)&IID_IStream,
  1765.                                     0,
  1766.                                     MAPI_MODIFY,
  1767.                                     (LPUNKNOWN FAR *)&lpstrm);
  1768.     if (!HR_FAILED (hr))
  1769.     {
  1770.         liTo.HighPart =  -1;
  1771.         liTo.LowPart = (ULONG)(-(LONG)(cchEOM));
  1772.         hr = lpstrm->lpVtbl->Seek (lpstrm, liTo, STREAM_SEEK_END, &liSize);
  1773.         if (!HR_FAILED (hr))
  1774.         {
  1775.             hr = lpstrm->lpVtbl->Read (lpstrm, lpszBuf, cchEOM, &cb);
  1776.             if (!HR_FAILED (hr) && (cb == cchEOM))
  1777.             {
  1778.                 if (!lstrcmpiA (lpszEOM, lpszBuf))
  1779.                 {
  1780.                     if (!HR_FAILED (hr = lpstrm->lpVtbl->SetSize (lpstrm, liSize)) &&
  1781.                         !HR_FAILED (hr = lpstrm->lpVtbl->Commit (lpstrm, 0L)))
  1782.                         hr = lpMessage->lpVtbl->SaveChanges (lpMessage, KEEP_OPEN_READWRITE);
  1783.                 }
  1784.             }
  1785.         }
  1786.     }
  1787.     UlRelease (lpstrm);
  1788.     DebugTraceResult (RemovePreprocessInfo(), hr);
  1789.     return hr;
  1790. }
  1791.