home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / sample.ms / mspmsg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  71.6 KB  |  2,253 lines

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