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 / xprcvmsg.c < prev    next >
C/C++ Source or Header  |  1996-04-11  |  52KB  |  1,812 lines

  1. /*
  2.  -  X P R C V M S G . C
  3.  -
  4.  *  Purpose:
  5.  *      Code to support the MAPI Transport SPI entry points for
  6.  *      message reception.  This module contains the following
  7.  *      SPI entry points:
  8.  *
  9.  *          Poll()
  10.  *          StartMessage()
  11.  *
  12.  *      Additional support functions found here:
  13.  *
  14.  *          HrIMsgFromTextMsg()
  15.  *          HrBuildSenderProps()
  16.  *          HrAddRecipToAdrList()
  17.  *          HrAddRecipToReplyList()
  18.  *          HrMakeSearchKey()
  19.  *          HrGetLine()
  20.  *          FGetTagAndToken()
  21.  *          FileTimeFromSzTime()
  22.  *          SetFromMeFlag()
  23.  *
  24.  *      Also, the Idle() code in XPQUEUE.C will call into this module to
  25.  *      find out if there's any mail to tell SpoolerNotify() about.
  26.  *
  27.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  28.  */
  29.  
  30. #include "xppch.h"
  31. #include <tnef.h>
  32. #include <stdlib.h>
  33. #include "xpsof.h"
  34. #include "xptxtmsg.h"
  35.  
  36. /* Local function prototype(s) */
  37.  
  38. VOID SetFromMeFlag(LPXPL lpxpl, LPMESSAGE lpMsg);
  39.  
  40.  
  41. /*
  42.  -  Poll
  43.  -
  44.  *  Purpose:
  45.  *      Called by the Spooler periodically in its idle loop. Also called from
  46.  *      the Idle() entry point code.
  47.  *
  48.  *  Parameters:
  49.  *      lpulIncoming        Pointer to a ULONG.
  50.  *
  51.  *  Returns:
  52.  *      *lpulIncoming       Nonzero if any messages are found;
  53.  *                          Zero if no messages were found.
  54.  *
  55.  *  Operation:
  56.  *      This routine uses FindFirst/FindNext to look for message container
  57.  *      files in the inbound directory. Because its FIND_DATA structure and
  58.  *      the associated Handle is contained in the session structure, it's
  59.  *      easy to keep context and pass it along to StartMessage().
  60.  *
  61.  *      Because it can be called from multiple places, and there's no
  62.  *      guarantee that StartMessage() will be called between the time we
  63.  *      signal the presence of inbound mail and the next time we're called,
  64.  *      we maintain a boolean in the session structure which we set to TRUE
  65.  *      when we find a message, and which StartMessage() sets to FALSE after
  66.  *      processing that message. If we see it as TRUE when called, we return
  67.  *      immediately with *lpulIncoming set.
  68.  *
  69.  *      Otherwise, we do a FindNext if a find handle is open. If it fails, we
  70.  *      close the handle.
  71.  *
  72.  *      If we have no open handle (either because one wasn't open or because
  73.  *      we just closed one), we do a FindFirst.
  74.  *
  75.  *      If we find a message container file, we set the boolean and
  76.  *      *lpulIncoming and return. Else we close the find handle.
  77.  *
  78.  *      This function always returns SUCCESS_SUCCESS.
  79.  */
  80.  
  81. STDMETHODIMP
  82. XPL_Poll(LPXPL lpxpl, ULONG * lpulIncoming)
  83. {
  84.     HANDLE *lpPollHandle;
  85.     LPWIN32_FIND_DATA lpFindData;
  86.     LPMAPISUP lpMAPISup;
  87.     LPSPropValue lpPropArray = lpxpl->lpPropArray;
  88.     BOOL fFound = FALSE;
  89.     DWORD dwIgnoreAttrs =
  90.     FILE_ATTRIBUTE_READONLY |
  91.     FILE_ATTRIBUTE_SYSTEM |
  92.     FILE_ATTRIBUTE_DIRECTORY |
  93.     FILE_ATTRIBUTE_TEMPORARY;
  94.  
  95.     lpPollHandle = &lpxpl->hInFindHandle;
  96.     lpFindData = &lpxpl->wfdInFindData;
  97.  
  98.     lpMAPISup = lpxpl->lpMAPISup;
  99.  
  100.     /* Start out with default of no incoming message */
  101.  
  102.     *lpulIncoming = 0;
  103.  
  104.     /* Is inbound enabled for this session? */
  105.  
  106.     if (!(lpxpl->ulTransportStatus & STATUS_INBOUND_ENABLED))
  107.         goto ret;
  108.  
  109.     /* See if we found one on a previous pass through here and haven't
  110.        done a StartMessage() yet... */
  111.  
  112.     if (lpxpl->fFoundInMessage)
  113.     {
  114.         *lpulIncoming = 1;
  115.         goto ret;
  116.     }
  117.  
  118.     /*  One of the following is now true:
  119.  
  120.         A)  We have a search already active, use FindNextFile.
  121.             If it fails, we will want to restart the search
  122.             (in case something appeared in the search behind
  123.             us.
  124.  
  125.         B)  We don't have a search active, use FindFirstFile.
  126.     */
  127.  
  128.     /* Try the active search */
  129.  
  130.     if (*lpPollHandle != INVALID_HANDLE_VALUE)
  131.     {
  132.         fFound = FindNextFile(*lpPollHandle, lpFindData);
  133.  
  134.         /* If we fail, close the old search so we can start
  135.            a new one below. */
  136.  
  137.         if (!fFound)
  138.         {
  139.             FindClose(*lpPollHandle);
  140.             *lpPollHandle = INVALID_HANDLE_VALUE;
  141.         }
  142.     }
  143.  
  144.     /* If there was no search or if the old one was just closed above */
  145.  
  146.     if (*lpPollHandle == INVALID_HANDLE_VALUE)
  147.     {
  148.         HANDLE hFindT;
  149.         TCHAR chFileName[MAX_PATH];
  150.  
  151.         /* Copy the directory name. Note that we trust the value in the
  152.            profile to be correct, since the UI should have enforced a
  153.            syntax that included a trailing : or \ in the spec. */
  154.  
  155.         lstrcpy(chFileName,
  156.             (ArrayIndex(PR_SAMPLE_INBOUND_DIR, lpPropArray)).Value.LPSZ);
  157.  
  158.         lstrcat(chFileName, TEXT("TNF*.TMP"));
  159.  
  160.         hFindT = FindFirstFile(chFileName, lpFindData);
  161.  
  162.         /* If nothing's found, we're done here. */
  163.  
  164.         if (hFindT == INVALID_HANDLE_VALUE)
  165.             goto ret;
  166.  
  167.         /* Found something, continue along. */
  168.  
  169.         fFound = TRUE;
  170.         *lpPollHandle = hFindT;
  171.     }
  172.  
  173.     /*
  174.         Here on a match. Exclude unwanted files.
  175.  
  176.         Any match with DIRECTORY, READONLY, SYSTEM or TEMPORARY attribute
  177.         is ignored. Keep trying until we exhaust the current supply or we
  178.         find a file without these attributes. Also, ignore files smaller
  179.         than some arbitrary size, they're probably trash.
  180.     */
  181.  
  182.     while (fFound)
  183.     {
  184.         /*  We found a file. Does it have any of the attributes we
  185.             want to ignore? If not, get out. If so, try another. */
  186.  
  187. #define MIN_USEFUL_FILESIZE ((DWORD) 64)
  188.  
  189.         if ((!((lpFindData)->dwFileAttributes & dwIgnoreAttrs)) &&
  190.             ((lpFindData->nFileSizeHigh != 0) ||
  191.                 (lpFindData->nFileSizeLow >= MIN_USEFUL_FILESIZE)))
  192.             break;
  193.  
  194.         fFound = FindNextFile(*lpPollHandle, lpFindData);
  195.     }
  196.  
  197.     if (fFound)
  198.         lpxpl->fFoundInMessage = TRUE;
  199.     else
  200.     {
  201.         FindClose(*lpPollHandle);
  202.         *lpPollHandle = INVALID_HANDLE_VALUE;
  203.     }
  204.  
  205. ret:
  206.     if (lpxpl->fFoundInMessage)
  207.     {
  208.         /*  Got a hit. If fFound is set, we found it this time. If fFound
  209.             is not set, we got it before and we were called again before
  210.             StartMessage(). */
  211.  
  212.         *lpulIncoming = 1;
  213.         DebugTrace("XPL_Poll returns *lpulIncoming=%lx\n", *lpulIncoming);
  214.     }
  215.     else if (lpxpl->ulTransportStatus & STATUS_INBOUND_FLUSH)
  216.     {
  217.         lpxpl->ulTransportStatus &= ~STATUS_INBOUND_FLUSH;
  218.         (void)HrUpdateTransportStatus(lpxpl, 0L);
  219.     }
  220.     return hrSuccess;
  221. }
  222.  
  223.  
  224. /*
  225.  -  StartMessage
  226.  -
  227.  *  Purpose:
  228.  *      Called by the Spooler for receipt of an inbound message. This sequence
  229.  *      of events is set off by a SpoolerNotify (NOTIFY_NEWMAIL) or a Poll()
  230.  *      returning *lpulIncoming != 0.
  231.  *
  232.  *  Parameters:
  233.  *      ulFlags             Flags from the Spooler. Currently
  234.  *                          there are no StartMessage() flags
  235.  *                          defined in the MAPI 1.0 TSPI.
  236.  *      lpMessage           Pointer to message object into which
  237.  *                          the Spooler wants the transport to
  238.  *                          store the incoming message.
  239.  *      lpulMsgRef          Pointer to where the transport should
  240.  *                          store a unsigned long for use in
  241.  *                          identifying TransportNotify() message
  242.  *                          events. Initialized to 0 by the
  243.  *                          Spooler. We don't do anything with
  244.  *                          message events in this transport, so
  245.  *                          we don't store anything there.
  246.  *
  247.  *  Returns:
  248.  *      (HRESULT)           MAPI_E_BUSY if the Spooler calls
  249.  *                          here again while we're busy, else
  250.  *                          errors encountered if any.
  251.  *      (*lpMessage)        Contains the new input message if any.
  252.  *
  253.  *  Operation:
  254.  *      Checks for the result of a FindFirst/FindNext operation in the
  255.  *      session's FIND_DATA buffer. If none, exit without changing the input
  256.  *      message (this should result in the new message being destroyed when
  257.  *      the Spooler releases its object).
  258.  *
  259.  *      If a file was found, attempt to open it. If the open fails and the
  260.  *      reason is not attributable to network locking, return the error; if
  261.  *      attributable to network locking (like if a peer's transmit code is
  262.  *      writing a container file), return no error. In either case, exit
  263.  *      without changing the input message.
  264.  *
  265.  *      Open a stream interface on the input message file.  Pass this off
  266.  *      to HrIMsgFromTxtMsg() to convert all the textized envelope properties
  267.  *      to SPropValues which are then set on the spoolers IMessage.  When
  268.  *      we return, the input stream will be pointing to the beginning of
  269.  *      the embedded TNEF encapsulation in the input message file.  Make
  270.  *      the appropriate calls to TNEF to extract the properties from the
  271.  *      encapsulation and set them on the message.
  272.  *
  273.  *      Finally, SaveChanges() on the Spooler's message to retain the result,
  274.  *      delete the container file, and reset the "found message" flag for the
  275.  *      benefit of Poll().
  276.  */
  277.  
  278. STDMETHODIMP
  279. XPL_StartMessage(LPXPL lpxpl,
  280.     ULONG ulFlags,
  281.     LPMESSAGE lpMessage,
  282.     ULONG * lpulMsgRef)
  283. {
  284.     LPWIN32_FIND_DATA lpFindData;
  285.     LPSPropValue lpMyIDArray = NULL;
  286.     LPSPropValue lpPropArray = NULL;
  287.     SPropValue rgDelegateProps[3];
  288.     SPropValue spvTime;
  289.     LPSPropProblemArray lpProblems = NULL;
  290.     TCHAR rgchFileName[MAX_PATH];
  291.     LPTSTR lptMyDir;
  292.     HRESULT hResult = 0;
  293.     SCODE sc = 0;
  294.     BOOL fUpdatedStatus = FALSE;
  295.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  296.     WORD wKey = 0;
  297.     SPropTagArray sptExcludeNone = {0};
  298.     LPITNEF lpTnef = (LPITNEF) NULL;
  299.     LPSTREAM lpSof = (LPSTREAM) NULL;
  300.     LPSTREAM lpXPSof = (LPSTREAM) NULL;
  301.     LPSTnefProblemArray lptpa = NULL;
  302.  
  303.     /* Reset our .2 second timer before starting. */
  304.  
  305.     HrCheckSpoolerYield(lpMAPISup, TRUE);
  306.  
  307.     /* Do this first so we know at exit time if we tried to open a file */
  308.  
  309.     rgchFileName[0] = '\0';
  310.  
  311.     /* Simple re-entrancy test. Should never happen anyway. */
  312.  
  313.     if (lpxpl->ulTransportStatus & STATUS_INBOUND_ACTIVE)
  314.     {
  315.         hResult = ResultFromScode(MAPI_E_BUSY);
  316.         DebugTrace("XPL_StartMessage reentrancy test failed\n");
  317.         goto ret;
  318.     }
  319.  
  320.     /* Signal that we're downloading. */
  321.  
  322.     *lpulMsgRef = 1L;           /* This is good enough. */
  323.     lpxpl->ulTransportStatus |= STATUS_INBOUND_ACTIVE;
  324.     hResult = HrUpdateTransportStatus(lpxpl, 0L);
  325.     if (hResult)
  326.     {
  327.         DebugTrace("Update of status row failed\n");
  328.         goto ret;
  329.     }
  330.     fUpdatedStatus = TRUE;
  331.  
  332.     /* Get the current findfirst/findnext buffer */
  333.  
  334.     lpFindData = &lpxpl->wfdInFindData;
  335.  
  336.     /* Is there actually a message available? If not, go away. */
  337.  
  338.     if (!lpxpl->fFoundInMessage)
  339.         goto ret;
  340.  
  341.     sc = ScCopySessionProps(lpxpl, &lpPropArray, &lpMyIDArray);
  342.  
  343.     if (FAILED(sc))
  344.     {
  345.         hResult = ResultFromScode(sc);
  346.         goto ret;
  347.     }
  348.  
  349.     /* Build file name of incoming message */
  350.  
  351.     lptMyDir = ArrayIndex(PR_SAMPLE_INBOUND_DIR, lpPropArray).Value.LPSZ;
  352.  
  353.     lstrcpy(rgchFileName, lptMyDir);
  354.     lstrcat(rgchFileName, lpFindData->cFileName);
  355.  
  356.     PrintfTransportLog(TEXT("Start Incoming: %s"), rgchFileName);
  357.  
  358.     hResult = OpenStreamOnFile(lpxpl->AllocateBuffer, lpxpl->FreeBuffer,
  359.         STGM_READ, rgchFileName, NULL, &lpSof);
  360.  
  361.     if (hResult)
  362.     {
  363.         sc = GetScode(hResult);
  364.         DebugTrace("OpenStreamOnFile() failed in StartMessage()\n");
  365.         PrintfTransportLog(TEXT("OpenStreamOnFile(%s) returns %lx"), rgchFileName, sc);
  366.  
  367.         /*  If "Access Denied" just don't do anything.
  368.             It's usually a situation that will clear up (when another
  369.             instance of this transport closes the msg file or when the
  370.             other system comes online) */
  371.  
  372.         if (sc == MAPI_E_NO_ACCESS)
  373.             hResult = hrSuccess;
  374.  
  375.         /*  If "Not Found", clear fFoundInMessage so that we'll do a
  376.             FindNext. */
  377.  
  378.         if (sc == MAPI_E_NOT_FOUND)
  379.             lpxpl->fFoundInMessage = FALSE;
  380.  
  381.         goto ret;
  382.     }
  383.  
  384.     /* Wrap the Stream-On-File object in our buffered wrapper. */
  385.  
  386.     hResult = HrWrapStreamOnFile(lpxpl->AllocateBuffer, lpxpl->FreeBuffer,
  387.             XPSOF_READ, lpSof, &lpXPSof);
  388.  
  389.     if (HR_FAILED(hResult))
  390.     {
  391.         DebugTrace("HrWrapStreamOnFile() failed\n");
  392.         goto ret;
  393.     }
  394.  
  395.     /* Check our .2 second timer before attempting to receive */
  396.  
  397.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  398.         
  399.     if (sc == MAPI_W_CANCEL_MESSAGE)
  400.     {
  401.         DebugTrace("Cancelling message download.\n");
  402.         goto ret;
  403.     }
  404.  
  405.     hResult = HrIMsgFromTextMsg(lpxpl, lpPropArray, lpMessage, lpXPSof);
  406.  
  407.     if (HR_FAILED(hResult))
  408.     {
  409.         DebugTrace("HrIMsgFromTextMsg() failed\n");
  410.         goto ret;
  411.     }
  412.  
  413.     /* Check our .2 second timer again. */
  414.  
  415.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  416.         
  417.     if (sc == MAPI_W_CANCEL_MESSAGE)
  418.     {
  419.         DebugTrace("Cancelling message download.\n");
  420.         goto ret;
  421.     }
  422.  
  423.     /* The 0x01AF if a key used to identify the TNEF.  A real transport
  424.        should generate a pseudo-random sequence for this field. */
  425.  
  426.     hResult = OpenTnefStream(lpxpl->lpMAPISup, lpXPSof, 
  427.             TEXT("MAPIMAIL.DAT"), TNEF_DECODE, lpMessage, 0x01AF, &lpTnef);
  428.  
  429.     if (HR_FAILED(hResult))
  430.     {
  431.         DebugTrace("OpenTNEF() failed.\n");
  432.         goto ret;
  433.     }
  434.  
  435.     /* Extract properties from the incomming message and add them to
  436.        the target message. */
  437.  
  438.     hResult = lpTnef->lpVtbl->ExtractProps(lpTnef,
  439.         TNEF_PROP_EXCLUDE, &sptExcludeNone, &lptpa);
  440.     lpxpl->FreeBuffer(lptpa);
  441.     if (HR_FAILED(hResult))
  442.     {
  443.         DebugTrace("GetTNEFProps() failed.\n");
  444.         goto ret;
  445.     }
  446.  
  447.     /* Check our .2 second timer again. */
  448.  
  449.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  450.         
  451.     if (sc == MAPI_W_CANCEL_MESSAGE)
  452.     {
  453.         DebugTrace("Cancelling message download.\n");
  454.         goto ret;
  455.     }
  456.  
  457.     /* All the properties have been copied over. Set the PR_RECEIVED_BY
  458.        delegate properties (all the others were set by the transmitter) */
  459.  
  460.     if (lpMyIDArray)
  461.     {
  462.         Assert(!IsBadReadPtr(lpMyIDArray, 3 * sizeof(SPropValue)));
  463.         Assert(lpMyIDArray[0].ulPropTag == PR_SENDER_ENTRYID);
  464.         Assert(lpMyIDArray[1].ulPropTag == PR_SENDER_NAME);
  465.         Assert(lpMyIDArray[2].ulPropTag == PR_SENDER_SEARCH_KEY);
  466.  
  467.         memcpy(rgDelegateProps, lpMyIDArray, 3 * sizeof(SPropValue));
  468.         rgDelegateProps[0].ulPropTag = PR_RECEIVED_BY_ENTRYID;
  469.         rgDelegateProps[1].ulPropTag = PR_RECEIVED_BY_NAME;
  470.         rgDelegateProps[2].ulPropTag = PR_RECEIVED_BY_SEARCH_KEY;
  471.  
  472.         /* At this point we have all the delegate properties set. Put them into
  473.            the old message and then we'll just get them on the CopyTo(). */
  474.  
  475.         hResult = lpMessage->lpVtbl->SetProps(lpMessage, 3,
  476.             rgDelegateProps, &lpProblems);
  477.  
  478.         if (hResult)
  479.         {
  480.             DebugTrace("SetProps of Receiver ID to message failed.\n");
  481.             goto ret;
  482.         }
  483.  
  484.         if (lpProblems)
  485.         {
  486.             /* If there were problems, let's dump them to the debugger. */
  487.  
  488.             DebugTraceProblems("XPL_StartMessage", lpProblems);
  489.  
  490.             lpxpl->FreeBuffer(lpProblems);
  491.             lpProblems = NULL;
  492.         }
  493.     }
  494.  
  495.     /* Set the Received Time to be the last-modified time on the file. */
  496.  
  497.     spvTime.ulPropTag = PR_MESSAGE_DELIVERY_TIME;
  498.     spvTime.Value.ft = lpFindData->ftLastWriteTime;
  499.  
  500.     hResult = lpMessage->lpVtbl->SetProps(lpMessage, 1, &spvTime, NULL);
  501.  
  502.     if (hResult)
  503.     {
  504.         DebugTrace("SetProps of PR_MESSAGE_DELIVERY_TIME failed.\n");
  505.         goto ret;
  506.     }
  507.  
  508.     /*  Finished with all properties and recipients now, SaveChanges
  509.         on the message. */
  510.  
  511.     /* Check our .2 second timer again. */
  512.  
  513.     sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  514.         
  515.     if (sc == MAPI_W_CANCEL_MESSAGE)
  516.     {
  517.         DebugTrace("Cancelling message download.\n");
  518.         goto ret;
  519.     }
  520.  
  521.     hResult = lpMessage->lpVtbl->SaveChanges(lpMessage, 0L);
  522.  
  523.     if (hResult)
  524.     {
  525.         DebugTrace("SaveChanges on incoming message failed.\n");
  526.         goto ret;
  527.     }
  528.  
  529.     /* Finally, set the found message flag so we'll get another file. */
  530.  
  531.     lpxpl->fFoundInMessage = FALSE;
  532.  
  533. ret:
  534.     /* Log end of incoming if we logged start. */
  535.  
  536.     if (*rgchFileName)
  537.         PrintfTransportLog(TEXT("End Incoming: %s"), rgchFileName);
  538.  
  539.     UlRelease(lpTnef);
  540.     UlRelease(lpXPSof);
  541.     UlRelease(lpSof);
  542.  
  543.     /* If we got the message into the store, delete the inbound file. */
  544.  
  545.     if (!(HR_FAILED (hResult)) && *rgchFileName)
  546.         (void)DeleteFile(rgchFileName);
  547.  
  548.     /* Release the prop tag array and/or problem array if any */
  549.  
  550.     lpxpl->FreeBuffer(lpPropArray);
  551.     lpxpl->FreeBuffer(lpMyIDArray);
  552.  
  553.     /* Reset download status if set. */
  554.  
  555.     if (fUpdatedStatus)
  556.     {
  557.         lpxpl->ulTransportStatus &= ~STATUS_INBOUND_ACTIVE;
  558.         (void)HrUpdateTransportStatus(lpxpl, 0L);
  559.     }
  560.  
  561.     DebugTraceResult(XPL_StartMessage, hResult);
  562.     return hResult;
  563. }
  564.  
  565.  
  566. /*
  567.  -  HrIMsgFromTextMsg
  568.  -
  569.  *  Purpose:
  570.  *      Called by StartMessage() to read a text formatted message file,
  571.  *      containing a TNEF encapsulation, and converting it into a
  572.  *      MAPI Message.  The TNEF DLL is used to decode the binary portion
  573.  *      of this message into all the correct IMessage components.
  574.  *
  575.  *  Parameters:
  576.  *      lpxpl               Pointer to Transport Logon object
  577.  *      lpPropArray         Array of the transports logon properties
  578.  *      lpMessage           Message to receive into
  579.  *      lpSof               Pointer to the stream interface
  580.  *
  581.  *  Returns:
  582.  *      hr                  Indicating Success/Failure
  583.  *
  584.  *  Operation:
  585.  *      Read each Tag out of the text file and process its Token according
  586.  *      to my rules for the Tag ID.  PR_SUBJECT and PR_BODY are automatically
  587.  *      streamed into the message, PR_CLIENT_SUBMIT_TIME, PR_PRIORITY,
  588.  *      PR_SENDER_NAME, and PR_SENDER_ENTRYID are added with SetProps().
  589.  *      All To: and Cc: recipients are added by building an AdrList
  590.  *      and doing a ModifyRecipients() on the message.  When we're finished
  591.  *      here, the file pointer in the input stream will (hopefully) be
  592.  *      left pointing to the start of the TNEF encapsulation.
  593.  */
  594.  
  595. HRESULT
  596. HrIMsgFromTextMsg(LPXPL lpxpl, LPSPropValue lpPropArray, LPMESSAGE lpMessage, LPSTREAM lpSof)
  597. {
  598.     SCODE sc;
  599.     HRESULT hr = hrSuccess;
  600.     BOOL fHaveTagAndToken = FALSE;
  601.     TCHAR szLine[MAX_LINE];
  602.     ULONG cbRead;
  603.     ULONG ulTag;
  604.     LPTSTR lpszToken;
  605.     ULONG cValues = 0;
  606.     LPSPropValue lpMsgProps = NULL;
  607.     LPMYADRLIST lpMyRecipList = NULL;
  608.     LPTSTR lpszAddrType;
  609.     LPTSTR lpszReplyNames = NULL;
  610.     ULONG cbReplyEntryList = 0;
  611.     LPFLATENTRYLIST lpReplyEntryList = NULL;
  612.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  613.  
  614.     lpszAddrType = ArrayIndex(PR_SAMPLE_EMAIL_ADDR_TYPE, lpPropArray).Value.LPSZ;
  615.  
  616.     sc = lpxpl->AllocateBuffer(MAX_TXTMSG_PROPS * sizeof(SPropValue),
  617.             &lpMsgProps);
  618.  
  619.     if (sc)
  620.     {
  621.         hr = ResultFromScode(sc);
  622.         DebugTrace("Allocation failed.\n");
  623.         goto ret;
  624.     }
  625.  
  626.     memset(lpMsgProps, 0, MAX_TXTMSG_PROPS * sizeof(SPropValue));
  627.  
  628.     sc = lpxpl->AllocateBuffer(sizeof(MYADRLIST), &lpMyRecipList);
  629.  
  630.     if (sc)
  631.     {
  632.         hr = ResultFromScode(sc);
  633.         DebugTrace("Allocation failed.\n");
  634.         goto ret;
  635.     }
  636.  
  637.     memset(lpMyRecipList, 0, sizeof(MYADRLIST));
  638.  
  639.     while (TRUE)
  640.     {
  641.         /* fHaveTagAndToken gets set only when we return from
  642.            HrGetStreamedProp and the call actually gets
  643.            the next tagged line and tokenizes it for us. */
  644.  
  645.         if (!fHaveTagAndToken)
  646.         {
  647.             hr = HrGetLine(lpSof, MAX_LINE, szLine, &cbRead);
  648.  
  649.             if (hr)
  650.                 break;
  651.  
  652.             if (szLine[0] == '\0')
  653.                 continue;
  654.         }
  655.  
  656.         if (fHaveTagAndToken || FGetTagAndToken(szLine, &ulTag, &lpszToken))
  657.         {
  658.             fHaveTagAndToken = FALSE;
  659.  
  660.             /* Check our .2 second timer again. */
  661.  
  662.             sc = GetScode(HrCheckSpoolerYield(lpMAPISup, FALSE));
  663.         
  664.             if (sc == MAPI_W_CANCEL_MESSAGE)
  665.             {
  666.                 DebugTrace("Cancelling message download.\n");
  667.                 goto ret;
  668.             }
  669.  
  670.             switch (ulTag)
  671.             {
  672.             case tagFrom:
  673.             case tagRepresenting:
  674.                 /* Create an addressing triplet (DisplayName, EntryID,
  675.                    SearchKey) from this line and add these properties
  676.                    to the array of props. */
  677.  
  678.                 hr = HrBuildSenderProps(lpxpl, lpPropArray, ulTag, lpszToken,
  679.                         lpszAddrType, lpMessage, &cValues, lpMsgProps);
  680.  
  681.                 if (hr)
  682.                 {
  683.                     DebugTrace("HrBuildSenderProps() failed.\n");
  684.                     goto ret;
  685.                 }
  686.                 break;
  687.  
  688.             case tagReplyTo:
  689.                 hr = HrAddRecipToReplyList(lpxpl, lpszToken, lpszAddrType,
  690.                     &lpszReplyNames, &cbReplyEntryList, &lpReplyEntryList);
  691.  
  692.                 if (hr)
  693.                     goto ret;
  694.                 break;
  695.  
  696.             case tagDate:
  697.                 lpMsgProps[cValues].ulPropTag = PR_CLIENT_SUBMIT_TIME;
  698.                 FileTimeFromSzTime(lpszToken, &lpMsgProps[cValues++].Value.ft);
  699.                 break;
  700.  
  701.             case tagTo:
  702.             case tagCc:
  703.                 hr = HrAddRecipToAdrList(lpxpl, (LONG) ulTag - tagDate, lpszToken,
  704.                     lpszAddrType, lpMyRecipList);
  705.                 if (hr)
  706.                     goto ret;
  707.                 break;
  708.  
  709.             case tagSubject:
  710.             case tagTextItem:
  711.                 hr = HrGetStreamedProp(lpxpl, lpSof, lpMessage,
  712.                         ((ulTag == tagSubject) ? PR_SUBJECT : PR_BODY),
  713.                         &cValues, lpMsgProps, szLine, &ulTag, &lpszToken);
  714.  
  715.                 if (HR_FAILED(hr))
  716.                     goto ret;
  717.  
  718.                 if(S_OK ==GetScode(hr))
  719.                     fHaveTagAndToken = TRUE;
  720.                 break;
  721.  
  722.             case tagPrioLow:
  723.             case tagPrioNormal:
  724.             case tagPrioUrgent:
  725.                 lpMsgProps[cValues].ulPropTag = PR_PRIORITY;
  726.                 lpMsgProps[cValues++].Value.l = tagPrioNormal - ulTag;
  727.                 break;
  728.  
  729.             case tagFileItem:
  730.                 goto ret;
  731.  
  732.             case tagMessage:
  733.             case tagBcc:
  734.             case tagContents:
  735.             default:
  736.                 break;
  737.             }   /* end switch() */
  738.         }       /* end if()     */
  739.     }           /* end while()  */
  740.  
  741. ret:
  742.     if (lpszReplyNames && lpReplyEntryList)
  743.     {
  744.         lpMsgProps[cValues].ulPropTag = PR_REPLY_RECIPIENT_NAMES;
  745.         lpMsgProps[cValues++].Value.LPSZ = lpszReplyNames;
  746.  
  747.         lpMsgProps[cValues].ulPropTag = PR_REPLY_RECIPIENT_ENTRIES;
  748.         lpMsgProps[cValues].Value.bin.cb = cbReplyEntryList;
  749.         lpMsgProps[cValues++].Value.bin.lpb = (LPBYTE) lpReplyEntryList;
  750.     }
  751.  
  752.     if (cValues)
  753.         lpMessage->lpVtbl->SetProps(lpMessage, cValues, lpMsgProps, NULL);
  754.  
  755.     if (lpMyRecipList && lpMyRecipList->lpAdrList)
  756.     {
  757.         hr = lpMessage->lpVtbl->ModifyRecipients(lpMessage,
  758.             MODRECIP_ADD, lpMyRecipList->lpAdrList);
  759.  
  760.         FreeMyAdrList(lpxpl, lpMyRecipList);
  761.  
  762.         if (hr)
  763.             DebugTrace("ModifyRecipients failed.\n");
  764.     }
  765.     else
  766.         lpxpl->FreeBuffer(lpMyRecipList);
  767.  
  768.     lpxpl->FreeBuffer(lpMsgProps);
  769.     lpxpl->FreeBuffer(lpszReplyNames);
  770.     lpxpl->FreeBuffer(lpReplyEntryList);
  771.  
  772.     DebugTraceResult(HrIMsgFromTextMsg(), hr);
  773.  
  774.     return hr;
  775. }
  776.  
  777.  
  778. /*
  779.  -  HrBuildSenderProps
  780.  -
  781.  *  Purpose:
  782.  *      Creates the 5 identity properties: PR_***_SEARCH_KEY,
  783.  *      PR_***_NAME, PR_***_ENTRYID, PR_***_EMAIL_ADDRESS, 
  784.  *      PR_***_ADDRTYPE, where *** is either SENDER or
  785.  *      SENT_REPRESENTING.
  786.  *
  787.  *  Parameters:
  788.  *      lpxpl               The transports logon object
  789.  *      lpPropArray         The session logon properties
  790.  *      ulTag               Either tagFrom or tagRepresenting
  791.  *      lpszToken           Display name and address for triplet
  792.  *      lpszAddrType        This transports address type
  793.  *      lpMessage           The spoolers IMessage object
  794.  *      lpcValues           Count of and index into lpMsgProps
  795.  *      lpMsgProps          Array of message properties we are building
  796.  *
  797.  *  Returns:
  798.  *      hr                  Indicating Success/Failure
  799.  */
  800.  
  801. HRESULT
  802. HrBuildSenderProps(LPXPL lpxpl,
  803.     LPSPropValue lpPropArray,
  804.     ULONG ulTag,
  805.     LPTSTR lpszToken,
  806.     LPTSTR lpszAddrType,
  807.     LPMESSAGE lpMessage,
  808.     ULONG * lpcValues,
  809.     LPSPropValue lpMsgProps)
  810. {
  811.     SCODE sc;
  812.     HRESULT hr;
  813.     LPTSTR lpszDisplayName = NULL;
  814.     LPTSTR lpszAddress = NULL;
  815.     LPTSTR lpsz;
  816.     ULONG cbEntryID = 0;
  817.     LPBYTE lpEntryID = NULL;
  818.     LPBYTE lpb = NULL;
  819.     ULONG cbSK;
  820.     LPBYTE lpSearchKey;
  821.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  822.     ULONG cValues = *lpcValues;
  823.  
  824.     lpsz = strtok(lpszToken, "[");
  825.  
  826.     sc = lpxpl->AllocateMore(lstrlen(lpsz) + 1,
  827.         lpMsgProps, &lpszDisplayName);
  828.  
  829.     if (sc)
  830.     {
  831.         hr = ResultFromScode(sc);
  832.         DebugTrace("AllocateMore failed.\n");
  833.         goto ret;
  834.     }
  835.  
  836.     lstrcpy(lpszDisplayName, lpsz);
  837.  
  838.     lpsz = strtok(NULL, "]");
  839.  
  840.     sc = lpxpl->AllocateMore(lstrlen(lpsz) + 1,
  841.         lpMsgProps, &lpszAddress);
  842.  
  843.     if (sc)
  844.     {
  845.         hr = ResultFromScode(sc);
  846.         DebugTrace("AllocateMore failed.\n");
  847.         goto ret;
  848.     }
  849.  
  850.     lstrcpy(lpszAddress, lpsz);
  851.  
  852.     /* Create OneOff Entry ID for Sender/Delegate */
  853.  
  854.     hr = lpMAPISup->lpVtbl->CreateOneOff(lpMAPISup,
  855.         lpszDisplayName, lpszAddrType, lpszAddress, 0,
  856.         &cbEntryID, (LPENTRYID FAR *) &lpEntryID);
  857.  
  858.     if (hr)
  859.     {
  860.         DebugTrace("CreateOneOff() failed.\n");
  861.         goto ret;
  862.     }
  863.  
  864.     /* Chain the EntryID to the lpMsgProp block */
  865.  
  866.     sc = lpxpl->AllocateMore(cbEntryID, lpMsgProps, &lpb);
  867.  
  868.     if (sc)
  869.     {
  870.         hr = ResultFromScode(sc);
  871.         DebugTrace("AllocateMore failed.\n");
  872.         goto ret;
  873.     }
  874.  
  875.     if (cbEntryID)
  876.         memcpy(lpb, lpEntryID, (size_t) cbEntryID);
  877.  
  878.     lpxpl->FreeBuffer(lpEntryID);
  879.     lpEntryID = NULL;
  880.  
  881.     /* Make the PR_***_SEARCH_KEY */
  882.  
  883.     hr = HrMakeSearchKey(lpxpl, lpMsgProps, lpszAddrType,
  884.         lpszAddress, &cbSK, &lpSearchKey);
  885.  
  886.     if (hr)
  887.     {
  888.         DebugTrace("HrMakeSearchKey() failed.\n");
  889.         goto ret;
  890.     }
  891.  
  892.     if (ulTag == tagFrom)
  893.     {
  894.         lpMsgProps[cValues].ulPropTag = PR_SENDER_NAME;
  895.         lpMsgProps[cValues + 1].ulPropTag = PR_SENDER_ENTRYID;
  896.         lpMsgProps[cValues + 2].ulPropTag = PR_SENDER_SEARCH_KEY;
  897.         lpMsgProps[cValues + 3].ulPropTag = PR_SENDER_ADDRTYPE;
  898.         lpMsgProps[cValues + 4].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
  899.  
  900.         if (!lstrcmpi(lpszAddress,
  901.                 ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS,
  902.                     lpPropArray).Value.LPSZ))
  903.             SetFromMeFlag(lpxpl, lpMessage);
  904.     }
  905.     else
  906.     {
  907.         Assert(ulTag == tagRepresenting);
  908.         lpMsgProps[cValues].ulPropTag = PR_SENT_REPRESENTING_NAME;
  909.         lpMsgProps[cValues + 1].ulPropTag = PR_SENT_REPRESENTING_ENTRYID;
  910.         lpMsgProps[cValues + 2].ulPropTag = PR_SENT_REPRESENTING_SEARCH_KEY;
  911.         lpMsgProps[cValues + 3].ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE;
  912.         lpMsgProps[cValues + 4].ulPropTag = PR_SENT_REPRESENTING_EMAIL_ADDRESS;
  913.     }
  914.  
  915.     lpMsgProps[cValues++].Value.LPSZ = lpszDisplayName;
  916.     lpMsgProps[cValues].Value.bin.cb = cbEntryID;
  917.     lpMsgProps[cValues++].Value.bin.lpb = lpb;
  918.     lpMsgProps[cValues].Value.bin.cb = cbSK;
  919.     lpMsgProps[cValues++].Value.bin.lpb = lpSearchKey;
  920.     lpMsgProps[cValues++].Value.LPSZ = lpszAddrType;
  921.     lpMsgProps[cValues++].Value.LPSZ = lpszAddress;
  922.  
  923.     *lpcValues = cValues;
  924.  
  925. ret:
  926.     lpxpl->FreeBuffer(lpEntryID);
  927.  
  928.     DebugTraceResult(HrBuildSenderProps(), hr);
  929.     return hr;
  930. }
  931.  
  932.  
  933. /*
  934.  -  HrGetStreamedProp
  935.  -
  936.  *  Purpose:
  937.  *      Reads either the PR_BODY or PR_SUBJECT from the message file and
  938.  *      adds it to the spoolers IMessage.
  939.  *
  940.  *  Parameters:
  941.  *      lpSof               The stream we are reading the message from
  942.  *      lpMsg               The spoolers IMessage we are building up
  943.  *      ulPropTag           Either PR_SUBJECT or PR_BODY
  944.  *      lpcValues           Current size of the lpMsgProps array
  945.  *      lpMsgProps          The message property array we are building
  946.  *      lpszLine            Char array we use to read lines into
  947.  *      lpulTag             The text file tag we return as a side effect
  948.  *      lppszToken          The token we return as a side effect
  949.  *
  950.  *  Returns:
  951.  *      hr                  Indicating Success/Failure
  952.  *
  953.  *  10/5/95 the functon can return S_FALSE when the call to OpenProperty fails.
  954.  *          in this case the property still gets set using SetProps, but we don't
  955.  *          get the next token for the caller. So S_FALSE is to inform the
  956.  *          caller that we didn't get token for him and he has to do it himself.
  957.  */
  958.  
  959. HRESULT
  960. HrGetStreamedProp(LPXPL lpxpl,
  961.     LPSTREAM lpSof,
  962.     LPMESSAGE lpMsg,
  963.     ULONG ulPropTag,
  964.     ULONG * lpcValues,
  965.     LPSPropValue lpMsgProps,
  966.     LPTSTR lpszLine,
  967.     ULONG * lpulTag,
  968.     LPTSTR * lppszToken)
  969. {
  970.     SCODE sc;
  971.     HRESULT hr;
  972.     LPSTREAM lpStrm = NULL;
  973.     LPTSTR lpsz = NULL;
  974.     ULONG cbCRLF = lstrlen(szCRLF);
  975.     ULONG cbRead;
  976.     ULONG cbWritten;
  977.  
  978.     #define cbSubjMax 4096
  979.     LPSTR szSubj = NULL;
  980.  
  981.     sc = lpxpl->AllocateBuffer(cbSubjMax, &szSubj);
  982.     if (sc)
  983.     {
  984.         hr = ResultFromScode(sc);
  985.         DebugTrace("Allocation failed.\n");
  986.         goto ret;
  987.     }
  988.  
  989.     *szSubj = '\0';
  990.  
  991.     hr = HrGetLine(lpSof, MAX_LINE, lpszLine, &cbRead);
  992.  
  993.     if (hr)
  994.     {
  995.         DebugTrace("HrGetLine failed.\n");
  996.         goto ret;
  997.     }
  998.  
  999.     if (ulPropTag == PR_SUBJECT)
  1000.     {
  1001.         /* Once in the context of the subject, go until we reach the
  1002.            next tag.  Then return and process the next tag and token */
  1003.  
  1004.         UINT cbCurrentSubj = 0;
  1005.         while (!FGetTagAndToken(lpszLine, lpulTag, lppszToken))
  1006.         {
  1007.             if(cbCurrentSubj < cbSubjMax)
  1008.             {
  1009.                 cbCurrentSubj += lstrlen(lpszLine);
  1010.                 
  1011.                 if(cbCurrentSubj < cbSubjMax)
  1012.                     lstrcat(szSubj, lpszLine);
  1013.             }
  1014.             
  1015.             hr = HrGetLine(lpSof, MAX_LINE, lpszLine, &cbRead);
  1016.                 
  1017.             if (hr)
  1018.                 goto ret;
  1019.         }
  1020.  
  1021.         /* this is in order to get inside the 'if' clause after OpenProperty*/
  1022.         hr = 1;
  1023.     }
  1024.     else
  1025.     {
  1026.     /* If we fail to open a stream on the property, then slam
  1027.        this line into the property array and break outta here */
  1028.  
  1029.     hr = lpMsg->lpVtbl->OpenProperty(lpMsg, ulPropTag,
  1030.         (LPIID) &IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY,
  1031.         (LPUNKNOWN *) &lpStrm);
  1032.     }
  1033.  
  1034.     if (hr)
  1035.     {
  1036.         LPSTR szBuf = (ulPropTag == PR_SUBJECT) ? szSubj : lpszLine;
  1037.         sc = lpxpl->AllocateMore(lstrlen(szBuf) + 1, lpMsgProps, &lpsz);
  1038.  
  1039.         if (sc)
  1040.         {
  1041.             hr = ResultFromScode(sc);
  1042.             DebugTrace("Allocation failed.\n");
  1043.             goto ret;
  1044.         }
  1045.  
  1046.         lstrcpy(lpsz, szBuf);
  1047.         lpMsgProps[*lpcValues].ulPropTag = ulPropTag;
  1048.         lpMsgProps[(*lpcValues)++].Value.LPSZ = lpsz;
  1049.  
  1050.         hr = (ulPropTag == PR_SUBJECT) ? hrSuccess : ResultFromScode(S_FALSE);
  1051.         goto ret;
  1052.     }
  1053.  
  1054.     /* Once we're in the context of the body, the only
  1055.        valid Tag is the TNEF File Item Tag.  So, we
  1056.        ignore all other message text lines that may
  1057.        contain a Tag (by chance). */
  1058.  
  1059.     while (TRUE)
  1060.     {
  1061.         FGetTagAndToken(lpszLine, lpulTag, lppszToken);
  1062.  
  1063.         if ((*lpulTag == tagFileItem) &&
  1064.             !lstrcmp(*lppszToken, "MESSAGE.TNF"))
  1065.             break;
  1066.  
  1067.         TraceFailedWrite(hr = lpStrm->lpVtbl->Write(lpStrm, lpszLine,
  1068.                 lstrlen(lpszLine), &cbWritten), ret);
  1069.  
  1070.         if (cbRead < MAX_LINE - 1)
  1071.         {
  1072.             TraceFailedWrite(lpStrm->lpVtbl->Write(lpStrm,
  1073.                     szCRLF, cbCRLF, &cbWritten), ret);
  1074.         }
  1075.  
  1076.         hr = HrGetLine(lpSof, MAX_LINE, lpszLine, &cbRead);
  1077.  
  1078.         if (hr)
  1079.             break;
  1080.     }
  1081.  
  1082.  
  1083. ret:
  1084.     UlRelease(lpStrm);
  1085.  
  1086.     lpxpl->FreeBuffer(szSubj);
  1087.  
  1088.     DebugTraceResult(HrGetStreamedProp(), hr);
  1089.     return hr;
  1090. }
  1091.  
  1092.  
  1093. /*
  1094.  -  HrAddRecipToAdrList
  1095.  -
  1096.  *  Purpose:
  1097.  *      Called by HrIMsgFromTextMsg() to add a single recipient to the
  1098.  *      AdrList.  Pre-allocates space (cMaxEntries) for 10 AdrEntrys the
  1099.  *      first time called.  If all cMaxEntries slots are filled, then we
  1100.  *      ReAlloc for (cMaxEntries + cMaxEntries/2) more slots and continue
  1101.  *      adding the recipient.  New memory is allocated for the attributes
  1102.  *      passed in and chained-up to the rgProps array.
  1103.  *
  1104.  *  Parameters:
  1105.  *      lpxpl           Needed for access to support obj and mem allocators
  1106.  *      ulRecipType     MAPI_TO or MAPI_CC
  1107.  *      lpszNameAddr    recipient token (Format: Display Name[email-address] )
  1108.  *      lpszAddrType    This transports Address Type
  1109.  *      lpMyAdrList     Pointer to the Recipient List
  1110.  *
  1111.  *  Returns:
  1112.  *      hr              Indicating Suucess/Failure
  1113.  */
  1114.  
  1115. HRESULT
  1116. HrAddRecipToAdrList(LPXPL lpxpl, LONG lRecipType,
  1117.     LPTSTR lpszNameAddr, LPTSTR lpszAddrType, LPMYADRLIST lpMyAdrList)
  1118. {
  1119.     HRESULT hr = hrSuccess;
  1120.     SCODE sc;
  1121.     BOOL fAlloc = FALSE;
  1122.     BOOL fReAlloc = FALSE;
  1123.     ULONG cb;
  1124.     ULONG cMaxEntries;
  1125.     LPADRLIST lpAdrList = NULL;
  1126.     LPSPropValue rgProps = NULL;
  1127.     LPTSTR lpsz;
  1128.     LPTSTR lpszDisplayName = NULL;
  1129.     LPTSTR lpszAddress = NULL;
  1130.     LPTSTR lpszAddrTypeT = NULL;
  1131.     ULONG cbEntryID = 0;
  1132.     LPBYTE lpEntryID = NULL;
  1133.     LPBYTE lpb = NULL;
  1134.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  1135.     enum enumRecipProps
  1136.     {
  1137.         iRecipType,
  1138.         iDisplayName,
  1139.         iAddrType,
  1140.         iAddress,
  1141.         iEntryID,
  1142.         cRecipProps
  1143.     };
  1144.  
  1145.     /* Determine if we need to Allocate or ReAllocate memory */
  1146.  
  1147.     if (!lpMyAdrList->cMaxEntries)
  1148.     {
  1149.         /* First time in; list should be NULL */
  1150.         Assert(lpMyAdrList->lpAdrList == NULL);
  1151.         fAlloc = TRUE;
  1152.         cMaxEntries = 10;
  1153.     }
  1154.     else if (lpMyAdrList->lpAdrList &&
  1155.         (lpMyAdrList->lpAdrList->cEntries == lpMyAdrList->cMaxEntries))
  1156.     {
  1157.         /* List is full; we need to ReAlloc */
  1158.         fReAlloc = TRUE;
  1159.         cMaxEntries = lpMyAdrList->cMaxEntries + lpMyAdrList->cMaxEntries / 2;
  1160.     }
  1161.     else
  1162.     {
  1163.         /* List exists and is not full; just point to it */
  1164.         Assert(lpMyAdrList->lpAdrList);
  1165.         Assert(lpMyAdrList->lpAdrList->cEntries < lpMyAdrList->cMaxEntries);
  1166.         lpAdrList = lpMyAdrList->lpAdrList;
  1167.     }
  1168.  
  1169.     /* If the list was NULL or full we'll Alloc/ReAlloc */
  1170.  
  1171.     if (fAlloc || fReAlloc)
  1172.     {
  1173.         cb = CbNewADRLIST(cMaxEntries);
  1174.         sc = lpxpl->AllocateBuffer(cb, &lpAdrList);
  1175.         if (sc)
  1176.         {
  1177.             DebugTrace("AllocateBuffer() failed in HrAddRecipToAdrList()");
  1178.             goto ret;
  1179.         }
  1180.  
  1181.         /* Zero-out new list */
  1182.  
  1183.         memset(lpAdrList, 0, (size_t) cb);
  1184.  
  1185.         if (fReAlloc)
  1186.         {
  1187.             /* We're ReAllocing; copy old list into new memory */
  1188.  
  1189.             cb = CbNewADRLIST(lpMyAdrList->lpAdrList->cEntries);
  1190.             if (cb)
  1191.                 memcpy(lpAdrList, lpMyAdrList->lpAdrList, (size_t) cb);
  1192.  
  1193.             /* Free old list */
  1194.  
  1195.             lpxpl->FreeBuffer(lpMyAdrList->lpAdrList);
  1196.         }
  1197.  
  1198.         /* Fix-up size and pointer elements */
  1199.  
  1200.         lpMyAdrList->cMaxEntries = cMaxEntries;
  1201.         lpMyAdrList->lpAdrList = lpAdrList;
  1202.     }
  1203.  
  1204.     /* Allocate room for cRecipProps PropValues and chain memory needed
  1205.        for AdrEntry data to the rgProps block to make freeing easier. */
  1206.  
  1207.     sc = lpxpl->AllocateBuffer(cRecipProps * sizeof(SPropValue), &rgProps);
  1208.  
  1209.     if (sc)
  1210.     {
  1211.         DebugTrace("AllocateBuffer() failed in HrAddRecipToAdrList()");
  1212.         goto ret;
  1213.     }
  1214.  
  1215.     /* Allocate memory for AddrType */
  1216.  
  1217.     sc = lpxpl->AllocateMore(lstrlen(lpszAddrType) + 1, rgProps, &lpszAddrTypeT);
  1218.  
  1219.     if (sc)
  1220.     {
  1221.         DebugTrace("AllocateMore() failed in HrAddRecipToAdrList()");
  1222.         goto ret;
  1223.     }
  1224.  
  1225.     /* Copy AddrType into chained memory buffer */
  1226.  
  1227.     lstrcpy(lpszAddrTypeT, lpszAddrType);
  1228.  
  1229.     /* Break lpszNameAddr into lpszDisplayName and lpszAddress */
  1230.  
  1231.     lpsz = strtok(lpszNameAddr, "[");
  1232.  
  1233.     sc = lpxpl->AllocateMore(lstrlen(lpsz) + 1, rgProps, &lpszDisplayName);
  1234.  
  1235.     if (sc)
  1236.     {
  1237.         DebugTrace("AllocateMore() failed in HrAddRecipToAdrList()");
  1238.         goto ret;
  1239.     }
  1240.  
  1241.     /* Copy Display Name into chained memory buffer */
  1242.  
  1243.     lstrcpy(lpszDisplayName, lpsz);
  1244.  
  1245.     lpsz = strtok(NULL, "]");
  1246.  
  1247.     sc = lpxpl->AllocateMore(lstrlen(lpsz) + 1, rgProps, &lpszAddress);
  1248.  
  1249.     if (sc)
  1250.     {
  1251.         DebugTrace("AllocateMore() failed in HrAddRecipToAdrList()");
  1252.         goto ret;
  1253.     }
  1254.  
  1255.     /* Copy Address into chained memory buffer */
  1256.  
  1257.     lstrcpy(lpszAddress, lpsz);
  1258.  
  1259.     /* Create OneOff Entry ID */
  1260.  
  1261.     hr = lpMAPISup->lpVtbl->CreateOneOff(lpMAPISup,
  1262.         lpszDisplayName, lpszAddrType, lpszAddress, 0,
  1263.         &cbEntryID, (LPENTRYID FAR *) &lpEntryID);
  1264.  
  1265.     if (hr)
  1266.     {
  1267.         DebugTrace("CreateOneOff() failed in HrAddRecipToAdrList()");
  1268.         goto ret;
  1269.     }
  1270.  
  1271.     /* We need to copy the EntryID into chained memory */
  1272.  
  1273.     sc = lpxpl->AllocateMore(cbEntryID, rgProps, &lpb);
  1274.  
  1275.     if (sc)
  1276.     {
  1277.         DebugTrace("AllocateMore() failed in HrAddRecipToAdrList()");
  1278.         goto ret;
  1279.     }
  1280.  
  1281.     /* Copy EntryID into chained memory buffer */
  1282.  
  1283.     if (cbEntryID)
  1284.         memcpy(lpb, lpEntryID, (size_t) cbEntryID);
  1285.  
  1286.     lpxpl->FreeBuffer(lpEntryID);
  1287.     lpEntryID = NULL;
  1288.  
  1289.     /* Now, build the PropValue array */
  1290.  
  1291.     rgProps[iRecipType].ulPropTag = PR_RECIPIENT_TYPE;
  1292.     rgProps[iRecipType].Value.l = lRecipType;
  1293.  
  1294.     rgProps[iDisplayName].ulPropTag = PR_DISPLAY_NAME;
  1295.     rgProps[iDisplayName].Value.LPSZ = lpszDisplayName;
  1296.  
  1297.     rgProps[iAddrType].ulPropTag = PR_ADDRTYPE;
  1298.     rgProps[iAddrType].Value.LPSZ = lpszAddrTypeT;
  1299.  
  1300.     rgProps[iAddress].ulPropTag = PR_EMAIL_ADDRESS;
  1301.     rgProps[iAddress].Value.LPSZ = lpszAddress;
  1302.  
  1303.     rgProps[iEntryID].ulPropTag = PR_ENTRYID;
  1304.     rgProps[iEntryID].Value.bin.cb = cbEntryID;
  1305.     rgProps[iEntryID].Value.bin.lpb = lpb;
  1306.  
  1307.     /* It's now safe to hook in the new AdrEntry */
  1308.  
  1309.     lpAdrList->aEntries[lpAdrList->cEntries].cValues = cRecipProps;
  1310.     lpAdrList->aEntries[lpAdrList->cEntries++].rgPropVals = rgProps;
  1311.  
  1312.     return hrSuccess;
  1313.  
  1314. ret:
  1315.     lpxpl->FreeBuffer(rgProps);
  1316.     lpxpl->FreeBuffer(lpEntryID);
  1317.  
  1318.     if (lpAdrList != lpMyAdrList->lpAdrList)
  1319.         lpxpl->FreeBuffer(lpAdrList);
  1320.  
  1321.     if (!hr && sc)
  1322.         hr = ResultFromScode(sc);
  1323.  
  1324.     DebugTraceResult(HrAddRecipToAdrList()Failed !, hr);
  1325.  
  1326.     return hr;
  1327. }
  1328.  
  1329.  
  1330. /*
  1331.  -  HrAddRecipToReplyList
  1332.  -
  1333.  *  Purpose:
  1334.  *      Builds the PR_REPLY_RECIPIENT_NAMES and PR_REPLY_RECIPIENT_ENTRIES
  1335.  *      properties by re-allocing as new ones are added to the list.
  1336.  *
  1337.  *  Parameters:
  1338.  *      lpxpl           Points to Transport Logon object
  1339.  *      lpszToken       Contains Display Name and E-Mail Address
  1340.  *      lpszAddrType    Address Type for this transport
  1341.  *      lppszNames      Semi-colon delimited list of Display Names
  1342.  *      lpcbEIDList     Current size of EntryList
  1343.  *      lppEIDList      Pointer to current EntryList
  1344.  *
  1345.  *  Returns:
  1346.  *      hr              Indicating Suucess/Failure
  1347.  */
  1348.  
  1349. HRESULT
  1350. HrAddRecipToReplyList(LPXPL lpxpl, LPTSTR lpszToken, LPTSTR lpszAddrType,
  1351.     LPTSTR * lppszNames, ULONG * lpcbEIDList, LPFLATENTRYLIST * lppEIDList)
  1352. {
  1353.     SCODE sc;
  1354.     HRESULT hr = hrSuccess;
  1355.     LPTSTR lpszAddress;
  1356.     LPTSTR lpszName;
  1357.     LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
  1358.     ULONG cbEID;
  1359.     LPBYTE lpEID = NULL;
  1360.     LPFLATENTRYLIST lpOld = *lppEIDList;
  1361.     ULONG cbOld;
  1362.     LPFLATENTRYLIST lpNew = NULL;
  1363.     LPFLATENTRY lpEntry;
  1364.     ULONG cbNew;
  1365.     LPTSTR lpszNewNames = NULL;
  1366.  
  1367.     lpszName = strtok(lpszToken, "[");
  1368.  
  1369.     lpszAddress = strtok(NULL, "]");
  1370.  
  1371.     /* Create OneOff Entry ID for this Recipient */
  1372.  
  1373.     hr = lpMAPISup->lpVtbl->CreateOneOff(lpMAPISup,
  1374.         lpszName, lpszAddrType, lpszAddress, 0,
  1375.         &cbEID, (LPENTRYID FAR *) &lpEID);
  1376.  
  1377.     if (hr)
  1378.         goto ret;
  1379.  
  1380.     /* Determine size of new list and allocate memory for it.
  1381.        The "+ 3) & -4L" will round up the allocation to be a
  1382.        multiple of 4 bytes. */
  1383.  
  1384.     if (lpOld)
  1385.     {
  1386.         Assert(!IsBadReadPtr(lpOld, CbNewFLATENTRYLIST(0)));
  1387.         Assert(!IsBadReadPtr(lpOld->abEntries, (UINT) lpOld->cbEntries));
  1388.  
  1389.         cbOld = lpOld->cbEntries;
  1390.         cbNew = (cbOld + offsetof(FLATENTRY, abEntry) + cbEID + 3) & -4L;
  1391.     }
  1392.     else
  1393.     {
  1394.         cbNew = cbOld = (cbEID + offsetof(FLATENTRY, abEntry) + 3) & -4L;
  1395.     }
  1396.  
  1397.     sc = lpxpl->AllocateBuffer(cbNew + offsetof(FLATENTRYLIST, abEntries), &lpNew);
  1398.  
  1399.     if (sc)
  1400.     {
  1401.         hr = ResultFromScode(sc);
  1402.         goto ret;
  1403.     }
  1404.  
  1405.     /* If Re-Allocing then copy old list and new EID, else build new list */
  1406.  
  1407.     if (lpOld)
  1408.     {
  1409.         ULONG cbNewOff = (cbOld + 3) & -4L;
  1410.  
  1411.         /* Copy the old data to the new structure */
  1412.  
  1413.         lpNew->cEntries = lpOld->cEntries + 1;
  1414.         lpNew->cbEntries = cbNew;
  1415.  
  1416.         if (cbOld)
  1417.             memcpy(lpNew->abEntries, lpOld->abEntries, (size_t) cbOld);
  1418.  
  1419.         /* Resolve the pointer to the new FLATENTRY */
  1420.  
  1421.         lpEntry = (LPFLATENTRY) & lpNew->abEntries[cbNewOff];
  1422.  
  1423.     }
  1424.     else
  1425.     {
  1426.         lpNew->cEntries = 1;
  1427.         lpNew->cbEntries = cbNew;
  1428.  
  1429.         /* Resolve the pointer to the new FLATENTRY */
  1430.  
  1431.         lpEntry = (LPFLATENTRY) lpNew->abEntries;
  1432.     }
  1433.  
  1434.     /* Add in the new FLATENTRY */
  1435.  
  1436.     lpEntry->cb = cbEID;
  1437.     if (cbEID)
  1438.         memcpy(lpEntry->abEntry, lpEID, (size_t) cbEID);
  1439.  
  1440.     /* Now, build the Display Name(s) String */
  1441.  
  1442.     if (*lppszNames)
  1443.     {
  1444.         /* We're Re-Allocing: copy old string and cat new one */
  1445.  
  1446.         sc = lpxpl->AllocateBuffer(lstrlen(*lppszNames) + lstrlen(lpszName) + 3,
  1447.             &lpszNewNames);
  1448.  
  1449.         if (sc)
  1450.         {
  1451.             hr = ResultFromScode(sc);
  1452.             goto ret;
  1453.         }
  1454.  
  1455.         lstrcpy(lpszNewNames, *lppszNames);
  1456.         lstrcat(lpszNewNames, "; ");
  1457.         lstrcat(lpszNewNames, lpszName);
  1458.  
  1459.         lpxpl->FreeBuffer(*lppszNames);
  1460.     }
  1461.     else
  1462.     {
  1463.         /* First name; just alloc and copy... */
  1464.  
  1465.         sc = lpxpl->AllocateBuffer(lstrlen(lpszName) + 1, &lpszNewNames);
  1466.  
  1467.         if (sc)
  1468.         {
  1469.             hr = ResultFromScode(sc);
  1470.             goto ret;
  1471.         }
  1472.  
  1473.         lstrcpy(lpszNewNames, lpszName);
  1474.     }
  1475.  
  1476.     /* It's now safe to hook in the new list. */
  1477.     /* Free old list and pass back new one.   */
  1478.  
  1479.     lpxpl->FreeBuffer(lpOld);
  1480.  
  1481.     *lppEIDList = lpNew;
  1482.     *lpcbEIDList = cbNew + offsetof(FLATENTRYLIST, abEntries);
  1483.     *lppszNames = lpszNewNames;
  1484.  
  1485. ret:
  1486.     lpxpl->FreeBuffer(lpEID);
  1487.  
  1488.     if (hr)
  1489.     {
  1490.         lpxpl->FreeBuffer(lpNew);
  1491.         lpxpl->FreeBuffer(lpszNewNames);
  1492.     }
  1493.     return hr;
  1494. }
  1495.  
  1496. /*
  1497.  -  HrMakeSearchKey
  1498.  -
  1499.  *  Purpose:
  1500.  *      Makes a Search Key (for the PR_???_SEARCH_KEY property) from
  1501.  *      the values passed in.  Memory is chained to some parent block.
  1502.  *      SearchKeys look like: ADDRTYPE:EMAILADDRESS.
  1503.  *
  1504.  *  Parameters:
  1505.  *      lpxpl           Points to Transport Logon object
  1506.  *      lpParent        Memory block to chain Search Key to
  1507.  *      lpszAddrType    Address Type
  1508.  *      lpszAddress     E-mail Address
  1509.  *      lpcbSK          Returned size of Search Key
  1510.  *      lppSK           The returned Search Key
  1511.  *
  1512.  *  Returns:
  1513.  *      hr              Indicating Suucess/Failure
  1514.  */
  1515.  
  1516. HRESULT
  1517. HrMakeSearchKey(LPXPL lpxpl, LPVOID lpParent, LPTSTR lpszAddrType,
  1518.     LPTSTR lpszAddress, ULONG * lpcbSK, LPBYTE * lppSK)
  1519. {
  1520.     SCODE sc;
  1521.     HRESULT hr = hrSuccess;
  1522.     LPBYTE lpb = NULL;
  1523.     ULONG ulSize;
  1524.  
  1525.     /* The 2 is for the colon and the NULL terminator */
  1526.  
  1527.     ulSize = sizeof(TCHAR) * (2 + lstrlen(lpszAddrType) + lstrlen(lpszAddress));
  1528.  
  1529.     sc = lpxpl->AllocateMore(ulSize, lpParent, &lpb);
  1530.  
  1531.     if (sc)
  1532.     {
  1533.         hr = ResultFromScode(sc);
  1534.         goto ret;
  1535.     }
  1536.  
  1537.     /* We need to convert to upper case, that's the law! */
  1538.  
  1539.     wsprintf((LPTSTR) lpb, "%s:%s", lpszAddrType, lpszAddress);
  1540.     CharUpperBuff((LPTSTR) lpb, (UINT) (ulSize - sizeof(TCHAR)));
  1541.  
  1542.     *lpcbSK = ulSize;
  1543.     *lppSK = lpb;
  1544.  
  1545. ret:
  1546.     return hr;
  1547. }
  1548.  
  1549. /*
  1550.  -  HrGetLine
  1551.  -
  1552.  *  Purpose:
  1553.  *      Kind of like fgets() except it strips CR\LF pairs for us.
  1554.  *      Reads cbDest bytes from the lpSof stream or until a CR/LF
  1555.  *      pair is reached, whichever comes first.  Returns count of
  1556.  *      bytes read into lpsz.
  1557.  *
  1558.  *  Parameters:
  1559.  *      lpSof           Points to an OLE 2.0 Stream (to be read from)
  1560.  *      cbDest          Size of memory pointed to by lpsz
  1561.  *      lpsz            Points to a chunck of memory that receives the
  1562.  *                      line being read in.  Must be of size cbDest.
  1563.  *      pcbRead         Receives the count of bytes actually read in.
  1564.  *
  1565.  *  Returns:
  1566.  *      hr              Indicating Suucess/Failure
  1567.  */
  1568.  
  1569. HRESULT
  1570. HrGetLine(LPSTREAM lpSof, ULONG cbDest, LPTSTR lpsz, ULONG * pcbRead)
  1571. {
  1572.     HRESULT hr = S_OK;
  1573.     BOOL fCRLF = FALSE;
  1574.     TCHAR rgch1[1];
  1575.     TCHAR rgch2[1];
  1576.     ULONG cbRead;
  1577.  
  1578.     if (!lpSof || (cbDest == 0) || !lpsz || !pcbRead)
  1579.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1580.  
  1581.     for (*pcbRead = 0; *pcbRead < cbDest - sizeof(TCHAR);)
  1582.     {
  1583.         /* read one TCHAR from stream */
  1584.  
  1585.         hr = lpSof->lpVtbl->Read(lpSof, (LPVOID) rgch1, sizeof(TCHAR), &cbRead);
  1586.  
  1587.         if (hr || (cbRead != sizeof(TCHAR)))
  1588.             break;
  1589.  
  1590.         /* Test for CR/LF pair; if not then add to line */
  1591.  
  1592.         if (*rgch1 == '\r')
  1593.         {
  1594.             hr = lpSof->lpVtbl->Read(lpSof, (LPVOID) rgch2, sizeof(TCHAR), &cbRead);
  1595.  
  1596.             if (hr)
  1597.                 break;
  1598.  
  1599.             if (cbRead == sizeof(TCHAR))
  1600.             {
  1601.                 if (*rgch2 == '\n')
  1602.                 {
  1603.                     fCRLF = TRUE;
  1604.                     break;
  1605.                 }
  1606.             }
  1607.             else
  1608.             {
  1609.                 *lpsz++ = *rgch1;
  1610.                 *lpsz++ = *rgch2;
  1611.                 *pcbRead += 2 * sizeof(TCHAR);
  1612.             }
  1613.         }
  1614.         else
  1615.         {
  1616.             *lpsz++ = *rgch1;
  1617.             *pcbRead += sizeof(TCHAR);
  1618.         }
  1619.     }
  1620.  
  1621.     /* NULL terminate and leave */
  1622.  
  1623.     *lpsz = '\0';
  1624.  
  1625.     /* Test for the EOF case.  Since the stream
  1626.        won't return errors, we will!!!! */
  1627.  
  1628.     if (!fCRLF && !*pcbRead)
  1629.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1630.  
  1631.     return hr;
  1632. }
  1633.  
  1634.  
  1635. /*
  1636.  -  FGetTagAndToken
  1637.  -
  1638.  *  Purpose:
  1639.  *      Breaks a line read from the input stream into Tag and Token.
  1640.  *      If no Tag is found, then returns FALSE indicating this is not
  1641.  *      a Tag'd line.
  1642.  *
  1643.  *  Parameters:
  1644.  *      lpsz            A line from the messsage file that may have
  1645.  *                      the format: 'Tag: Token'.  Must be of size MAX_LINE.
  1646.  *      pulTag          Will receive the index of lpsz's Tag in rgszTag
  1647.  *      lppszToken      Will point to the token in lpsz
  1648.  *
  1649.  *  Returns:
  1650.  *      TRUE            If lpsz starts with a valid Tag
  1651.  *      FALSE           otherwise
  1652.  */
  1653.  
  1654. BOOL
  1655. FGetTagAndToken(LPTSTR lpsz, ULONG * pulTag, LPTSTR * lppszToken)
  1656. {
  1657.     ULONG uli;
  1658.     TCHAR chT;
  1659.     LPTSTR lpszT;
  1660.  
  1661.     if (!lpsz || !pulTag || !lppszToken)
  1662.         return FALSE;
  1663.  
  1664.     /* Tags end with ':'  If lpsz has a ':' then it MIGHT be
  1665.        a Tag'd line, else it's definitely NOT a Tag'd line. */
  1666.  
  1667.     lpszT = strchr(lpsz, ':');
  1668.  
  1669.     if (!lpszT)
  1670.         return FALSE;
  1671.  
  1672.     /* Check that we're not at the MAX_LINE extent of lpsz.  If we are
  1673.        then just return, cause this can't possibly be a Tag'd line!
  1674.        The '3' accounts for the space, colon, and null terminator. */
  1675.  
  1676.     if ((lpszT - lpsz) > (MAX_LINE - 3))
  1677.         return FALSE;
  1678.  
  1679.     /* Swap *(lpszT+2) with a NULL to seperate Tag from Token */
  1680.  
  1681.     lpszT += 2;
  1682.     chT = *lpszT;
  1683.     *lpszT = '\0';
  1684.  
  1685.     /* Look-Up 'Potential' Tag in Tag Table */
  1686.  
  1687.     for (uli = 0; uli < NUM_TAGS; uli++)
  1688.     {
  1689.         if (!lstrcmp(lpsz, rgszTags[uli]))
  1690.         {
  1691.             /* Found!  Remember index */
  1692.  
  1693.             *pulTag = uli;
  1694.             break;
  1695.         }
  1696.     }
  1697.  
  1698.     /* Swap that NULL out.  lpszT now points to the token (maybe) */
  1699.  
  1700.     *lpszT = chT;
  1701.  
  1702.     if (uli == NUM_TAGS)
  1703.         return FALSE;           /* Tag wasn't found; it's just a line */
  1704.  
  1705.     *lppszToken = lpszT;
  1706.  
  1707.     return TRUE;
  1708. }
  1709.  
  1710.  
  1711. /*
  1712.  -  FileTimeFromSzTime
  1713.  -
  1714.  *  Purpose:
  1715.  *      Converts the textized data field in the text file format
  1716.  *      to a FILETIME struct format.  If we encounter errors in
  1717.  *      parsing the lpszDateTime string, we jump to the conversion
  1718.  *      call and will translate as much as we've filled in so far.
  1719.  *
  1720.  *  Parameters:
  1721.  *      lpszDateTime        Date/Time in the format: yyyy/mm/dd hh:mm
  1722.  *      pft                 Pointer to a FILETIME struct
  1723.  */
  1724.  
  1725. void
  1726. FileTimeFromSzTime(LPTSTR lpszDateTime, FILETIME * pft)
  1727. {
  1728.     SYSTEMTIME systime =
  1729.     {0, 0, 0, 0, 0, 0, 0, 0};
  1730.     LPTSTR lpsz;
  1731.  
  1732.     /* Feeble attempt at parameter validation! */
  1733.  
  1734.     if (!lpszDateTime || !pft)
  1735.         return;
  1736.  
  1737.     /* Grab the Year */
  1738.  
  1739.     lpsz = strtok(lpszDateTime, "/");
  1740.     if (!lpsz)
  1741.         goto ret;
  1742.  
  1743.     systime.wYear = atoi(lpsz);
  1744.  
  1745.     /* Grab the Month */
  1746.  
  1747.     lpsz = strtok(NULL, "/");
  1748.     if (!lpsz)
  1749.         goto ret;
  1750.  
  1751.     systime.wMonth = atoi(lpsz);
  1752.  
  1753.     /* Grab the Day */
  1754.  
  1755.     lpsz = strtok(NULL, " ");
  1756.     if (!lpsz)
  1757.         goto ret;
  1758.  
  1759.     systime.wDay = atoi(lpsz);
  1760.  
  1761.     /* Grab the Hour */
  1762.  
  1763.     lpsz = strtok(NULL, ":");
  1764.     if (!lpsz)
  1765.         goto ret;
  1766.  
  1767.     systime.wHour = atoi(lpsz);
  1768.  
  1769.     /* Grab the Minutes */
  1770.  
  1771.     lpsz = strtok(NULL, "\r\n:");
  1772.     if (!lpsz)
  1773.         goto ret;
  1774.  
  1775.     systime.wMinute = atoi(lpsz);
  1776.  
  1777. ret:
  1778.     SystemTimeToFileTime(&systime, pft);
  1779. }
  1780.  
  1781.  
  1782. /*
  1783.  -  SetFromMeFlag
  1784.  -
  1785.  *  Purpose:
  1786.  *      Sets the PR_MESSAGE_FLAGS MSGFLAG_FROMME bit to on.
  1787.  *
  1788.  */
  1789.  
  1790. VOID
  1791. SetFromMeFlag(LPXPL lpxpl, LPMESSAGE lpMsg)
  1792. {
  1793.     HRESULT hResult;
  1794.     LPSPropValue lpProp = NULL;
  1795.  
  1796.     /* Get the current state of the Message Flags */
  1797.  
  1798.     hResult = HrGetOneProp((LPMAPIPROP)lpMsg, PR_MESSAGE_FLAGS, &lpProp);
  1799.  
  1800.     if (HR_FAILED(hResult))
  1801.         goto ret;
  1802.  
  1803.     /* Add the FromMe bit */
  1804.  
  1805.     lpProp->Value.l |= MSGFLAG_FROMME;
  1806.  
  1807.     hResult = HrSetOneProp((LPMAPIPROP)lpMsg, lpProp);
  1808.  
  1809. ret:
  1810.     lpxpl->FreeBuffer(lpProp);
  1811. }
  1812.