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