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

Windows编程

开发平台:

Visual C++

  1. /*
  2.  *  M S P M S G . C
  3.  *
  4.  *  Code for the MAPI Sample Store Provider implementation of the
  5.  *  IMessage object.  The implementation is, in fact, a thin
  6.  *  wrapping layer around the implementation of IMessage on
  7.  *  IStorage.  We wrap the IMessage object returned by IMsgOnIStg
  8.  *  so that we can handle those methods (like SubmitMessage) not
  9.  *  understood by a standalone message (e.g. one embedded in a word
  10.  *  document) but which makes sense for a message in the context of
  11.  *  a message store.
  12.  *
  13.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  14.  */
  15. #include "msp.h"
  16. #define MSG_ValidateParameters(pobj, intf, method, arglist)     
  17.         OBJ_ValidateParameters(pobj, intf, method, sizeof(IMSG), &vtblIMSG, arglist)
  18. /* Manifest constants */
  19. /* Number of properties to initialize a normal message with */
  20. #define cpropIMSGInit       11
  21. /* Number of properties to initialize a message-in-message with */
  22. #define cpropMsgInMsgInit   3
  23. /* Number of in-memory properties associated with a message object */
  24. #define cpropIMSGInternal   4
  25. /* The property attributes to set on the initial set of message properties */
  26. #define grfpropattrIMSGInit (PROPATTR_MANDATORY | PROPATTR_READABLE)
  27. #define IMSG_EnterCriticalSection(pimsg)    OBJ_EnterCriticalSection((POBJ)pimsg)
  28. #define IMSG_LeaveCriticalSection(pimsg)    OBJ_LeaveCriticalSection((POBJ)pimsg)
  29. typedef enum _mrflavor
  30. {
  31.     ENUM_ADD = 1,
  32.     ENUM_MODIFY,
  33.     ENUM_REMOVE
  34. } MRFLAVOR;
  35. /* internal functions */
  36. static HRESULT HrSaveMsgInMsg(PIMSG pimsg, ULONG ulFlags);
  37. static SCODE ScFillOneSBPval(PLMR plmr, LPVOID pvOrigBuf, ULONG ulPropTag,
  38.     ULONG cb, LPBYTE lpbData, LPSPropValue pval);
  39. /* Global variables */
  40. /* Dispatch table for IMessage objects */
  41. IMSG_Vtbl vtblIMSG =
  42. {
  43.     (IMSG_QueryInterface_METHOD *)  OBJ_QueryInterface,
  44.     (IMSG_AddRef_METHOD *)          OBJ_AddRef,
  45.     (IMSG_Release_METHOD *)         OBJ_Release,
  46.     (IMSG_GetLastError_METHOD *)    IMS_GetLastError,
  47.     IMSG_SaveChanges,
  48.     IMSG_GetProps,
  49.     IMSG_GetPropList,
  50.     IMSG_OpenProperty,
  51.     IMSG_SetProps,
  52.     IMSG_DeleteProps,
  53.     IMSG_CopyTo,
  54.     IMSG_CopyProps,
  55.     (IMSG_GetNamesFromIDs_METHOD *) IMS_GetNamesFromIDs,
  56.     (IMSG_GetIDsFromNames_METHOD *) IMS_GetIDsFromNames,
  57.     IMSG_GetAttachmentTable,
  58.     IMSG_OpenAttach,
  59.     IMSG_CreateAttach,
  60.     IMSG_DeleteAttach,
  61.     IMSG_GetRecipientTable,
  62.     IMSG_ModifyRecipients,
  63.     IMSG_SubmitMessage,
  64.     IMSG_SetReadFlag
  65. };
  66. /*
  67.  *  OBJECT METHODS
  68.  */
  69. /*
  70.  *  IMSG_SaveChanges
  71.  *
  72.  *  Purpose:
  73.  *      Saves changes made to a message object and all of its
  74.  *      sub-objects (attachments, et al.).
  75.  *
  76.  *  Arguments:
  77.  *      pimsg       Pointer to the object.
  78.  *      ulFlags     Flags.  The following are defined:
  79.  *                  KEEP_OPEN_READONLY  Do not invalidate the
  80.  *                                      object, make it read-only.
  81.  *                  KEEP_OPEN_READWRITE Don't invalidate the
  82.  *                                      object, keep it open
  83.  *                                      read/write.
  84.  *                  FORCE_SAVE          Overwrite any changes made by
  85.  *                                      others since message was openned.
  86.  *
  87.  *  Returns:
  88.  *      HRESULT
  89.  *
  90.  *  Side effects:
  91.  *      None.
  92.  *
  93.  *  Errors:
  94.  */
  95. STDMETHODIMP IMSG_SaveChanges(PIMSG pimsg, ULONG ulFlags)
  96. {
  97.     HRESULT hr = hrSuccess;
  98.     PIFLD pifldParent = NULL;   /* parent as an open folder */
  99.     ULONG ulPropMsgFlags;       /* flags for save on property message */
  100.     ULONG ulChangeType = TABLE_ROW_MODIFIED;
  101.     BOOL fUnread = FALSE;
  102.     MSG_ValidateParameters(
  103.             pimsg,
  104.             IMAPIProp,
  105.             SaveChanges,
  106.             (pimsg, 
  107.             ulFlags));
  108.     IMSG_EnterCriticalSection(pimsg);
  109.     /* Handle msg-in-msg separately. */
  110.     if (OBJ_TestFlag(pimsg, MSGF_MSGINMSG))
  111.     {
  112.         hr = HrSaveMsgInMsg(pimsg, ulFlags);
  113.         goto exit;
  114.     }
  115.     /* open up lpmsg's parent so we can update tables and contents counts */
  116.     hr = HrOpenParent(pimsg->pims, pimsg->peid, MAPI_MODIFY, &pifldParent);
  117.     if (hr != hrSuccess)
  118.         goto exit;
  119.     /* mark the new message as complete by updating its ENTRYID property */
  120.     if (OBJ_TestFlag(pimsg, MSGF_NEWLYCREATED))
  121.     {
  122.         ULONG ulMF;
  123.         PEID peidInt = NULL;
  124.         ReplaceExt(pimsg->peid->szPath, MESSAGE_EXT);
  125.         /* Assume that PR_ENTRYID is at position 0 in the in-memory */
  126.         /* array. That's where it went in HrSetInternalProps. */
  127.         AssertSz(pimsg->pval->ulPropTag == PR_ENTRYID,
  128.             "The location of PR_ENTRYID in the in-memory array has changed");
  129.         peidInt = (PEID) pimsg->pval->Value.bin.lpb;
  130.         AssertSz(!FIsInvalidEID(pimsg->pval->Value.bin.cb, peidInt, NULL),
  131.             "Invalid internal Entryid");
  132.         if (peidInt)
  133.             ReplaceExt(peidInt->szPath, MESSAGE_EXT);
  134.         /* Now, we need to update PR_INSTANCE_KEY in the same way. Since */
  135.         /* the message is just becoming permanent, it won't be in any */
  136.         /* tables yet, so it can change without affecting any tables. */
  137.         /* NOTE: this code assumes knowledge of the format of */
  138.         /* PR_INSTANCE_KEY. */
  139.         AssertSz(pimsg->pval[1].ulPropTag == PR_INSTANCE_KEY, "The location "
  140.             "of PR_INSTANCE_KEY in the in-memory array has changed");
  141.         peidInt = (PEID) pimsg->pval[1].Value.bin.lpb;
  142.         AssertSz(!FIsInvalidEID(pimsg->pval[1].Value.bin.cb, peidInt, NULL),
  143.             "Invalid internal Entryid");
  144.         if (peidInt)
  145.             ReplaceExt(peidInt->szPath, MESSAGE_EXT);
  146.         hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, &pimsg->pims->lmr,
  147.             PR_MESSAGE_FLAGS, &ulMF);
  148.         if (hr != hrSuccess)
  149.             goto exit;
  150.         /* remember the message's unread status, and to update open tables */
  151.         fUnread = !(ulMF & MSGFLAG_READ);
  152.         ulChangeType = TABLE_ROW_ADDED;
  153.     }
  154.     /* When the spooler saves a message, from the client's perspective, it */
  155.     /* hasn't been modified, because it has just arrived. */
  156.     if (!OBJ_TestFlag(pimsg, MSGF_NEWLYCREATED)
  157.         && !OBJ_TestFlag(pimsg->pims, MSF_SPOOLER))
  158.     {
  159.         /* unset the UNMODIFIED bit */
  160.         hr = HrSetFlags(pimsg, UNSET, PR_MESSAGE_FLAGS, MSGFLAG_UNMODIFIED);
  161.         if (hr != hrSuccess)
  162.             goto exit;
  163.     }
  164.     /* save the changes but keep the property file open */
  165.     ulPropMsgFlags = ulFlags;
  166.     if (!(ulFlags & KEEP_OPEN_READWRITE))
  167.         ulPropMsgFlags |= KEEP_OPEN_READONLY;
  168.     hr = pimsg->lpmsg->lpVtbl->SaveChanges(pimsg->lpmsg, ulPropMsgFlags);
  169.     if (hr != hrSuccess)
  170.         goto exit;
  171.     /* update the parent's contents table and parent folder properties */
  172.     if (ulChangeType == TABLE_ROW_ADDED)
  173.     {
  174.         (void) HrIncrementOneROProp(pifldParent, 1, PR_CONTENT_COUNT);
  175.         if (fUnread)
  176.             (void) HrIncrementOneROProp(pifldParent, 1, PR_CONTENT_UNREAD);
  177.     }
  178.     ChangeTable(pimsg->pims, pifldParent->peid, pimsg->peid, MAPI_MESSAGE,
  179.         ulChangeType, TRUE);
  180. exit:
  181.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  182.     UlRelease(pifldParent);
  183.     if (hr == hrSuccess)
  184.     {
  185.         /* Unless the user requests to continue with modify access, switch */
  186.         /* down to read-only access. This means that specifying neither of */
  187.         /* the KEEP_OPEN flags means the same thing as KEEP_OPEN_READONLY. */
  188.         if (!(ulFlags & KEEP_OPEN_READWRITE))
  189.             OBJ_ClearFlag(pimsg, OBJF_MODIFY);
  190.         OBJ_ClearFlag(pimsg, MSGF_NEWLYCREATED);
  191.     }
  192.     IMSG_LeaveCriticalSection(pimsg);
  193.     DebugTraceResult(IMSG_SaveChanges, hr);
  194.     return HrCheckHr(hr, IMAPIProp_SaveChanges);
  195. }
  196. /*
  197.  *  IMSG_GetProps
  198.  *
  199.  *  Purpose:
  200.  *      Returns to the caller the value(s) of one or more
  201.  *      properties existent on an IMSG object.  The order of the
  202.  *      properties in the returned ppval structure exactly
  203.  *      matches the order in which the properties were requested in
  204.  *      ptaga.  The caller must free the returned
  205.  *      structure by calling MAPIFreeBuffer(*ppval), but
  206.  *      only if the function returns zero or the error
  207.  *      MAPI_W_ERRORS_RETURNED.  Uses the IMessage on IStorage
  208.  *      property interface implementation.
  209.  *
  210.  *  Arguments:
  211.  *      pimsg           Pointer to the object.
  212.  *      ptaga   Pointer to a counted array of property tags
  213.  *                      ("names") that identify the values to be
  214.  *                      returned.
  215.  *      ulFlags         UNICODE / String8
  216.  *      pcval       Location in which to return the count of
  217.  *                      elements in *ppval.
  218.  *      ppval   Location in which to return an allocated
  219.  *                      array of property values (the caller frees
  220.  *                      by calling MAPIFreeBuffer).
  221.  *
  222.  *  Returns:
  223.  *      HRESULT
  224.  *
  225.  *  Side effects:
  226.  *      None.
  227.  *
  228.  *  Errors:
  229.  *      If the call succeeded overall but access to one or more
  230.  *      properties failed, the function returns the warning
  231.  *      MAPI_W_ERRORS_RETURNED.  The calling application should
  232.  *      then check the Property Tag of each of the returned
  233.  *      properties to determine which ones failed.  Those that fail
  234.  *      have their Property Type set to PT_ERROR and their value (a
  235.  *      ULONG) indicates which error occurred.
  236.  *
  237.  *      MAPI_E_NO_ACCESS    The caller does not have access
  238.  *                                  to the requested properties.
  239.  *      MAPI_W_ERRORS_RETURNED      See above.
  240.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  241.  *                                  call to the service provider
  242.  *                                  failed.
  243.  */
  244. STDMETHODIMP IMSG_GetProps(PIMSG pimsg, LPSPropTagArray ptaga, ULONG ulFlags,
  245.     ULONG *pcval, LPSPropValue *ppval)
  246. {
  247.     HRESULT hr = hrSuccess;
  248.     BOOL fLocked = FALSE;
  249.     MSG_ValidateParameters(
  250.             pimsg,
  251.             IMAPIProp,
  252.             GetProps,
  253.             (pimsg, 
  254.             ptaga, 
  255.             ulFlags, 
  256.             pcval, 
  257.             ppval));
  258.     #ifdef VALIDATE
  259.     if (ulFlags & MAPI_UNICODE)
  260.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  261.     #endif
  262.         
  263.     IMSG_EnterCriticalSection(pimsg);
  264.     hr = pimsg->lpmsg->lpVtbl->GetProps(pimsg->lpmsg, ptaga, ulFlags,
  265.         pcval, ppval);
  266.     {if(HR_SUCCEEDED(hr))
  267.     {
  268.         LPSPropValue pvalStoreSupMask = PpropFindProp(*ppval, *pcval, 
  269.                     PROP_TAG(PT_UNSPECIFIED, PROP_ID(PR_STORE_SUPPORT_MASK)));
  270.         if(pvalStoreSupMask)
  271.         {
  272.             pvalStoreSupMask->ulPropTag = PR_STORE_SUPPORT_MASK;
  273.             pvalStoreSupMask->Value.l = SMS_SUPPORTMASK;
  274.             /* fix up hr */
  275.             if(ptaga->cValues == 1)
  276.                 hr = hrSuccess;
  277.         }
  278.     }
  279.     }
  280.     /* If not message-in-message then wrap values. */
  281.     /* Note that this wrapping function takes as an */
  282.     /* argument the HRESULT from the previous GetProps call. */
  283.     /* We aren't ignoring the error. */
  284.     if (!OBJ_TestFlag(pimsg, MSGF_MSGINMSG))
  285.         hr = HrWrap_GetProps(hr, pimsg->pims, pimsg->cval, pimsg->pval,
  286.             pcval, ppval, FALSE, (ptaga != NULL), (POBJ)pimsg);
  287.     IMSG_LeaveCriticalSection(pimsg);
  288.     #ifdef DEBUG
  289.     if (GetScode(hr) != MAPI_W_ERRORS_RETURNED)
  290.         DebugTraceResult(IMSG_GetProps, hr);
  291.     #endif
  292.     return HrCheckHr(hr, IMAPIProp_GetProps);
  293. }
  294. /*
  295.  *  IMSG_GetPropList
  296.  *
  297.  *  Purpose:
  298.  *      Returns a list of all the properties currently accessible.
  299.  *      Uses the IMessage on IStorage property implementation.
  300.  *
  301.  *  Arguments:
  302.  *      pimsg       Pointer to the object.
  303.  *      ulFlags     UNICODE / String8
  304.  *      pptaga      Location in which to return a pointer
  305.  *                  to a counted array of property tags.
  306.  *
  307.  *  Returns:
  308.  *      HRESULT
  309.  *
  310.  *  Side effects:
  311.  *      None.
  312.  *
  313.  *  Errors:
  314.  *      MAPI_E_NO_ACCESS    The caller does not have access
  315.  *                                  to the requested properties.
  316.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  317.  *                                  call to the service provider
  318.  *                                  failed.
  319.  */
  320. STDMETHODIMP IMSG_GetPropList(PIMSG pimsg, ULONG ulFlags, LPSPropTagArray *pptaga)
  321. {
  322.     HRESULT hr = hrSuccess;
  323.     LPSPropTagArray ptaga = NULL;
  324.     LPSPropTagArray ptagaRet = NULL;
  325.     UINT ind;
  326.     SizedSPropTagArray(2, sptaToAdd) = 
  327.         { 2,
  328.             {   PR_MESSAGE_RECIPIENTS, 
  329.                 PR_MESSAGE_ATTACHMENTS
  330.             }
  331.         };
  332.     MSG_ValidateParameters(
  333.             pimsg,
  334.             IMAPIProp,
  335.             GetPropList,
  336.             (pimsg, 
  337.             ulFlags, 
  338.             pptaga));
  339.     #ifdef VALIDATE
  340.     if (ulFlags & MAPI_UNICODE)
  341.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  342.     #endif
  343.         
  344.     IMSG_EnterCriticalSection(pimsg);
  345.     hr = pimsg->lpmsg->lpVtbl->GetPropList(pimsg->lpmsg, ulFlags, &ptaga);
  346.     if (hr == hrSuccess && FIsUnsavedMsg(pimsg))
  347.     {
  348.         /* Remove PR_ENTRYID from the array. Since the message is unsaved, */
  349.         /* don't return the entryid. Overwrite the PR_ENTRYID entry with */
  350.         /* the last prop tag in the array. */
  351.         /* //$ Should PR_INSTANCE_KEY be removed from the array too? */
  352.         ULONG *pulPT;
  353.         ULONG *pulPTMac;
  354.         pulPT = ptaga->aulPropTag;
  355.         pulPTMac = pulPT + ptaga->cValues;
  356.         while (pulPT < pulPTMac)
  357.         {
  358.             if (*pulPT == PR_ENTRYID)
  359.             {
  360.                 ptaga->cValues--;
  361.                 pulPTMac--;
  362.                 if (pulPT < pulPTMac)
  363.                     memcpy(pulPT, pulPTMac, sizeof(ULONG));
  364.                 break;
  365.             }
  366.             ++pulPT;
  367.         }
  368.     }
  369.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  370.     if(hr == hrSuccess)
  371.     {
  372.         if(!(hr = ResultFromScode(MAPIAllocateBuffer(
  373.                         CbNewSPropTagArray(ptaga->cValues + sptaToAdd.cValues),
  374.                          (LPVOID *)&ptagaRet))))
  375.         {
  376.             CopyMemory(ptagaRet, ptaga, CbNewSPropTagArray(ptaga->cValues));
  377.             for(ind = 0; ind < sptaToAdd.cValues; ++ind)
  378.             {
  379.                 if(!FContainsProp(sptaToAdd.aulPropTag[ind], ptaga))
  380.                 {
  381.                     ptagaRet->aulPropTag[ptagaRet->cValues++] =
  382.                         sptaToAdd.aulPropTag[ind];
  383.                 }
  384.             }
  385.             
  386.         }
  387.     }
  388.     MAPIFreeBuffer(ptaga);
  389.     
  390.     if (hr == hrSuccess)
  391.         *pptaga = ptagaRet;
  392.     IMSG_LeaveCriticalSection(pimsg);
  393.     DebugTraceResult(IMSG_GetPropList, hr);
  394.     return HrCheckHr(hr, IMAPIProp_GetPropList);
  395. }
  396. /*
  397.  *  IMSG_OpenProperty
  398.  *
  399.  *  Purpose:
  400.  *      Open a requested interface on a property for further
  401.  *      access.  Commonly used for stream access to a large binary
  402.  *      or text property.  This is the only way to access a
  403.  *      property of type PT_OBJECT, and may be used on other
  404.  *      properties depending on the implementation.  Uses the
  405.  *      IMessage on IStorage property implementation.
  406.  *
  407.  *  Arguments:
  408.  *      pimsg           Pointer to the object.
  409.  *      ulPropTag   Property tag for the desired property.  Only
  410.  *                      the ID bits of the tag are used; the type bits
  411.  *                      are ignored.
  412.  *      lpiid           Pointer to the GUID identifying which interface
  413.  *                      is desired.
  414.  *      ulInterfaceOptions  specifies interface-specific behavior
  415.  *      ulFlags     MAPI_CREATE, MAPI_MODIFY, MAPI_DEFERRED_ERRORS
  416.  *      lppUnk      Location in which to return a pointer to the
  417.  *                      newly created interface pointer.
  418.  *
  419.  *  Returns:
  420.  *      HRESULT
  421.  *
  422.  *  Side effects:
  423.  *      None.
  424.  *
  425.  *  Errors:
  426.  *      MAPI_E_INTERFACE_NOT_SUPPORTED      An error occurred opening a
  427.  *                                          supported interface.
  428.  */
  429. STDMETHODIMP IMSG_OpenProperty(PIMSG pimsg, ULONG ulPropTag, LPCIID lpiid,
  430.     ULONG ulInterfaceOptions, ULONG ulFlags, LPUNKNOWN *lppUnk)
  431. {
  432.     HRESULT hr = hrSuccess;
  433.     MSG_ValidateParameters(
  434.             pimsg,
  435.             IMAPIProp,
  436.             OpenProperty,
  437.             (pimsg, 
  438.             ulPropTag, 
  439.             lpiid, 
  440.             ulInterfaceOptions, 
  441.             ulFlags, 
  442.             lppUnk));
  443.     IMSG_EnterCriticalSection(pimsg);
  444.     hr = pimsg->lpmsg->lpVtbl->OpenProperty(pimsg->lpmsg, ulPropTag, lpiid,
  445.         ulInterfaceOptions, ulFlags, lppUnk);
  446.     if (hr == hrSuccess
  447.         && (ulFlags & (MAPI_MODIFY | MAPI_CREATE)))
  448.         OBJ_ClearFlag(pimsg, MSGF_FRESH);
  449.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  450.     IMSG_LeaveCriticalSection(pimsg);
  451.     DebugTraceResult(IMSG_OpenProperty, hr);
  452.     return HrCheckHr(hr, IMAPIProp_OpenProperty);
  453. }
  454. /*
  455.  *  IMSG_SetProps
  456.  *
  457.  *  Purpose:
  458.  *      Sets the value of one or more properties.  This call passes
  459.  *      a number of Property Value structures.  The Property Tag in
  460.  *      each indicates which property is having its values set and
  461.  *      the value indicates what should be stored.  The caller must
  462.  *      free the returned property problem structure by calling
  463.  *      MAPIFreeBuffer(*lppProblems), but only if the call
  464.  *      succeeded overall.  Uses the IMessage on IStorage property
  465.  *      implementation.
  466.  *
  467.  *  Arguments:
  468.  *      pimsg           Pointer to the object.
  469.  *      cValues         Number of values in lpPropArray.
  470.  *      lpPropArray     Pointer to a Property Value array.
  471.  *      lppProblems     Location in which to return a pointer to a
  472.  *                      counted array of property problem
  473.  *                      structures.
  474.  *
  475.  *  Returns:
  476.  *      HRESULT.  If the call succeeds overall, a zero is returned.
  477.  *      If there are problems with setting some or all of the
  478.  *      selected values, and a non-NULL is passed for lppProblems,
  479.  *      then a SPropProblemArray structure is returned with details
  480.  *      about each problem.  The value returned in lppProblems is
  481.  *      only valid if zero is returned in the HRESULT.  If an error
  482.  *      occurs on the call such that a non-zero value is returned
  483.  *      for the HRESULT then the contents of *lppProblems are
  484.  *      undefined.  In particular, do not use or free the structure
  485.  *      if an error occurs on the call.
  486.  *
  487.  *  Side effects:
  488.  *      None.
  489.  *
  490.  *  Errors:
  491.  *      MAPI_E_NO_ACCESS    The caller does not have access
  492.  *                                  to the requested properties.
  493.  *      MAPI_E_CALL_FAILED      A general problem affecting
  494.  *                                  access to all of the object's
  495.  *                                  properties occurred.
  496.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  497.  *                                  call to the service provider
  498.  *                                  failed.
  499.  */
  500. STDMETHODIMP IMSG_SetProps(PIMSG pimsg, ULONG cValues, LPSPropValue lpPropArray,
  501.     LPSPropProblemArray *lppProblems)
  502. {
  503.     HRESULT hr;
  504.     MSG_ValidateParameters(
  505.             pimsg,
  506.             IMAPIProp,
  507.             SetProps,
  508.             (pimsg, 
  509.             cValues, 
  510.             lpPropArray, 
  511.             lppProblems));
  512.     IMSG_EnterCriticalSection(pimsg);
  513.     hr = pimsg->lpmsg->lpVtbl->SetProps(pimsg->lpmsg, cValues, lpPropArray,
  514.         lppProblems);
  515.     if (hr == hrSuccess)
  516.         OBJ_ClearFlag(pimsg, MSGF_FRESH);
  517.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  518.     IMSG_LeaveCriticalSection(pimsg);
  519.     DebugTraceResult(IMSG_SetProps, hr);
  520.     return HrCheckHr(hr, IMAPIProp_SetProps);
  521. }
  522. /*
  523.  *  IMSG_DeleteProps
  524.  *
  525.  *  Purpose:
  526.  *      Deletes the list of properties given in ptaga.
  527.  *      The caller must free the returned property problem
  528.  *      structure by calling MAPIFreeBuffer(*ppErr), but only
  529.  *      if the call succeeded overall.  Uses the IMessage on
  530.  *      IStorage property implementation.
  531.  *
  532.  *  Arguments:
  533.  *      pimsg           Pointer to the object.
  534.  *      ptaga   Pointer to an array of Property Tags
  535.  *                      identifying the properties to delete.
  536.  *      ppErr       Location in which to return a pointer to a
  537.  *                      counted array of property problem
  538.  *                      structures.
  539.  *
  540.  *  Returns:
  541.  *      HRESULT.  If the call succeeds overall, zero is returned.
  542.  *      If there are problems with deleting some or all of the
  543.  *      selected values, and a non-NULL is passed for ppErr,
  544.  *      then a SPropProblemArray structure is returned with details
  545.  *      about each problem.  The value returned in ppErr is
  546.  *      only valid if zero is returned in the HRESULT.  If an error
  547.  *      occurs on the call such that a non-zero value is returned
  548.  *      for the HRESULT then the contents of *ppErr are
  549.  *      undefined.  In particular, do not use or free the structure
  550.  *      if an error occurs on the call.
  551.  *
  552.  *  Side effects:
  553.  *      None.
  554.  *
  555.  *  Errors:
  556.  *      MAPI_E_NO_ACCESS    The caller does not have access
  557.  *                                  to the requested properties.
  558.  *      MAPI_E_CALL_FAILED      A general problem affecting
  559.  *                                  access to all of the object's
  560.  *                                  properties occurred.
  561.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  562.  *                                  call to the service provider
  563.  *                                  failed.
  564.  */
  565. STDMETHODIMP IMSG_DeleteProps(PIMSG pimsg, LPSPropTagArray ptaga,
  566.     LPSPropProblemArray *ppErr)
  567. {
  568.     HRESULT hr;
  569.     MSG_ValidateParameters(
  570.             pimsg,
  571.             IMAPIProp,
  572.             DeleteProps,
  573.             (pimsg, 
  574.             ptaga, 
  575.             ppErr));
  576.     IMSG_EnterCriticalSection(pimsg);
  577.     hr = pimsg->lpmsg->lpVtbl->DeleteProps(pimsg->lpmsg, ptaga, ppErr);
  578.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  579.     IMSG_LeaveCriticalSection(pimsg);
  580.     DebugTraceResult(IMSG_DeleteProps, hr);
  581.     return HrCheckHr(hr, IMAPIProp_DeleteProps);
  582. }
  583. /*
  584.  *  IMSG_CopyTo
  585.  *
  586.  *  Purpose:
  587.  *      Copies the contents of the current object to a destination
  588.  *      object.  The entire contents, including contained objects,
  589.  *      are copied, or optionally the caller can provide a list of
  590.  *      properties that are not to be copied.  Previous information
  591.  *      in the destination object which is not overwritten by
  592.  *      copied data is neither deleted nor modified.
  593.  *
  594.  *  Arguments:
  595.  *      pimsg           Pointer to the source object.
  596.  *      ciidExcl        Count of the excluded interfaces in
  597.  *                      rgiidExcl.
  598.  *      rgiidExcl   Array of interface IDs specifying
  599.  *                      interfaces not to be attempted in trying to
  600.  *                      copy supplemental information to the
  601.  *                      destination object.
  602.  *      ptagaExcl   Counted array of property tags of
  603.  *                      properties that are not to be copied to the
  604.  *                      destination object.  NULL indicates all
  605.  *                      properties are to be copied.
  606.  *      ulUIParam       Handle of parent window cast to ULONG.
  607.  *      lpProgress      Callback for doing progress UI.
  608.  *      piidDst     Interface ID of the interface of lpDestObj,
  609.  *                      the destination object.
  610.  *      lpDestObj       Pointer to the open destination object.
  611.  *      ulFlags         Flags.  Defined as follows:
  612.  *                      MAPI_MOVE       Indicates a move operation.
  613.  *                                      The default is to copy.
  614.  *                      MAPI_NOREPLACE  Indicates that existing
  615.  *                                      properties should not be
  616.  *                                      overridden.  The default is
  617.  *                                      to overwrite existing
  618.  *                                      properties.
  619.  *                      MAPI_DIALOG     Display a progress dialog
  620.  *                                      as the operation proceeds.
  621.  *                      MAPI_STD_DIALOG Use MAPI standard dialog
  622.  *                                      instead of
  623.  *                                      provider-specific dialog.
  624.  *      ppErr       Pointer to a variable that is filled in
  625.  *                      with a pointer to a set of property
  626.  *                      problems.  If NULL, no problem set is
  627.  *                      returned on an error.
  628.  *
  629.  *  Returns:
  630.  *      HRESULT
  631.  *
  632.  *  Side effects:
  633.  *      None.
  634.  *
  635.  */
  636. STDMETHODIMP IMSG_CopyTo(PIMSG pimsg, ULONG ciidExcl, LPCIID rgiidExcl,
  637.     LPSPropTagArray ptagaExcl, ULONG ulUIParam, LPMAPIPROGRESS lpProgress,
  638.     LPCIID piidDst, LPVOID lpDestObj, ULONG ulFlags,
  639.     LPSPropProblemArray *ppErr)
  640. {
  641.     HRESULT hr;
  642.     MSG_ValidateParameters(
  643.             pimsg,
  644.             IMAPIProp,
  645.             CopyTo,
  646.             (pimsg, 
  647.             ciidExcl, 
  648.             rgiidExcl, 
  649.             ptagaExcl, 
  650.             ulUIParam, 
  651.             lpProgress, 
  652.             piidDst, 
  653.             lpDestObj, 
  654.             ulFlags, 
  655.             ppErr));
  656.     IMSG_EnterCriticalSection(pimsg);
  657.     hr = pimsg->lpmsg->lpVtbl->CopyTo(pimsg->lpmsg, ciidExcl, rgiidExcl,
  658.         ptagaExcl, ulUIParam, lpProgress, piidDst, lpDestObj,
  659.         ulFlags, ppErr);
  660.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  661.     IMSG_LeaveCriticalSection(pimsg);
  662.     DebugTraceResult(IMSG_CopyTo, hr);
  663.     return HrCheckHr(hr, IMAPIProp_CopyTo);
  664. }
  665. /*
  666.  *  IMSG_CopyProps
  667.  *
  668.  *  Purpose:
  669.  *      Copies the specified properties of the current object to a destination
  670.  *      object.  Previous information
  671.  *      in the destination object which is not overwritten by
  672.  *      copied data is neither deleted nor modified.
  673.  *
  674.  *  Arguments:
  675.  *      pimsg           Pointer to the source object.
  676.  *      ptagaIncl       Counted array of property tags of
  677.  *                      properties that are not to be copied to the
  678.  *                      destination object.  NULL indicates all
  679.  *                      properties are to be copied.
  680.  *      ulUIParam       Handle of parent window cast to ULONG.
  681.  *      lpProgress      Callback for doing progress UI.
  682.  *      piidDst         Interface ID of the interface of lpDestObj,
  683.  *                      the destination object.
  684.  *      lpDestObj       Pointer to the open destination object.
  685.  *      ulFlags         Flags.  Defined as follows:
  686.  *                      MAPI_MOVE       Indicates a move operation.
  687.  *                                      The default is to copy.
  688.  *                      MAPI_NOREPLACE  Indicates that existing
  689.  *                                      properties should not be
  690.  *                                      overridden.  The default is
  691.  *                                      to overwrite existing
  692.  *                                      properties.
  693.  *                      MAPI_DIALOG     Display a progress dialog
  694.  *                                      as the operation proceeds.
  695.  *                      MAPI_DECLINE_OK
  696.  *      ppErr       Pointer to a variable that is filled in
  697.  *                      with a pointer to a set of property
  698.  *                      problems.  If NULL, no problem set is
  699.  *                      returned on an error.
  700.  *
  701.  *  Returns:
  702.  *      HRESULT
  703.  *
  704.  *  Side effects:
  705.  *      None.
  706.  *
  707.  */
  708. STDMETHODIMP IMSG_CopyProps(PIMSG pimsg,
  709.     LPSPropTagArray ptagaIncl, ULONG ulUIParam, LPMAPIPROGRESS lpProgress,
  710.     LPCIID piidDst, LPVOID lpDestObj, ULONG ulFlags,
  711.     LPSPropProblemArray *ppErr)
  712. {
  713.     HRESULT hr;
  714.     MSG_ValidateParameters(
  715.             pimsg,
  716.             IMAPIProp,
  717.             CopyProps,
  718.             (pimsg,
  719.             ptagaIncl, 
  720.             ulUIParam, 
  721.             lpProgress, 
  722.             piidDst, 
  723.             lpDestObj, 
  724.             ulFlags, 
  725.             ppErr));
  726.     IMSG_EnterCriticalSection(pimsg);
  727.     hr = pimsg->lpmsg->lpVtbl->CopyProps(pimsg->lpmsg,
  728.         ptagaIncl, ulUIParam, lpProgress, piidDst, lpDestObj,
  729.         ulFlags, ppErr);
  730.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  731.     IMSG_LeaveCriticalSection(pimsg);
  732.     DebugTraceResult(IMSG_CopyProps, hr);
  733.     return HrCheckHr(hr, IMAPIProp_CopyProps);
  734. }
  735. /*
  736.  *  IMSG_GetAttachmentTable
  737.  *
  738.  *  Purpose:
  739.  *      Returns, in table form, the list of attachments contained
  740.  *      in this message (one row per attachment).  The table has at
  741.  *      least the PR_ATTACH_NUM and PR_RENDERING_POSITION columns.
  742.  *      Additional columns may be in the table depending on the
  743.  *      implementation.  This table may change while it is open if
  744.  *      the application calls CreateAttach() or DeleteAttach(), or
  745.  *      if an attachment is modified in a way that some properties
  746.  *      in the table get changed.
  747.  *
  748.  *  Arguments:
  749.  *      pimsg       Pointer to the object.
  750.  *      ulFlags     Flags.  Reserved for future use.  Ignored.
  751.  *      lppTable    Pointer to a variable in which the address of
  752.  *                  the returned table object is placed.
  753.  *
  754.  *  Returns:
  755.  *      HRESULT
  756.  *
  757.  *  Side effects:
  758.  *      None.
  759.  *
  760.  *  Errors:
  761.  *      MAPI_E_NOT_ENOUGH_MEMORY    Unable to allocate memory for
  762.  *                                  the returned table object or
  763.  *                                  its underlying data.
  764.  */
  765. STDMETHODIMP
  766. IMSG_GetAttachmentTable(PIMSG pimsg, ULONG ulFlags, LPMAPITABLE *lppTable)
  767. {
  768.     HRESULT hr;
  769.     MSG_ValidateParameters(
  770.             pimsg,
  771.             IMessage,
  772.             GetAttachmentTable,
  773.             (pimsg, 
  774.             ulFlags, 
  775.             lppTable));
  776.     #ifdef VALIDATE
  777.     if (ulFlags & MAPI_UNICODE)
  778.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  779.     #endif
  780.     IMSG_EnterCriticalSection(pimsg);
  781.     hr = pimsg->lpmsg->lpVtbl->GetAttachmentTable(pimsg->lpmsg, ulFlags,
  782.         lppTable);
  783.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  784.     IMSG_LeaveCriticalSection(pimsg);
  785.     DebugTraceResult(IMSG_GetAttachmentTable, hr);
  786.     return HrCheckHr(hr, IMessage_GetAttachmentTable);
  787. }
  788. /*
  789.  *  IMSG_OpenAttach
  790.  *
  791.  *  Purpose:
  792.  *      Opens an existing attachment and returns a pointer which
  793.  *      provides further access to the open attachment.  We get a
  794.  *      pointer to the attachment object from the IMessage on
  795.  *      IStorage implementation, and then wrap it with our own
  796.  *      attachment object.
  797.  *
  798.  *  Arguments:
  799.  *      pimsg           Pointer to the object.
  800.  *      ulAttachmentNum Number of the attachment to be opened (the
  801.  *                      value of this parameter comes from the
  802.  *                      attachment table.
  803.  *      piidDst     IID of interface requested for the
  804.  *                      newly-opened object.  NULL or IID_IMAPIProp
  805.  *                      means to open the object using the standard
  806.  *                      MAPI 1.0 interface for the object.
  807.  *                      IID_IUnknown means to open it using
  808.  *                      the easiest interface you can open.
  809.  *      ulFlags         Flags.  The following are defined:
  810.  *                      MAPI_MODIFY     Write access is desired.
  811.  *                                      The message must also be
  812.  *                                      open for write access.
  813.  *      lppAttach       Pointer to a variable which is to receive
  814.  *                      the pointer to the open attachment object.
  815.  *
  816.  *  Returns:
  817.  *      HRESULT
  818.  *
  819.  *  Side effects:
  820.  *      None.
  821.  *
  822.  *  Errors:
  823.  *      IStorage errors, plus,
  824.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  825.  *                                  the new attachment instance.
  826.  */
  827. STDMETHODIMP IMSG_OpenAttach(PIMSG pimsg, ULONG ulAttachmentNum, LPCIID piidDst,
  828.     ULONG ulFlags, LPATTACH *lppAttach)
  829. {
  830.     HRESULT hr = hrSuccess;
  831.     LPATTACH lpattach = NULL;
  832.     PIATCH piatch = NULL;
  833.     BOOL fModify;
  834.     MSG_ValidateParameters(
  835.             pimsg,
  836.             IMessage,
  837.             OpenAttach,
  838.             (pimsg, 
  839.             ulAttachmentNum, 
  840.             piidDst, 
  841.             ulFlags, 
  842.             lppAttach));
  843.     IMSG_EnterCriticalSection(pimsg);
  844.     /* Check for modification rights on the message. Switch to read-only */
  845.     /* if the client asked for best access. */
  846.     if (ulFlags & MAPI_BEST_ACCESS)
  847.         fModify = !!OBJ_TestFlag(pimsg, OBJF_MODIFY);
  848.     else
  849.     {
  850.         fModify = !!(ulFlags & MAPI_MODIFY);
  851.         if (fModify && !OBJ_TestFlag(pimsg, OBJF_MODIFY))
  852.         {
  853.             hr = ResultFromScode(MAPI_E_NO_ACCESS);
  854.             goto exit;
  855.         }
  856.     }
  857.     hr = pimsg->lpmsg->lpVtbl->OpenAttach(pimsg->lpmsg, ulAttachmentNum,
  858.         piidDst, ulFlags, &lpattach);
  859.     if (hr != hrSuccess)
  860.         goto exit;
  861.     hr = HrNewIATCH(lpattach, pimsg, fModify, &piatch);
  862.     UlRelease(lpattach);
  863.     lpattach = NULL;
  864.     if (hr != hrSuccess)
  865.         goto exit;
  866.     *lppAttach = (LPATTACH) piatch;
  867. exit:
  868.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  869.     if (hr != hrSuccess)
  870.         UlRelease(piatch);
  871.     IMSG_LeaveCriticalSection(pimsg);
  872.     DebugTraceResult(IMSG_OpenAttach, hr);
  873.     return HrCheckHr(hr, IMessage_OpenAttach);
  874. }
  875. /*
  876.  *  IMSG_CreateAttach
  877.  *
  878.  *  Purpose:
  879.  *      Creates a new attachment in a message and returns a pointer
  880.  *      which provides further access to the open attachment.  We
  881.  *      get a pointer to the attachment object from the IMessage on
  882.  *      IStorage implementation, and then wrap it with our own
  883.  *      attachment object.
  884.  *
  885.  *  Arguments:
  886.  *      pimsg               Pointer to the object.
  887.  *      piidDst         IID of interface requested for the
  888.  *                          newly-opened object.  NULL or IID_IMAPIProp
  889.  *                          means to open the object using the standard
  890.  *                          MAPI 1.0 interface for the object.
  891.  *                          IID_IUnknown means to open it using
  892.  *                          the easiest interface you can open.
  893.  *      ulFlags             Flags.  Reserved for future use.
  894.  *                          Ignored.
  895.  *      lpulAttachmentNum   Pointer to a variable which is to
  896.  *                          receive the number of the newly created
  897.  *                          attachment.  This number is valid only
  898.  *                          within this message.
  899.  *      lppAttach           Pointer to a variable which is to
  900.  *                          receive the pointer to the open
  901.  *                          attachment object.
  902.  *
  903.  *  Returns:
  904.  *      HRESULT
  905.  *
  906.  *  Side effects:
  907.  *      None.
  908.  *
  909.  *  Errors:
  910.  *      IStorage errors, plus,
  911.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  912.  *                                  the new attachment instance.
  913.  */
  914. STDMETHODIMP IMSG_CreateAttach(PIMSG pimsg, LPCIID piidDst, ULONG ulFlags,
  915.     ULONG *lpulAttachmentNum, LPATTACH *lppAttach)
  916. {
  917.     HRESULT hr = hrSuccess;
  918.     LPATTACH lpattach = NULL;
  919.     PIATCH piatch = NULL;
  920.     ULONG ulAttachNum = 0L;
  921.     MSG_ValidateParameters(
  922.             pimsg,
  923.             IMessage,
  924.             CreateAttach,
  925.             (pimsg, 
  926.             piidDst, 
  927.             ulFlags, 
  928.             lpulAttachmentNum, 
  929.             lppAttach));
  930.     IMSG_EnterCriticalSection(pimsg);
  931.     if (!OBJ_TestFlag(pimsg, OBJF_MODIFY))
  932.     {
  933.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  934.         goto exit;
  935.     }
  936.     hr = pimsg->lpmsg->lpVtbl->CreateAttach(pimsg->lpmsg, piidDst,
  937.         ulFlags, &ulAttachNum, &lpattach);
  938.     if (hr != hrSuccess)
  939.         goto exit;
  940.     /* Wrap the attachment object returned by IMessage. */
  941.     hr = HrNewIATCH(lpattach, pimsg, TRUE, &piatch);
  942.     UlRelease(lpattach);
  943.     lpattach = NULL;
  944.     if (hr != hrSuccess)
  945.         goto exit;
  946.     *lpulAttachmentNum = ulAttachNum;
  947.     *lppAttach = (LPATTACH) piatch;
  948.     OBJ_ClearFlag(pimsg, MSGF_FRESH);
  949. exit:
  950.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  951.     if (hr != hrSuccess)
  952.         UlRelease(piatch);
  953.     IMSG_LeaveCriticalSection(pimsg);
  954.     DebugTraceResult(IMSG_CreateAttach, hr);
  955.     return HrCheckHr(hr, IMessage_CreateAttach);
  956. }
  957. /*
  958.  *  IMSG_DeleteAttach
  959.  *
  960.  *  Purpose:
  961.  *      Deletes an attachment in a message.  The current
  962.  *      application should release all pointers to an attachment
  963.  *      and its streams prior to deleting the attachment.  Deleted
  964.  *      attachments are not permanently gone until changes to the
  965.  *      message are saved.
  966.  *
  967.  *  Arguments:
  968.  *      pimsg               Pointer to the object.
  969.  *      ulAttachmentNum     Index of the attachment to be deleted.
  970.  *      ulUIParam           Window handle cast to a ULONG.
  971.  *      lpProgress          Callback for displaying progress UI.
  972.  *      ulFlags             Flags.  Reserved for future use.
  973.  *                          Ignored.
  974.  *
  975.  *  Returns:
  976.  *      HRESULT
  977.  *
  978.  *  Side effects:
  979.  *      Invalidates all pointers to the attachment, if it is
  980.  *      currently open (this is done by the IMessage on IStorage
  981.  *      implementation).  These invalidated sub-objects then only
  982.  *      support the Release, AddRef, and QueryInterface methods.
  983.  *
  984.  *  Errors:
  985.  *      IStorage errors (MAPI_E_WRITE_FAULT, etc.).
  986.  */
  987. STDMETHODIMP IMSG_DeleteAttach(PIMSG pimsg, ULONG ulAttachmentNum,
  988.     ULONG ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  989. {
  990.     HRESULT hr = hrSuccess;
  991.     MSG_ValidateParameters(
  992.             pimsg,
  993.             IMessage,
  994.             DeleteAttach,
  995.             (pimsg, 
  996.             ulAttachmentNum, 
  997.             ulUIParam, 
  998.             lpProgress, 
  999.             ulFlags));
  1000.     IMSG_EnterCriticalSection(pimsg);
  1001.     if (!OBJ_TestFlag(pimsg, OBJF_MODIFY))
  1002.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1003.     else
  1004.         hr = pimsg->lpmsg->lpVtbl->DeleteAttach(pimsg->lpmsg, ulAttachmentNum,
  1005.             ulUIParam, lpProgress, ulFlags);
  1006.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  1007.     IMSG_LeaveCriticalSection(pimsg);
  1008.     DebugTraceResult(IMSG_DeleteAttach, hr);
  1009.     return HrCheckHr(hr, IMessage_DeleteAttach);
  1010. }
  1011. /*
  1012.  *  IMSG_GetRecipientTable
  1013.  *
  1014.  *  Purpose:
  1015.  *      Opens the recipient table in a message.  The recipient
  1016.  *      table for a received message or a message under composition
  1017.  *      contains one row for each recipient of the message.  The
  1018.  *      table will have at least the following columns:  PR_ROWID,
  1019.  *      PR_DISPLAY_NAME, PR_ENTRYID, PR_RECIPIENT_TYPE.  The
  1020.  *      additional properties PR_ADDRTYPE, PR_SENDER_NAME,
  1021.  *      PR_SENDER_ENTRYID, and PR_CLIENT_SUBMIT_TIME will appear in sent
  1022.  *      messages (messages not under composition).  Additional
  1023.  *      columns may be in the table, depending on the
  1024.  *      implementation.
  1025.  *
  1026.  *  Arguments:
  1027.  *      pimsg       Pointer to the object.
  1028.  *      ulFlags     Flags.  Reserved for future use.  Ignored.
  1029.  *      lppTable    Pointer to a variable in which the address of
  1030.  *                  the returned table object is placed.
  1031.  *
  1032.  *  Returns:
  1033.  *      HRESULT
  1034.  *
  1035.  *  Side effects:
  1036.  *      None.
  1037.  *
  1038.  *  Errors:
  1039.  *      MAPI_E_NOT_ENOUGH_MEMORY    Unable to allocate memory for
  1040.  *                                  the returned table object or
  1041.  *                                  its underlying data.
  1042.  */
  1043. STDMETHODIMP IMSG_GetRecipientTable(PIMSG pimsg, ULONG ulFlags,
  1044.     LPMAPITABLE *lppTable)
  1045. {
  1046.     HRESULT hr = hrSuccess;
  1047.     MSG_ValidateParameters(
  1048.             pimsg,
  1049.             IMessage,
  1050.             GetRecipientTable,
  1051.             (pimsg, 
  1052.             ulFlags, 
  1053.             lppTable));
  1054.     #ifdef VALIDATE
  1055.     if (ulFlags & MAPI_UNICODE)
  1056.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1057.     #endif
  1058.     IMSG_EnterCriticalSection(pimsg);
  1059.     hr = pimsg->lpmsg->lpVtbl->GetRecipientTable(pimsg->lpmsg, ulFlags,
  1060.         lppTable);
  1061.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  1062.     IMSG_LeaveCriticalSection(pimsg);
  1063.     DebugTraceResult(IMSG_GetRecipientTable, hr);
  1064.     return HrCheckHr(hr, IMessage_GetRecipientTable);
  1065. }
  1066. /*
  1067.  *  IMSG_ModifyRecipients
  1068.  *
  1069.  *  Purpose:
  1070.  *      Adds, deletes, and/or modifies the recipients in a message.
  1071.  *      The property set for each recipient being added or modified
  1072.  *      must include a PR_ROWID, PR_DISPLAY_NAME, PR_ADDRTYPE (it
  1073.  *      may be empty), PR_ENTRYID (it may be empty), and
  1074.  *      PR_RECIPIENT_TYPE.  Additional properties may be specified,
  1075.  *      but depending on the implementation they may be ignored or
  1076.  *      discarded.
  1077.  *
  1078.  *      The recipient table may be used to represent both
  1079.  *      "resolved" and "unresolved" entries.  An unresolved entry
  1080.  *      is one that consists only of a display name.  Applications
  1081.  *      which allow users to type recipient names directly will
  1082.  *      create these entries.  A resolved entry contains more
  1083.  *      information relating the display name to a recipient:  an
  1084.  *      email address type and an EntryID.  Unresolved entries are
  1085.  *      stored as entries with zero as a value for PR_ENTRYID and
  1086.  *      PR_ADDRTYPE.  A message with unresolved entries in the
  1087.  *      recipient table will generate a non-delivery-report if
  1088.  *      submitted.
  1089.  *
  1090.  *  Parameters
  1091.  *      pimsg       pointer to message object
  1092.  *      ulFlags     flags:  MESSAGE_REPLACE
  1093.  *      lpMods      Pointer to list of recipient modifications, additions, or
  1094.  *                  deletions to be performed on pimsg
  1095.  *  Returns:
  1096.  *      HRESULT
  1097.  *
  1098.  *  Side effects:
  1099.  *      This method converts all short-term EntryIDs to long-term
  1100.  *      EntryIDs.
  1101.  *
  1102.  *  Errors:
  1103.  */
  1104. STDMETHODIMP IMSG_ModifyRecipients(PIMSG pimsg, ULONG ulFlags, LPADRLIST lpMods)
  1105. {
  1106.     HRESULT hr = hrSuccess;
  1107.     MSG_ValidateParameters(
  1108.             pimsg,
  1109.             IMessage,
  1110.             ModifyRecipients,
  1111.             (pimsg, 
  1112.             ulFlags, 
  1113.             lpMods));
  1114.     IMSG_EnterCriticalSection(pimsg);
  1115.     hr = pimsg->lpmsg->lpVtbl->ModifyRecipients(pimsg->lpmsg, ulFlags, lpMods);
  1116.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  1117.     IMSG_LeaveCriticalSection(pimsg);
  1118.     DebugTraceResult(IMSG_ModifyRecipients, hr);
  1119.     return HrCheckHr(hr, IMessage_ModifyRecipients);
  1120. }
  1121. /*
  1122.  *  IMSG_SubmitMessage
  1123.  *
  1124.  *  Purpose:
  1125.  *      Mark a message as ready for sending and saves all changes
  1126.  *      to it and all its attachments.  Since this is not
  1127.  *      implemented in IMessage on IStorage, we must do it
  1128.  *      ourselves.
  1129.  *
  1130.  *  Arguments:
  1131.  *      pimsg       Pointer to the object.
  1132.  *      ulFlags     Flags.  Defined as follows:
  1133.  *                  FORCE_SUBMIT    If set, MAPI should submit the
  1134.  *                                  message even if it might not be
  1135.  *                                  sent right away.
  1136.  *
  1137.  *  Returns:
  1138.  *      HRESULT
  1139.  *
  1140.  *  Side effects:
  1141.  *      If the submission is successful, the pointer to the message
  1142.  *      and all associated sub-objects (messages, attachments,
  1143.  *      streams, tables) are no longer valid, except for their
  1144.  *      Release() methods.  No other operations on these pointers
  1145.  *      are permitted.  MAPI expects the application to release the
  1146.  *      message object and all associated sub-objects.
  1147.  *
  1148.  *  Errors:
  1149.  *      MAPI_E_NON_STANDARD     Unexpected queueing time is
  1150.  *                              possible.  This error is only
  1151.  *                              returned if the FORCE_SUBMIT flag
  1152.  *                              is not set.
  1153.  */
  1154. STDMETHODIMP IMSG_SubmitMessage(PIMSG pimsg, ULONG ulFlags)
  1155. {
  1156.     HRESULT hr = hrSuccess;
  1157.     LPMAPITABLE pmtRecip = NULL;
  1158.     PIMS pims;
  1159.     ULONG ulcRow = 0L;
  1160.     ULONG cValues = 0L;
  1161.     LPSPropValue pval = NULL;
  1162.     ULONG ulMF;
  1163.     LPSPropProblemArray pprba = NULL;
  1164.     SYSTEMTIME st;
  1165.     FILETIME ft;
  1166.     ULONG ulPrepareFlags;
  1167.     ULONG ulPreprocess;
  1168.     LPSRowSet prws = NULL;
  1169. #define NUM_FLAGTIME    3
  1170.     const static SizedSPropTagArray(NUM_FLAGTIME, proptagFlagTime) =
  1171.     {
  1172.         NUM_FLAGTIME,
  1173.         {
  1174.             PR_MESSAGE_FLAGS,
  1175.             PR_CLIENT_SUBMIT_TIME,
  1176.             PR_SUBMIT_FLAGS
  1177.         }
  1178.     };
  1179. #define NUM_RECIP_COLS  2
  1180.     const static SizedSPropTagArray(NUM_RECIP_COLS, proptagRecips) =
  1181.     {
  1182.         NUM_RECIP_COLS,
  1183.         {
  1184.             PR_ROWID,           /* make sure this stays first */
  1185.             PR_RESPONSIBILITY
  1186.         }
  1187.     };
  1188.     MSG_ValidateParameters(
  1189.             pimsg,
  1190.             IMessage,
  1191.             SubmitMessage,
  1192.             (pimsg, 
  1193.             ulFlags));
  1194.     IMSG_EnterCriticalSection(pimsg);
  1195.     pims = pimsg->pims;
  1196.     if (OBJ_TestFlag(pimsg, MSGF_MSGINMSG))
  1197.     {
  1198.         hr = ResultFromScode(MAPI_E_NO_SUPPORT);
  1199.         goto exit;
  1200.     }
  1201.     if (!OBJ_TestFlag(pimsg, OBJF_MODIFY))
  1202.     {
  1203.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1204.         goto exit;
  1205.     }
  1206.     /* Get message flag to check for resubmit. */
  1207.     hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, &pims->lmr,
  1208.         PR_MESSAGE_FLAGS, &ulMF);
  1209.     if (hr != hrSuccess)
  1210.         goto exit;
  1211.     /* Check to see if the message was already submitted. If so, clear the */
  1212.     /* Preprocess bit in PR_SUBMIT_FLAGS, save the message, and update the */
  1213.     /* outgoing queue. */
  1214.     if (ulMF & MSGFLAG_SUBMIT)
  1215.     {
  1216.         ULONG ulSF;
  1217.         AssertSz(OBJ_TestFlag(pimsg->pims, MSF_SPOOLER),
  1218.             "Message being resubmitted by other than the spooler");
  1219.         
  1220.         hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, &pims->lmr,
  1221.             PR_SUBMIT_FLAGS, &ulSF);
  1222.         if (hr != hrSuccess)
  1223.         {
  1224.             if (GetScode(hr) == MAPI_E_NOT_FOUND)
  1225.             {
  1226.                 TraceSz1 ("SAMPLE MS: IMSG_SubmitMessage: Error %s getting "
  1227.                     "PR_SUBMIT_FLAGS during resubmit.",
  1228.                     SzDecodeScode(GetScode(hr)));
  1229.                 hr = hrSuccess;
  1230.                 ulSF = 0;
  1231.             }
  1232.             else
  1233.                 goto exit;
  1234.         }
  1235.         ulSF &= ~SUBMITFLAG_PREPROCESS;
  1236.         hr = HrSetSingleProp((LPMAPIPROP) pimsg->lpmsg, &pims->lmr,
  1237.             PR_SUBMIT_FLAGS, &ulSF);
  1238.         if (hr != hrSuccess)
  1239.             goto exit;
  1240.     }
  1241.     else
  1242.     {
  1243.         /* Begin by making sure that all recipients have a PR_RESPONSIBILITY */
  1244.         /* property. If they don't, we need to put it in. */
  1245.     
  1246.         hr = pimsg->lpmsg->lpVtbl->GetRecipientTable(pimsg->lpmsg, 0L,
  1247.             &pmtRecip);
  1248.         if (hr != hrSuccess)
  1249.             goto exit;
  1250.     
  1251.         /* Get all columns and ensure that the PR_ROWID and PR_RESPONSIBILITY */
  1252.         /* columns are the first two table columns using the MAPI API function */
  1253.         /* HrAddColumns. */
  1254.     
  1255.         hr = HrAddColumns(pmtRecip, (LPSPropTagArray) &proptagRecips,
  1256.             pims->lmr.lpAllocBuf, pims->lmr.lpFreeBuf);
  1257.         if (hr != hrSuccess)
  1258.             goto exit;
  1259.     
  1260.         /* Check for PR_RESPONSIBILITY in each table row and set it if it's */
  1261.         /* missing. */
  1262.     
  1263.         while (TRUE)
  1264.         {
  1265.             LPSRow prw;
  1266.             LPSRow prwMac;
  1267.             LPSPropValue pvalT;
  1268.     
  1269.             /* Get 10 rows at a time. In general, GetRowCount may not */
  1270.             /* be supported by every provider. This loop does not count */
  1271.             /* on it working, even though I know that it would in this */
  1272.             /* implementation. */
  1273.     
  1274.             hr = pmtRecip->lpVtbl->QueryRows(pmtRecip, 10, 0L, &prws);
  1275.             if (hr != hrSuccess)
  1276.                 goto exit;
  1277.     
  1278.             /* All table implementations will return zero rows from QueryRows */
  1279.             /* when you're actually at the end of the table. This routine */
  1280.             /* uses that to test when to exit this loop. Note that this loop */
  1281.             /* doesn't need to check for no recipients at all, because */
  1282.             /* ExpandRecips (below) will do that as part of its processing. */
  1283.             if (prws->cRows == 0)
  1284.                 break;
  1285.     
  1286.             prw = prws->aRow;
  1287.             prwMac = prw + prws->cRows;
  1288.     
  1289.             /* Loop through the rows. For each row, put in PR_RESPONSIBILITY */
  1290.             /* if it's missing. Don't change recipients that already have */
  1291.             /* a PR_RESPONSIBILITY property (due to resubmission). */
  1292.     
  1293.             while (prw < prwMac)
  1294.             {
  1295.                 AssertSz(prw->cValues >= NUM_RECIP_COLS,
  1296.                     "Bad # of values returned");
  1297.         
  1298.                 pvalT = prw->lpProps;
  1299.         
  1300.                 AssertSz(!IsBadReadPtr(pvalT, (UINT) prws->aRow->cValues
  1301.                     * sizeof(SPropValue)), "Bad pval array");
  1302.         
  1303.                 /* PR_ROWID is in column zero. Leave it alone, and start */
  1304.                 /* with the next column. */
  1305.                 ++pvalT;
  1306.         
  1307.                 /* We don't ever want responsibility for any recipient. */
  1308.                 /* If this isn't the spooler calling, then force ALL */
  1309.                 /* responsibilities to FALSE regardless of what they were. */
  1310.                 if (PROP_TYPE(pvalT->ulPropTag) == PT_NULL
  1311.                     || !OBJ_TestFlag(pimsg->pims, MSF_SPOOLER))
  1312.                 {
  1313.                     pvalT->ulPropTag = PR_RESPONSIBILITY;
  1314.                     pvalT->Value.b = FALSE;
  1315.                 }
  1316.     
  1317.                 ++prw;
  1318.             }
  1319.     
  1320.             /* modify the rows */
  1321.             hr = pimsg->lpmsg->lpVtbl->ModifyRecipients(pimsg->lpmsg,
  1322.                 MODRECIP_MODIFY, (LPADRLIST) prws);
  1323.             if (hr != hrSuccess)
  1324.                 goto exit;
  1325.     
  1326.             FreeProws(prws);
  1327.             prws = NULL;
  1328.         }
  1329.     
  1330.         /* We're done with the recip table now, so release it. */
  1331.         UlRelease(pmtRecip);
  1332.         pmtRecip = NULL;
  1333.     
  1334.         ulPrepareFlags = 0;
  1335.     
  1336.         hr = pims->psup->lpVtbl->PrepareSubmit(pims->psup, pimsg->lpmsg,
  1337.             &ulPrepareFlags);
  1338.         if (hr != hrSuccess)
  1339.             goto exit;
  1340.     
  1341.         /* ExpandRecips checks for no recip, and will return an error. */
  1342.         /* Therefore, this code doesn't need to check here for that case. */
  1343.     
  1344.         hr = pims->psup->lpVtbl->ExpandRecips(pims->psup, pimsg->lpmsg,
  1345.             &ulPreprocess);
  1346.         if (hr != hrSuccess)
  1347.             goto exit;
  1348.     
  1349.         /* Now, your store provider, if it wishes, may take responsibility */
  1350.         /* for any recipients that it wishes to handle. (For example, if */
  1351.         /* it is tightly coupled to a transport.) */
  1352.     
  1353.         /* Get the time to add to the message as PR_CLIENT_SUBMIT_TIME */
  1354.         GetSystemTime(&st);
  1355.         SideAssert(SystemTimeToFileTime(&st, &ft));
  1356.     
  1357.         /* get the old values of PR_MESSAGE_FLAGS & PR_CLIENT_SUBMIT_TIME */
  1358.     
  1359.         hr = pimsg->lpmsg->lpVtbl->GetProps(pimsg->lpmsg,
  1360.             (LPSPropTagArray) &proptagFlagTime, 0, /* ansi */
  1361.             &cValues, &pval);
  1362.         if (HR_FAILED(hr))
  1363.             goto exit;
  1364.     
  1365.         /* Warnings are OK from GetProps here. */
  1366.     
  1367.         /* turn on the MSGFLAG_SUBMIT bit of PR_MESSAGE_FLAGS */
  1368.         pval[0].ulPropTag = PR_MESSAGE_FLAGS;
  1369.         pval[0].Value.l = pval[0].Value.l | MSGFLAG_SUBMIT;
  1370.     
  1371.         /* set the client submission time */
  1372.         pval[1].ulPropTag = PR_CLIENT_SUBMIT_TIME;
  1373.         pval[1].Value.ft = ft;
  1374.     
  1375.         /* set the submit flag. If necessary, turn on the preprocess flag */
  1376.         pval[2].ulPropTag = PR_SUBMIT_FLAGS;
  1377.     
  1378.         if (ulPreprocess & NEEDS_PREPROCESSING)
  1379.             pval[2].Value.l = SUBMITFLAG_PREPROCESS;
  1380.         else
  1381.             pval[2].Value.l = 0L;
  1382.     
  1383.         hr = pimsg->lpmsg->lpVtbl->SetProps(pimsg->lpmsg, NUM_FLAGTIME, pval,
  1384.             &pprba);
  1385.         if (hr != hrSuccess || pprba)
  1386.             goto exit;
  1387.     }
  1388.     hr = pimsg->lpVtbl->SaveChanges(pimsg, KEEP_OPEN_READWRITE);
  1389.     if (hr != hrSuccess)
  1390.         goto exit;
  1391.     /* add or update (for resubmit) the message in the outgoing queue */
  1392.     hr = HrUpdateOutgoingQueue(pims, pimsg, NULL, TABLE_ROW_ADDED);
  1393.     if (hr != hrSuccess)
  1394.         goto exit;
  1395. exit:
  1396.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  1397.     if (pprba)
  1398.     {
  1399.         LMFree(&pims->lmr, pprba);
  1400.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1401.     }
  1402.     UlRelease(pmtRecip);
  1403.     LMFree(&pims->lmr, pval);
  1404.     FreeProws(prws);
  1405.     if (hr == hrSuccess)
  1406.         OBJ_ClearFlag(pimsg, MSGF_NEWLYCREATED | OBJF_MODIFY);
  1407.     IMSG_LeaveCriticalSection(pimsg);
  1408.     DebugTraceResult(IMSG_SubmitMessage, hr);
  1409.     return HrCheckHr(hr, IMessage_SubmitMessage);
  1410. }
  1411. /*
  1412.  *  IMSG_SetReadFlag
  1413.  *
  1414.  *  Purpose:
  1415.  *      Sets the MSGFLAG_READ bit in the PR_MESSAGE_FLAGS property.
  1416.  *      In addition, it sends a read report to the originator, if
  1417.  *      appropriate.  A read report is only sent if the originator
  1418.  *      of the message requested it.  Applications generally cannot
  1419.  *      determine if a read report has been requested.
  1420.  *
  1421.  *  Arguments:
  1422.  *      pimsg       Pointer to the object.
  1423.  *      ulFlags     Flags.  SUPPRESS_RECEIPT
  1424.  *
  1425.  *  Returns:
  1426.  *      HRESULT
  1427.  *
  1428.  *  Side effects:
  1429.  *      In order to generate a read report, a new message is
  1430.  *      created which gets filled in by MAPI and then submitted.
  1431.  *      This message will be in the same folder as pimsg, and will
  1432.  *      have the same filename, except that it will have the read
  1433.  *      receipt filename extension (.rrt) instead of the normal
  1434.  *      message filename extension (.msg).
  1435.  *
  1436.  *  Errors:
  1437.  *      All errors associated with property modification, message
  1438.  *      creation, or message submission.
  1439.  */
  1440. STDMETHODIMP IMSG_SetReadFlag(PIMSG pimsg, ULONG ulFlags)
  1441. {
  1442.     HRESULT hr;
  1443.     LPTSTR szFull = NULL;
  1444.     PEID peidCopy = NULL;
  1445.     PIMSG pimsgRRT = NULL;
  1446.     PIFLD pifldParent = NULL;
  1447.     ULONG ulObjType = 0L;
  1448.     PEID peidCopyFld = NULL;
  1449.     ULONG ulSeqNumber;
  1450.     PLMR plmr;
  1451.     WORD fRR;
  1452.     ULONG ulMF;
  1453.     LONG lUnreadChange;
  1454.     MSG_ValidateParameters(
  1455.             pimsg,
  1456.             IMessage,
  1457.             SetReadFlag,
  1458.             (pimsg, 
  1459.             ulFlags));
  1460.     IMSG_EnterCriticalSection(pimsg);
  1461.     plmr = &pimsg->pims->lmr;
  1462.     hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, plmr, PR_MESSAGE_FLAGS,
  1463.         &ulMF);
  1464.     if (hr != hrSuccess)
  1465.         goto exit;
  1466.     /* if the flag is already set correctly, don't do anything. */
  1467.     if (ulFlags & CLEAR_READ_FLAG)
  1468.     {
  1469.         if (!(ulMF & MSGFLAG_READ))
  1470.             goto exit;
  1471.         ulMF &= ~MSGFLAG_READ;
  1472.         lUnreadChange = 1;
  1473.     }
  1474.     else
  1475.     {
  1476.         if (ulMF & MSGFLAG_READ)
  1477.             goto exit;
  1478.         else
  1479.             ulMF |= MSGFLAG_READ;
  1480.         lUnreadChange = -1;
  1481.     }
  1482.     hr = HrSetSingleProp((LPMAPIPROP) pimsg->lpmsg, plmr, PR_MESSAGE_FLAGS, &ulMF);
  1483.     if (hr != hrSuccess)
  1484.         goto exit;
  1485.     hr = HrOpenParent(pimsg->pims, pimsg->peid, MAPI_MODIFY, &pifldParent);
  1486.     if (hr != hrSuccess)
  1487.         goto exit;
  1488.     /* see if read receipts are requested */
  1489.     hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, plmr,
  1490.         PR_READ_RECEIPT_REQUESTED, &fRR);
  1491.     if (hr == hrSuccess
  1492.         && !(ulFlags & SUPPRESS_RECEIPT)
  1493.         && fRR
  1494.         && lUnreadChange == -1)
  1495.     {
  1496.         PIFLD pifldRoot = NULL;
  1497.         ULONG ulObjType;
  1498.         NFSideAssertSz(pimsg->peid,
  1499.             "PR_READ_RECEIPT_REQUESTED set on a message in a message ");
  1500.         /* Open the root folder */
  1501.         hr = pimsg->pims->lpVtbl->OpenEntry(pimsg->pims, 0, NULL,
  1502.             NULL, 0L, &ulObjType, (LPUNKNOWN *) &pifldRoot);
  1503.         if (hr != hrSuccess)
  1504.             goto exit;
  1505.         Assert(ulObjType == MAPI_FOLDER);
  1506.         /* Create a read receipt message in the root folder and call */
  1507.         /* IMAPISupport::ReadReceipt */
  1508.         hr = HrNewEID(pifldRoot, pimsg->pims, READRECEIPT_EXT,
  1509.             &ulSeqNumber, &peidCopy);
  1510.         UlRelease(pifldRoot);
  1511.         if (hr != hrSuccess)
  1512.             goto exit;
  1513.         hr = HrNewIMSG(peidCopy, pimsg->pims, TRUE, TRUE, ulSeqNumber,
  1514.                 &szFull, &pimsgRRT);
  1515.         if (hr != hrSuccess)
  1516.             goto exit;
  1517.         hr = InitIMSGProps(pimsgRRT);
  1518.         if (hr != hrSuccess)
  1519.             goto exit;
  1520.         hr = pimsg->pims->psup->lpVtbl->ReadReceipt(pimsg->pims->psup, 0L,
  1521.             (LPMESSAGE) pimsg, (LPMESSAGE *) &pimsgRRT);
  1522.         if (hr != hrSuccess)
  1523.             goto exit;
  1524.         /* If ReadReceipt didn't release and NULL the returned message, */
  1525.         /* then submit it. */
  1526.         if (pimsgRRT)
  1527.         {
  1528.             hr = pimsgRRT->lpVtbl->SubmitMessage(pimsgRRT, FORCE_SUBMIT);
  1529.             if (hr != hrSuccess)
  1530.                 goto exit;
  1531.         }
  1532.     }
  1533.     hr = pimsg->lpVtbl->SaveChanges(pimsg, KEEP_OPEN_READWRITE);
  1534.     if (HR_FAILED(hr))
  1535.         goto exit;
  1536.     /* Update the folder properties file -- WARNING:  if we fail from   */
  1537.     /* now until the end of the procedure, the folder unread count will */
  1538.     /* be inconsistent with the actual unread messages in this folder,  */
  1539.     /* and we have no transactional way of backing out changes.         */
  1540.     /* don't change folder properties for a message in a message        */
  1541.     if (!OBJ_TestFlag(pimsg, MSGF_MSGINMSG))
  1542.     {
  1543.         hr = HrIncrementOneROProp(pifldParent, lUnreadChange, PR_CONTENT_UNREAD);
  1544.         #ifdef DEBUG
  1545.         if (HR_FAILED(hr))
  1546.             TraceSz1("Sample MS: IMSG_SetReadFlag: error %s changing the "
  1547.                 "unread count on a folder.", SzDecodeScode(GetScode(hr)));
  1548.         #endif
  1549.         /* Ignore the error. It isn't fatal. */
  1550.         hr = hrSuccess;
  1551.     }
  1552. exit:
  1553.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  1554.     UlRelease(pimsgRRT);
  1555.     FreeNull(szFull);
  1556.     LMFree(plmr, peidCopy);
  1557.     LMFree(plmr, peidCopyFld);
  1558.     UlRelease(pifldParent);
  1559.     IMSG_LeaveCriticalSection(pimsg);
  1560.     DebugTraceResult(IMSG_SetReadFlag, hr);
  1561.     return HrCheckHr(hr, IMessage_SetReadFlag);
  1562. }
  1563. /*
  1564.  *  External functions (called from outside of this file).
  1565.  */
  1566. /*
  1567.  *  HrNewIMSG
  1568.  *
  1569.  *  Purpose:
  1570.  *      Allocates and initializes an IMSG object (internal
  1571.  *      implementation of IMessage).  Optionally creates storage
  1572.  *      for the object (else it tries to open existing storage).
  1573.  *
  1574.  *  Arguments:
  1575.  *      peid        Internal form of EntryID for message.
  1576.  *      pims        Message Store in which this message resides.
  1577.  *      fCreate     Boolean. TRUE means to create the storage for this message.
  1578.  *      fModify     Boolean. TRUE means to open the message for writing.
  1579.  *      ulSeqNum    The sequence number of the entryid.
  1580.  *      pszFull     Location in which to return the full pathname to the
  1581.  *                  file created or opened. If NULL, don't return the path.
  1582.  *      ppimsg      Location in which to return a pointer to the
  1583.  *                  newly created IMSG instance.
  1584.  *
  1585.  *  Returns:
  1586.  *      HRESULT
  1587.  *
  1588.  *  Side effects:
  1589.  *      None.
  1590.  *
  1591.  *  Errors:
  1592.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  1593.  *                                  the IMSG instance.
  1594.  */
  1595. HRESULT HrNewIMSG(PEID peid, PIMS pims, BOOL fCreate, BOOL fModify,
  1596.     ULONG ulSeqNum, LPSTR *pszFull, PIMSG *ppimsg)
  1597. {
  1598.     HRESULT hr;
  1599.     LPTSTR szFull = NULL;
  1600.     LPMESSAGE lpmsg = NULL;
  1601.     PEID peidCopy = NULL;
  1602.     ULONG cbeid;
  1603.     PIMSG pimsgNew = NULL;
  1604.     BOOL fDoneCreate = FALSE;
  1605.     SCODE sc = S_OK;
  1606.     PEID peidParent = NULL;
  1607.     AssertSz(peid, "Bad peid");
  1608.     AssertSz(pims, "Bad pims");
  1609.     AssertSz(ppimsg, "Bad ppimsg");
  1610.     /* Get all the necessary parts of an IMSG */
  1611.     hr = HrFullPathName(pims->szStorePath, peid->szPath, NULL, &szFull);
  1612.     if (hr != hrSuccess)
  1613.         goto exit;
  1614.     ReplaceExt(szFull, MESSAGE_EXT);
  1615.     /* always open the internal message for modification so that */
  1616.     /* SetReadFlag will work. */
  1617.     hr = HrOpenIMsg(pims->pmsgsess, szFull, &pims->lmr, pims->psup, fCreate,
  1618.         TRUE, FALSE, &lpmsg);
  1619.     if (hr != hrSuccess)
  1620.         goto exit;
  1621.     if (fCreate)
  1622.         fDoneCreate = TRUE;
  1623.     cbeid = CbEID(peid);
  1624.     hr = HrAlloc(cbeid, (PPV) &peidCopy);
  1625.     if (hr != hrSuccess)
  1626.         goto exit;
  1627.     memcpy(peidCopy, peid, (UINT) cbeid);
  1628.     /* Allocate and initialize IMSG instance */
  1629.     sc = LMAllocZ(&pims->lmr, sizeof(IMSG), &pimsgNew);
  1630.     if (sc != S_OK)
  1631.     {
  1632.         hr = ResultFromScode(sc);
  1633.         goto exit;
  1634.     }
  1635.     OBJ_Initialize(pimsgNew, &vtblIMSG, OT_MESSAGE, pims, pims->pcs);
  1636.     pimsgNew->peid = peidCopy;
  1637.     pimsgNew->lpmsg = lpmsg;
  1638.     if (fCreate)
  1639.     {
  1640.         OBJ_SetFlag(pimsgNew, MSGF_NEWLYCREATED);
  1641.         OBJ_SetFlag(pimsgNew, MSGF_FRESH);
  1642.     }
  1643.     else
  1644.     {
  1645.         ULONG ulMF;
  1646.         hr = HrGetSingleProp((LPMAPIPROP) lpmsg, &pims->lmr,
  1647.                 PR_MESSAGE_FLAGS, &ulMF);
  1648.         if (hr != hrSuccess)
  1649.             goto exit;
  1650.         if (fModify
  1651.             && (ulMF & MSGFLAG_SUBMIT)
  1652.             && !OBJ_TestFlag(pims, MSF_SPOOLER))
  1653.         {
  1654.             hr = ResultFromScode(MAPI_E_SUBMITTED);
  1655.             goto exit;
  1656.         }
  1657.     }
  1658.     if (fModify)
  1659.         OBJ_SetFlag(pimsgNew, OBJF_MODIFY);
  1660.     hr = HrGetParentEID(&pims->lmr, peid, &peidParent);
  1661.     if (hr != hrSuccess)
  1662.         goto exit;
  1663.     hr = HrSetInternalProps(&pims->lmr, cpropIMSGInternal, &(pimsgNew->pval),
  1664.             &(pimsgNew->cval), peid, peidParent, ulSeqNum);
  1665.     if (hr != hrSuccess)
  1666.         goto exit;
  1667.     OBJ_Enqueue((POBJ) pimsgNew, (POBJ) pims);
  1668. exit:
  1669.     LMFree(&pims->lmr, peidParent);
  1670.     if (hr != hrSuccess)
  1671.     {
  1672.         UlRelease(lpmsg);
  1673.         FreeNull(peidCopy);
  1674.         if (fDoneCreate)
  1675.             DeleteFile(szFull);
  1676.         if (pimsgNew)
  1677.             LMFree(&pims->lmr, pimsgNew->pval);
  1678.         LMFree(&pims->lmr, pimsgNew);
  1679.         FreeNull(szFull);
  1680.     }
  1681.     else
  1682.     {
  1683.         *ppimsg = pimsgNew;
  1684.         if (pszFull)
  1685.             *pszFull = szFull;
  1686.         else
  1687.             FreeNull(szFull);
  1688.     }
  1689.     DebugTraceResult(HrNewIMSG, hr);
  1690.     return hr;
  1691. }
  1692. /*
  1693.  *  IMSG_Neuter
  1694.  *
  1695.  *  Purpose:
  1696.  *      Neuters an IMSG
  1697.  *
  1698.  *  Parameter
  1699.  *      pimsg       pointer to IMSG to be neutered
  1700.  */
  1701. void IMSG_Neuter(PIMSG pimsg)
  1702. {
  1703.     /* Free IMSG object's internal memory */
  1704.     UlRelease(pimsg->lpmsg);
  1705.     LMFree(&pimsg->pims->lmr, pimsg->pval);
  1706.     /* delete the file if it was never saved */
  1707.     if (OBJ_TestFlag(pimsg, MSGF_NEWLYCREATED))
  1708.     {
  1709.         LPTSTR szFull = NULL;   /* full path name of message */
  1710.         /* delete the file */
  1711.         if (HrFullPathName(pimsg->pims->szStorePath, pimsg->peid->szPath,
  1712.                 NULL, &szFull) == hrSuccess)
  1713.         {
  1714.             ReplaceExt(szFull, MESSAGE_EXT);
  1715.             DeleteFile(szFull);
  1716.             FreeNull(szFull);
  1717.         }
  1718.     }
  1719.     FreeNull(pimsg->peid);
  1720. }
  1721. /*
  1722.  *  NewIMSGInIATCH
  1723.  *
  1724.  *  Purpose:
  1725.  *      Wraps an IMSG object around a message that is contained in
  1726.  *      an attachment (message in message).  A message in message
  1727.  *      has no EntryID, does not expose a PR_STORE_ENTRYID or
  1728.  *      PR_STORE_RECORD_KEY, and cannot be submitted.
  1729.  *
  1730.  *  Arguments:
  1731.  *      lpmsg       Message in message we get back from the
  1732.  *                  IMessage on IStorage implementation.
  1733.  *      pobj        Pointer to the parent object of the msg-in-msg.
  1734.  *      ulFlags     ulFlags from OpenProperty call
  1735.  *      ppimsg      Address in which to place a pointer to the
  1736.  *                  newly created IMSG instance.
  1737.  *
  1738.  *  Returns:
  1739.  *      HRESULT
  1740.  *
  1741.  *  Side effects:
  1742.  *      None.
  1743.  *
  1744.  *  Errors:
  1745.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  1746.  *                                  the IMSG instance.
  1747.  */
  1748. HRESULT NewIMSGInIATCH(LPMESSAGE lpmsg, POBJ pobj, ULONG ulFlags, PIMSG *ppimsg)
  1749. {
  1750.     PIMSG pimsgNew = NULL;
  1751.     SCODE sc = S_OK;
  1752.     PIMS pims;
  1753.     AssertSz(lpmsg, "Bad lpmsg");
  1754.     AssertSz(pobj, "Bad pobj");
  1755.     AssertSz(ppimsg, "Bad ppimsg");
  1756.     pims = pobj->pims;
  1757.     /* Allocate and initialize IMSG instance */
  1758.     sc = LMAllocZ(&pims->lmr, sizeof(IMSG), (LPVOID *) &pimsgNew);
  1759.     if (sc != S_OK)
  1760.         goto exit;
  1761.     OBJ_Initialize(pimsgNew, &vtblIMSG, OT_MESSAGE, pims, pims->pcs);
  1762.     OBJ_SetFlag(pimsgNew, MSGF_MSGINMSG);
  1763.     if(ulFlags & MAPI_MODIFY)
  1764.         OBJ_SetFlag(pimsgNew, OBJF_MODIFY);
  1765.     pimsgNew->peid = NULL;
  1766.     pimsgNew->lpmsg = lpmsg;
  1767.     OBJ_Enqueue((POBJ) pimsgNew, pobj);
  1768.     *ppimsg = pimsgNew;
  1769. exit:
  1770.     if (sc != S_OK)
  1771.         LMFree(&pims->lmr, pimsgNew);
  1772.     DebugTraceSc(NewIMSGInIATCH, sc);
  1773.     return ResultFromScode(sc);
  1774. }
  1775. /*
  1776.  *  InitIMSGProps
  1777.  *
  1778.  *  Purpose:
  1779.  *      Sets the initial (and for read-only properties, the only)
  1780.  *      values for the base properties of the Message Object:
  1781.  *      takes as input parameters the values of those properties
  1782.  *      that are specific to this message and derives the values of
  1783.  *      those properties that are identical for all messages in the
  1784.  *      Microsoft Sample Store Provider.
  1785.  *
  1786.  *  Arguments:
  1787.  *      pimsg       Internal IMessage object instance.
  1788.  *
  1789.  *  Returns:
  1790.  *      HRESULT
  1791.  *
  1792.  *  Side effects:
  1793.  *      None.
  1794.  *
  1795.  *  Errors:
  1796.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  1797.  *                                  the property arrays.
  1798.  */
  1799. HRESULT InitIMSGProps(PIMSG pimsg)
  1800. {
  1801.     HRESULT hr = hrSuccess;
  1802.     LPSPropValue pval = NULL;
  1803.     LPSPropProblemArray pprba = NULL;
  1804.     LPSPropTagArray ptaga = NULL;
  1805.     LPSPropAttrArray patra = NULL;
  1806.     ULONG cpropInit = 0L;
  1807.     BOOL fMsgInMsg;
  1808.     MAPIUID uid;
  1809.     PIMS pims;
  1810.     LPMESSAGE lpmsg;
  1811.     AssertSz(pimsg, "Bad pimsg");
  1812.     if (OBJ_TestFlag(pimsg, MSGF_MSGINMSG))
  1813.     {
  1814.         AssertSz(pimsg->peid == NULL, "Msg in msg has no entryid");
  1815.         fMsgInMsg = TRUE;
  1816.         cpropInit = cpropMsgInMsgInit;
  1817.     }
  1818.     else
  1819.     {
  1820.         fMsgInMsg = FALSE;
  1821.         cpropInit = cpropIMSGInit;
  1822.     }
  1823.     pims = pimsg->pims;
  1824.     lpmsg = pimsg->lpmsg;
  1825.     /* Allocate the property arrays. */
  1826.     hr = HrAllocPropArrays(cpropInit, &pval, &ptaga, &patra);
  1827.     if (hr != hrSuccess)
  1828.         goto exit;
  1829.     /* Initialize property value array and all property tags. */
  1830.     ptaga->cValues = patra->cValues = cpropInit;
  1831.     pval[0].ulPropTag = ptaga->aulPropTag[0] = PR_OBJECT_TYPE;
  1832.     pval[0].Value.l = MAPI_MESSAGE;
  1833.     pval[1].ulPropTag = ptaga->aulPropTag[1] = PR_MESSAGE_FLAGS;
  1834.     /* When the spooler creates a message, it is arriving into the store. */
  1835.     /* Therefore, the message should be unread, sent and unmodified. When */
  1836.     /* the client creates a message, it is unsent, read, and unmodified. */
  1837.     if (OBJ_TestFlag(pims, MSF_SPOOLER))
  1838.         pval[1].Value.l = MSGFLAG_UNMODIFIED;
  1839.     else
  1840.         pval[1].Value.l = MSGFLAG_READ | MSGFLAG_UNSENT | MSGFLAG_UNMODIFIED;
  1841.     pval[2].ulPropTag = ptaga->aulPropTag[2] = PR_MESSAGE_CLASS;
  1842.     pval[2].Value.LPSZ = TEXT("IPM");
  1843.     if (!fMsgInMsg)
  1844.     {
  1845.         hr = pims->psup->lpVtbl->NewUID(pims->psup, &uid);
  1846.         if (hr != hrSuccess)
  1847.             goto exit;
  1848.         pval[3].ulPropTag = ptaga->aulPropTag[3] = PR_SEARCH_KEY;
  1849.         pval[3].Value.bin.cb = sizeof(uid);
  1850.         pval[3].Value.bin.lpb = (LPBYTE) &uid;
  1851.         pval[4].ulPropTag = ptaga->aulPropTag[4] = PR_STORE_ENTRYID;
  1852.         pval[4].Value.bin.cb = pims->eidStore.cb;
  1853.         pval[4].Value.bin.lpb = pims->eidStore.lpb;
  1854.         pval[5].ulPropTag = ptaga->aulPropTag[5] = PR_STORE_RECORD_KEY;
  1855.         pval[5].Value.bin.cb = sizeof(pims->uidResource);
  1856.         pval[5].Value.bin.lpb = (LPBYTE) &pims->uidResource;
  1857.         pval[6].ulPropTag = ptaga->aulPropTag[6] = PR_MSG_STATUS;
  1858.         pval[6].Value.l = 0;
  1859.         /* Set PR_ENTRYID, PR_PARENT_ENTRYID, PR_RECORD_KEY and 
  1860.          * PR_INSTANCE_KEY to null strings to keep clients from writing over
  1861.          * them. We get the actual values internally.
  1862.          */
  1863.         pval[7].ulPropTag = ptaga->aulPropTag[7] = PR_ENTRYID;
  1864.         pval[7].Value.bin.cb = 1;
  1865.         pval[7].Value.bin.lpb = (LPBYTE) "";
  1866.         pval[8].ulPropTag = ptaga->aulPropTag[8] = PR_PARENT_ENTRYID;
  1867.         pval[8].Value.bin.cb = 1;
  1868.         pval[8].Value.bin.lpb = (LPBYTE) "";
  1869.         pval[9].ulPropTag = ptaga->aulPropTag[9] = PR_RECORD_KEY;
  1870.         pval[9].Value.bin.cb = 1;
  1871.         pval[9].Value.bin.lpb = (LPBYTE) "";
  1872.         pval[10].ulPropTag = ptaga->aulPropTag[10] = PR_INSTANCE_KEY;
  1873.         pval[10].Value.bin.cb = 1;
  1874.         pval[10].Value.bin.lpb = (LPBYTE) "";
  1875.         /* this message is not complete until changes have been saved */
  1876.         /* mark it as such so that others don't see a partial message */
  1877.     }
  1878.     /* Initialize the property attribute array. */
  1879.     patra->aPropAttr[0] = grfpropattrIMSGInit;
  1880.     patra->aPropAttr[1] = grfpropattrIMSGInit | PROPATTR_WRITEABLE;
  1881.     patra->aPropAttr[2] = grfpropattrIMSGInit | PROPATTR_WRITEABLE;
  1882.     if (!fMsgInMsg)
  1883.     {
  1884.         patra->aPropAttr[3] = grfpropattrIMSGInit | PROPATTR_WRITEABLE;
  1885.         patra->aPropAttr[4] = grfpropattrIMSGInit;
  1886.         patra->aPropAttr[5] = grfpropattrIMSGInit;
  1887.         patra->aPropAttr[6] = grfpropattrIMSGInit;
  1888.         patra->aPropAttr[7] = grfpropattrIMSGInit;
  1889.         patra->aPropAttr[8] = grfpropattrIMSGInit;
  1890.         patra->aPropAttr[9] = grfpropattrIMSGInit;
  1891.         patra->aPropAttr[10] = grfpropattrIMSGInit;
  1892.     }
  1893.     /* Set the property values. */
  1894.     hr = lpmsg->lpVtbl->SetProps(lpmsg, cpropInit, pval, &pprba);
  1895.     if (hr != hrSuccess || pprba)
  1896.         goto exit;
  1897.     /* Set the property attributes. */
  1898.     hr = SetAttribIMsgOnIStg(lpmsg, ptaga, patra, &pprba);
  1899.     if (hr != hrSuccess || pprba)
  1900.         goto exit;
  1901.     /* If we succeeded up to this point, commit the properties. */
  1902.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  1903.     /* if (hr), fall through to exit */
  1904. exit:
  1905.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  1906.     if (pprba)
  1907.     {
  1908.         LMFree(&pims->lmr, pprba);
  1909.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1910.     }
  1911.     FreePropArrays(&pval, &ptaga, &patra);
  1912.     DebugTraceResult(InitIMSGProps, hr);
  1913.     return hr;
  1914. }
  1915. /*
  1916.  *  HrSetFlags
  1917.  *
  1918.  *  Purpose
  1919.  *      Set the value of flag bits on a message property. The
  1920.  *      properties that this function is used for are PR_MESSAGE_FLAGS
  1921.  *      and PR_SUBMIT_FLAGS.
  1922.  *
  1923.  *  Parameters
  1924.  *      pimsg       A pointer to the message object.
  1925.  *      ulAction    SET or UNSET
  1926.  *      ulPropTag   the property tag to be changed.
  1927.  *      ulFlag      flags to be set or unset.
  1928.  */
  1929. HRESULT HrSetFlags(PIMSG pimsg, ULONG ulAction, ULONG ulPropTag, ULONG ulFlag)
  1930. {
  1931.     HRESULT hr;
  1932.     LPSPropValue pval = NULL;
  1933.     AssertSz1(ulPropTag == PR_MESSAGE_FLAGS || ulPropTag == PR_SUBMIT_FLAGS,
  1934.         "HrSetFlags: not designed for property %s", SzDecodeUlPropTag(ulPropTag));
  1935.     AssertSz1((ulAction == SET) || (ulAction == UNSET), "Bad ulAction: %08lX",
  1936.         ulAction);
  1937.     /* get the current value of the flag */
  1938.     hr = HrGetOneProp((LPMAPIPROP) pimsg, ulPropTag, &pval);
  1939.     /* compute and set the new value */
  1940.     if (HR_SUCCEEDED(hr))
  1941.     {
  1942.         LONG lOldFlag = pval->Value.l;
  1943.         if (ulAction & SET)
  1944.             pval->Value.l |= ulFlag;
  1945.         else
  1946.             pval->Value.l &= ~ulFlag;
  1947.         /* If the value that's there is correct, then don't set it. */
  1948.         if (pval->Value.l == lOldFlag)
  1949.             goto exit;
  1950.     }
  1951.     else if (GetScode(hr) == MAPI_E_NOT_FOUND)
  1952.     {
  1953.         pval->Value.l = (ulAction & SET) ? ulFlag : 0L;
  1954.         pval->ulPropTag = ulPropTag;
  1955.     }
  1956.     else
  1957.         goto exit;
  1958.     hr = HrSetOneProp((LPMAPIPROP) pimsg, pval);
  1959. exit:
  1960.     LMFree(&pimsg->pims->lmr, pval);
  1961.     return hr;
  1962. }
  1963. /*
  1964.  *  HrSetInternalProps
  1965.  *
  1966.  *  Purpose
  1967.  *      Sets up the in-memory array to hold properties that may change
  1968.  *      when the message or folder object is moved or copied. See the
  1969.  *      routine ProcessGetProps in mspmisc.c for details of how this
  1970.  *      array is used.
  1971.  *
  1972.  *  Parameters
  1973.  *      plmr: A pointer to the linked memory allocation routines.
  1974.  *      cprop: The number of properties to set into the in-memory array.
  1975.  *          Note that the properties that are saved in-memory are placed
  1976.  *          into the array in a hard-coded order (see below). Therefore,
  1977.  *          changing cprop to a smaller number eliminates specific props
  1978.  *          from the array.
  1979.  *      ppval: A pointer to the location to place the in-memory pval array.
  1980.  *      pcval: A pointer to the location to place the number of properties
  1981.  *          placed into *ppval.
  1982.  *      peid: The object's entryid that we are saving in-memory props for.
  1983.  *          (Used to get the value for PR_ENTRYID and PR_INSTANCE_KEY).
  1984.  *      peidParent: The entryid of the parent object. (Used to get the value
  1985.  *          for PR_PARENT_ENTRYID).
  1986.  *      ulSeqNum: The value with which to fill PR_RECORD_KEY. (Only used
  1987.  *          on message objects).
  1988.  *
  1989.  *  Returns
  1990.  *      HRESULT. Only errors are from memory allocation failures.
  1991.  */
  1992. HRESULT HrSetInternalProps(PLMR plmr, ULONG cprop, LPSPropValue *ppval,
  1993.     ULONG *pcval, PEID peid, PEID peidParent, ULONG ulSeqNum)
  1994. {
  1995.     LPSPropValue pval = NULL;
  1996.     LPSPropValue pvalT;
  1997.     LPSPropValue pvalTMac;
  1998.     SCODE sc;
  1999.     AssertSz(*ppval == NULL, "pval already allocated");
  2000.     AssertSz(*pcval == 0, "cval already non-zero");
  2001.     /* Allocate the property array. */
  2002.     sc = LMAlloc(plmr, cprop * sizeof(SPropValue), &pval);
  2003.     if (sc != S_OK)
  2004.         goto exit;
  2005.     pvalT = pval;
  2006.     pvalTMac = pvalT + cprop;
  2007.     if (pvalT < pvalTMac)
  2008.     {
  2009.         sc = ScFillOneSBPval(plmr, (LPVOID) pval, PR_ENTRYID,
  2010.             CbEID(peid), (LPBYTE) peid, pvalT);
  2011.         if (sc != S_OK)
  2012.             goto exit;
  2013.     
  2014.         pvalT++;
  2015.     }
  2016.     if (pvalT < pvalTMac)
  2017.     {
  2018.         sc = ScFillOneSBPval(plmr, (LPVOID) pval, PR_INSTANCE_KEY,
  2019.             CbEID(peid), (LPBYTE) peid, pvalT);
  2020.         if (sc != S_OK)
  2021.             goto exit;
  2022.     
  2023.         pvalT++;
  2024.     }
  2025.     if (pvalT < pvalTMac)
  2026.     {
  2027.         sc = ScFillOneSBPval(plmr, (LPVOID) pval, PR_PARENT_ENTRYID,
  2028.             CbEID(peidParent), (LPBYTE) peidParent, pvalT);
  2029.         if (sc != S_OK)
  2030.             goto exit;
  2031.         pvalT++;
  2032.     }
  2033.     if (pvalT < pvalTMac)
  2034.     {
  2035.         sc = ScFillOneSBPval(plmr, (LPVOID) pval, PR_RECORD_KEY,
  2036.             sizeof(ulSeqNum), (LPBYTE) &ulSeqNum, pvalT);
  2037.         if (sc != S_OK)
  2038.             goto exit;
  2039.         pvalT++;
  2040.     }
  2041.     AssertSz(pvalT == pvalTMac, "Not enough values to fill internal array");
  2042.     *pcval = cprop;
  2043.     *ppval = pval;
  2044. exit:
  2045.     if (sc != S_OK)
  2046.         LMFree(plmr, pval);
  2047.     DebugTraceSc(HrSetInternalProps, sc);
  2048.     return ResultFromScode(sc);
  2049. }
  2050. /*
  2051.  * Internal functions (called only from within this file).
  2052.  *
  2053.  */
  2054. /* HrSaveMsgInMsg
  2055.  *
  2056.  * Perform the necessary steps to save changes on a msg-in-msg type message.
  2057.  *
  2058.  */
  2059. static HRESULT HrSaveMsgInMsg(PIMSG pimsg, ULONG ulFlags)
  2060. {
  2061.     ULONG ulPropMsgFlags = ulFlags;
  2062.     if (!(ulFlags & KEEP_OPEN_READWRITE))
  2063.         ulPropMsgFlags |= KEEP_OPEN_READONLY;
  2064.     return pimsg->lpmsg->lpVtbl->SaveChanges(pimsg->lpmsg, ulPropMsgFlags);
  2065. }
  2066. /* Fills in an SBinary PropValue via AllocMore. */
  2067. static SCODE ScFillOneSBPval(PLMR plmr, LPVOID pvOrigBuf, ULONG ulPropTag,
  2068.     ULONG cb, LPBYTE lpbData, LPSPropValue pval)
  2069. {
  2070.     SCODE sc;
  2071.     sc = LMAllocMore(plmr, cb, pvOrigBuf, &(pval->Value.bin.lpb));
  2072.     if (sc == S_OK)
  2073.     {
  2074.         pval->ulPropTag = ulPropTag;
  2075.         pval->Value.bin.cb = cb;
  2076.         if (cb)
  2077.             memcpy(pval->Value.bin.lpb, lpbData, (UINT) cb);
  2078.     }
  2079.     return sc;
  2080. }