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

Windows编程

开发平台:

Visual C++

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