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 / mspfld.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  150.8 KB  |  5,167 lines

  1. /*
  2.  *  M S P F L D . C
  3.  *
  4.  *  Code that implements the object methods of IMAPIFolder.
  5.  *
  6.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  7.  */
  8.  
  9. #include "msp.h"
  10.  
  11. #define FLD_ValidateParameters(pobj, intf, method, arglist)     \
  12.         OBJ_ValidateParameters(pobj, intf, method, sizeof(IFLD), &vtblIFLD, arglist)
  13.  
  14. static BOOL IFLD_IsInvalid(PIFLD pifld);
  15. static HRESULT HrCreateContTblMutex(HANDLE *phCTMutex);
  16. static BOOL FFoldInSameStore(PIFLD pifld, PIFLD lpDestFld);
  17. static BOOL FIsAncestor(PIFLD pifldAncestor, PIFLD pifldDescendent);
  18. static HRESULT HrGetSortOrder(PIFLD pifld, LPSSortOrderSet *lppsSortOrder);
  19. static HRESULT HrFillHierarchyTable(PIFLD pifld, LPTABLEDATA lptbl);
  20. static HRESULT HrIsRead(PIFLD pifld, PEID peidMsg, BOOL *pfRead);
  21. static HRESULT HrCreateFolder(PIFLD pifldParent, LPSTR szName, LPSTR szComment,
  22.     BOOL fOpenIfExists, PIFLD *ppifldNew, BOOL *pfCreated);
  23. static HRESULT HrDeleteSubDirectory(PIFLD pifldParent, PEID peidToDelete,
  24.     ULONG ulFlags, BOOL fContentsOnly);
  25. static HRESULT HrCopyContents(PIFLD pifldSrc, PIFLD pifldDst, ULONG ulFlags,
  26.     LPSPropTagArray ptagaExcl);
  27. static BOOL FFolderExists(PEID peid, PIMS pims);
  28. static HRESULT HrSetSubFolderProp(PIFLD pifld, BOOL fSubFolder);
  29. static HRESULT HrSetOneROFolderProp(PIFLD pifld, LONG lValue, ULONG ulPT);
  30. static HRESULT HrEIDFromDisplayName(LPMAPITABLE pmt, LPSTR szName, PLMR plmr,
  31.     PEID *ppeid);
  32. static HRESULT HrCreateMessageList(PIFLD pifld, LPENTRYLIST *lppEntryList);
  33. static void DestroyMessageList(PLMR plmr, LPENTRYLIST *lppEntryList);
  34.  
  35. #define NUM_RW_FOLDER_PROPS      2  /* number read write initial folder props */
  36.  
  37. #define IFLD_EnterCriticalSection(pifld)    OBJ_EnterCriticalSection((POBJ)pifld)
  38. #define IFLD_LeaveCriticalSection(pifld)    OBJ_LeaveCriticalSection((POBJ)pifld)
  39.  
  40. CALLERRELEASE ViewRelease;
  41.  
  42. TCHAR szSubfolderPropFile[] = TEXT("subfoldr.prp");
  43. TCHAR szAllFilesTemplate[] = TEXT("*.*");
  44.  
  45. /* properties excluded on a copy of folders' .prp file */
  46. const static SizedSPropTagArray(13, sptaExclFldProps) =
  47. {
  48.     13,
  49.     {
  50.         PR_CONTENT_COUNT,
  51.         PR_CONTENT_UNREAD,
  52.         PR_ENTRYID,
  53.         PR_INSTANCE_KEY,
  54.         PR_PARENT_ENTRYID,
  55.         PR_OBJECT_TYPE,
  56.         PR_STORE_ENTRYID,
  57.         PR_STORE_RECORD_KEY,
  58.         PR_RECORD_KEY,
  59.         PR_FOLDER_TYPE,
  60.         PR_SUBFOLDERS,
  61.         PR_MESSAGE_RECIPIENTS,
  62.         PR_MESSAGE_ATTACHMENTS
  63.     }
  64. };
  65.  
  66. /* read only property proptags */
  67. /* order must match order initialized on CreateFolderStorage */
  68. #define NUM_RO_FOLDER_PROPS     11  /* number read only initial folder props */
  69. const static SizedSPropTagArray(NUM_RO_FOLDER_PROPS, sptaReadOnly) =
  70. {
  71.     NUM_RO_FOLDER_PROPS,
  72.     {
  73.         PR_OBJECT_TYPE,
  74.         PR_RECORD_KEY,
  75.         PR_FOLDER_TYPE,
  76.         PR_CONTENT_COUNT,
  77.         PR_CONTENT_UNREAD,
  78.         PR_STORE_ENTRYID,
  79.         PR_STORE_RECORD_KEY,
  80.         PR_SUBFOLDERS,
  81.         PR_ENTRYID,
  82.         PR_PARENT_ENTRYID,
  83.         PR_INSTANCE_KEY
  84.     }
  85. };
  86.  
  87. /* read only property attributes */
  88. const static SizedSPropAttrArray(NUM_RO_FOLDER_PROPS, spaReadOnly) =
  89. {
  90.     NUM_RO_FOLDER_PROPS,
  91.     {
  92.         PROPATTR_READABLE,
  93.         PROPATTR_READABLE,
  94.         PROPATTR_READABLE,
  95.         PROPATTR_READABLE,
  96.         PROPATTR_READABLE,
  97.         PROPATTR_READABLE,
  98.         PROPATTR_READABLE,
  99.         PROPATTR_READABLE,
  100.         PROPATTR_READABLE,
  101.         PROPATTR_READABLE,
  102.         PROPATTR_READABLE
  103.     }
  104. };
  105.  
  106. /* initial sort order set for a contents table */
  107. static SizedSSortOrderSet(1, sSortOrderContentsDefault) =
  108. {
  109.     1,
  110.     0,
  111.     0,
  112.     {
  113.         PR_INSTANCE_KEY,                /* the index column */
  114.         TABLE_SORT_ASCEND
  115.     }
  116. };
  117.  
  118. /* message status property array */
  119. static SPropTagArray sptaMessageStatus =
  120. {
  121.     1,
  122.     {
  123.         PR_MESSAGE_FLAGS
  124.     }
  125. };
  126.  
  127. /* DISPATCH TABLE */
  128. IFLD_Vtbl vtblIFLD =
  129. {
  130.     (IFLD_QueryInterface_METHOD *)  OBJ_QueryInterface,
  131.     (IFLD_AddRef_METHOD *)          OBJ_AddRef,
  132.     IFLD_Release,
  133.     (IFLD_GetLastError_METHOD *)    IMS_GetLastError,
  134.     (IFLD_SaveChanges_METHOD *)     IMS_SaveChanges,
  135.     IFLD_GetProps,
  136.     IFLD_GetPropList,
  137.     IFLD_OpenProperty,
  138.     IFLD_SetProps,
  139.     IFLD_DeleteProps,
  140.     IFLD_CopyTo,
  141.     IFLD_CopyProps,
  142.     (IFLD_GetNamesFromIDs_METHOD *) IMS_GetNamesFromIDs,
  143.     (IFLD_GetIDsFromNames_METHOD *) IMS_GetIDsFromNames,
  144.     IFLD_GetContentsTable,
  145.     IFLD_GetHierarchyTable,
  146.     (IFLD_OpenEntry_METHOD *)       IMS_OpenEntry,
  147.     IFLD_SetSearchCriteria,
  148.     IFLD_GetSearchCriteria,
  149.     IFLD_CreateMessage,
  150.     IFLD_CopyMessages,
  151.     IFLD_DeleteMessages,
  152.     IFLD_CreateFolder,
  153.     IFLD_CopyFolder,
  154.     IFLD_DeleteFolder,
  155.     IFLD_SetReadFlags,
  156.     IFLD_GetMessageStatus,
  157.     IFLD_SetMessageStatus,
  158.     IFLD_SaveContentsSort,
  159.     IFLD_EmptyFolder
  160. };
  161.  
  162. /****************************************************
  163. *           Methods on IMAPIFolder                  *
  164. *****************************************************/
  165.  
  166. /****************************************************************
  167.     Folder properties are all implemented by creating a file
  168.     (FOLDER.PRP) inside each folder and using message properties
  169.     on it.
  170. *************************************************************/
  171.  
  172. /***************************************************************************
  173.  -  IFLD_Release
  174.  -
  175.  *  Purpose:
  176.  *      Given a valid folder object, decrements the reference count, and if
  177.  *      zero, NULLS out the lpVtbl. If no child objects of the folder remain
  178.  *      open, and no contents or hierarchy tables remain open, the routine
  179.  *      also destroys the object (see OBJ_Destroy for details of that action).
  180.  *
  181.  *      This release method is very similar to the standard one found in
  182.  *      mspobj.c. The only difference is that this one also checks for any
  183.  *      open contents or hierarchy tables, and doesn't actually destroy
  184.  *      the folder until they are gone. See ViewRelease() for the other side
  185.  *      of this implementation.
  186.  *
  187.  *  Parameters
  188.  *       pifld      The folder object to release
  189.  *
  190.  *  Returns:
  191.  *       ULONG: The value of the reference count on the whole object. When
  192.  *              fully released, it is zero. This information can be used for
  193.  *              diagnostic and testing purposes only. It CANNOT be used by
  194.  *              shipping code. See the OLE 2.0 programmers reference for more
  195.  *              details. Note that this function will also return zero when
  196.  *              the identity of the object has been destroyed. Therefore,
  197.  *              clients may not rely on zero having any special significance
  198.  *              other than for debugging purposes.
  199.  */
  200. STDMETHODIMP_(ULONG) IFLD_Release(PIFLD pifld)
  201. {
  202.     LONG cRef;
  203.     POBJ pobj;
  204.  
  205.     if (IFLD_IsInvalid(pifld))
  206.     {
  207.         TraceSz1("SampleMS: IFLD_Release(pifld=%08lX): Folder is invalid and is "
  208.             "being ignored", pifld);
  209.         return (0);
  210.     }
  211.  
  212.     IFLD_EnterCriticalSection(pifld);
  213.  
  214.     pobj = (POBJ) pifld;
  215.  
  216.     AssertSz(pobj->cRef > 0, "IFLD_Release(): Too many releases");
  217.  
  218.     cRef = --pobj->cRef;
  219.  
  220.     if (cRef == 0)
  221.     {
  222.         pobj->lpVtbl = 0;   /* should prevent being called again. */
  223.  
  224.         if (    pobj->pobjHead == 0
  225.             &&  pifld->lptblContents == NULL
  226.             &&  pifld->lptblHierarchy == NULL)
  227.         {
  228.             OBJ_Destroy(pobj);  /* will leave the critical section */
  229.             return (0);
  230.         }
  231.     }
  232.  
  233.     IFLD_LeaveCriticalSection(pifld);
  234.  
  235.     return (cRef);
  236. }
  237.  
  238. /***************************************************************************
  239.  -  IFLD_GetProps
  240.  -
  241.  *  Purpose:
  242.  *      Returns in pcval and ppval the values of the properties
  243.  *      in ptaga.  If the latter is NULL, all properties in the folder
  244.  *      are returned.
  245.  *
  246.  *  Parameters
  247.  *       pifld      The folder object whose properties are requested
  248.  *       ptaga      Pointer to a counted array of property tags of
  249.  *                  properties requested
  250.  *       ulFlags    UNICODE / String8
  251.  *       pcval      Pointer to number of values returned
  252.  *       ppval      Pointer to a variable in which the address of the
  253.  *                  returned property values is placed
  254.  *
  255.  *  Returns:
  256.  *       HRESULT
  257.  *
  258.  */
  259. STDMETHODIMP IFLD_GetProps(PIFLD pifld, LPSPropTagArray ptaga, ULONG ulFlags,
  260.     ULONG *pcval, LPSPropValue *ppval)
  261. {
  262.     LPMESSAGE lpmsg = NULL;     /* open property message */
  263.     HRESULT hr = hrSuccess;
  264.  
  265.     FLD_ValidateParameters(
  266.             pifld, 
  267.             IMAPIProp,
  268.             GetProps,
  269.             (pifld, 
  270.             ptaga, 
  271.             ulFlags,
  272.             pcval, 
  273.             ppval));
  274.  
  275.     #ifdef VALIDATE
  276.     if (ulFlags & MAPI_UNICODE)
  277.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  278.     #endif
  279.         
  280.     IFLD_EnterCriticalSection(pifld);
  281.  
  282.     /* open the property message */
  283.     hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims, FALSE, &lpmsg);
  284.     if (hr != hrSuccess)
  285.         goto exit;
  286.  
  287.     /* Pass the call off to IMessage */
  288.     hr = lpmsg->lpVtbl->GetProps(lpmsg, ptaga, ulFlags, pcval, ppval);
  289.  
  290.     UlRelease(lpmsg);
  291.  
  292.     {if(HR_SUCCEEDED(hr))
  293.     {
  294.         LPSPropValue pvalStoreSupMask = PpropFindProp(*ppval, *pcval, 
  295.                     PROP_TAG(PT_UNSPECIFIED, PROP_ID(PR_STORE_SUPPORT_MASK)));
  296.         if(pvalStoreSupMask)
  297.         {
  298.             pvalStoreSupMask->ulPropTag = PR_STORE_SUPPORT_MASK;
  299.             pvalStoreSupMask->Value.l = SMS_SUPPORTMASK;
  300.  
  301.             /* fix up hr */
  302.             if(ptaga->cValues == 1)
  303.                 hr = hrSuccess;
  304.         }
  305.     }
  306.     }
  307.  
  308.     /* Wrap internal properties. Note that this function takes as an */
  309.     /* argument the HRESULT from the previous IMessage::GetProps call. */
  310.     /* We aren't ignoring the error. */
  311.  
  312.     hr = HrWrap_GetProps(hr, pifld->pims, pifld->cval, pifld->pval, pcval,
  313.         ppval, FALSE, (ptaga != NULL), (POBJ)pifld);
  314.  
  315. exit:
  316.     IFLD_LeaveCriticalSection(pifld);
  317.  
  318.     #ifdef DEBUG
  319.     if (GetScode(hr) != MAPI_W_ERRORS_RETURNED)
  320.         DebugTraceResult(IFLD_GetProps, hr);
  321.     #endif
  322.  
  323.     return HrCheckHr(hr, IMAPIProp_GetProps);
  324. }
  325.  
  326. /***************************************************************************
  327.  -  IFLD_GetPropList
  328.  -
  329.  *  Purpose:
  330.  *      Returns in ptaga the list of all currently accessible properties
  331.  *      of pifld.
  332.  *
  333.  *  Parameters
  334.  *       pifld          The folder object whose properties are requested
  335.  *       ulFlags        UNICODE / String8
  336.  *       pptaga         Pointer to a counted array of property tags of
  337.  *                      properties requested
  338.  *  Returns:
  339.  *       hr
  340.  *
  341.  */
  342. STDMETHODIMP IFLD_GetPropList(PIFLD pifld, ULONG ulFlags, LPSPropTagArray *pptaga)
  343. {
  344.     HRESULT hr;
  345.     LPMESSAGE lpmsg = NULL;
  346.  
  347.     FLD_ValidateParameters(
  348.             pifld, 
  349.             IMAPIProp,
  350.             GetPropList,
  351.             (pifld, 
  352.             ulFlags, 
  353.             pptaga));
  354.  
  355.     #ifdef VALIDATE
  356.     if (ulFlags & MAPI_UNICODE)
  357.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  358.     #endif
  359.         
  360.     IFLD_EnterCriticalSection(pifld);
  361.  
  362.     /* open the property message */
  363.     hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims, FALSE,
  364.             &lpmsg);
  365.  
  366.     /* If the property message was successfully opened, then pass the */
  367.     /* call off to IMessage */
  368.  
  369.     if (hr == hrSuccess)
  370.         hr = lpmsg->lpVtbl->GetPropList(lpmsg, ulFlags, pptaga);
  371.  
  372.     UlRelease(lpmsg);
  373.  
  374.     IFLD_LeaveCriticalSection(pifld);
  375.     DebugTraceResult(IFLD_GetPropList, hr);
  376.     return HrCheckHr(hr, IMAPIProp_GetPropList);
  377. }
  378.  
  379. /***************************************************************************
  380.  -  IFLD_OpenProperty
  381.  -
  382.  *  Purpose:
  383.  *      Returns in *lppUnk a pointer to a newly created interface for the
  384.  *                  propery of pifld in ulPropTag
  385.  *      Not supported on folders.
  386.  *
  387.  *  Parameters
  388.  *       pifld          the folder object whose property interface is requested
  389.  *       ulPropTag          Property tag for the desired property--only ID is used.
  390.  *       lpiid              Pointer to the GUID for the interface
  391.  *       ulInterfaceOptions Specifies interface-specific behavior
  392.  *       ulFlags            MAPI_CREATE, MAPI_MODIFY, MAPI_DEFERRED_ERRORS
  393.  *       lppUnk             Pointer to the newly created interface pointer
  394.  *
  395.  *  Returns:
  396.  *       hr
  397.  *
  398.  */
  399. STDMETHODIMP IFLD_OpenProperty(PIFLD pifld, ULONG ulPropTag, LPCIID lpiid,
  400.     ULONG ulInterfaceOptions, ULONG ulFlags, LPUNKNOWN *lppUnk)
  401. {
  402.     SCODE sc;
  403.  
  404.     FLD_ValidateParameters(
  405.             pifld, 
  406.             IMAPIProp,
  407.             OpenProperty,
  408.             (pifld, 
  409.             ulPropTag, 
  410.             lpiid, 
  411.             ulInterfaceOptions, 
  412.             ulFlags, 
  413.             lppUnk));
  414.  
  415.     sc = MAPI_E_NO_SUPPORT;
  416.     
  417.     DebugTraceSc(IFLD_OpenProperty, sc);
  418.     return ResultFromScode(sc);
  419. }
  420.  
  421. /***************************************************************************
  422.  -  IFLD_SetProps
  423.  -
  424.  *  Purpose:
  425.  *      Sets the properties in pval for pifld.
  426.  *      lppProblems is a pointer to a structure of problems,
  427.  *      NULL If there weren't any.
  428.  *
  429.  *  Parameters
  430.  *       pifld          the folder object whose properties are to be set
  431.  *       cValues            count of properties to be set
  432.  *       pval               Pointer to a an array of property value structures.
  433.  *       ppErr      Pointer to address of a property problem structure
  434.  *                              to be returned.
  435.  *
  436.  *  Returns:
  437.  *       hr         PROP_E_SECURITY_VIOLATION, PROP_E_CALL_FAILED,
  438.  *                          PROP_E_CALL_FAILED
  439.  *
  440.  */
  441. STDMETHODIMP IFLD_SetProps(PIFLD pifld, ULONG cValues, LPSPropValue pval,
  442.     LPSPropProblemArray *ppErr)
  443. {
  444.     LPMESSAGE lpmsgNew = NULL;      /* new instance of property message */
  445.     HRESULT hr;
  446.  
  447.     FLD_ValidateParameters(
  448.             pifld, 
  449.             IMAPIProp,
  450.             SetProps,
  451.             (pifld, 
  452.             cValues, 
  453.             pval, 
  454.             ppErr));
  455.  
  456.     IFLD_EnterCriticalSection(pifld);
  457.  
  458.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  459.     {
  460.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  461.         goto exit;
  462.     }
  463.  
  464.     /* open the property message */
  465.     hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims,
  466.         TRUE, &lpmsgNew);
  467.     if (hr != hrSuccess)
  468.         goto exit;
  469.  
  470.     /* set props on the property message */
  471.     hr = lpmsgNew->lpVtbl->SetProps(lpmsgNew, cValues, pval, ppErr);
  472.     if (hr != hrSuccess)
  473.         goto exit;
  474.  
  475.     hr = lpmsgNew->lpVtbl->SaveChanges(lpmsgNew, KEEP_OPEN_READWRITE);
  476.     if (hr != hrSuccess)
  477.         goto exit;
  478.  
  479.     /* Must release the message before trying to update the hierarchy */
  480.     /* table. Multiple opens on the internal message are not allowed by */
  481.     /* IStorage. */
  482.  
  483.     UlRelease(lpmsgNew);
  484.     lpmsgNew = NULL;
  485.  
  486.     /* If this isn't the root folder, get the parent entryid, and call */
  487.     /* ChangeTable so that any new properties are updated in its */
  488.     /* hierarchy table row. */
  489.  
  490.     if (FIsRoot(pifld->peid) == FALSE)
  491.     {
  492.         PEID peidParent = NULL;
  493.         PIMS pims = pifld->pims;
  494.  
  495.         hr = HrGetParentEID(&pims->lmr, pifld->peid, &peidParent);
  496.         if (hr == hrSuccess)
  497.         {
  498.             ChangeTable(pims, peidParent, pifld->peid, MAPI_FOLDER,
  499.                 TABLE_ROW_MODIFIED, TRUE);
  500.             LMFree(&pims->lmr, peidParent);
  501.         }
  502.         else
  503.         {
  504.             TraceSz1("Sample MS: IFLD_SetProps: failed to change hierarchy "
  505.                 "table. sc == %s\n", SzDecodeScode(GetScode(hr)));
  506.             hr = hrSuccess;
  507.         }
  508.     }
  509.  
  510. exit:
  511.     UlRelease(lpmsgNew);
  512.  
  513.     IFLD_LeaveCriticalSection(pifld);
  514.     DebugTraceResult(IFLD_SetProps, hr);
  515.     return HrCheckHr(hr, IMAPIProp_SetProps);
  516. }
  517.  
  518. /***************************************************************************
  519.  -  IFLD_DeleteProps
  520.  -
  521.  *  Purpose:
  522.  *      Deletes the properties in ptaga for pifld.  ppErr
  523.  *      is a pointer to a structure of problems, NULL If there weren't any.
  524.  *
  525.  *  Parameters
  526.  *       pifld          the folder object whose properties are to be deleted
  527.  *       ptaga      Pointer to a counted array of property tags of the
  528.  *                          properties to be deleted.  Must not be NULL.
  529.  *       ppErr      Pointer to address of a property problem structure
  530.  *                          to be returned.
  531.  *  Returns:
  532.  *       hr         PROP_E_SECURITY_VIOLATION, PROP_E_CALL_FAILED,
  533.  *                          PROP_E_CALL_FAILED
  534.  */
  535. STDMETHODIMP IFLD_DeleteProps(PIFLD pifld, LPSPropTagArray ptaga,
  536.     LPSPropProblemArray *ppErr)
  537. {
  538.     LPMESSAGE lpmsgNew = NULL;  /* new instance of property message */
  539.     HRESULT hr = hrSuccess;
  540.  
  541.     FLD_ValidateParameters(
  542.             pifld, 
  543.             IMAPIProp,
  544.             DeleteProps,
  545.             (pifld, 
  546.             ptaga, 
  547.             ppErr));
  548.  
  549.     IFLD_EnterCriticalSection(pifld);
  550.  
  551.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  552.     {
  553.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  554.         goto exit;
  555.     }
  556.  
  557.     /* open the property message */
  558.     hr = HrOpenPropertyMessageRetry(pifld->peid,
  559.         pifld->pims, TRUE, &lpmsgNew);
  560.     if (hr != hrSuccess)
  561.         goto exit;
  562.  
  563.     /* Pass the call off to IMessage */
  564.     hr = lpmsgNew->lpVtbl->DeleteProps(lpmsgNew, ptaga, ppErr);
  565.     if (hr != hrSuccess)
  566.         goto exit;
  567.  
  568.     hr = lpmsgNew->lpVtbl->SaveChanges(lpmsgNew, KEEP_OPEN_READWRITE);
  569.     if (hr != hrSuccess)
  570.         goto exit;
  571.  
  572.     /* Must release the message before trying to update the hierarchy */
  573.     /* table. Multiple opens on the internal message are not allowed by */
  574.     /* IStorage. */
  575.  
  576.     UlRelease(lpmsgNew);
  577.     lpmsgNew = NULL;
  578.  
  579.     /* If this isn't the root folder, get the parent entryid, and call */
  580.     /* ChangeTable so that any new properties are updated in its */
  581.     /* hierarchy table row. */
  582.  
  583.     if (FIsRoot(pifld->peid) == FALSE)
  584.     {
  585.         PEID peidParent = NULL;
  586.         PIMS pims = pifld->pims;
  587.  
  588.         hr = HrGetParentEID(&pims->lmr, pifld->peid, &peidParent);
  589.         if (hr == hrSuccess)
  590.         {
  591.             ChangeTable(pims, peidParent, pifld->peid, MAPI_FOLDER,
  592.                 TABLE_ROW_MODIFIED, TRUE);
  593.             LMFree(&pims->lmr, peidParent);
  594.         }
  595.         else
  596.         {
  597.             TraceSz1("Sample MS: IFLD_DeleteProps: failed to change hierarchy "
  598.                 "table. sc == %s\n", SzDecodeScode(GetScode(hr)));
  599.             hr = hrSuccess;
  600.         }
  601.     }
  602.  
  603. exit:
  604.     UlRelease(lpmsgNew);
  605.  
  606.     IFLD_LeaveCriticalSection(pifld);
  607.  
  608.     DebugTraceResult(IFLD_DeleteProps, hr);
  609.     return HrCheckHr(hr, IMAPIProp_DeleteProps);
  610. }
  611.  
  612. /***************************************************************************
  613.  -  IFLD_CopyTo
  614.  -
  615.  *  Purpose:
  616.  *      Copy the properties and/or contents of the current object
  617.  *          to a destination object
  618.  *
  619.  *  Parameters
  620.  *       pifldSrc       current object
  621.  *       ciidExcl       Count of excluded interfaces in rgiidExcl
  622.  *       rgiidExcl      Array of iid's specifying excluded interfaces
  623.  *       ptagaExcl      Pointer to a counted array of property tags of
  624.  *                      properties not to be copied, can be NULL
  625.  *       ulUIParam      Handle of parent window cast to ULONG, NULL if
  626.  *                      no dialog requested
  627.  *       piidDst        Interface ID of the interface of lpDestObj
  628.  *       lpDestObj      destination object
  629.  *       ulFlags        MAPI_MOVE, MAPI_NOREPLACE, MAPI_DIALOG, MAPI_DECLINE_OK
  630.  *       pprba          Pointer to address of a property problem
  631.  *                      structure to be returned.
  632.  *
  633.  *  Returns:
  634.  *       hr
  635.  *
  636.  *  Side effects:
  637.  *
  638.  *  Errors:
  639.  */
  640. STDMETHODIMP IFLD_CopyTo(PIFLD pifldSrc, ULONG ciidExcl, LPCIID rgiidExcl,
  641.     LPSPropTagArray ptagaExcl, ULONG ulUIParam, LPMAPIPROGRESS lpProgress,
  642.     LPCIID piidDst, LPVOID lpDestObj, ULONG ulFlagsIn,
  643.     LPSPropProblemArray *pprba)
  644. {
  645.     HRESULT hr = hrSuccess;
  646.     BOOL fIsIMapiProp = TRUE;   /* TRUE if are to copy as imapiprop */
  647.     BOOL fIsIMAPIFolder = TRUE;     /* TRUE if are to copy as IMAPIFolder */
  648.     SCODE sc = S_OK;
  649.     LPCIID piid;
  650.     LPCIID piidMax;
  651.     ULONG ulFlags;
  652.  
  653.     FLD_ValidateParameters(
  654.             pifldSrc, 
  655.             IMAPIProp,
  656.             CopyTo,
  657.             (pifldSrc, 
  658.             ciidExcl, 
  659.             rgiidExcl,
  660.             ptagaExcl, 
  661.             ulUIParam, 
  662.             lpProgress,
  663.             piidDst, 
  664.             lpDestObj, 
  665.             ulFlagsIn,
  666.             pprba));
  667.  
  668.     IFLD_EnterCriticalSection(pifldSrc);
  669.  
  670.     /* Turn off MAPI_DIALOG flag, since we don't support it. */
  671.     ulFlags = (ulFlagsIn & ~MAPI_DIALOG);
  672.  
  673.     piid = rgiidExcl;
  674.     piidMax = piid + ciidExcl;
  675.  
  676.     /* find the interface to copy : IMAPIPROP or IMAPIFOLDER */
  677.     while (piid < piidMax)
  678.     {
  679.         if (IsEqualIID(piid, &IID_IMAPIFolder))
  680.             fIsIMAPIFolder = FALSE;
  681.         else if (IsEqualIID(piid, (LPIID) &IID_IMAPIProp))
  682.             fIsIMapiProp = FALSE;
  683.         piid++;
  684.     }
  685.  
  686.     /* make sure that the destination can support this */
  687.     if (fIsIMAPIFolder && !IsEqualIID(piidDst, (LPIID) &IID_IMAPIFolder))
  688.     {
  689.         LPMAPIFOLDER pfld = NULL;
  690.  
  691.         hr = ((LPUNKNOWN) lpDestObj)->lpVtbl->QueryInterface(lpDestObj,
  692.             (LPIID) &IID_IMAPIFolder, (LPVOID *) &pfld);
  693.         if (hr != hrSuccess)
  694.         {
  695.             Assert(GetScode(hr) == MAPI_E_INTERFACE_NOT_SUPPORTED);
  696.             goto exit;
  697.         }
  698.         UlRelease(pfld);
  699.     }
  700.     else if (fIsIMapiProp &&
  701.             !(IsEqualIID(piidDst, (LPIID) &IID_IMAPIProp) ||
  702.             IsEqualIID(piidDst, (LPIID) &IID_IMAPIFolder)))
  703.     {
  704.         LPMAPIPROP pmp = NULL;
  705.  
  706.         hr = ((LPUNKNOWN) lpDestObj)->lpVtbl->QueryInterface(lpDestObj,
  707.             (LPIID) &IID_IMAPIProp, (LPVOID *) &pmp);
  708.         if (hr != hrSuccess)
  709.         {
  710.             Assert(GetScode(hr) == MAPI_E_INTERFACE_NOT_SUPPORTED);
  711.             goto exit;
  712.         }
  713.         UlRelease(pmp);
  714.     }
  715.  
  716.     if (!FFoldInSameStore(pifldSrc, (PIFLD) lpDestObj))
  717.     {
  718.         LPMAPISUP psup = pifldSrc->pims->psup;
  719.  
  720.         /* leave critical section before calling back to MAPI */
  721.         IFLD_LeaveCriticalSection(pifldSrc);
  722.  
  723.         /* call MAPI's CopyProps */
  724.         hr = psup->lpVtbl->DoCopyTo(psup, (LPIID) &IID_IMAPIFolder, (LPVOID) pifldSrc,
  725.             ciidExcl, rgiidExcl, ptagaExcl, ulUIParam,
  726.             lpProgress, piidDst, lpDestObj, ulFlagsIn, pprba);
  727.  
  728.         DebugTraceResult(IFLD_CopyTo, hr);
  729.         return (HrCheckHr(hr, IMAPIProp_CopyTo));
  730.     }
  731.  
  732.     /* copy them as folders */
  733.     if (fIsIMAPIFolder)
  734.     {
  735.         PIFLD pifldDst = (PIFLD) lpDestObj;
  736.  
  737.         /* make sure we have write access */
  738.  
  739.         if (!OBJ_TestFlag(pifldDst, OBJF_MODIFY))
  740.         {
  741.             hr = ResultFromScode(MAPI_E_NO_ACCESS);
  742.             goto exit;
  743.         }
  744.  
  745.         if (!FContainsProp(PR_CONTAINER_HIERARCHY, ptagaExcl))
  746.         {
  747.             /* can not move or copy a folder to a decendant or itself */
  748.             if (FIsAncestor(pifldSrc, pifldDst))
  749.                 hr = ResultFromScode(MAPI_E_NO_ACCESS);
  750.         }
  751.         else if (ulFlags & MAPI_MOVE)
  752.         {
  753.             /* Can't set MAPI_MOVE and also exclude subfolders. */
  754.             hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
  755.         }
  756.  
  757.         if (hr != hrSuccess)
  758.             goto exit;
  759.  
  760.         if (!(ulFlags & MAPI_NOREPLACE))
  761.         {
  762.             /* Empty the destination folder before beginning. */
  763.             hr = pifldDst->lpVtbl->EmptyFolder(pifldDst, 0, NULL, 0);
  764.             if (hr != hrSuccess)
  765.                 goto exit;
  766.         }
  767.  
  768.         /* copy the contents of the source folder to the destination folder */
  769.         hr = HrCopyContents(pifldSrc, pifldDst, ulFlags, ptagaExcl);
  770.         if (hr != hrSuccess)
  771.             goto exit;
  772.     }
  773.  
  774.     /* copy the properties */
  775.     if (fIsIMapiProp || fIsIMAPIFolder)
  776.     {
  777.         LPMESSAGE lpmsg;
  778.         BOOL fModify = FALSE;
  779.  
  780.         if (ulFlags & MAPI_MOVE)
  781.             fModify = TRUE;
  782.  
  783.         hr = HrOpenPropertyMessageRetry(pifldSrc->peid,
  784.             pifldSrc->pims, fModify, &lpmsg);
  785.         if (hr != hrSuccess)
  786.             goto exit;
  787.  
  788.         hr = lpmsg->lpVtbl->CopyTo(lpmsg, ciidExcl,
  789.             rgiidExcl, ptagaExcl, ulUIParam, lpProgress,
  790.             (LPIID) &IID_IMAPIProp, lpDestObj, ulFlags, pprba);
  791.  
  792.         if (hr == hrSuccess && fModify)
  793.             lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  794.  
  795.         UlRelease(lpmsg);
  796.     }
  797.  
  798. exit:
  799.     IFLD_LeaveCriticalSection(pifldSrc);
  800.  
  801.     DebugTraceResult(IFLD_CopyTo, hr);
  802.     return HrCheckHr(hr, IMAPIProp_CopyTo);
  803. }
  804.  
  805. /***************************************************************************
  806.  -  IFLD_CopyProps
  807.  -
  808.  *  Purpose:
  809.  *      Copy the properties and/or contents of the current object
  810.  *          to a destination object
  811.  *
  812.  *  Parameters
  813.  *       pifldSrc       current object
  814.  *       ptagaIncl      Pointer to a counted array of property tags of
  815.  *                      properties to be copied
  816.  *       ulUIParam      Handle of parent window cast to ULONG, NULL if
  817.  *                      no dialog requested
  818.  *       piidDst        Interface ID of the interface of lpDestObj
  819.  *       lpDestObj      destination object
  820.  *       ulFlags        MAPI_MOVE, MAPI_NOREPLACE, MAPI_DIALOG, MAPI_DECLINE_OK
  821.  *       pprba          Pointer to address of a property problem
  822.  *                      structure to be returned.
  823.  *
  824.  *  Returns:
  825.  *       hr
  826.  *
  827.  *  Side effects:
  828.  *
  829.  *  Errors:
  830.  */
  831. STDMETHODIMP IFLD_CopyProps(PIFLD pifldSrc,
  832.     LPSPropTagArray ptagaIncl, ULONG ulUIParam, LPMAPIPROGRESS lpProgress,
  833.     LPCIID piidDst, LPVOID lpDestObj, ULONG ulFlagsIn,
  834.     LPSPropProblemArray *pprba)
  835. {
  836.     HRESULT hr = hrSuccess;
  837.  
  838. #ifdef WHEN_WORKING
  839.     PIFLD pifldDst = NULL;      /* lpDestObj if it's a folder */
  840.     BOOL fIsIMAPIFolder;        /* TRUE if are to copy as IMAPIFolder */
  841.     SCODE sc = S_OK;
  842.     LPIID piid;
  843.     LPIID piidMax;
  844.     ULONG ulFlags;
  845. #endif /* WHEN_WORKING */
  846.  
  847.     FLD_ValidateParameters(
  848.             pifldSrc, 
  849.             IMAPIProp,
  850.             CopyProps,
  851.             (pifldSrc,
  852.             ptagaIncl, 
  853.             ulUIParam, 
  854.             lpProgress,
  855.             piidDst, 
  856.             lpDestObj, 
  857.             ulFlagsIn,
  858.             pprba));
  859.  
  860.     IFLD_EnterCriticalSection(pifldSrc);
  861.  
  862. #ifdef WHEN_WORKING
  863.     /* Turn off MAPI_DIALOG flag, since we don't support it. */
  864.     ulFlags = (ulFlagsIn & ~MAPI_DIALOG);
  865.  
  866.     fIsIMAPIFolder =   FContainsProp(PR_CONTAINER_HIERARCHY, ptagaIncl)
  867.                     || FContainsProp(PR_CONTAINER_CONTENTS, ptagaIncl);
  868.     /* make sure that the destination can support this */
  869.     if (fIsIMAPIFolder && !IsEqualIID(piidDst, (LPIID) &IID_IMAPIFolder))
  870.     {
  871.         LPMAPIFOLDER pfld = NULL;
  872.  
  873.         hr = ((LPUNKNOWN) lpDestObj)->lpVtbl->QueryInterface(lpDestObj,
  874.             (LPIID) &IID_IMAPIFolder, (LPVOID *) &pfld);
  875.         if (hr != hrSuccess)
  876.         {
  877.             Assert(GetScode(hr) == MAPI_E_INTERFACE_NOT_SUPPORTED);
  878.             goto exit;
  879.         }
  880.         UlRelease(pfld);
  881.     }
  882.     else if (!(IsEqualIID(piidDst, (LPIID) &IID_IMAPIProp) ||
  883.             IsEqualIID(piidDst, (LPIID) &IID_IMAPIFolder)))
  884.     {
  885.         LPMAPIPROP pmp = NULL;
  886.  
  887.         hr = ((LPUNKNOWN) lpDestObj)->lpVtbl->QueryInterface(lpDestObj,
  888.             (LPIID) &IID_IMAPIProp, (LPVOID *) &pmp);
  889.         if (hr != hrSuccess)
  890.         {
  891.             Assert(GetScode(hr) == MAPI_E_INTERFACE_NOT_SUPPORTED);
  892.             goto exit;
  893.         }
  894.         UlRelease(pmp);
  895.     }
  896.  
  897.     if (!FFoldInSameStore(pifldSrc, (PIFLD) lpDestObj))
  898.     {
  899.         LPMAPISUP psup = pifldSrc->pims->psup;
  900.  
  901.         /* leave critical section before calling back to MAPI */
  902.         IFLD_LeaveCriticalSection(pifldSrc);
  903.  
  904.         /* call MAPI's CopyProps */
  905.         hr = psup->lpVtbl->DoCopyProps(psup, (LPIID) &IID_IMAPIFolder,
  906.             (LPVOID) pifldSrc, ptagaIncl, ulUIParam,
  907.             lpProgress, piidDst, lpDestObj, ulFlagsIn, pprba);
  908.  
  909.         DebugTraceResult(IFLD_CopyProps, hr);
  910.         return (HrCheckHr(hr, IMAPIProp_CopyProps));
  911.     }
  912.  
  913.     /* copy them as folders */
  914.     if (fIsIMAPIFolder)
  915.     {
  916.         PIFLD pifldDst = (PIFLD) lpDestObj;
  917.  
  918.         /* make sure we have write access */
  919.  
  920.         if (!OBJ_TestFlag(pifldDst, OBJF_MODIFY))
  921.         {
  922.             hr = ResultFromScode(MAPI_E_NO_ACCESS);
  923.             goto exit;
  924.         }
  925.  
  926.         if (FContainsProp(PR_CONTAINER_HIERARCHY, ptagaIncl))
  927.         {
  928.             /* can not move or copy a folder to a decendant or itself */
  929.             if (FIsAncestor(pifldSrc, pifldDst))
  930.                 hr = ResultFromScode(MAPI_E_NO_ACCESS);
  931.         }
  932.         else if (ulFlags & MAPI_MOVE)
  933.         {
  934.             /* Can't set MAPI_MOVE and also exclude subfolders. */
  935.             hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
  936.         }
  937.  
  938.         if (hr != hrSuccess)
  939.             goto exit;
  940.  
  941.         if (!(ulFlags & MAPI_NOREPLACE))
  942.         {
  943.             /* Empty the destination folder before beginning. */
  944.             hr = pifldDst->lpVtbl->EmptyFolder(pifldDst, 0, NULL, 0);
  945.             if (hr != hrSuccess)
  946.                 goto exit;
  947.         }
  948.  
  949.  
  950.         if (FContainsProp(PR_CONTAINER_CONTENTS, ptagaIncl))
  951.         {
  952.             /* copy the contents of the source folder to the destination folder */
  953.             hr = HrCopyContents(pifldSrc, pifldDst, ulFlags, NULL);
  954.         }
  955.         if (hr != hrSuccess)
  956.             goto exit;
  957.     }
  958.  
  959.     /* copy the properties */
  960.     /* //$ Rebuild ptagaIncl without Contents and Hierarchy */
  961.     {
  962.         LPMESSAGE lpmsg;
  963.         BOOL fModify = FALSE;
  964.  
  965.         if (ulFlags & MAPI_MOVE)
  966.             fModify = TRUE;
  967.  
  968.         hr = HrOpenPropertyMessageRetry(pifldSrc->peid,
  969.             pifldSrc->pims, fModify, &lpmsg);
  970.         if (hr != hrSuccess)
  971.             goto exit;
  972.  
  973.         hr = lpmsg->lpVtbl->CopyProps(lpmsg,
  974.             ptagaIncl, ulUIParam, lpProgress,
  975.             (LPIID) &IID_IMAPIProp, lpDestObj, ulFlags, pprba);
  976.  
  977.         if (hr == hrSuccess && fModify)
  978.             lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  979.  
  980.         UlRelease(lpmsg);
  981.     }
  982.  
  983.  
  984. exit:
  985.     IFLD_LeaveCriticalSection(pifldSrc);
  986.  
  987. #else
  988.  
  989.     IFLD_LeaveCriticalSection(pifldSrc);
  990.  
  991.     /* call MAPI's CopyProps */
  992.     hr = pifldSrc->pims->psup->lpVtbl->DoCopyProps(pifldSrc->pims->psup,
  993.         (LPIID) &IID_IMAPIFolder, (LPVOID) pifldSrc, ptagaIncl, ulUIParam,
  994.         lpProgress, piidDst, lpDestObj, ulFlagsIn, pprba);
  995.  
  996. #endif  /* WHEN_WORKING */
  997.  
  998.     DebugTraceResult(IFLD_CopyProps, hr);
  999.     return HrCheckHr(hr, IMAPIProp_CopyProps);
  1000. }
  1001.  
  1002. /***************************************************************************
  1003.  -  IFLD_GetContentsTable
  1004.  -
  1005.  *  Purpose:
  1006.  *      Returns a table with the summary information about the messages
  1007.  *      in the folder
  1008.  *
  1009.  *  Parameters
  1010.  *       pifld  the folder object
  1011.  *       ulFlags    MAPI_DEFERRED_ERRORS, MAPI_ASSOCIATED
  1012.  *       lppTable   location to place the table object
  1013.  *
  1014.  *  Returns:
  1015.  *
  1016.  *  Side effects:
  1017.  *
  1018.  *  Errors:
  1019.  */
  1020. STDMETHODIMP IFLD_GetContentsTable(PIFLD pifld, ULONG ulFlags,
  1021.     LPMAPITABLE *lppTable)
  1022. {
  1023.     HRESULT hr = hrSuccess;
  1024.     SCODE sc;
  1025.     HANDLE hFindFile = FAILED_SEARCH;
  1026.     LPTSTR szMessageTemplate = NULL;    /* template for a message name */
  1027.     LPSSortOrderSet lpsSortOrder = NULL;
  1028.     BOOL fTableCreated = FALSE;
  1029.     BOOL fInMutex = FALSE;
  1030.     PIMS pims;
  1031.  
  1032.     FLD_ValidateParameters(
  1033.             pifld, 
  1034.             IMAPIContainer,
  1035.             GetContentsTable,
  1036.             (pifld, 
  1037.             ulFlags,
  1038.             lppTable));
  1039.  
  1040.     #ifdef VALIDATE
  1041.     if (ulFlags & MAPI_ASSOCIATED)
  1042.         return ResultFromScode(MAPI_E_NO_SUPPORT);
  1043.     
  1044.     if (ulFlags & MAPI_UNICODE)
  1045.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1046.     #endif
  1047.  
  1048.     IFLD_EnterCriticalSection(pifld);
  1049.  
  1050.     pims = pifld->pims;
  1051.  
  1052.     /* If the table mutex doesn't yet exist on this process, create it. */
  1053.  
  1054.     if (pims->hContTblMutex == NULL)
  1055.     {
  1056.         hr = HrCreateContTblMutex(&pims->hContTblMutex);
  1057.         if (hr != hrSuccess)
  1058.             goto exit;
  1059.     }
  1060.  
  1061.     if (pifld->lptblContents == NULL)
  1062.     {
  1063.         PINST pinst = (PINST) PvGetInstanceGlobals();
  1064.         PLMR plmr = &pifld->pims->lmr;
  1065.  
  1066.         if (pinst == NULL)
  1067.         {
  1068.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1069.             goto exit;
  1070.         }
  1071.  
  1072.         /* create the data for the table */
  1073.         sc = CreateTable((LPIID) &IID_IMAPITableData,
  1074.             plmr->lpAllocBuf, plmr->lpAllocMore,
  1075.             plmr->lpFreeBuf, pinst->lpmalloc, TBLTYPE_DYNAMIC,
  1076.             PR_INSTANCE_KEY, (LPSPropTagArray) &sPropTagsContents,
  1077.             &(pifld->lptblContents));
  1078.  
  1079.         if (sc != S_OK)
  1080.         {
  1081.             hr = ResultFromScode(sc);
  1082.             goto exit;
  1083.         }
  1084.         fTableCreated = TRUE;
  1085.  
  1086.         /* Get the table mutex so that we can access the file without */
  1087.         /* crossing paths with another process. */
  1088.  
  1089.         WaitForSingleObject(pims->hContTblMutex, INFINITE);
  1090.         fInMutex = TRUE;
  1091.  
  1092.         hr = HrReadTableFromDisk(pifld->lptblContents, (POBJ) pifld,
  1093.             pifld->peid->szPath, CONTENTS_COLUMNS, szContentsFileName);
  1094.         if (hr != hrSuccess)
  1095.         {
  1096.             /* non-fatal. If the data on disk is missing or invalid, we */
  1097.             /* should regenerate the data via HrSyncContentsTable. */
  1098.  
  1099.             TraceSz1("SMS: Error %s reading table from disk. Regenerating "
  1100.                 "table on disk.", SzDecodeScode(GetScode(hr)));
  1101.  
  1102.             hr = hrSuccess;
  1103.         }
  1104.     }
  1105.  
  1106.     /* If we don't already have the mutex, get it so that we can access */
  1107.     /* the file without crossing paths with another process. */
  1108.  
  1109.     if (!fInMutex)
  1110.     {
  1111.         WaitForSingleObject(pims->hContTblMutex, INFINITE);
  1112.         fInMutex = TRUE;
  1113.     }
  1114.  
  1115.     hr = HrSyncContentsTable(pifld, TRUE);
  1116.  
  1117.     Assert(fInMutex);
  1118.  
  1119.     ReleaseMutex(pims->hContTblMutex);
  1120.     fInMutex = FALSE;
  1121.  
  1122.     if (hr != hrSuccess)
  1123.         goto exit;
  1124.  
  1125.     /* open a view to the table */
  1126.     /* get the sort order from the PR_SMS_CONTENTS_SORT_ORDER property */
  1127.     hr = HrGetSortOrder(pifld, &lpsSortOrder);
  1128.     if (hr != hrSuccess)
  1129.         goto exit;
  1130.  
  1131.     /* get the view */
  1132.     hr = pifld->lptblContents->lpVtbl->HrGetView(pifld->lptblContents,
  1133.         lpsSortOrder, ViewRelease, (ULONG) pifld, lppTable);
  1134.     if (hr != hrSuccess)
  1135.         goto exit;
  1136.  
  1137.     /* record this view in pifld */
  1138.     pifld->cContentsViews++;
  1139.  
  1140. exit:
  1141.     if (fInMutex)
  1142.         ReleaseMutex(pims->hContTblMutex);
  1143.  
  1144.     /* free the sort order */
  1145.     LMFree(&pifld->pims->lmr, lpsSortOrder);
  1146.  
  1147.     /* remove the table if it is created in error */
  1148.     if (hr != hrSuccess && fTableCreated)
  1149.     {
  1150.         UlRelease(pifld->lptblContents);
  1151.         pifld->cContentsViews = 0;
  1152.         pifld->lptblContents = NULL;
  1153.     }
  1154.  
  1155.     CloseIDSearch(&hFindFile, &szMessageTemplate);
  1156.  
  1157.     IFLD_LeaveCriticalSection(pifld);
  1158.  
  1159.     DebugTraceResult(IFLD_GetContentsTable, hr);
  1160.     return HrCheckHr(hr, IMAPIFolder_GetContentsTable);
  1161. }
  1162.  
  1163. /***************************************************************************
  1164.  -  IFLD_GetHierarchyTable
  1165.  -
  1166.  *  Purpose:
  1167.  *      Returns a table with the summary information about the child
  1168.  *      folders of the current folder
  1169.  *
  1170.  *  Parameters
  1171.  *       pifld      the folder object
  1172.  *       ulFlags    CONVENIENT_DEPTH if set indicates pre-order traversal
  1173.  *                  MAPI_DEFERRED_ERRORS
  1174.  *                  MAPI_UNICODE
  1175.  *       lppTable   location to place the table object
  1176.  *
  1177.  *  Returns:
  1178.  *
  1179.  *  Side effects:
  1180.  *
  1181.  *  Errors:
  1182.  */
  1183. STDMETHODIMP IFLD_GetHierarchyTable(PIFLD pifld, ULONG ulFlags,
  1184.     LPMAPITABLE *lppTable)
  1185. {
  1186.     HRESULT hr;
  1187.     LPTABLEDATA lptad = NULL;
  1188.  
  1189.     FLD_ValidateParameters(
  1190.             pifld, 
  1191.             IMAPIContainer,
  1192.             GetHierarchyTable,
  1193.             (pifld, 
  1194.             ulFlags, 
  1195.             lppTable));
  1196.  
  1197.     #ifdef VALIDATE
  1198.     if (ulFlags & MAPI_UNICODE)
  1199.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1200.     #endif
  1201.  
  1202.     IFLD_EnterCriticalSection(pifld);
  1203.  
  1204.     /* if table needs refreshing */
  1205.     if (pifld->lptblHierarchy == NULL)
  1206.     {
  1207.         PINST pinst = (PINST) PvGetInstanceGlobals();
  1208.         SCODE sc;
  1209.         PLMR plmr = &pifld->pims->lmr;
  1210.  
  1211.         pifld->cHierarchyViews = 0;
  1212.  
  1213.         if (pinst == NULL)
  1214.         {
  1215.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1216.             goto exit;
  1217.         }
  1218.  
  1219.         /* create the table */
  1220.         sc = CreateTable((LPIID) &IID_IMAPITableData,
  1221.             plmr->lpAllocBuf, plmr->lpAllocMore,
  1222.             plmr->lpFreeBuf, pinst->lpmalloc, TBLTYPE_DYNAMIC,
  1223.             PR_INSTANCE_KEY, (LPSPropTagArray) &sPropTagsHierarchy, &lptad);
  1224.  
  1225.         if (sc != S_OK)
  1226.         {
  1227.             hr = ResultFromScode(sc);
  1228.             goto exit;
  1229.         }
  1230.  
  1231.         /* add a row or traversal for each subfolder of pifld */
  1232.         /* Note that the sample store only supports a max depth of 1. */
  1233.  
  1234.         hr = HrFillHierarchyTable(pifld, lptad);
  1235.         if (hr != hrSuccess)
  1236.             goto exit;
  1237.  
  1238.         pifld->lptblHierarchy = lptad;
  1239.         lptad = NULL;
  1240.     }
  1241.  
  1242.     /* open a view to the table */
  1243.     hr = pifld->lptblHierarchy->lpVtbl->HrGetView(pifld->lptblHierarchy,
  1244.         NULL, ViewRelease, (ULONG) pifld, lppTable);
  1245.     if (hr != hrSuccess)
  1246.         goto exit;
  1247.  
  1248.     /* record this view in pifld */
  1249.     pifld->cHierarchyViews++;
  1250.  
  1251. exit:
  1252.     /* remove the table if it is created in error */
  1253.     if (hr != hrSuccess && lptad)
  1254.         UlRelease(lptad);
  1255.  
  1256.     IFLD_LeaveCriticalSection(pifld);
  1257.  
  1258.     DebugTraceResult(IFLD_GetHierarchyTable, hr);
  1259.     return HrCheckHr(hr, IMAPIFolder_GetHierarchyTable);
  1260. }
  1261.  
  1262. /***************************************************************************
  1263.  -  IFLD_CreateMessage
  1264.  -
  1265.  *  Purpose:
  1266.  *      Creates a new message in the store
  1267.  *
  1268.  *  Parameters
  1269.  *      pifld           the folder object
  1270.  *      ulFlags         flags. Valid flags to pass are MAPI_DEFERRED_ERRORS
  1271.  *                      and MAPI_ASSOCIATED. The sample store doesn't support
  1272.  *                      MAPI_ASSOCIATED, so the code returns no support if
  1273.  *                      that flag is passed.
  1274.  *      piid            reserved for future use
  1275.  *      lppMessage      location to place the message object
  1276.  *
  1277.  *  Returns:
  1278.  *
  1279.  *  Side effects:
  1280.  *
  1281.  *  Errors:
  1282.  */
  1283. STDMETHODIMP IFLD_CreateMessage(PIFLD pifld, LPCIID piid, ULONG ulFlags,
  1284.     LPMESSAGE *lppMessage)
  1285. {
  1286.     HRESULT hr;
  1287.     PEID peid = NULL;
  1288.     ULONG ulSeqNumber;
  1289.     PIMSG pimsg = NULL;
  1290.     LPTSTR szFull = NULL;
  1291.  
  1292.     FLD_ValidateParameters(
  1293.             pifld, 
  1294.             IMAPIFolder,
  1295.             CreateMessage,
  1296.             (pifld, 
  1297.             piid, 
  1298.             ulFlags, 
  1299.             lppMessage));
  1300.  
  1301.     #ifdef VALIDATE
  1302.     if (ulFlags & MAPI_ASSOCIATED)
  1303.         return ResultFromScode(MAPI_E_NO_SUPPORT);
  1304.  
  1305.     if (piid && !FQueryInterface(OT_MESSAGE, piid))
  1306.         return ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
  1307.     #endif
  1308.  
  1309.     IFLD_EnterCriticalSection(pifld);
  1310.  
  1311.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  1312.     {
  1313.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1314.         goto exit;
  1315.     }
  1316.  
  1317.     /* create the message */
  1318.  
  1319.     hr = HrNewEID(pifld, pifld->pims, TEMP_EXT, &ulSeqNumber, &peid);
  1320.     if (hr != hrSuccess)
  1321.         goto exit;
  1322.  
  1323.     hr = HrNewIMSG(peid, pifld->pims, TRUE, TRUE, ulSeqNumber, &szFull, &pimsg);
  1324.     if (hr != hrSuccess)
  1325.         goto exit;
  1326.  
  1327.     hr = InitIMSGProps(pimsg);
  1328.     if (hr != hrSuccess)
  1329.         goto exit;
  1330.  
  1331.     /* folder contents counts and tables are not updated until message */
  1332.     /* has changes saved */
  1333.  
  1334. exit:
  1335.     LMFree(&pifld->pims->lmr, peid);
  1336.  
  1337.     if (hr == hrSuccess)
  1338.         *lppMessage = (LPMESSAGE) pimsg;
  1339.     else if (pimsg)
  1340.     {
  1341.         UlRelease(pimsg);
  1342.  
  1343.         Assert(szFull);
  1344.         DeleteFile(szFull);
  1345.     }
  1346.  
  1347.     FreeNull(szFull);
  1348.  
  1349.     IFLD_LeaveCriticalSection(pifld);
  1350.  
  1351.     DebugTraceResult(IFLD_CreateMessage, hr);
  1352.     return HrCheckHr(hr, IMAPIFolder_CreateMessage);
  1353. }
  1354.  
  1355. /***************************************************************************
  1356.  -  IFLD_CopyMessages
  1357.  -
  1358.  *  Purpose:
  1359.  *      Moves/Copies messages from the source folder to the destination folder
  1360.  *      EntryId's are not changed here since they get stamped in when the
  1361.  *      messages are opened.
  1362.  *
  1363.  *  Parameters
  1364.  *      pifld                   the source folder
  1365.  *      lpMsgList               the list of messages to be moved/copied
  1366.  *      piid                    the interface id of destination folder
  1367.  *      lpfolderDst             the destination folder
  1368.  *      ulUIParam               Handle of parent window,
  1369.  *                                  ignored if MESSAGE_DIALOG not set
  1370.  *      lpProgress              points to progress dialog object
  1371.  *      ulFlags                 MAPI_DECLINE_OK, MESSAGE_MOVE, or MESSAGE_DIALOG
  1372.  */
  1373. STDMETHODIMP IFLD_CopyMessages(PIFLD pifld, LPENTRYLIST lpMsgList,
  1374.     LPCIID piid, LPMAPIFOLDER lpfolderDst, ULONG ulUIParam,
  1375.     LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  1376. {
  1377.     LONG cHandled = 0;          /* number messages copied/moved */
  1378.     LPTSTR szSrcName = NULL;    /* name of file to be moved */
  1379.     LPTSTR szDestName = NULL;   /* name of the file after it is moved */
  1380.     ULONG cbSrcPath = 0;        /* length in bytes of path to src fld */
  1381.     ULONG cbDestPath = 0;       /* length in bytes of path to dest fld */
  1382.     ULONG cbStorePath = 0;      /* length in bytes of path to root of store */
  1383.     TCHAR szBlanks[CCH_NAME] = TEXT("        .   ");
  1384.     HRESULT hr = hrSuccess;
  1385.     PIFLD pifldDst;
  1386.     BOOL fMove;
  1387.     SBinary *psb;
  1388.     SBinary *psbMax;
  1389.     PIMS pims;
  1390.  
  1391.     FLD_ValidateParameters(
  1392.             pifld, 
  1393.             IMAPIFolder,
  1394.             CopyMessages,
  1395.             (pifld, 
  1396.             lpMsgList, 
  1397.             piid, 
  1398.             lpfolderDst, 
  1399.             ulUIParam, 
  1400.             lpProgress, 
  1401.             ulFlags));
  1402.  
  1403.     IFLD_EnterCriticalSection(pifld);
  1404.  
  1405.     pifldDst = (PIFLD) lpfolderDst;
  1406.     pims = pifld->pims;
  1407.  
  1408.     /* if the folders aren't in the same store, call back to MAPI */
  1409.  
  1410.     if (!FFoldInSameStore(pifld, pifldDst))
  1411.     {
  1412.         /* leave critical section before calling back to MAPI */
  1413.         IFLD_LeaveCriticalSection(pifld);
  1414.  
  1415.         return pims->psup->lpVtbl->CopyMessages(pims->psup,
  1416.             (LPIID) &IID_IMAPIFolder, (LPVOID) pifld, lpMsgList, piid,
  1417.             (LPVOID) lpfolderDst, ulUIParam, lpProgress, ulFlags);
  1418.     }
  1419.  
  1420.     fMove   = !!(ulFlags & MESSAGE_MOVE);
  1421.  
  1422.     if (    !OBJ_TestFlag(pifldDst, OBJF_MODIFY)
  1423.         ||  (   fMove
  1424.             &&  !OBJ_TestFlag(pifld, OBJF_MODIFY)))
  1425.     {
  1426.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1427.         goto exit;
  1428.     }
  1429.  
  1430.     if (fMove)
  1431.     {
  1432.         /* If moving messages and the source and destination are the same */
  1433.         /* folder, we are done even before we begin. */
  1434.  
  1435.         ULONG ulTheSame;
  1436.  
  1437.         hr = pims->lpVtbl->CompareEntryIDs(pims,
  1438.                 CbEID(pifldDst->peid), (LPENTRYID) pifldDst->peid,
  1439.                 CbEID(pifld->peid), (LPENTRYID) pifld->peid, 0L, &ulTheSame);
  1440.         if (hr != hrSuccess)
  1441.             goto exit;
  1442.  
  1443.         if (ulTheSame)
  1444.         {
  1445.             hr = hrSuccess;
  1446.             goto exit;
  1447.         }
  1448.     }
  1449.  
  1450.     /* get storage for the path names */
  1451.     cbStorePath = Cbtszsize(pims->szStorePath);
  1452.     cbSrcPath = cbStorePath + CbEIDPath(pifld->peid);
  1453.     if (FIsRoot(pifld->peid))
  1454.         cbSrcPath--;
  1455.  
  1456.     cbDestPath = cbStorePath + CbEIDPath(pifldDst->peid);
  1457.     if (FIsRoot(pifldDst->peid))
  1458.         cbDestPath--;
  1459.  
  1460.     hr = HrFullPathName(pims->szStorePath, pifld->peid->szPath,
  1461.         szBlanks, &szSrcName);
  1462.     if (hr != hrSuccess)
  1463.         goto exit;
  1464.  
  1465.     hr = HrFullPathName(pims->szStorePath, pifldDst->peid->szPath,
  1466.         szBlanks, &szDestName);
  1467.     if (hr != hrSuccess)
  1468.         goto exit;
  1469.  
  1470.     psb = lpMsgList->lpbin;
  1471.     psbMax = psb + lpMsgList->cValues;
  1472.  
  1473.     /* move/copy each message */
  1474.     while (psb < psbMax)
  1475.     {
  1476.         ULONG ulMF;
  1477.         ULONG ulObjType;
  1478.         BOOL fLocked;
  1479.         BOOL fRead;
  1480.         PIMSG pimsg;
  1481.         LPSTR szRelativePath;
  1482.         PEID peidNewID;
  1483.         PEID peidToCopy;
  1484.         ULONG cbEntryID;
  1485.  
  1486.         cbEntryID = psb->cb;
  1487.         peidToCopy = (PEID) (psb->lpb);
  1488.  
  1489.         if (    FIsInvalidEID(cbEntryID, peidToCopy, pims)
  1490.             ||  !FIsMessage((PEID) peidToCopy))
  1491.         {
  1492.             hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1493.             goto exit;
  1494.         }
  1495.  
  1496.         /* open the message */
  1497.         hr = pifld->lpVtbl->OpenEntry(pifld, CbEID(peidToCopy),
  1498.                 (LPENTRYID) peidToCopy, NULL, 0L, &ulObjType,
  1499.                 (LPUNKNOWN *) &pimsg);
  1500.         if (hr != hrSuccess)
  1501.             goto exit;
  1502.  
  1503.         Assert(ulObjType == MAPI_MESSAGE);
  1504.  
  1505.         /* retrieve the PR_MESSAGE_FLAGS property */
  1506.         hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, &pims->lmr,
  1507.                 PR_MESSAGE_FLAGS, &ulMF);
  1508.  
  1509.         UlRelease(pimsg);
  1510.  
  1511.         if (hr != hrSuccess)
  1512.             goto exit;
  1513.  
  1514.         fLocked = !!(ulMF & MSGFLAG_SUBMIT);
  1515.         fRead = !!(ulMF & MSGFLAG_READ);
  1516.  
  1517.         /* if the message is locked then this is an error */
  1518.         if (fLocked)
  1519.         {
  1520.             hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1521.             goto exit;
  1522.         }
  1523.  
  1524.         /* store in szSrcName the root relative path of the message to be moved*/
  1525.         lstrcpy(szSrcName + cbSrcPath, SzBaseName(peidToCopy));
  1526.  
  1527.         /* store in szDestName the store relative path name of */
  1528.         /* destination file */
  1529.         if (fMove)
  1530.             lstrcpy(szDestName + cbDestPath, SzBaseName(peidToCopy));
  1531.         else
  1532.         {
  1533.             /* If we're copying, we need to generate a new filename for the */
  1534.             /* message, or else we will have two messages with the same */
  1535.             /* filename, and copying again (or moving back) will fail. */
  1536.  
  1537.             PEID peidTemp = NULL;
  1538.  
  1539.             hr = HrNewEID(pifld, pims, MESSAGE_EXT, NULL, &peidTemp);
  1540.             if (hr != hrSuccess)
  1541.                 goto exit;
  1542.  
  1543.             lstrcpy(szDestName + cbDestPath, SzBaseName(peidTemp));
  1544.  
  1545.             LMFree(&pims->lmr, peidTemp);
  1546.         }
  1547.  
  1548.         /* either move or copy the message */
  1549.         if (fMove)
  1550.         {
  1551.             if (!MoveFile(szSrcName, szDestName))
  1552.             {
  1553.                 TraceSz1("MoveFile failed: OS error %08lX.", GetLastError());
  1554.                 hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1555.                 goto exit;
  1556.             }
  1557.  
  1558.             /* remove this message from contents tables */
  1559.  
  1560.             (void) HrIncrementOneROProp(pifld, -1, PR_CONTENT_COUNT);
  1561.  
  1562.             if (!fRead)
  1563.                 (void) HrIncrementOneROProp(pifld, -1, PR_CONTENT_UNREAD);
  1564.  
  1565.             ChangeTable(pims, pifld->peid, peidToCopy, MAPI_MESSAGE,
  1566.                 TABLE_ROW_DELETED, TRUE);
  1567.         }
  1568.         else
  1569.         {
  1570.             if (!CopyFile(szSrcName, szDestName, TRUE))
  1571.             {
  1572.                 TraceSz1("CopyFile failed: OS error %08lX.", GetLastError());
  1573.                 hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1574.                 goto exit;
  1575.             }
  1576.         }
  1577.  
  1578.         /* get new entryid for this message */
  1579.         hr = HrFullToRelative(szDestName, pims, &szRelativePath);
  1580.         if (hr != hrSuccess)
  1581.             goto exit;
  1582.  
  1583.         hr = HrConstructEID(&(peidToCopy->uidResource), &pims->lmr,
  1584.                 szRelativePath, &peidNewID);
  1585.  
  1586.         FreeNull(szRelativePath);
  1587.  
  1588.         if (hr != hrSuccess)
  1589.             goto exit;
  1590.  
  1591.         /* If we're copying, then we just used a new name for the file. */
  1592.         /* Note that PR_RECORD_KEY, PR_ENTRYID, PR_INSTANCE_KEY, and */
  1593.         /* PR_PARENT_ENTRYID are now different, but since these are stored */
  1594.         /* in the opened msg, everything should be fine. */
  1595.  
  1596.         /* add a row to the contents table of the destination folder */
  1597.         (void) HrIncrementOneROProp(pifldDst, 1, PR_CONTENT_COUNT);
  1598.  
  1599.         if (!fRead)
  1600.             (void) HrIncrementOneROProp(pifldDst, 1, PR_CONTENT_UNREAD);
  1601.  
  1602.         ChangeTable(pims, pifldDst->peid, peidNewID, MAPI_MESSAGE,
  1603.             TABLE_ROW_ADDED, TRUE);
  1604.  
  1605.         LMFree(&pims->lmr, peidNewID);
  1606.  
  1607.         cHandled++;
  1608.         psb++;
  1609.     }
  1610.  
  1611. exit:
  1612.     FreeNull(szDestName);
  1613.     FreeNull(szSrcName);
  1614.  
  1615.     IFLD_LeaveCriticalSection(pifld);
  1616.  
  1617.     DebugTraceResult(IFLD_CopyMessages, hr);
  1618.     return HrCheckHr(hr, IMAPIFolder_CopyMessages);
  1619. }
  1620.  
  1621. /***************************************************************************
  1622.  -  IFLD_DeleteMessages
  1623.  -
  1624.  *  Purpose:
  1625.  *      Deletes from the folder pifld all messages in the list lpMsgList
  1626.  *
  1627.  *  Parameters
  1628.  *      pifld           the folder from which messages are to be deleted
  1629.  *      lpMsgList       the list of messages to be deleted
  1630.  *      ulUIParam       handle to main window, cast to ULONG
  1631.  *      lpProgress      points to progress dialog information
  1632.  *      ulFlags         MESSAGE_DIALOG
  1633.  *
  1634.  *  Returns:
  1635.  *
  1636.  *  Side effects:
  1637.  *
  1638.  *  Errors:
  1639.  */
  1640. STDMETHODIMP IFLD_DeleteMessages(PIFLD pifld, LPENTRYLIST lpMsgList,
  1641.     ULONG ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  1642. {
  1643.     PEID peidToDelete = NULL;   /* pointer to next eid to be deleted */
  1644.     LONG cDeleted = 0;          /* number messages deleted */
  1645.     LONG cUnreadDeleted = 0;    /* number of unread messages deleted */
  1646.     LPTSTR szToDeleteName = NULL;   /* name of the next message to be deleted */
  1647.     HRESULT hr = hrSuccess;
  1648.     LPTSTR szBlankName = TEXT("        .   ");
  1649.     ULONG cchPath;              /* length of path to message */
  1650.     BOOL fIsRead;               /* TRUE if deleting a read message */
  1651.     DWORD dwErr;
  1652.  
  1653.     FLD_ValidateParameters(
  1654.             pifld, 
  1655.             IMAPIFolder,
  1656.             DeleteMessages,
  1657.             (pifld, 
  1658.             lpMsgList,
  1659.             ulUIParam, 
  1660.             lpProgress, 
  1661.             ulFlags));
  1662.  
  1663.     IFLD_EnterCriticalSection(pifld);
  1664.  
  1665.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  1666.     {
  1667.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1668.         goto exit;
  1669.     }
  1670.  
  1671.     hr = HrFullPathName(pifld->pims->szStorePath, pifld->peid->szPath,
  1672.         szBlankName, &szToDeleteName);
  1673.     if (hr != hrSuccess)
  1674.         goto exit;
  1675.  
  1676.     cchPath = lstrlen(szToDeleteName) - lstrlen(szBlankName);
  1677.  
  1678.     /* delete each message in lpMsgList up until the first error */
  1679.     while ((ULONG) cDeleted < lpMsgList->cValues)
  1680.     {
  1681.         peidToDelete = (PEID) (lpMsgList->lpbin[cDeleted].lpb);
  1682.  
  1683.         /* make sure that this is a message's eid */
  1684.         if (!FIsMessage(peidToDelete))
  1685.         {
  1686.             hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1687.             goto exit;
  1688.         }
  1689.  
  1690.         /* make sure the message is not submitted */
  1691.         if (FIsSubmittedMessage(pifld->pims, peidToDelete))
  1692.         {
  1693.             hr = ResultFromScode(MAPI_E_SUBMITTED);
  1694.             goto exit;
  1695.         }
  1696.  
  1697.         /* See if it has been read */
  1698.         hr = HrIsRead(pifld, peidToDelete, &fIsRead);
  1699.         if (hr != hrSuccess)
  1700.             goto exit;
  1701.  
  1702.         /* remove the message from disk */
  1703.         lstrcpy(szToDeleteName + cchPath, SzBaseName(peidToDelete));
  1704.         if (!DeleteFile(szToDeleteName))
  1705.         {
  1706.             dwErr = GetLastError();
  1707.  
  1708.             if ((dwErr != ERROR_FILE_NOT_FOUND && dwErr != ERROR_PATH_NOT_FOUND)
  1709.                 || lpMsgList->cValues == 1)
  1710.             {
  1711.                 hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1712.                 goto exit;
  1713.             }
  1714.         }
  1715.  
  1716.         /* remove this message from any open tables */
  1717.         ChangeTable(pifld->pims, pifld->peid, peidToDelete, MAPI_MESSAGE,
  1718.             TABLE_ROW_DELETED, TRUE);
  1719.  
  1720.         if (!fIsRead)
  1721.             cUnreadDeleted++;
  1722.  
  1723.         cDeleted++;
  1724.     }
  1725.  
  1726. exit:
  1727.  
  1728.     FreeNull(szToDeleteName);
  1729.  
  1730.     if (cDeleted)
  1731.         (void) HrIncrementOneROProp(pifld, -cDeleted, PR_CONTENT_COUNT);
  1732.     if (cUnreadDeleted)
  1733.         (void) HrIncrementOneROProp(pifld, -cUnreadDeleted, PR_CONTENT_UNREAD);
  1734.  
  1735.     IFLD_LeaveCriticalSection(pifld);
  1736.  
  1737.     DebugTraceResult(IFLD_DeleteMessages, hr);
  1738.     return HrCheckHr(hr, IMAPIFolder_DeleteMessages);
  1739. }
  1740.  
  1741. /***************************************************************************
  1742.  -  IFLD_CreateFolder
  1743.  -
  1744.  *  Purpose:
  1745.  *      Create a new folder within the message store
  1746.  *
  1747.  *  Parameters
  1748.  *      pifld               the parent folder of the newly created folder
  1749.  *      ulFldType           type of folder to be created
  1750.  *      szFldName           name of the new folder
  1751.  *      szComment           comment string for the new folder
  1752.  *      piid                Reserved; must be NULL.
  1753.  *      ulFlags             MAPI_UNICODE and/or MAPI_DEFERRED_ERRORS
  1754.  *      lppfolder           pointer to variable to receive new folder object
  1755.  *
  1756.  */
  1757. STDMETHODIMP IFLD_CreateFolder(PIFLD pifld, ULONG ulFldType, LPSTR szFldName,
  1758.     LPSTR szComment, LPCIID piid, ULONG ulFlags, LPMAPIFOLDER *lppfolder)
  1759. {
  1760.     HRESULT hr;
  1761.     PIFLD pifldNew = NULL;
  1762.  
  1763.     FLD_ValidateParameters(
  1764.             pifld, 
  1765.             IMAPIFolder,
  1766.             CreateFolder,
  1767.             (pifld, 
  1768.             ulFldType, 
  1769.             szFldName, 
  1770.             szComment, 
  1771.             piid, 
  1772.             ulFlags, 
  1773.             lppfolder));
  1774.  
  1775.     #ifdef VALIDATE
  1776.     if (ulFlags & MAPI_UNICODE)
  1777.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1778.  
  1779.     if (piid && !FQueryInterface(OT_FOLDER, piid))
  1780.         return ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
  1781.     #endif
  1782.  
  1783.     /* Sample store can only create generic folders. */
  1784.     if (ulFldType != FOLDER_GENERIC)
  1785.         return ResultFromScode(MAPI_E_NO_SUPPORT);
  1786.  
  1787.     IFLD_EnterCriticalSection(pifld);
  1788.  
  1789.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  1790.     {
  1791.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1792.         goto exit;
  1793.     }
  1794.  
  1795.     hr = HrCreateFolder(pifld, szFldName, szComment,
  1796.         !!(ulFlags & OPEN_IF_EXISTS), &pifldNew, NULL);
  1797.  
  1798.     if (hr != hrSuccess)
  1799.         UlRelease(pifldNew);
  1800.     else
  1801.         *lppfolder = (LPMAPIFOLDER) pifldNew;
  1802.  
  1803. exit:
  1804.     IFLD_LeaveCriticalSection(pifld);
  1805.  
  1806.     DebugTraceResult(IFLD_CreateFolder, hr);
  1807.     return HrCheckHr(hr, IMAPIFolder_CreateFolder);
  1808. }
  1809.  
  1810. /***************************************************************************
  1811.  -  IFLD_CopyFolder
  1812.  -
  1813.  *  Purpose:
  1814.  *      Copy my child folder to the specified parent
  1815.  *
  1816.  *  Parameters
  1817.  *      pifld           the parent folder of the newly created folder
  1818.  *      cbEntryID       length of entry ID of folder to copy
  1819.  *      lpEntryID       entry ID of folder to copy
  1820.  *      lpiid           interface to the destination folder
  1821.  *      lpfolder        pointer to destination object
  1822.  *      szNewName       new name to give the copied folder, may be NULL
  1823.  *      ulUIParam       Handle of parent window,
  1824.  *                      ignored if MESSAGE_DIALOG not set
  1825.  *      lpProgress      points to progress dialog information
  1826.  *      ulFlags         control flags
  1827.  *
  1828.  */
  1829. STDMETHODIMP IFLD_CopyFolder(PIFLD pifld, ULONG cbEntryID, LPENTRYID lpEntryID,
  1830.     LPCIID piid, LPVOID lpfolder, LPSTR szNewName,
  1831.     ULONG ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  1832. {
  1833.     HRESULT hr;
  1834.  
  1835.     FLD_ValidateParameters(
  1836.             pifld, 
  1837.             IMAPIFolder,
  1838.             CopyFolder,
  1839.             (pifld, 
  1840.             cbEntryID, 
  1841.             lpEntryID,
  1842.             piid, 
  1843.             lpfolder, 
  1844.             szNewName, 
  1845.             ulUIParam, 
  1846.             lpProgress, 
  1847.             ulFlags));
  1848.  
  1849.     #ifdef VALIDATE
  1850.     if (ulFlags & MAPI_UNICODE)
  1851.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1852.     #endif
  1853.  
  1854.     /* For now, all we do is call back to mapi. */
  1855.  
  1856.     hr = pifld->pims->psup->lpVtbl->CopyFolder(pifld->pims->psup,
  1857.         (LPIID) &IID_IMAPIFolder, (LPVOID) pifld, cbEntryID, lpEntryID,
  1858.         piid, lpfolder, szNewName, ulUIParam, lpProgress, ulFlags);
  1859.  
  1860.     DebugTraceResult(IFLD_CopyFolder, hr);
  1861.     return HrCheckHr(hr, IMAPIFolder_CopyFolder);
  1862. }
  1863.  
  1864. /***************************************************************************
  1865.  -  IFLD_DeleteFolder
  1866.  -
  1867.  *  Purpose:
  1868.  *      Deletes a subfolder
  1869.  *
  1870.  *  Parameters
  1871.  *      pifld               the current folder
  1872.  *      cbEntryID           byte count of lpEntryID
  1873.  *      lpEntryID           entryID of subfolder to be deleted
  1874.  *      ulUIParam           handle to main window cast to ULONG
  1875.  *      lpProgress          pointer to progess dialog information
  1876.  *      ulFlags             DEL_MESSAGES and/or DEL_FOLDERS
  1877.  *                                  and/or FOLDER_DIALOG
  1878.  *
  1879.  *  Returns:
  1880.  *
  1881.  *  Errors:
  1882.  */
  1883. STDMETHODIMP IFLD_DeleteFolder(PIFLD pifld, ULONG cbEntryID,
  1884.     LPENTRYID lpEntryID, ULONG ulUIParam, LPMAPIPROGRESS lpProgress,
  1885.     ULONG ulFlags)
  1886. {
  1887.     HRESULT hr = hrSuccess;
  1888.  
  1889.     FLD_ValidateParameters(
  1890.             pifld, 
  1891.             IMAPIFolder,
  1892.             DeleteFolder,
  1893.             (pifld, 
  1894.             cbEntryID,
  1895.             lpEntryID, 
  1896.             ulUIParam, 
  1897.             lpProgress, 
  1898.             ulFlags));
  1899.  
  1900.     IFLD_EnterCriticalSection(pifld);
  1901.  
  1902.     if (FIsInvalidEID(cbEntryID, (PEID) lpEntryID, pifld->pims)
  1903.         || !FIsFolder((PEID) lpEntryID))
  1904.     {
  1905.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1906.         goto exit;
  1907.     }
  1908.  
  1909.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  1910.     {
  1911.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1912.         goto exit;
  1913.     }
  1914.  
  1915.     /* delete this subdirectory of the current directory */
  1916.     hr = HrDeleteSubDirectory(pifld, (PEID) lpEntryID, ulFlags, FALSE);
  1917.  
  1918. exit:
  1919.     IFLD_LeaveCriticalSection(pifld);
  1920.  
  1921.     DebugTraceResult(IFLD_DeleteFolder, hr);
  1922.     return HrCheckHr(hr, IMAPIFolder_DeleteFolder);
  1923. }
  1924.  
  1925. STDMETHODIMP IFLD_SetSearchCriteria(PIFLD pifld, LPSRestriction lpRestriction,
  1926.     LPENTRYLIST lpentrylist, ULONG ulSearchFlags)
  1927. {
  1928.     DebugTraceSc(IFLD_SetSearchCriteria, MAPI_E_NO_SUPPORT);
  1929.     return ResultFromScode(MAPI_E_NO_SUPPORT);
  1930. }
  1931.  
  1932. STDMETHODIMP IFLD_GetSearchCriteria(PIFLD pifld, ULONG ulFlags,
  1933.     LPSRestriction *lppRestriction, LPENTRYLIST *lppfolderList, 
  1934.     ULONG *lpulSearchState)
  1935. {
  1936.     DebugTraceSc(IFLD_GetSearchCriteria, MAPI_E_NO_SUPPORT);
  1937.     return ResultFromScode(MAPI_E_NO_SUPPORT);
  1938. }
  1939.  
  1940. /***************************************************************************
  1941.  -  IFLD_SetReadFlags
  1942.  -
  1943.  *  Purpose:
  1944.  *      Sets the READ flag for each of the given messages.
  1945.  *
  1946.  *  Parameters
  1947.  *      pifld                   the source folder
  1948.  *      lpMsgList               the list of messages to be moved/copied
  1949.  *      ulUIParam               Handle of parent window,
  1950.  *                                  ignored if MESSAGE_DIALOG not set
  1951.  *      lpProgress              points to progress dialog object
  1952.  *      ulFlags                 SUPPRESS_RECEIPT, CLEAR_READ_FLAG
  1953.  */
  1954. STDMETHODIMP IFLD_SetReadFlags(PIFLD pifld, LPENTRYLIST lpMsgList,
  1955.     ULONG ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  1956. {
  1957.     HRESULT hr = hrSuccess;
  1958.     LPENTRYLIST pelst = NULL;
  1959.     BOOL fFreeList = FALSE;
  1960.     SBinary *psb;
  1961.     SBinary *psbMax;
  1962.     BOOL fMultipleNotifs = TRUE;
  1963.  
  1964.     FLD_ValidateParameters(
  1965.             pifld, 
  1966.             IMAPIFolder,
  1967.             SetReadFlags,
  1968.             (pifld, 
  1969.             lpMsgList, 
  1970.             ulUIParam, 
  1971.             lpProgress, 
  1972.             ulFlags));
  1973.  
  1974.     IFLD_EnterCriticalSection(pifld);
  1975.  
  1976.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  1977.     {
  1978.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1979.         goto exit;
  1980.     }
  1981.  
  1982.     pelst = lpMsgList;
  1983.  
  1984.     if (pelst == NULL)
  1985.     {
  1986.         /* Create the message list if the caller didn't give us one. */
  1987.  
  1988.         hr = HrCreateMessageList(pifld, &pelst);
  1989.         if (hr != hrSuccess)
  1990.             goto exit;
  1991.  
  1992.         fFreeList = TRUE;
  1993.     }
  1994.  
  1995.     if (pelst->cValues > 5)
  1996.         fMultipleNotifs = FALSE;
  1997.  
  1998.     psb = pelst->lpbin;
  1999.     psbMax = psb + pelst->cValues;
  2000.  
  2001.     /* Call IMessage::SetReadFlag on each message */
  2002.  
  2003.     while (psb < psbMax)
  2004.     {
  2005.         ULONG ulObjType;
  2006.         PIMSG pimsg;
  2007.         PEID peid;
  2008.         ULONG cbEntryID;
  2009.  
  2010.         cbEntryID = psb->cb;
  2011.         peid = (PEID) (psb->lpb);
  2012.  
  2013.         if (    FIsInvalidEID(cbEntryID, peid, pifld->pims)
  2014.             ||  !FIsMessage((PEID) peid))
  2015.         {
  2016.             hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
  2017.             goto exit;
  2018.         }
  2019.  
  2020.         /* open the message */
  2021.         hr = pifld->lpVtbl->OpenEntry(pifld, CbEID(peid),
  2022.                 (LPENTRYID) peid, NULL, 0L, &ulObjType,
  2023.                 (LPUNKNOWN *) &pimsg);
  2024.         if (hr != hrSuccess)
  2025.             goto exit;
  2026.  
  2027.         Assert(ulObjType == MAPI_MESSAGE);
  2028.  
  2029.         hr = pimsg->lpVtbl->SetReadFlag(pimsg, ulFlags);
  2030.  
  2031.         if (hr == hrSuccess && fMultipleNotifs)
  2032.             ChangeTable(pimsg->pims, pifld->peid, pimsg->peid, MAPI_MESSAGE,
  2033.                 TABLE_ROW_MODIFIED, TRUE);
  2034.  
  2035.         UlRelease(pimsg);
  2036.  
  2037.         if (hr != hrSuccess)
  2038.             goto exit;
  2039.  
  2040.         psb++;
  2041.     }
  2042.  
  2043.     /* If several messages changed, then just send one notification to */
  2044.     /* re-sync the entire contents table on other processes. */
  2045.  
  2046.     if (!fMultipleNotifs)
  2047.         ChangeTable(pifld->pims, pifld->peid, NULL, MAPI_MESSAGE,
  2048.             TABLE_CHANGED, TRUE);
  2049.  
  2050. exit:
  2051.     if (fFreeList)
  2052.         DestroyMessageList(&pifld->pims->lmr, &pelst);
  2053.  
  2054.     IFLD_LeaveCriticalSection(pifld);
  2055.  
  2056.     DebugTraceResult(IFLD_SetReadFlags, hr);
  2057.     return HrCheckHr(hr, IMAPIFolder_SetReadFlags);
  2058. }
  2059.  
  2060. /***************************************************************************
  2061.  -  GetMessageStatus
  2062.  -
  2063.  *  Purpose:
  2064.  *      Retrieves the status associated with a message in a folder
  2065.  *
  2066.  *  Parameters
  2067.  *      pifld               the current folder
  2068.  *      cbEntryID           byte count of lpEntryID
  2069.  *      lpEntryID           entryID of the message
  2070.  *      ulFlags             reserved for future use, must be 0
  2071.  *      lpulMessageStatus   pointer to variable to receive the status
  2072.  *
  2073.  */
  2074. STDMETHODIMP IFLD_GetMessageStatus(PIFLD pifld, ULONG cbEntryID,
  2075.     LPENTRYID lpEntryID, ULONG ulFlags, ULONG *lpulMessageStatus)
  2076. {
  2077.     LPMESSAGE lpmsgMsg = NULL;  /* lpEntryID as an open message */
  2078.     ULONG ulObjType;            /* type of object opened */
  2079.     HRESULT hr = hrSuccess;
  2080.  
  2081.     FLD_ValidateParameters(
  2082.             pifld, 
  2083.             IMAPIFolder,
  2084.             GetMessageStatus,
  2085.             (pifld, 
  2086.             cbEntryID, 
  2087.             lpEntryID, 
  2088.             ulFlags, 
  2089.             lpulMessageStatus));
  2090.  
  2091.     IFLD_EnterCriticalSection(pifld);
  2092.  
  2093.     if (FIsInvalidEID(cbEntryID, (PEID) lpEntryID, pifld->pims)
  2094.         || !FIsMessage((PEID) lpEntryID))
  2095.     {
  2096.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  2097.         goto exit;
  2098.     }
  2099.  
  2100.     /* open up lpEntryID */
  2101.     hr = pifld->lpVtbl->OpenEntry(pifld, cbEntryID,
  2102.         lpEntryID, NULL, 0L, &ulObjType, (LPUNKNOWN *) &lpmsgMsg);
  2103.     if (hr != hrSuccess)
  2104.         goto exit;
  2105.  
  2106.     /* get the property */
  2107.     hr = HrGetSingleProp((LPMAPIPROP) lpmsgMsg, &pifld->pims->lmr,
  2108.         PR_MSG_STATUS, lpulMessageStatus);
  2109.  
  2110.     if (hr != hrSuccess)
  2111.         goto exit;
  2112.  
  2113. exit:
  2114.     UlRelease(lpmsgMsg);
  2115.  
  2116.     IFLD_LeaveCriticalSection(pifld);
  2117.  
  2118.     DebugTraceResult(IFLD_GetMessageStatus, hr);
  2119.     return HrCheckHr(hr, IMAPIFolder_GetMessageStatus);
  2120. }
  2121.  
  2122. /***************************************************************************
  2123.  -  SetMessageStatus
  2124.  -
  2125.  *  Purpose:
  2126.  *      Sets the 32-bit status associated with a message in a folder
  2127.  *
  2128.  *  Parameters
  2129.  *      pifld               the current folder
  2130.  *      cbEntryID           byte count of lpentryID
  2131.  *      lpEntryID           entryID of the message
  2132.  *      ulNewStatus         new status to be assigned
  2133.  *      ulNewStatusMask     mask applied to new status indicating bits to set
  2134.  *      lpulOldStatus       pointer to variable to hold previous status
  2135.  *
  2136.  */
  2137. STDMETHODIMP IFLD_SetMessageStatus(PIFLD pifld, ULONG cbEntryID,
  2138.     LPENTRYID lpEntryID, ULONG ulNewStatus, ULONG ulNewStatusMask,
  2139.     ULONG *lpulOldStatus)
  2140. {
  2141.     PIMSG pimsg = NULL;
  2142.     LPMESSAGE lpmsg;
  2143.     ULONG ulObjType;            /* type of object just opened */
  2144.     HRESULT hr = hrSuccess;
  2145.     ULONG ulStatus;
  2146.  
  2147.     FLD_ValidateParameters(
  2148.             pifld, 
  2149.             IMAPIFolder,
  2150.             SetMessageStatus,
  2151.             (pifld, 
  2152.             cbEntryID, 
  2153.             lpEntryID, 
  2154.             ulNewStatus, 
  2155.             ulNewStatusMask, 
  2156.             lpulOldStatus));
  2157.  
  2158.     IFLD_EnterCriticalSection(pifld);
  2159.  
  2160.     if (FIsInvalidEID(cbEntryID, (PEID) lpEntryID, pifld->pims) ||
  2161.         !FIsMessage((PEID) lpEntryID))
  2162.     {
  2163.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  2164.         goto exit;
  2165.     }
  2166.  
  2167.     if (    ulNewStatusMask
  2168.         &&  !OBJ_TestFlag(pifld, OBJF_MODIFY))
  2169.     {
  2170.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  2171.         goto exit;
  2172.     }
  2173.  
  2174.     /* open up lpEntryID */
  2175.     hr = pifld->lpVtbl->OpenEntry(pifld, CbEID((PEID) lpEntryID),
  2176.         lpEntryID, NULL, 0L, &ulObjType, (LPUNKNOWN *) &pimsg);
  2177.     if (hr != hrSuccess)
  2178.         goto exit;
  2179.  
  2180.     lpmsg = pimsg->lpmsg;
  2181.  
  2182.     /* get the old status */
  2183.     hr = HrGetSingleProp((LPMAPIPROP) lpmsg, &pifld->pims->lmr,
  2184.         PR_MSG_STATUS, &ulStatus);
  2185.     if (hr != hrSuccess)
  2186.         goto exit;
  2187.  
  2188.     if (lpulOldStatus)
  2189.         *lpulOldStatus = ulStatus;
  2190.  
  2191.     if (ulNewStatusMask)
  2192.     {
  2193.         ulNewStatus &= ulNewStatusMask;
  2194.         ulStatus &= ~ulNewStatusMask;
  2195.         ulStatus |= ulNewStatus;
  2196.  
  2197.         hr = HrSetOneROProp(lpmsg, &pifld->pims->lmr, PR_MSG_STATUS,
  2198.             &ulStatus);
  2199.         if (hr != hrSuccess)
  2200.             goto exit;
  2201.  
  2202.         hr = lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  2203.  
  2204.         UlRelease(pimsg);
  2205.         pimsg = NULL;
  2206.  
  2207.         if (hr != hrSuccess)
  2208.             goto exit;
  2209.  
  2210.         ChangeTable(pifld->pims, pifld->peid, (PEID) lpEntryID, MAPI_MESSAGE,
  2211.             TABLE_ROW_MODIFIED, TRUE);
  2212.     }
  2213.  
  2214. exit:
  2215.     UlRelease(pimsg);
  2216.  
  2217.     IFLD_LeaveCriticalSection(pifld);
  2218.  
  2219.     DebugTraceResult(IFLD_SetMessageStatus, hr);
  2220.     return HrCheckHr(hr, IMAPIFolder_SetMessageStatus);
  2221. }
  2222.  
  2223. /***************************************************************************
  2224.  -  IFLD_SaveContentsSort
  2225.  -
  2226.  *  Purpose:
  2227.  *      set default sort order for contents tables on pifld
  2228.  *
  2229.  *  Parameters
  2230.  *      pifld               the current folder
  2231.  *      lpSortCriteria      points to the sort criteria
  2232.  *      ulFlags             RECURSIVE_SORT
  2233.  *
  2234.  */
  2235. STDMETHODIMP IFLD_SaveContentsSort(PIFLD pifld, LPSSortOrderSet lpSortCriteria,
  2236.     ULONG ulFlags)
  2237. {
  2238.     SPropValue pval;            /* sort order property value */
  2239.     LPSPropProblemArray pprba = NULL;
  2240.     HRESULT hr = hrSuccess;
  2241.  
  2242.     FLD_ValidateParameters(
  2243.             pifld, 
  2244.             IMAPIFolder,
  2245.             SaveContentsSort,
  2246.             (pifld, 
  2247.             lpSortCriteria,
  2248.             ulFlags));
  2249.  
  2250.     IFLD_EnterCriticalSection(pifld);
  2251.  
  2252.     pval.Value.MVl.lpl = NULL;
  2253.  
  2254.     /* store the sort order in pval */
  2255.     pval.ulPropTag = PR_SMS_CONTENTS_SORT_ORDER;
  2256.     pval.Value.MVl.cValues = 2 * lpSortCriteria->cSorts;
  2257.     pval.Value.MVl.lpl = (LONG *) lpSortCriteria->aSort;
  2258.  
  2259.     /*  set the sort order property */
  2260.     hr = pifld->lpVtbl->SetProps(pifld, 1, &pval, &pprba);
  2261.  
  2262.     if (pprba)
  2263.     {
  2264.         LMFree(&pifld->pims->lmr, pprba);
  2265.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  2266.     }
  2267.  
  2268.     IFLD_LeaveCriticalSection(pifld);
  2269.  
  2270.     DebugTraceResult(IFLD_SaveContentsSort, hr);
  2271.     return HrCheckHr(hr, IMAPIFolder_SaveContentsSort);
  2272. }
  2273.  
  2274. /*
  2275.  *  IFLD_EmptyFolder
  2276.  *
  2277.  *  Purpose:
  2278.  *      This function empties a folder based on the flags given. It will never
  2279.  *      actually delete the folder itself, and will only empty the specified
  2280.  *      items.
  2281.  *
  2282.  *  Parameters
  2283.  *      pifld           The address of the folder object to empty.
  2284.  *      ulUIParam       Handle of the parent window. (for progress). NYI
  2285.  *      lpProgress      Address of progress dialog object
  2286.  *      ulFlags         Control flags. Setting DEL_ASSOCIATED causes FAI
  2287.  *                      to also be deleted. The FOLDER_DIALOG flag is NYI.
  2288.  *                      DEL_ASSOCIATED has no meaning to the sample store.
  2289.  *
  2290.  *  Returns:
  2291.  */
  2292. STDMETHODIMP IFLD_EmptyFolder(PIFLD pifld, ULONG ulUIParam,
  2293.     LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  2294. {
  2295.     HRESULT hr;
  2296.     PIFLD pifldParent = NULL;
  2297.  
  2298.     FLD_ValidateParameters(
  2299.             pifld, 
  2300.             IMAPIFolder,
  2301.             EmptyFolder,
  2302.             (pifld, 
  2303.             ulUIParam, 
  2304.             lpProgress, 
  2305.             ulFlags));
  2306.  
  2307.     IFLD_EnterCriticalSection(pifld);
  2308.  
  2309.     if (!OBJ_TestFlag(pifld, OBJF_MODIFY))
  2310.     {
  2311.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  2312.         goto exit;
  2313.     }
  2314.  
  2315.     /* Empty the folder. */
  2316.  
  2317.     hr = HrOpenParent(pifld->pims, pifld->peid, MAPI_MODIFY, &pifldParent);
  2318.     if (hr != hrSuccess)
  2319.         goto exit;
  2320.  
  2321.     hr = HrDeleteSubDirectory(pifldParent, pifld->peid,
  2322.         (DEL_MESSAGES | DEL_FOLDERS), TRUE);
  2323.     if (hr != hrSuccess)
  2324.         goto exit;
  2325.  
  2326. exit:
  2327.     UlRelease(pifldParent);
  2328.  
  2329.     IFLD_LeaveCriticalSection(pifld);
  2330.  
  2331.     DebugTraceResult(IFLD_EmptyFolder, hr);
  2332.     return HrCheckHr(hr, IMAPIFolder_EmptyFolder);
  2333. }
  2334.  
  2335. /****************************************************
  2336.  *          Private Functions                       *
  2337.  ****************************************************/
  2338.  
  2339. /*
  2340.  *  IFLD_IsInvalid
  2341.  *
  2342.  *  Purpose         check if pifld points to an invalid folder.
  2343.  *
  2344.  *  Parameter
  2345.  *      pifld       pointer to the folder
  2346.  *
  2347.  *  Returns: BOOL - TRUE means the folder is invalid.
  2348.  */
  2349.  
  2350. static BOOL IFLD_IsInvalid(PIFLD pifld)
  2351. {
  2352.     return (IsBadWritePtr(pifld, sizeof(IFLD)) ||
  2353.         pifld->lpVtbl != &vtblIFLD);
  2354. }
  2355.  
  2356. /* 
  2357.  * HrCreateContTblMutex
  2358.  *
  2359.  *  Purpose
  2360.  *      Create the contents table mutex, and return it to the caller.
  2361.  *
  2362.  *  Arguments
  2363.  *      phCTMutex: Pointer to the location to return the new mutex.
  2364.  *
  2365.  *  Returns:
  2366.  *      HRESULT: Will return an error only if the CreateMutex call fails.
  2367.  */
  2368. static HRESULT HrCreateContTblMutex(HANDLE *phCTMutex)
  2369. {
  2370.     HRESULT hr = hrSuccess;
  2371.     HANDLE hMutex;
  2372.     LPTSTR szMutexName = "SMS_CONTTBLFILE_MUTEX";
  2373.  
  2374.     hMutex = CreateMutex(NULL, FALSE, szMutexName);
  2375.  
  2376.     if (hMutex)
  2377.         *phCTMutex = hMutex;
  2378.  
  2379.     #ifndef WIN16
  2380.     else
  2381.     {
  2382.         TraceSz1("SampleMS: HrCreateContTblMutex: call to"
  2383.             " CreateMutex failed (error %08lX)", GetLastError());
  2384.         
  2385.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  2386.     }
  2387.     #endif
  2388.  
  2389.     DebugTraceResult(HrCreateContTblMutex, hr);
  2390.     return hr;
  2391. }
  2392.  
  2393. /*
  2394.  *  FFoldInSameStore
  2395.  *
  2396.  *  Purpose
  2397.  *      Given that the source folder is a folder object within
  2398.  *      our store (i.e. it's a PIFLD, not LPMAPIFOLDER), check if the
  2399.  *      destination folder object is also within the same store.
  2400.  *
  2401.  *  Parameters
  2402.  *      pifld: The folder that we already know is within this store.
  2403.  *      lpDestFld: The folder that we are checking. It may not be in this store.
  2404.  *
  2405.  *  Returns: BOOL - TRUE means the folders are both in this store.
  2406.  *
  2407.  */
  2408.  
  2409. static BOOL FFoldInSameStore(PIFLD pifld, PIFLD lpDestFld)
  2410. {
  2411.     if (IsBadWritePtr(lpDestFld, sizeof(IFLD)))
  2412.         return FALSE;
  2413.  
  2414.     if (lpDestFld->lpVtbl != &vtblIFLD)
  2415.         return FALSE;
  2416.  
  2417.     if (pifld->pims != lpDestFld->pims)
  2418.         return FALSE;
  2419.  
  2420.     return TRUE;
  2421. }
  2422.  
  2423. /***************************************************************************
  2424.  *  IFLD_Neuter
  2425.  *
  2426.  *  Purpose     Free all memory allocated inside a folder object, and release
  2427.  *              any objects held inside a folder object. Leaves the object
  2428.  *              itself alone (the caller will free it).
  2429.  *
  2430.  *  Argument    pifld       the folder to be cleaned out.
  2431.  *
  2432.  */
  2433. void IFLD_Neuter(PIFLD pifld)
  2434. {
  2435.     /* free allocated memory */
  2436.     FreeNull(pifld->peid);
  2437.  
  2438.     LMFree(&pifld->pims->lmr, pifld->pval);
  2439.  
  2440.     /* remove any tables */
  2441.     if (pifld->lptblContents)
  2442.     {
  2443.         TraceSz1("SampleMS (IFLD_Neuter): %d contents table views left",
  2444.             pifld->cContentsViews);
  2445.  
  2446.         UlRelease(pifld->lptblContents);
  2447.         pifld->cContentsViews = 0;
  2448.         pifld->lptblContents = NULL;
  2449.     }
  2450.  
  2451.     if (pifld->lptblHierarchy)
  2452.     {
  2453.         TraceSz1("SampleMS (IFLD_Neuter): %d hierarchy table views left",
  2454.             pifld->cHierarchyViews);
  2455.  
  2456.         UlRelease(pifld->lptblHierarchy);
  2457.         pifld->cHierarchyViews = 0;
  2458.         pifld->lptblHierarchy = NULL;
  2459.     }
  2460.  
  2461.     return;
  2462. }
  2463.  
  2464. /*
  2465.  -  ViewRelease
  2466.  -
  2467.  *  Purpose:
  2468.  *      Call back function from itable on release of a view
  2469.  *      removes the view from the list of open views
  2470.  *      releases the table if there are no more open views on it
  2471.  *
  2472.  *  Parameters
  2473.  *       ulCallerData   pointer to folder object
  2474.  *       lptbl          pointer to the table on which this is a view
  2475.  *       lpvtView       pointer to the view that was released
  2476.  *
  2477.  *
  2478.  */
  2479. STDAPI_(void)ViewRelease(ULONG ulCallerData, LPTABLEDATA lptbl,
  2480.     LPMAPITABLE lpvtView)
  2481. {
  2482.     PIFLD pifld;                /* folder object for this view */
  2483.     ULONG *pulViewsLeft;        /* ptr to number of open views left */
  2484.     LPTABLEDATA *pptbl;         /* ptr to folder's internal table data */
  2485.  
  2486.     pifld = (PIFLD) ulCallerData;
  2487.  
  2488.     /* do nothing if the folder is invalid. Don't use IFLD_IsInvalid */
  2489.     /* because the folder may have already been released, and we still */
  2490.     /* want to release the view in that case. */
  2491.     if (IsBadWritePtr(pifld, sizeof(IFLD)))
  2492.         return;
  2493.  
  2494.     IFLD_EnterCriticalSection(pifld);
  2495.  
  2496.     /* find the kind of table this view is on */
  2497.     if (pifld->lptblContents == lptbl)
  2498.     {
  2499.         pulViewsLeft = &(pifld->cContentsViews);
  2500.         pptbl = &(pifld->lptblContents);
  2501.     }
  2502.     else if (pifld->lptblHierarchy == lptbl)
  2503.     {
  2504.         pulViewsLeft = &(pifld->cHierarchyViews);
  2505.         pptbl = &(pifld->lptblHierarchy);
  2506.     }
  2507.     else
  2508.     {
  2509.         /* invalid table */
  2510.         TrapSz("Invalid call to ViewRelease");
  2511.         goto exit;
  2512.     }
  2513.  
  2514.     AssertSz(*pptbl == lptbl, "Different table data given to ViewRelease");
  2515.  
  2516.     /* decrement count of views and release*/
  2517.     /* the table data if the viewlist is empty */
  2518.     if (--(*pulViewsLeft) == 0)
  2519.     {
  2520.         POBJ pobj = (POBJ) pifld;
  2521.  
  2522.         UlRelease(lptbl);
  2523.         *pptbl = NULL;
  2524.  
  2525.         /* These tests are the same as IFLD_Release in this module. That is */
  2526.         /* the release method that folders use. */
  2527.  
  2528.         if (    pobj->cRef == 0
  2529.             &&  pobj->pobjHead == 0
  2530.             &&  pifld->lptblContents == NULL
  2531.             &&  pifld->lptblHierarchy == NULL)
  2532.         {
  2533.             OBJ_Destroy(pobj);  /* will leave the critical section */
  2534.             return;
  2535.         }
  2536.  
  2537.     }
  2538.  
  2539. exit:
  2540.     IFLD_LeaveCriticalSection(pifld);
  2541.  
  2542.     return;
  2543. }
  2544.  
  2545. /*
  2546.  *  HrIsRead
  2547.  *
  2548.  *  Purpose
  2549.  *      Given a parent folder and the entryid of a message, determine
  2550.  *      whether the message has been read (i.e., whether the MSGFLAG_READ
  2551.  *      bit inside the PR_MESSAGE_FLAGS property is set).
  2552.  *
  2553.  *  Parameters
  2554.  *      pifld       A pointer to the parent folder object of the message.
  2555.  *      peidMsg     The entryid of the message.
  2556.  *      pfRead      A pointer to the location to return a BOOL saying
  2557.  *                  whether or not the message was read. Set to TRUE
  2558.  *                  if the message was read, FALSE otherwise.
  2559.  */
  2560. static HRESULT HrIsRead(PIFLD pifld, PEID peidMsg, BOOL *pfRead)
  2561. {
  2562.     HRESULT hr;
  2563.     PIMSG pimsg = NULL;         /* opened version of lpidMsg */
  2564.     ULONG ulObjType;            /* type of object opened */
  2565.     ULONG ulMF;
  2566.  
  2567.     /* open the message */
  2568.     hr = pifld->lpVtbl->OpenEntry(pifld, CbEID(peidMsg), (LPENTRYID) peidMsg,
  2569.         NULL, 0L, &ulObjType, (LPUNKNOWN *) &pimsg);
  2570.     if (hr != hrSuccess)
  2571.         goto exit;
  2572.  
  2573.     Assert(ulObjType == MAPI_MESSAGE);
  2574.  
  2575.     /* retrieve the PR_MESSAGE_FLAGS property */
  2576.     hr = HrGetSingleProp((LPMAPIPROP) pimsg->lpmsg, &pifld->pims->lmr,
  2577.             PR_MESSAGE_FLAGS, &ulMF);
  2578.  
  2579.     /* set pfRead: no PR_MESSAGE_FLAGS means unread */
  2580.     /* The "!!" operator is used to change a bit that is set anywhere in a */
  2581.     /* word to exactly TRUE (only one bit set at the far right of the word). */
  2582.     /* The first "!" is NOT, which makes all zeros in the value into TRUE, */
  2583.     /* and anything else into FALSE. The second "!" reverses that back, so */
  2584.     /* that all zeros become FALSE, and anything else becomes TRUE. */
  2585.  
  2586.     if (hr == hrSuccess)
  2587.         *pfRead = !!(ulMF & MSGFLAG_READ);
  2588.     else if (GetScode(hr) == MAPI_E_NOT_FOUND)
  2589.     {
  2590.         *pfRead = FALSE;
  2591.         hr = hrSuccess;
  2592.     }
  2593.  
  2594.     UlRelease(pimsg);
  2595.  
  2596. exit:
  2597.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  2598.  
  2599.     DebugTraceResult(HrIsRead, hr);
  2600.     return hr;
  2601. }
  2602.  
  2603. /*
  2604.  *  HrRemoveRow
  2605.  *
  2606.  *  Purpose     remove a row from a contents or hierarchy table
  2607.  *
  2608.  *  Argument    lptbl       the table whose row is to be deleted
  2609.  *              peid        the entryid of row to be deleted. Used to
  2610.  *                          generate the instance key.
  2611.  *
  2612.  */
  2613. HRESULT HrRemoveRow(LPTABLEDATA lptbl, PEID peid)
  2614. {
  2615.     SPropValue valIK;   /* instance key stored in a property value */
  2616.     HRESULT hr;
  2617.  
  2618.     if (lptbl == NULL)
  2619.         return hrSuccess;
  2620.  
  2621.     /* make the instance key into a property value */
  2622.     valIK.ulPropTag = PR_INSTANCE_KEY;
  2623.     valIK.Value.bin.cb = CbEID(peid);
  2624.     valIK.Value.bin.lpb = (BYTE *) peid;
  2625.  
  2626.     /* delete the row from the table */
  2627.     hr = lptbl->lpVtbl->HrDeleteRow(lptbl, &valIK);
  2628.  
  2629.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  2630.  
  2631.     DebugTraceResult(HrRemoveRow, hr);
  2632.     return hr;
  2633. }
  2634.  
  2635. /*
  2636.  *  HrUpdateRow
  2637.  *
  2638.  *  Purpose
  2639.  *      alter the row in the table with the given index property
  2640.  *      to the properties in the proptag array.  If the index is
  2641.  *      not present, a new row is added to the table. The index
  2642.  *      property MUST be the first in pPTA.
  2643.  *      For contents table, an entry is made only if the object has
  2644.  *      been saved (and therefore has a PR_ENTRYID property).
  2645.  *
  2646.  *  Parameters
  2647.  *      pims            A pointer to the message store object.
  2648.  *      lptbl           A pointer to the table data object to update.
  2649.  *      peid            EntryID of object whose row is to be added
  2650.  *      pPTA            array of property tags of properties to appear in
  2651.  *                      this row
  2652.  *      pft             pointer to the file time to write
  2653.  *      ulObjType       MAPI_MESSAGE (for contents table) or MAPI_FOLDER
  2654.  *                      (for hierarchy table)
  2655.  *
  2656.  */
  2657. HRESULT HrUpdateRow(PIMS pims, LPTABLEDATA lptbl, PEID peid,
  2658.     LPSPropTagArray pPTA, FILETIME *pft, ULONG ulObjType)
  2659. {
  2660.     SRow srUpdateRow;           /* new table row */
  2661.     LPSPropValue pval = NULL;
  2662.     LPMAPIPROP lpEntry = NULL;  /* szName as an open mapiprop */
  2663.     ULONG ulObjectType;         /* type of object szName is */
  2664.     HRESULT hr = hrSuccess;
  2665.     ULONG cbEID;                /* number of bytes in lpEntryID */
  2666.     ULONG cValues;
  2667.  
  2668.     if (lptbl == NULL)
  2669.         return hrSuccess;
  2670.  
  2671.     cbEID = CbEID(peid);
  2672.  
  2673.     /* Don't add a contents table row for unsaved messages */
  2674.     if (ulObjType == MAPI_MESSAGE && FIsUnsavedEID(peid))
  2675.         goto exit;
  2676.  
  2677.     /* open this object. Do not open with MAPI_MODIFY because if the */
  2678.     /* store is open read-only, the OpenEntry will fail. */
  2679.  
  2680.     hr = ((LPMDB) pims)->lpVtbl->OpenEntry((LPMDB) pims, cbEID,
  2681.         (LPENTRYID) peid, NULL, 0L, &ulObjectType, (LPUNKNOWN *) &lpEntry);
  2682.     if (hr != hrSuccess)
  2683.         goto exit;
  2684.  
  2685.     if (ulObjType == MAPI_MESSAGE && FIsUnsavedMsg((PIMSG) lpEntry))
  2686.     {
  2687.         UlRelease(lpEntry);
  2688.         goto exit;
  2689.     }
  2690.  
  2691.     hr = lpEntry->lpVtbl->GetProps(lpEntry, pPTA, 0, /* ansi */
  2692.             &cValues, &pval);
  2693.  
  2694.     UlRelease(lpEntry);
  2695.  
  2696.     if (HR_FAILED(hr))
  2697.         goto exit;
  2698.  
  2699.     hr = hrSuccess;             /* ignore warnings */
  2700.  
  2701.     Assert(cValues == pPTA->cValues);
  2702.  
  2703.     /* add depth if this is a hierarchy table */
  2704.     if (ulObjType == MAPI_FOLDER)
  2705.     {
  2706.         AssertSz(PROP_ID(pval[cValues - 1].ulPropTag) == PROP_ID(PR_DEPTH),
  2707.             "Invalid assumption. PR_DEPTH must be last in hier ptags.");
  2708.  
  2709.         pval[cValues - 1].ulPropTag = PR_DEPTH;
  2710.         pval[cValues - 1].Value.l = 1L;
  2711.     }
  2712.  
  2713.     /* stamp in the last mod time (based on what's passed in) */
  2714.     /* Note assumption of where the proptag is. */
  2715.  
  2716.     AssertSz1(pPTA->aulPropTag[MODIFY_INDEX] == PR_LAST_MODIFICATION_TIME,
  2717.         "Invalid assumption. PR_LAST_MODIFICATION_TIME must be at "
  2718.         "index %d in pPTA.", MODIFY_INDEX);
  2719.  
  2720.     pval[MODIFY_INDEX].ulPropTag = PR_LAST_MODIFICATION_TIME;
  2721.     pval[MODIFY_INDEX].Value.ft = *pft;
  2722.  
  2723.     /* add the row */
  2724.     srUpdateRow.cValues = cValues;
  2725.     srUpdateRow.lpProps = pval;
  2726.  
  2727.     hr = lptbl->lpVtbl->HrModifyRow(lptbl, &srUpdateRow);
  2728.  
  2729.     /* Fall through to exit */
  2730.  
  2731. exit:
  2732.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  2733.  
  2734.     LMFree(&pims->lmr, pval);
  2735.  
  2736.     DebugTraceResult(HrUpdateRow, hr);
  2737.     return hr;
  2738. }
  2739.  
  2740. /*
  2741.  * HrCreateFolder
  2742.  *
  2743.  * This routine searches for a child folder of the given parent folder
  2744.  * for a folder with the specified PR_DISPLAY_NAME. If found and the caller
  2745.  * wishes to open if exists, then the found folder is opened and returned. If
  2746.  * found and the caller doesn't want to open if exists, then the error
  2747.  * MAPI_E_COLLISION is returned. If no child folder with the matching display
  2748.  * name is found, then a child folder with the correct display name is created
  2749.  * and the new folder is returned.
  2750.  *
  2751.  * Parameters
  2752.  *
  2753.  * pifldParent: The parent folder in which to search for matching display names,
  2754.  *              and, if necessary, to create the child folder.
  2755.  * szName:      The display name to search for and to create the child folder
  2756.  *              with.
  2757.  * szComment:   If a child folder is created, the value to set its PR_COMMENT
  2758.  *              property to.
  2759.  * fOpenIfExists: If a child folder with a matching PR_DISPLAY_NAME is found,
  2760.  *              open the matching folder when this BOOL is TRUE. If this
  2761.  *              BOOL is FALSE, and a match is found, return MAPI_E_COLLISION.
  2762.  *              If no match is found, this parameter is not referenced.
  2763.  * ppifldNew:   The location to return the newly created or opened folder.
  2764.  * pfCreated:   The location to return whether this function created or opened
  2765.  *              the child folder. Returning TRUE means the folder was created.
  2766.  *
  2767.  * Errors: memory or disk errors, and MAPI_E_COLLISION.
  2768.  *
  2769.  */
  2770. static HRESULT HrCreateFolder(PIFLD pifldParent, LPSTR szName, LPSTR szComment,
  2771.     BOOL fOpenIfExists, PIFLD *ppifldNew, BOOL *pfCreated)
  2772. {
  2773.     HRESULT hr;
  2774.     BOOL fCreated = FALSE;
  2775.     PEID peid = NULL;
  2776.     PIFLD pifldNew = NULL;
  2777.     LPMAPITABLE pmt = NULL;
  2778.     PIMS pims = pifldParent->pims;
  2779.     ULONG ulRowCount;
  2780.     BOOL fFirstSubFld;
  2781.  
  2782.     hr = pifldParent->lpVtbl->GetHierarchyTable(pifldParent, 0, &pmt);
  2783.     if (hr != hrSuccess)
  2784.         goto exit;
  2785.  
  2786.     hr = pmt->lpVtbl->GetRowCount(pmt, 0, &ulRowCount);
  2787.     if (hr != hrSuccess)
  2788.         goto exit;
  2789.  
  2790.     fFirstSubFld = (ulRowCount == 0);
  2791.         
  2792.     if (!fFirstSubFld)
  2793.     {
  2794.         hr = HrEIDFromDisplayName(pmt, szName, &pims->lmr, &peid);
  2795.     
  2796.         if (hr != hrSuccess)
  2797.             goto exit;
  2798.     
  2799.         /* If we found a duplicate, and we don't want it, we have an error. */
  2800.     
  2801.         if ((peid != NULL) && (fOpenIfExists == FALSE))
  2802.         {
  2803.             hr = ResultFromScode(MAPI_E_COLLISION);
  2804.             goto exit;
  2805.         }
  2806.     }
  2807.     
  2808.     UlRelease(pmt);
  2809.     pmt = NULL;
  2810.     
  2811.     if (peid == NULL)
  2812.     {
  2813.         /* //$ Need to handle cleanup of directory and propfile when errors */
  2814.         /* //$ occur later in this function... */
  2815.  
  2816.         hr = HrCreateFolderStorage(pifldParent, FOLDER_GENERIC, szName,
  2817.             szComment, TRUE, pims, &peid);
  2818.  
  2819.         if (hr != hrSuccess)
  2820.             goto exit;
  2821.  
  2822.         fCreated = TRUE;
  2823.     }
  2824.  
  2825.     /* Create the instance data */
  2826.     hr = HrNewIFLD(peid, pims, TRUE, &pifldNew);
  2827.     if (hr != hrSuccess)
  2828.         goto exit;
  2829.  
  2830.     hr = HrSetInternalProps(&pims->lmr, cpropIFLDInternal, &(pifldNew->pval),
  2831.             &(pifldNew->cval), peid, pifldParent->peid, 0);
  2832.     if (hr != hrSuccess)
  2833.         goto exit;
  2834.  
  2835.     /* update the hierarchy tables, if any */
  2836.     if (fCreated)
  2837.     {
  2838.         if (fFirstSubFld)
  2839.         {
  2840.             hr = HrSetSubFolderProp(pifldParent, TRUE);
  2841.             if (hr != hrSuccess)
  2842.                 goto exit;
  2843.         }
  2844.  
  2845.         ChangeTable(pims, pifldParent->peid, peid, MAPI_FOLDER,
  2846.             TABLE_ROW_ADDED, TRUE);
  2847.     }
  2848.  
  2849.     if (pfCreated)
  2850.         *pfCreated = fCreated;
  2851.  
  2852.     *ppifldNew = pifldNew;
  2853.  
  2854. exit:
  2855.     UlRelease(pmt);
  2856.     LMFree(&pims->lmr, peid);
  2857.  
  2858.     if (hr != hrSuccess)
  2859.         UlRelease(pifldNew);
  2860.  
  2861.     DebugTraceResult(HrCreateFolder, hr);
  2862.     return hr;
  2863. }
  2864.  
  2865. /*
  2866.  *  HrIsParent
  2867.  *
  2868.  *  Purpose
  2869.  *      Checks if peidParent is the parent of peidChild. Note that peidChild
  2870.  *      may be either a message or a folder entryid. Returns answer
  2871.  *      (TRUE or FALSE) in *pfIsParent.
  2872.  *
  2873.  *  Parameters
  2874.  *      peidParent      A pointer to the entryid of the potential parent.
  2875.  *      peidChild       A pointer to the entryid of the potential child.
  2876.  *      pfIsParent      A pointer to the location to return the answer.
  2877.  */
  2878. HRESULT HrIsParent(PEID peidParent, PEID peidChild, BOOL *pfIsParent)
  2879. {
  2880.     SCODE sc = S_OK;
  2881.     LPSTR szLastSlash;          /* pointer to last slash in Childs' path */
  2882.     BOOL fAnswer = FALSE;
  2883.     LPSTR szTemp = NULL;
  2884.     ULONG cbPathCopy;
  2885.  
  2886.     if (!IsEqualMAPIUID(&(peidParent->uidResource), &(peidChild->uidResource)))
  2887.         goto exit;
  2888.  
  2889.     szLastSlash = SzFindLastCh(peidChild->szPath, '\\');
  2890.     if (szLastSlash == NULL)
  2891.     {
  2892.         /* if child is the root it can have no parent */
  2893.         /* otherwise, check if child lives in the root folder */
  2894.  
  2895.         if (*(peidChild->szPath) != '\0')
  2896.             fAnswer = (*(peidParent->szPath) == '\0');
  2897.  
  2898.         goto exit;
  2899.     }
  2900.  
  2901.     cbPathCopy = ((szLastSlash - peidChild->szPath) * sizeof(TCHAR))
  2902.         + sizeof(TCHAR);
  2903.  
  2904.     sc = ScAlloc(cbPathCopy, &szTemp);
  2905.     if (sc != S_OK)
  2906.         goto exit;
  2907.  
  2908.     memcpy(szTemp, peidChild->szPath, (UINT) cbPathCopy);
  2909.     szTemp[cbPathCopy - 1] = '\0';
  2910.  
  2911.     /* see if paths are the same */
  2912.     fAnswer = !lstrcmpi(peidParent->szPath, szTemp);
  2913.  
  2914. exit:
  2915.     if (sc == S_OK)
  2916.         *pfIsParent = fAnswer;
  2917.  
  2918.     FreeNull(szTemp);
  2919.  
  2920.     DebugTraceSc(HrIsParent, sc);
  2921.     return ResultFromScode(sc);
  2922. }
  2923.  
  2924. /*
  2925.  *  FIsAncestor
  2926.  *
  2927.  *  Purpose
  2928.  *      returns TRUE if pifldDescendent is a descendent of pifldAncestor
  2929.  *
  2930.  *  Parameters
  2931.  *      pifldAncestor       the ancestor(?) folder
  2932.  *      pifldDescendent     the descendent(?) folder
  2933.  *
  2934.  */
  2935. static BOOL FIsAncestor(PIFLD pifldAncestor, PIFLD pifldDescendent)
  2936. {
  2937.     PEID peidAncestor;          /* entryID of pifldAncestor */
  2938.     PEID peidDescendent;        /* entryID of pifldDescendent */
  2939.     LPTSTR szFirstOccurrence = NULL;    /* pointer to first occurrence in */
  2940.  
  2941.     /* pifldDescendent's path of */
  2942.     /* pifldAncestor's path */
  2943.  
  2944.     if (pifldAncestor == NULL || pifldDescendent == NULL)
  2945.         return FALSE;
  2946.  
  2947.     peidAncestor = pifldAncestor->peid;
  2948.     peidDescendent = pifldDescendent->peid;
  2949.  
  2950.     if (!IsEqualMAPIUID(&(peidAncestor->uidResource),
  2951.             &(peidDescendent->uidResource)))
  2952.         return FALSE;
  2953.  
  2954.     /* check for either one being the root */
  2955.     if (FIsRoot(peidAncestor))
  2956.         return TRUE;
  2957.     if (FIsRoot(peidDescendent))
  2958.         return FALSE;
  2959.  
  2960.     szFirstOccurrence = SzFindSz(peidDescendent->szPath,
  2961.         peidAncestor->szPath);
  2962.  
  2963.     return (BOOL) (szFirstOccurrence == peidDescendent->szPath);
  2964. }
  2965.  
  2966. /*
  2967.  *  HrFullToRelative
  2968.  *
  2969.  *  Purpose             given a full path name and store, returns the
  2970.  *                      root relative path.  Free with FreeNull.
  2971.  *
  2972.  *  Parameters
  2973.  *      szFullPath      the full path
  2974.  *      pims            the store object
  2975.  *      pszRelative     [out] pointer to the relative path
  2976.  *
  2977.  *  NB      memory is allocated using ScAlloc and should be freed
  2978.  *          with FreeNull
  2979.  */
  2980. HRESULT HrFullToRelative(LPTSTR szFullPath, PIMS pims, LPTSTR *pszRelative)
  2981. {
  2982.     ULONG cchStore;     /* length of the store path in tchars */
  2983.     ULONG cchFull;      /* length of the full path in tchars */
  2984.     SCODE sc;
  2985.  
  2986.     cchStore = lstrlen(pims->szStorePath);
  2987.     cchFull = lstrlen(szFullPath);
  2988.  
  2989.     Assert(cchFull >= cchStore);
  2990.  
  2991.     sc = ScAlloc((cchFull - cchStore + sizeof(TCHAR)), (PPV) pszRelative);
  2992.  
  2993.     if (sc != S_OK)
  2994.     {
  2995.         DebugTraceSc(HrFullToRelative, sc);
  2996.         return ResultFromScode(sc);
  2997.     }
  2998.  
  2999.     lstrcpy(*pszRelative, szFullPath + cchStore + 1);
  3000.  
  3001.     return hrSuccess;
  3002. }
  3003.  
  3004. /*
  3005.  *  HrFullPathName
  3006.  *
  3007.  *  Purpose         Allocate space for and return a pointer to the full path
  3008.  *                  name of the given object. Free with FreeNull.
  3009.  *
  3010.  *  Parameters
  3011.  *      szStorePath     full path to the store root
  3012.  *      szPath1         first portion of path past the store root.
  3013.  *                      May be NULL.
  3014.  *      szPath2         second portion of path to the object
  3015.  *                      whose full path name is needed, may be NULL.
  3016.  *      pszName         Pointer to a location to return the full pathname
  3017.  *
  3018.  *  Returns:
  3019.  *      HRESULT
  3020.  */
  3021. HRESULT HrFullPathName(LPTSTR szStorePath, LPTSTR szPath1, LPTSTR szPath2,
  3022.     LPTSTR *pszName)
  3023. {
  3024.     UINT cbPath1 = 0;           /* length in bytes of first portion of path */
  3025.  
  3026.     /* excluding NULL */
  3027.     UINT cbPath2 = 0;           /* length in bytes of second portion of path */
  3028.  
  3029.     /* excluding NULL */
  3030.     UINT cbStorePath = 0;
  3031.     SCODE sc;
  3032.  
  3033.     Assert(!IsBadWritePtr(pszName, sizeof(LPSTR)));
  3034.     Assert(!IsBadStringPtr(szStorePath, (UINT) -1));
  3035.  
  3036.     Assert(szPath1 == NULL || !IsBadStringPtr(szPath1, (UINT) -1));
  3037.     Assert(szPath2 == NULL || !IsBadStringPtr(szPath2, (UINT) -1));
  3038.  
  3039.     cbStorePath = lstrlen(szStorePath) * sizeof(TCHAR);
  3040.  
  3041.     if (szPath1 != NULL)
  3042.         cbPath1 = lstrlen(szPath1) * sizeof(TCHAR);
  3043.  
  3044.     if (szPath2 != NULL)
  3045.         cbPath2 = lstrlen(szPath2) * sizeof(TCHAR);
  3046.  
  3047.     /* allocate space for the path name */
  3048.     sc = ScAlloc(cbStorePath + cbPath1 + cbPath2 + (3 * sizeof(TCHAR)),
  3049.         (PPV) pszName);
  3050.  
  3051.     if (sc != S_OK)
  3052.     {
  3053.         DebugTraceSc(HrFullPathName, sc);
  3054.         return ResultFromScode(sc);
  3055.     }
  3056.  
  3057.     /* fill in the name */
  3058.     lstrcpy(*pszName, szStorePath);
  3059.  
  3060.     if (cbPath1 != 0)
  3061.     {
  3062.         lstrcat(*pszName, TEXT("\\"));
  3063.         lstrcat(*pszName, szPath1);
  3064.     }
  3065.  
  3066.     if (cbPath2 != 0)
  3067.     {
  3068.         lstrcat(*pszName, TEXT("\\"));
  3069.         lstrcat(*pszName, szPath2);
  3070.     }
  3071.  
  3072.     return hrSuccess;
  3073. }
  3074.  
  3075. /*
  3076.  -  HrNewEID
  3077.  -
  3078.  *  Purpose     Return the EID of a new object (folder or message)
  3079.  *
  3080.  *  Parameters
  3081.  *      pifld           Parent folder of object to receive new ID, NULL
  3082.  *                      means we need EID of the root folder
  3083.  *      pims            Store containing the new object
  3084.  *      szExtension     File extension of new object, including '.'
  3085.  *      lpulSeqNumber   pointer to sequence number of this entryID (optional)
  3086.  *      ppeid           Pointer to pointer to new entryID
  3087.  *
  3088.  *  Returns:
  3089.  *      HRESULT
  3090.  */
  3091. HRESULT HrNewEID(PIFLD pifld, PIMS pims, LPTSTR szExtension,
  3092.     ULONG *lpulSeqNumber, PEID *ppeid)
  3093. {
  3094.     PEID peidNew = NULL;
  3095.     LPSTR szNewPath = NULL;
  3096.     HRESULT hr = hrSuccess;
  3097.     MAPIUID uid;
  3098.     ULONG ulSeq;
  3099.     LPSTR szNewObjName = NULL;  /* name of new object */
  3100.  
  3101.     Assert(ppeid);
  3102.     Assert(pims);
  3103.  
  3104.     /* get the new name of the object, and a unique sequence number. */
  3105.     hr = HrUniqueFileName(pims, &ulSeq, &szNewObjName);
  3106.     if (hr != hrSuccess)
  3107.         goto exit;
  3108.  
  3109.     /* code in the sample store depends on the number of chars in the */
  3110.     /* file / folder name being OK. Verify that it is. */
  3111.  
  3112.     Assert(lstrlen(szNewObjName) == CCH_BASE);
  3113.  
  3114.     GetResourceUID(pims, &uid);
  3115.  
  3116.     if (pifld == NULL)
  3117.     {
  3118.         CHAR ch = '\0';
  3119.  
  3120.         /* Construct the EID for the root folder */
  3121.         hr = HrConstructEID(&uid, &pims->lmr, (LPSTR) &ch, &peidNew);
  3122.         if (hr != hrSuccess)
  3123.             goto exit;
  3124.     }
  3125.     else
  3126.     {
  3127.         ULONG cbPath;
  3128.         LPSTR szPath = NULL;
  3129.         SCODE sc;
  3130.         BOOL fIsRoot;
  3131.  
  3132.         /* Add 1 TCHAR for the NULL terminator */
  3133.         cbPath = (lstrlen(szNewObjName) + lstrlen(szExtension)) * sizeof(TCHAR)
  3134.             + sizeof(TCHAR);
  3135.  
  3136.         fIsRoot = FIsRoot(pifld->peid);
  3137.  
  3138.         if (fIsRoot == FALSE)
  3139.         {
  3140.             /* Add 1 TCHAR for the slash between the folder and the new name */
  3141.             /* Subtract 1 TCHAR because we don't need to count the NULL term */
  3142.  
  3143.             cbPath += CbEIDPath(pifld->peid) + sizeof(TCHAR) - sizeof(TCHAR);
  3144.         }
  3145.  
  3146.         /* allocate space for the new path */
  3147.         sc = ScAlloc(cbPath, &szPath);
  3148.         if (sc != S_OK)
  3149.         {
  3150.             hr = ResultFromScode(sc);
  3151.             goto exit;
  3152.         }
  3153.  
  3154.         *szPath = 0;
  3155.  
  3156.         if (fIsRoot == FALSE)
  3157.         {
  3158.             lstrcpy(szPath, pifld->peid->szPath);
  3159.             lstrcat(szPath, TEXT("\\"));
  3160.         }
  3161.  
  3162.         lstrcat(szPath, szNewObjName);
  3163.         lstrcat(szPath, szExtension);
  3164.  
  3165.         hr = HrConstructEID(&uid, &pims->lmr, szPath, &peidNew);
  3166.  
  3167.         FreeNull(szPath);
  3168.  
  3169.         if (hr != hrSuccess)
  3170.             goto exit;
  3171.     }
  3172.  
  3173.     *ppeid = peidNew;
  3174.  
  3175.     if (lpulSeqNumber)
  3176.         *lpulSeqNumber = ulSeq;
  3177.  
  3178.     Assert(hr == hrSuccess);
  3179.  
  3180. exit:
  3181.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3182.  
  3183.     if (hr != hrSuccess)
  3184.         LMFree(&pims->lmr, peidNew);
  3185.  
  3186.     FreeNull(szNewObjName);
  3187.  
  3188.     DebugTraceResult(HrNewEID, hr);
  3189.     return hr;
  3190. }
  3191.  
  3192. /*
  3193.  * HrOpenPropertyMessage
  3194.  *
  3195.  *  Purpose         open the property message for the folder with entryid
  3196.  *                  pointed to peid
  3197.  *
  3198.  *  Parameters
  3199.  *      peid                pointer to folder's entryid
  3200.  *      pims                pointer to the message store object
  3201.  *      fModifyExclusive    TRUE if we want to open for read/write,
  3202.  *                          exclusive access.
  3203.  *      lppmsg              pointer to location to return the open message
  3204.  *                          object
  3205.  *
  3206.  *  Returns: Storage and IMSG errors.
  3207.  */
  3208. static HRESULT HrOpenPropertyMessage(PEID peid, PIMS pims, BOOL fModifyExclusive,
  3209.     LPMESSAGE *lppmsg)
  3210. {
  3211.     LPTSTR szPropFolderPathName = NULL;
  3212.     HRESULT hr;
  3213.  
  3214.     /* get full path to message file */
  3215.     hr = HrFullPathName(pims->szStorePath, peid->szPath, szPropertyFileName,
  3216.         &szPropFolderPathName);
  3217.     if (hr != hrSuccess)
  3218.         goto exit;
  3219.  
  3220.     hr = HrOpenIMsg(pims->pmsgsess, szPropFolderPathName, &pims->lmr,
  3221.         pims->psup, FALSE, fModifyExclusive, fModifyExclusive, lppmsg);
  3222.  
  3223.     if (hr != hrSuccess)
  3224.     {
  3225.         if (GetScode(hr) == MAPI_E_NOT_FOUND)
  3226.             hr = ResultFromScode(MAPI_E_OBJECT_DELETED);
  3227.         goto exit;
  3228.     }
  3229.  
  3230. exit:
  3231.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3232.  
  3233.     FreeNull(szPropFolderPathName);
  3234.  
  3235.     DebugTraceResult(HrOpenPropertyMessage, hr);
  3236.     return hr;
  3237. }
  3238.  
  3239. /*
  3240.  * HrOpenPropertyMessageRetry
  3241.  *
  3242.  *  Purpose         open the property message for the folder with entryid
  3243.  *                  pointed to peid. The function will retry if message is busy
  3244.  *                  for the specified number of times.
  3245.  *
  3246.  *  Parameters
  3247.  *      peid                pointer to folder's eid
  3248.  *      pims                pointer to the message store object
  3249.  *      fModifyExclusive    TRUE if we want to open for read/write,
  3250.  *                          exclusive access.
  3251.  *      lppmsg              pointer to location to return the open message
  3252.  *                          object
  3253.  *
  3254.  */
  3255. HRESULT HrOpenPropertyMessageRetry(PEID peid, PIMS pims,
  3256.     BOOL fModifyExclusive, LPMESSAGE *lppmsg)
  3257. {
  3258.     UINT iRetry;                /* number of attempts to open */
  3259.     HRESULT hr = hrSuccess;
  3260.  
  3261.     iRetry = 0;
  3262.     while (TRUE)
  3263.     {
  3264.         hr = HrOpenPropertyMessage(peid, pims, fModifyExclusive, lppmsg);
  3265.  
  3266.         if (GetScode(hr) != MAPI_E_NO_ACCESS || ++iRetry >= NUM_RETRIES)
  3267.             break;
  3268.  
  3269.         Sleep(500);
  3270.     }
  3271.  
  3272.     #ifdef DEBUG
  3273.     if (iRetry >= NUM_RETRIES)
  3274.         TraceSz("HrOpenPropertyMessageRetry: Failing open. Too many tries.");
  3275.     #endif
  3276.  
  3277.     DebugTraceResult(HrOpenPropertyMessageRetry, hr);
  3278.     return hr;
  3279. }
  3280.  
  3281. /*
  3282.  * HrGetFileModTime
  3283.  *
  3284.  * Gets the last write time of the file from the OS by calling
  3285.  * FindFirstFile, and retrieving it from the FIND_DATA.
  3286.  *
  3287.  * szStorePath: the path to the message store.
  3288.  * szFileName: the relative path to the file.
  3289.  * pft: a pointer to the location to return the FILETIME
  3290.  *
  3291.  */
  3292. HRESULT HrGetFileModTime(LPTSTR szStorePath, LPTSTR szFileName,
  3293.     FILETIME *pft)
  3294. {
  3295.     HRESULT hr;
  3296.     LPSTR szFullPath;
  3297.  
  3298.     hr = HrFullPathName(szStorePath, szFileName, NULL, &szFullPath);
  3299.  
  3300.     if (hr == hrSuccess)
  3301.     {
  3302.         HANDLE hFile;
  3303.         WIN32_FIND_DATA ffd;
  3304.  
  3305.         hFile = FindFirstFile(szFullPath, &ffd);
  3306.     
  3307.         if (hFile == FAILED_SEARCH)
  3308.             hr = ResultFromScode(MAPI_E_NOT_FOUND);
  3309.         else
  3310.         {
  3311.             *pft = ffd.ftLastWriteTime;
  3312.             FindClose(hFile);
  3313.         }
  3314.     
  3315.         FreeNull(szFullPath);
  3316.     }
  3317.  
  3318.     DebugTraceResult(HrGetFileModTime, hr);
  3319.     return hr;
  3320. }
  3321.  
  3322. /*
  3323.  *  HrFindFirstID
  3324.  *
  3325.  *  Purpose         returns in *ppeid the entryid of the
  3326.  *                  first object in pifldParent whose name is of the
  3327.  *                  form specified in szTemplate. The caller must free
  3328.  *                  storage with CloseIDSearch even in error,
  3329.  *                  except *ppeid is always freed with LMFree.
  3330.  *
  3331.  *  Parameters
  3332.  *      pifldParent     parent folder
  3333.  *      szTemplate      template local name for object to be found
  3334.  *      pichLocal       pointer to variable for position of start of local
  3335.  *                      name of the found object
  3336.  *      pszRRPath       pointer to root relative path of next object
  3337.  *      phFindFile      pointer to location to return search handle
  3338.  *      pffd            pointer to location to return Windows find file data
  3339.  *                      struct for use by HrFindNextID.
  3340.  *      ppeid           pointer to eid of found object
  3341.  *
  3342.  *  Returns:
  3343.  *      HRESULT
  3344.  */
  3345. HRESULT HrFindFirstID(PIFLD pifldParent, LPTSTR szTemplate, ULONG *pichLocal,
  3346.     LPTSTR *pszRRPath, HANDLE *phFindFile, WIN32_FIND_DATA *pffd, PEID *ppeid)
  3347. {
  3348.     HRESULT hr = hrSuccess;
  3349.     MAPIUID uidStore;           /* uid for message store */
  3350.     LPTSTR szSubObject = NULL;  /* template name of subobject */
  3351.     PEID peid = NULL;
  3352.     PLMR plmr;
  3353.     LPSTR szRRPath = NULL;
  3354.     ULONG ichLocal = 0;
  3355.     HANDLE hFindFile = 0;
  3356.     WIN32_FIND_DATA ffd;
  3357.  
  3358.     plmr = &pifldParent->pims->lmr;
  3359.  
  3360.     /* find the first subobject */
  3361.     hr = HrFullPathName(pifldParent->pims->szStorePath,
  3362.         pifldParent->peid->szPath, szTemplate, &szSubObject);
  3363.     if (hr != hrSuccess)
  3364.         goto exit;
  3365.  
  3366.     hFindFile = FindFirstFile(szSubObject, &ffd);
  3367.  
  3368.     if (hFindFile == FAILED_SEARCH)
  3369.     {
  3370.         hr = ResultFromScode(MAPI_E_NOT_FOUND);
  3371.         goto exit;
  3372.     }
  3373.  
  3374.     /* get components of entryids for messages in this folder */
  3375.     GetResourceUID(pifldParent->pims, &uidStore);
  3376.  
  3377.     /* get the root relative path */
  3378.     hr = HrAlloc(CbEIDPath(pifldParent->peid)
  3379.         + (CCH_NAME * sizeof(TCHAR)), (PPV) &szRRPath);
  3380.     if (hr != hrSuccess)
  3381.         goto exit;
  3382.  
  3383.     lstrcpy(szRRPath, pifldParent->peid->szPath);
  3384.     if (*szRRPath != (TCHAR) 0)
  3385.         lstrcat(szRRPath, TEXT("\\"));
  3386.  
  3387.     ichLocal = lstrlen(szRRPath);
  3388.  
  3389.     lstrcpy(szRRPath + ichLocal, ffd.cFileName);
  3390.  
  3391.     /* construct the ID */
  3392.     hr = HrConstructEID(&uidStore, plmr, szRRPath, &peid);
  3393.     /* fall through to exit */
  3394.  
  3395. exit:
  3396.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3397.  
  3398.     if (hr != hrSuccess)
  3399.     {
  3400.         LMFree(plmr, peid);
  3401.         FreeNull(szRRPath);
  3402.  
  3403.         if (hFindFile && hFindFile != FAILED_SEARCH)
  3404.             FindClose(hFindFile);
  3405.     }
  3406.     else
  3407.     {
  3408.         *ppeid = peid;
  3409.         *pszRRPath = szRRPath;
  3410.         *phFindFile = hFindFile;
  3411.         *pichLocal = ichLocal;
  3412.         *pffd = ffd;
  3413.     }
  3414.  
  3415.     FreeNull(szSubObject);
  3416.  
  3417.     #ifdef DEBUG
  3418.     if (GetScode(hr) != MAPI_E_NOT_FOUND)
  3419.         DebugTraceResult(HrFindFirstID, hr);
  3420.     #endif
  3421.  
  3422.     return hr;
  3423. }
  3424.  
  3425. /*
  3426.  *  HrFindNextID
  3427.  *
  3428.  *  Purpose         returns in ppeid the eid of the next object in the search
  3429.  *                  free *ppeid with LMFree.
  3430.  *
  3431.  *  Parameters
  3432.  *      pifldParent     parent folder
  3433.  *      ichLocal        position of start of local name of the found object
  3434.  *      szRRPath        name of found object from last search
  3435.  *      hFindFile       search handle
  3436.  *      pffd            [in/out] pointer to location to use and return
  3437.  *                      Windows find file data struct.
  3438.  *      ppeid           pointer to location to return eid of next file.
  3439.  *
  3440.  *  Returns:
  3441.  *      HRESULT
  3442.  */
  3443. HRESULT HrFindNextID(PIFLD pifldParent, ULONG ichLocal, LPTSTR szRRPath,
  3444.     HANDLE hFindFile, WIN32_FIND_DATA *lpFindFileData, PEID *ppeid)
  3445. {
  3446.     HRESULT hr = hrSuccess;
  3447.     MAPIUID uidStore;           /* resource uid of store */
  3448.     PLMR plmr;
  3449.     PEID peid = NULL;
  3450.  
  3451.     plmr = &pifldParent->pims->lmr;
  3452.  
  3453.     if (FindNextFile(hFindFile, lpFindFileData))
  3454.     {
  3455.         lstrcpy(szRRPath + ichLocal, lpFindFileData->cFileName);
  3456.         GetResourceUID(pifldParent->pims, &uidStore);
  3457.  
  3458.         /* construct the ID */
  3459.         hr = HrConstructEID(&uidStore, plmr, szRRPath, &peid);
  3460.     }
  3461.     else
  3462.         hr = ResultFromScode(MAPI_E_NOT_FOUND);
  3463.  
  3464.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3465.  
  3466.     if (hr != hrSuccess)
  3467.         LMFree(plmr, peid);
  3468.     else
  3469.         *ppeid = peid;
  3470.  
  3471.     #ifdef DEBUG
  3472.     if (GetScode(hr) != MAPI_E_NOT_FOUND)
  3473.         DebugTraceResult(HrFindNextID, hr);
  3474.     #endif
  3475.  
  3476.     return hr;
  3477. }
  3478.  
  3479. /*
  3480.  *  CloseIDSearch
  3481.  *
  3482.  *  Purpose         
  3483.  *      Calls FindClose on the search handle, and FreeNull on the root-relative
  3484.  *      pathname given as arguments. After cleaning up these values, the
  3485.  *      function also invalidates the pointers.
  3486.  *
  3487.  *  Parameters
  3488.  *      lphFindFile     pointer to search handle, returned with FAILED_SEARCH.
  3489.  *      pszRRPath       pointer to name of found object from last search,
  3490.  *                      returned with NULL.
  3491.  *
  3492.  *  Returns: void.
  3493.  */
  3494. void CloseIDSearch(HANDLE *lphFindFile, LPTSTR *pszRRPath)
  3495. {
  3496.     if (lphFindFile && *lphFindFile && *lphFindFile != FAILED_SEARCH)
  3497.     {
  3498.         FindClose(*lphFindFile);
  3499.         *lphFindFile = FAILED_SEARCH;
  3500.     }
  3501.  
  3502.     FreeNull(*pszRRPath);
  3503.     *pszRRPath = NULL;
  3504. }
  3505.  
  3506. /*
  3507.  *  HrNewIFLD
  3508.  *
  3509.  *  Purpose:
  3510.  *      Allocates and initializes new instance data for a folder
  3511.  *
  3512.  *  Parameters
  3513.  *      peid            EID of folder
  3514.  *      pims            address of the message store for this folder
  3515.  *      fModify         TRUE if caller wants to modify, FALSE otherwise
  3516.  *      ppifld          pointer to newly created instance data
  3517.  *
  3518.  *  Returns:
  3519.  *      HRESULT
  3520.  *
  3521.  *  Side effects:
  3522.  *      Allocates memory for IFLD object.  Freed by calling Release.
  3523.  *
  3524.  */
  3525. HRESULT HrNewIFLD(PEID peid, PIMS pims, BOOL fModify, PIFLD *ppifld)
  3526. {
  3527.     SCODE sc;
  3528.     PIFLD pifld = NULL;         /* pointer to new instance data */
  3529.  
  3530.     Assert(peid);
  3531.     Assert(pims);
  3532.     Assert(ppifld);
  3533.  
  3534.     /* Don't open the folder object unless the directory and the property */
  3535.     /* file are present on disk. */
  3536.  
  3537.     if (!FFolderExists(peid, pims))
  3538.     {
  3539.         sc = MAPI_E_NOT_FOUND;
  3540.         goto exit;
  3541.     }
  3542.  
  3543.     /* Allocate memory for the instance data */
  3544.     sc = LMAllocZ(&pims->lmr, sizeof(IFLD), &pifld);
  3545.     if (sc != S_OK)
  3546.         goto exit;
  3547.  
  3548.     /* allocate memory to hold the folder's eid */
  3549.     sc = ScAllocZ(CbEID(peid), &pifld->peid);
  3550.     if (sc != S_OK)
  3551.         goto exit;
  3552.  
  3553.     Assert(CbEIDPath(peid) == (UINT) Cbtszsize(peid->szPath));
  3554.  
  3555.     OBJ_Initialize(pifld, &vtblIFLD, OT_FOLDER, pims, pims->pcs);
  3556.  
  3557.     memcpy(pifld->peid, peid, CbEID(peid));
  3558.  
  3559.     if (fModify)
  3560.         OBJ_SetFlag(pifld, OBJF_MODIFY);
  3561.  
  3562.     OBJ_Enqueue((POBJ) pifld, (POBJ) pims);
  3563.  
  3564.     *ppifld = pifld;
  3565.  
  3566. exit:
  3567.     AssertSz(sc == S_OK || FAILED(sc), "No warning expected");
  3568.  
  3569.     if (sc != S_OK && pifld)
  3570.     {
  3571.         FreeNull(pifld->peid);
  3572.         LMFree(&pims->lmr, pifld);
  3573.     }
  3574.  
  3575.     DebugTraceSc(HrNewIFLD, sc);
  3576.     return ResultFromScode(sc);
  3577. }
  3578.  
  3579. /* 
  3580.  * FFolderExists
  3581.  *
  3582.  * Check for the existence of the folder's property file inside the folder
  3583.  * passed in. If the file isn't there or we can't get to it, return FALSE.
  3584.  * If the file is there, return TRUE.
  3585.  *
  3586.  * Parameters:
  3587.  *      peid -- The entryid of the folder to test.
  3588.  *      pims -- a pointer to the message store object.
  3589.  *
  3590.  * Returns: TRUE if the folder's property file is found.
  3591.  *          FALSE in all other cases.
  3592.  *
  3593.  */
  3594. BOOL FFolderExists(PEID peid, PIMS pims)
  3595. {
  3596.     LPTSTR  szPropFolderPathName = NULL;
  3597.     HRESULT hr;
  3598.     BOOL    fFolderValid = FALSE;
  3599.  
  3600.     /* get full path to message file */
  3601.     hr = HrFullPathName(pims->szStorePath, peid->szPath, szPropertyFileName,
  3602.         &szPropFolderPathName);
  3603.  
  3604.     if (hr == hrSuccess)
  3605.     {
  3606.         HANDLE hFile;
  3607.         WIN32_FIND_DATA ffd;
  3608.  
  3609.         hFile = FindFirstFile(szPropFolderPathName, &ffd);
  3610.     
  3611.         if (hFile != FAILED_SEARCH)
  3612.         {
  3613.             fFolderValid = TRUE;  /* it's there! */
  3614.             FindClose(hFile);
  3615.         }
  3616.     
  3617.         FreeNull(szPropFolderPathName);
  3618.     }
  3619.  
  3620.     return fFolderValid;
  3621. }
  3622.  
  3623. /*
  3624.  -  HrCreateFolderStorage
  3625.  -
  3626.  *  Purpose:
  3627.  *      Create a new folder directory and its property file
  3628.  *
  3629.  *  Parameters
  3630.  *      pifld               the parent folder of the newly created folder
  3631.  *                          If NULL, the new folder is the root folder.
  3632.  *      ulFolderType        type of folder to be created
  3633.  *      szFolderName        name of the new folder
  3634.  *      szFolderComment     comment string for the new folder
  3635.  *      fCreateDir          TRUE means create directory for folder
  3636.  *      pims                pointer to store in which new folder resides
  3637.  *      ppeid               pointer to entryID of new folder
  3638.  *
  3639.  *  Returns:
  3640.  *
  3641.  *  Side effects:   Sets PR_COMMENT property to szFolderComment.
  3642.  *
  3643.  *  Errors:
  3644.  *
  3645. */
  3646. HRESULT HrCreateFolderStorage(PIFLD pifld, ULONG ulFolderType,
  3647.     LPSTR szFolderName, LPSTR szFolderComment, BOOL fCreateDir,
  3648.     PIMS pims, PEID *ppeid)
  3649. {
  3650.     HRESULT hr;
  3651.     PEID peid = NULL;
  3652.     ULONG ulSeqNumber;          /* sequence number of new folder*/
  3653.     SPropValue pvalRO[NUM_RO_FOLDER_PROPS];
  3654.     SPropValue pvalRW[NUM_RW_FOLDER_PROPS];
  3655.     ULONG cSet;
  3656.     LPSPropProblemArray pprba = NULL;
  3657.     LPMESSAGE lpmsgProp = NULL; /* property message for this folder */
  3658.     BOOL fDirCreated = FALSE;
  3659.     BOOL fFolderPropFileCreated = FALSE;
  3660.  
  3661.     LPTSTR szNewDirectoryName = NULL;
  3662.     LPTSTR szPropertyFolderName = NULL;
  3663.  
  3664.     Assert(pims);
  3665.     Assert(ppeid);
  3666.  
  3667.     hr = HrNewEID(pifld, pims, FOLDER_EXT, &ulSeqNumber, &peid);
  3668.     if (hr != hrSuccess)
  3669.         goto exit;
  3670.  
  3671.     /* create new directory for the new folder object */
  3672.     hr = HrFullPathName(pims->szStorePath, peid->szPath, NULL,
  3673.         &szNewDirectoryName);
  3674.     if (hr != hrSuccess)
  3675.         goto exit;
  3676.  
  3677.     if (fCreateDir)
  3678.     {
  3679.         if (!CreateDirectory(szNewDirectoryName, NULL))
  3680.         {
  3681.             hr = ResultFromScode(MAPI_E_NO_ACCESS);
  3682.             goto exit;
  3683.         }
  3684.         fDirCreated = TRUE;
  3685.     }
  3686.  
  3687.     hr = HrFullPathName(pims->szStorePath, peid->szPath,
  3688.         szPropertyFileName, &szPropertyFolderName);
  3689.     if (hr != hrSuccess)
  3690.         goto exit;
  3691.  
  3692.     /* create and open the message object for properites */
  3693.     hr = HrOpenIMsg(pims->pmsgsess, szPropertyFolderName, &pims->lmr,
  3694.         pims->psup, TRUE, TRUE, TRUE, &lpmsgProp);
  3695.  
  3696.     if (hr != hrSuccess)
  3697.         goto exit;
  3698.  
  3699.     fFolderPropFileCreated = TRUE;
  3700.  
  3701.     /* set the initial read only properties */
  3702.  
  3703.     pvalRO[0].ulPropTag = sptaReadOnly.aulPropTag[0];
  3704.     pvalRO[1].ulPropTag = sptaReadOnly.aulPropTag[1];
  3705.     pvalRO[2].ulPropTag = sptaReadOnly.aulPropTag[2];
  3706.     pvalRO[3].ulPropTag = sptaReadOnly.aulPropTag[3];
  3707.     pvalRO[4].ulPropTag = sptaReadOnly.aulPropTag[4];
  3708.     pvalRO[5].ulPropTag = sptaReadOnly.aulPropTag[5];
  3709.     pvalRO[6].ulPropTag = sptaReadOnly.aulPropTag[6];
  3710.     pvalRO[7].ulPropTag = sptaReadOnly.aulPropTag[7];
  3711.     pvalRO[8].ulPropTag = sptaReadOnly.aulPropTag[8];
  3712.     pvalRO[9].ulPropTag = sptaReadOnly.aulPropTag[9];
  3713.     pvalRO[10].ulPropTag = sptaReadOnly.aulPropTag[10];
  3714.  
  3715.     pvalRO[0].Value.l = MAPI_FOLDER;
  3716.     pvalRO[1].Value.bin.cb = sizeof(ULONG);
  3717.     pvalRO[1].Value.bin.lpb = (BYTE *) &ulSeqNumber;
  3718.     pvalRO[2].Value.l = (pifld == NULL) ? FOLDER_ROOT : FOLDER_GENERIC;
  3719.     pvalRO[3].Value.l = 0;
  3720.     pvalRO[4].Value.l = 0;
  3721.  
  3722.     pvalRO[5].Value.bin.cb = pims->eidStore.cb;
  3723.     pvalRO[5].Value.bin.lpb = pims->eidStore.lpb;
  3724.  
  3725.     pvalRO[6].Value.bin.cb = sizeof(pims->uidResource);
  3726.     pvalRO[6].Value.bin.lpb = (LPBYTE) &pims->uidResource;
  3727.  
  3728.     pvalRO[7].Value.b = FALSE;
  3729.  
  3730.     /* Set PR_ENTRYID, PR_PARENT_ENTRYID and PR_INSTANCE_KEY to null */
  3731.     /* strings to keep clients from writing over them. */
  3732.     /* We get the actual values internally. */
  3733.  
  3734.     pvalRO[8].Value.bin.cb = 1;
  3735.     pvalRO[8].Value.bin.lpb = (LPBYTE) "";
  3736.  
  3737.     pvalRO[9].Value.bin.cb = 1;
  3738.     pvalRO[9].Value.bin.lpb = (LPBYTE) "";
  3739.  
  3740.     pvalRO[10].Value.bin.cb = 1;
  3741.     pvalRO[10].Value.bin.lpb = (LPBYTE) "";
  3742.  
  3743.     /* set these read only props */
  3744.     hr = lpmsgProp->lpVtbl->SetProps(lpmsgProp, NUM_RO_FOLDER_PROPS,
  3745.         pvalRO, &pprba);
  3746.     if (hr != hrSuccess || pprba)
  3747.         goto exit;
  3748.  
  3749.     Assert(sptaReadOnly.cValues == NUM_RO_FOLDER_PROPS);
  3750.     Assert(spaReadOnly.cValues == NUM_RO_FOLDER_PROPS);
  3751.  
  3752.     hr = SetAttribIMsgOnIStg(lpmsgProp, (LPSPropTagArray) &sptaReadOnly,
  3753.         (LPSPropAttrArray) &spaReadOnly, &pprba);
  3754.     if (hr != hrSuccess || pprba)
  3755.         goto exit;
  3756.  
  3757.     /* set the read write properties */
  3758.  
  3759.     cSet = NUM_RW_FOLDER_PROPS;
  3760.  
  3761.     pvalRW[0].ulPropTag = PR_DISPLAY_NAME;
  3762.  
  3763.     /* must be last since it is optional */
  3764.     pvalRW[1].ulPropTag = PR_COMMENT;
  3765.  
  3766.     pvalRW[0].Value.LPSZ = szFolderName;
  3767.     if (szFolderComment != NULL)
  3768.         pvalRW[1].Value.LPSZ = szFolderComment;
  3769.     else
  3770.         cSet--;
  3771.  
  3772.     /* set the ReadWrite properties */
  3773.  
  3774.     hr = lpmsgProp->lpVtbl->SetProps(lpmsgProp, cSet, pvalRW, &pprba);
  3775.     if (hr != hrSuccess || pprba)
  3776.         goto exit;
  3777.  
  3778.     hr = lpmsgProp->lpVtbl->SaveChanges(lpmsgProp, 0);
  3779.  
  3780. exit:
  3781.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3782.  
  3783.     UlRelease(lpmsgProp);
  3784.  
  3785.     /* free the property problem structure */
  3786.     if (pprba)
  3787.     {
  3788.         LMFree(&pims->lmr, pprba);
  3789.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  3790.     }
  3791.  
  3792.     if (hr != hrSuccess)
  3793.     {
  3794.         LMFree(&pims->lmr, peid);
  3795.  
  3796.         if (fFolderPropFileCreated == TRUE)
  3797.             DeleteFile(szPropertyFolderName);
  3798.  
  3799.         if (fDirCreated == TRUE)
  3800.             RemoveDirectory(szNewDirectoryName);
  3801.     }
  3802.     else
  3803.         *ppeid = peid;
  3804.  
  3805.     FreeNull(szNewDirectoryName);
  3806.     FreeNull(szPropertyFolderName);
  3807.  
  3808.     DebugTraceResult(HrCreateFolderStorage, hr);
  3809.     return hr;
  3810. }
  3811.  
  3812. /*
  3813.  *  HrIncrementOneROProp
  3814.  *
  3815.  *  Purpose     increment the read only property by delta. Only works on
  3816.  *              properties of type PT_LONG.
  3817.  *
  3818.  *  Argument    pifld   pointer to the folder object
  3819.  *              lDelta  size of increment
  3820.  *              ulPT    the property tag to be changed
  3821.  */
  3822. HRESULT HrIncrementOneROProp(PIFLD pifld, LONG lDelta, ULONG ulPT)
  3823. {
  3824.     LONG lValue;
  3825.     HRESULT hr;
  3826.     LPMESSAGE lpmsg = NULL; /* property message for pifld */
  3827.     PLMR plmr = &pifld->pims->lmr;
  3828.  
  3829.     AssertSz1(PROP_TYPE(ulPT) == PT_LONG,
  3830.         "Trying to increment property %s; not PT_LONG",
  3831.         SzDecodeUlPropTag(ulPT));
  3832.  
  3833.     /* open the property message exclusively */
  3834.     hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims,
  3835.         TRUE, &lpmsg);
  3836.     if (hr != hrSuccess)
  3837.         goto exit;
  3838.  
  3839.     /* get the current value of the properties */
  3840.  
  3841.     hr = HrGetSingleProp((LPMAPIPROP) lpmsg, plmr, ulPT, &lValue);
  3842.     if (hr != hrSuccess)
  3843.         goto exit;
  3844.  
  3845.     lValue += lDelta;
  3846.  
  3847.     /* reset the new value */
  3848.  
  3849.     hr = HrSetOneROProp(lpmsg, plmr, ulPT, &lValue);
  3850.     if (hr != hrSuccess)
  3851.         goto exit;
  3852.  
  3853.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, FORCE_SAVE);
  3854.  
  3855. exit:
  3856.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3857.  
  3858.     UlRelease(lpmsg);
  3859.  
  3860.     DebugTraceResult(HrIncrementOneROProp, hr);
  3861.     return hr;
  3862. }
  3863.  
  3864. /*
  3865.  *  HrSetOneROFolderProp
  3866.  *
  3867.  *  Purpose     set the read only folder property to the value given. The
  3868.  *              property must be of type PT_LONG.
  3869.  *
  3870.  *  Argument    pifld   pointer to the folder object
  3871.  *              lValue  value to set the property to.
  3872.  *              ulPT    the property tag to be changed
  3873.  */
  3874. static HRESULT HrSetOneROFolderProp(PIFLD pifld, LONG lValue, ULONG ulPT)
  3875. {
  3876.     HRESULT hr;
  3877.     LPMESSAGE lpmsg = NULL; /* property message for pifld */
  3878.     PLMR plmr = &pifld->pims->lmr;
  3879.  
  3880.     AssertSz1(PROP_TYPE(ulPT) == PT_LONG,
  3881.         "Trying to increment property %s; not PT_LONG",
  3882.         SzDecodeUlPropTag(ulPT));
  3883.  
  3884.     /* open the property message exclusively */
  3885.     hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims,
  3886.         TRUE, &lpmsg);
  3887.     if (hr != hrSuccess)
  3888.         goto exit;
  3889.  
  3890.     /* reset the new value */
  3891.  
  3892.     hr = HrSetOneROProp(lpmsg, plmr, ulPT, &lValue);
  3893.     if (hr != hrSuccess)
  3894.         goto exit;
  3895.  
  3896.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, FORCE_SAVE);
  3897.  
  3898. exit:
  3899.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3900.  
  3901.     UlRelease(lpmsg);
  3902.  
  3903.     DebugTraceResult(HrSetOneROFolderProp, hr);
  3904.     return hr;
  3905. }
  3906.  
  3907. /*
  3908.  *  HrSetSubFolderProp
  3909.  *
  3910.  *  Purpose     set the PR_SUBFOLDERS property
  3911.  *
  3912.  *  Argument    pifld       pointer to the folder object
  3913.  *              fSubFolder  the value to write into the PR_SUBFOLDER property
  3914.  */
  3915. static HRESULT HrSetSubFolderProp(PIFLD pifld, BOOL fSubFolder)
  3916. {
  3917.     HRESULT hr;
  3918.     LPMESSAGE lpmsg = NULL; /* property message for pifld */
  3919.     PIFLD pifldParent = NULL;
  3920.     PLMR plmr = &pifld->pims->lmr;
  3921.     PEID peid = pifld->peid;
  3922.  
  3923.     /* open the property message exclusively */
  3924.     hr = HrOpenPropertyMessageRetry(peid, pifld->pims, TRUE, &lpmsg);
  3925.     if (hr != hrSuccess)
  3926.         goto exit;
  3927.  
  3928.     /* set the new value */
  3929.  
  3930.     hr = HrSetOneROProp(lpmsg, plmr, PR_SUBFOLDERS, &fSubFolder);
  3931.     if (hr != hrSuccess)
  3932.         goto exit;
  3933.  
  3934.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, FORCE_SAVE);
  3935.     if (hr != hrSuccess)
  3936.         goto exit;
  3937.  
  3938.     UlRelease(lpmsg);
  3939.     lpmsg = NULL;
  3940.  
  3941.     /* If this isn't the root folder, get the parent entryid, and call */
  3942.     /* ChangeTable so that any new properties are updated in its */
  3943.     /* hierarchy table row. */
  3944.  
  3945.     if (FIsRoot(peid) == FALSE)
  3946.     {
  3947.         PEID peidParent = NULL;
  3948.         PIMS pims = pifld->pims;
  3949.  
  3950.         hr = HrGetParentEID(&pims->lmr, pifld->peid, &peidParent);
  3951.         if (hr == hrSuccess)
  3952.         {
  3953.             ChangeTable(pims, peidParent, peid, MAPI_FOLDER,
  3954.                 TABLE_ROW_MODIFIED, TRUE);
  3955.             LMFree(&pims->lmr, peidParent);
  3956.         }
  3957.         else
  3958.         {
  3959.             TraceSz1("Sample MS: HrSetSubFolderProp: failed to change "
  3960.                 "hierarchy table. sc == %s\n", SzDecodeScode(GetScode(hr)));
  3961.             hr = hrSuccess;
  3962.         }
  3963.     }
  3964.  
  3965. exit:
  3966.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  3967.  
  3968.     UlRelease(lpmsg);
  3969.     UlRelease(pifldParent);
  3970.  
  3971.     DebugTraceResult(HrSetSubFolderProp, hr);
  3972.     return hr;
  3973. }
  3974.  
  3975. /*
  3976.  *  ChangeTable
  3977.  *
  3978.  * Purpose
  3979.  *  Changes all contents or hierarchy tables in all open folders to reflect
  3980.  *  the change specified. Also updates the folder's content and unread counts.
  3981.  *  Never updates the table on disk. That will happen when the table is
  3982.  *  read-in and verified.
  3983.  *
  3984.  *  Parameters
  3985.  *
  3986.  *      pims            Pointer to the message store object.
  3987.  *      peidTable       EID of the parent folder of the table to change
  3988.  *      peidObject      the object that has been added or deleted or modified.
  3989.  *                      May be NULL if ulTableEvent is TABLE_CHANGED. This 
  3990.  *                      means multiple items changed.
  3991.  *      ulObjType       MAPI_MESSAGE (for contents tables) or MAPI_FOLDER
  3992.  *                      (for hierarchy tables)
  3993.  *      ulTableEvent    either TABLE_ROW_ADDED, DELETED, MODIFIED, or
  3994.  *                      TABLE_CHANGED (TABLE_CHANGED for contents tables only)
  3995.  *      fSendNotif      TRUE if this routine should send notifications to
  3996.  *                      other processes about this change.
  3997.  *
  3998.  */
  3999. void ChangeTable(PIMS pims, PEID peidTable, PEID peidObject,
  4000.     ULONG ulObjType, ULONG ulTableEvent, BOOL fSendNotif)
  4001. {
  4002.     HRESULT hr = hrSuccess;
  4003.     ULONG cbEIDTable;
  4004.     LPSPropTagArray pPTA;       /* proptags of column headings */
  4005.     POBJ pobj;
  4006.  
  4007.     Assert(pims);
  4008.     Assert(peidTable);
  4009.     AssertSz1(ulTableEvent == TABLE_ROW_ADDED
  4010.         || ulTableEvent == TABLE_ROW_MODIFIED
  4011.         || ulTableEvent == TABLE_ROW_DELETED
  4012.         || ulTableEvent == TABLE_CHANGED, "Bad ulTableEvent %08lX",
  4013.         ulTableEvent);
  4014.     AssertSz1(ulObjType == MAPI_MESSAGE || ulObjType == MAPI_FOLDER,
  4015.         "Bad ulObjType %08lX", ulObjType);
  4016.  
  4017.     /*
  4018.      * Look for all open tables within this process. We find them
  4019.      * by search the open object chain for folders whose entryids match
  4020.      * the eid of the folder passed in. We only update open tables.
  4021.      * Therefore, don't bother checking objects that aren't folders or 
  4022.      * folder objects without open tables.
  4023.      */
  4024.     cbEIDTable = CbEID(peidTable);
  4025.  
  4026.     for (pobj = pims->pobjHead; pobj != NULL; pobj = pobj->pobjNext)
  4027.     {
  4028.         PIFLD pifld;
  4029.         ULONG ulTheSame;
  4030.         LPTABLEDATA lptbl;
  4031.         FILETIME ft;
  4032.  
  4033.         if (pobj->wType != OT_FOLDER)
  4034.             continue;
  4035.  
  4036.         pifld = (PIFLD) pobj;
  4037.  
  4038.         if (ulObjType == MAPI_MESSAGE)
  4039.         {
  4040.             pPTA = (LPSPropTagArray) &sPropTagsContents;
  4041.             lptbl = pifld->lptblContents;
  4042.         }
  4043.         else
  4044.         {
  4045.             pPTA = (LPSPropTagArray) &sPropTagsHierarchy;
  4046.             lptbl = pifld->lptblHierarchy;
  4047.         }
  4048.  
  4049.         if (lptbl == NULL)
  4050.             continue;
  4051.  
  4052.         hr = pims->lpVtbl->CompareEntryIDs(pims, cbEIDTable,
  4053.                 (LPENTRYID) peidTable, CbEID(pifld->peid),
  4054.                 (LPENTRYID) pifld->peid, 0L, &ulTheSame);
  4055.         if (hr != hrSuccess)
  4056.             goto exit;
  4057.  
  4058.         if (!ulTheSame)
  4059.             continue;
  4060.  
  4061.         switch (ulTableEvent)
  4062.         {
  4063.         case TABLE_CHANGED:
  4064.             if (ulObjType != MAPI_MESSAGE)
  4065.             {
  4066.                 TrapSz("ChangeTable doesn't handle TABLE_CHANGED on"
  4067.                     " hierarchy tables");
  4068.                 goto exit;
  4069.             }
  4070.  
  4071.             /* Don't update the file on disk. */
  4072.             hr = HrSyncContentsTable(pifld, FALSE);
  4073.             if (hr != hrSuccess)
  4074.                 goto exit;
  4075.             break;
  4076.  
  4077.         case TABLE_ROW_DELETED:
  4078.             hr = HrRemoveRow(lptbl, peidObject);
  4079.             if (hr != hrSuccess)
  4080.             {
  4081.                 if (GetScode(hr) == MAPI_E_NOT_FOUND)
  4082.                     hr = hrSuccess;
  4083.                 else
  4084.                     goto exit;
  4085.             }
  4086.             break;
  4087.  
  4088.         case TABLE_ROW_ADDED:
  4089.         case TABLE_ROW_MODIFIED:
  4090.             hr = HrGetFileModTime(pims->szStorePath, peidObject->szPath, &ft);
  4091.             if (hr != hrSuccess)
  4092.                 goto exit;
  4093.  
  4094.             hr = HrUpdateRow(pims, lptbl, peidObject, pPTA, &ft, ulObjType);
  4095.             if (hr != hrSuccess)
  4096.                 goto exit;
  4097.             break;
  4098.  
  4099.         default:
  4100.             /* We've already asserted this above. */
  4101.             goto exit;
  4102.         }
  4103.     }
  4104.  
  4105.     if (fSendNotif)
  4106.         hr = HrSendNotif(pims, peidTable, peidObject, ulTableEvent, ulObjType);
  4107.  
  4108. exit:
  4109.     if (hr != hrSuccess)
  4110.         TraceSz1("SampleMS: ChangeTable: failed to update "
  4111.             "table. Error %s.\n", SzDecodeScode(GetScode(hr)));
  4112.  
  4113.     return;
  4114. }
  4115.  
  4116. /*
  4117.  *  HrDestroyFolderStorage
  4118.  *
  4119.  *  Purpose         Removes storage associated with a folder
  4120.  *
  4121.  *  Parameters
  4122.  *
  4123.  *      szFullPath  full path name of the folder to be removed
  4124.  *
  4125.  */
  4126. HRESULT HrDestroyFolderStorage(LPTSTR szFullPath)
  4127. {
  4128.     HRESULT hr;
  4129.     LPTSTR szAll = NULL;    /* path to all files in the folder */
  4130.     HANDLE hFile;
  4131.     WIN32_FIND_DATA ffd;
  4132.  
  4133.     /* Find all files in the directory, and attempt to delete them. */
  4134.     /* Note that if somehow a subdirectory was created in this directory, */
  4135.     /* this function will fail. The loop below (that deletes files) goes */
  4136.     /* ahead regardless of error until it has tried to delete all files. */
  4137.     /* The only fatal error occurs if we can't remove the directory itself. */
  4138.  
  4139.     hr = HrAppendPath(szFullPath, szAllFilesTemplate, &szAll);
  4140.     if (hr != hrSuccess)
  4141.         goto exit;
  4142.  
  4143.     hFile = FindFirstFile(szAll, &ffd);
  4144.  
  4145.     if (hFile != INVALID_HANDLE_VALUE)
  4146.     {
  4147.         while (TRUE)
  4148.         {
  4149.             LPTSTR szTemp = NULL;
  4150.  
  4151.             /* Don't even attempt to delete directories. The directories */
  4152.             /* named "." and ".." are always included in the contents of */
  4153.             /* a directory listing, and this avoids the attempt to delete */
  4154.             /* them. */
  4155.  
  4156.             if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  4157.             {
  4158.                 if (HrAppendPath(szFullPath, ffd.cFileName, &szTemp)
  4159.                     == hrSuccess)
  4160.                 {
  4161.                     DeleteFile(szTemp);
  4162.                     FreeNull(szTemp);
  4163.                 }
  4164.             }
  4165.     
  4166.             if (!FindNextFile(hFile, &ffd))
  4167.             {
  4168.                 FindClose(hFile);
  4169.                 break;
  4170.             }
  4171.         }
  4172.     }
  4173.  
  4174.     /* Attempt to delete the folder itself. */
  4175.     if (!RemoveDirectory(szFullPath))
  4176.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  4177.  
  4178. exit:
  4179.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  4180.  
  4181.     FreeNull(szAll);
  4182.  
  4183.     DebugTraceResult(HrDestroyFolderStorage, hr);
  4184.     return hr;
  4185. }
  4186.  
  4187. /*
  4188.  *  HrFillHierarchyTable
  4189.  *
  4190.  *  Purpose     Construct table data for hierarchy below the folder pifld
  4191.  *
  4192.  *  Argument
  4193.  *              pifld       pointer to the folder to be added to
  4194.  *                          the hierarchy table.
  4195.  *              lptbl       underlying table data
  4196.  *
  4197.  */
  4198. static HRESULT HrFillHierarchyTable(PIFLD pifld, LPTABLEDATA lptbl)
  4199. {
  4200.     HRESULT hr;
  4201.     PEID peidSub = NULL;
  4202.     LPTSTR szSubFolder = NULL;  /* template for subfolder of szFolder */
  4203.     HANDLE hFindFile = FAILED_SEARCH;
  4204.     ULONG ichLocal;
  4205.     WIN32_FIND_DATA ffd;
  4206.     PIMS pims = pifld->pims;
  4207.  
  4208.     /* build a hierarchy entry for each subfolder */
  4209.     hr = HrFindFirstID(pifld, szFolderTemplate, &ichLocal,
  4210.         &szSubFolder, &hFindFile, &ffd, &peidSub);
  4211.  
  4212.     while (hr == hrSuccess)
  4213.     {
  4214.         /* build hierarchy entry for this subfolder */
  4215.         hr = HrUpdateRow(pims, lptbl, peidSub,
  4216.             (LPSPropTagArray) &sPropTagsHierarchy,
  4217.             &(ffd.ftLastWriteTime), MAPI_FOLDER);
  4218.         if (hr != hrSuccess)
  4219.             goto exit;
  4220.  
  4221.         LMFree(&pims->lmr, peidSub);
  4222.         peidSub = NULL;
  4223.  
  4224.         hr = HrFindNextID(pifld, ichLocal, szSubFolder,
  4225.             hFindFile, &ffd, &peidSub);
  4226.     }
  4227.  
  4228.     if (GetScode(hr) == MAPI_E_NOT_FOUND)
  4229.         hr = hrSuccess;
  4230.  
  4231. exit:
  4232.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  4233.  
  4234.     /* close the search */
  4235.     CloseIDSearch(&hFindFile, &szSubFolder);
  4236.     LMFree(&pims->lmr, peidSub);
  4237.  
  4238.     DebugTraceResult(HrFillHierarchyTable, hr);
  4239.     return hr;
  4240. }
  4241.  
  4242. /*
  4243.  * HrDuplicateIFLD
  4244.  *
  4245.  *  Searches the existing subfolders of pifldParent for a folder with
  4246.  *  PR_DISPLAY_NAME of szName. If it finds a match, opens it and returns
  4247.  *  the matching folder (doing nothing else). If no folder matches, then
  4248.  *  the routine creates a new folder and copies all properties from the
  4249.  *  old folder into the new folder.
  4250.  *
  4251.  *      pifld           the old folder
  4252.  *      pifldParent     parent of the new folder
  4253.  *      szName          name of new folder
  4254.  *      szComment       comment for new folder
  4255.  *      ppifldNew       pointer to the location to return the new folder
  4256.  *
  4257.  */
  4258. static HRESULT HrDuplicateIFLD(PIFLD pifld, PIFLD pifldParent, LPTSTR szName,
  4259.     LPTSTR szComment, PIFLD *ppifldNew)
  4260. {
  4261.     HRESULT hr = hrSuccess;
  4262.     BOOL fCreated = FALSE;
  4263.     PIFLD pifldNew = NULL;
  4264.  
  4265.     /* create new folder */
  4266.  
  4267.     hr = HrCreateFolder(pifldParent, szName, szComment, TRUE, &pifldNew,
  4268.         &fCreated);
  4269.     if (hr != hrSuccess)
  4270.         goto exit;
  4271.  
  4272.     /* copy pifld's properties over except excluded stuff (see */
  4273.     /* sptaExclFldProps definition). */
  4274.  
  4275.     if (fCreated)
  4276.     {
  4277.         LPSPropProblemArray pprba = NULL;
  4278.         LPMESSAGE lpmsgPropSrc = NULL;  /* pifld's property message */
  4279.         LPMESSAGE lpmsgPropNew = NULL;  /* new ifld's property message */
  4280.  
  4281.         hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims, FALSE,
  4282.                 &lpmsgPropSrc);
  4283.  
  4284.         if (hr == hrSuccess)
  4285.             hr = HrOpenPropertyMessageRetry(pifldNew->peid,
  4286.                     pifldNew->pims, TRUE, &lpmsgPropNew);
  4287.  
  4288.         if (hr == hrSuccess)
  4289.             hr = lpmsgPropSrc->lpVtbl->CopyTo(lpmsgPropSrc, 0, NULL,
  4290.                     (LPSPropTagArray) &sptaExclFldProps, 0, NULL,
  4291.                     (LPIID) &IID_IMessage, lpmsgPropNew, 0L, &pprba);
  4292.  
  4293.         UlRelease(lpmsgPropSrc);
  4294.         UlRelease(lpmsgPropNew);
  4295.  
  4296.         if (pprba)
  4297.         {
  4298.             Assert(hr == hrSuccess);
  4299.             LMFree(&pifld->pims->lmr, pprba);
  4300.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  4301.         }
  4302.     }
  4303.  
  4304.     if (hr != hrSuccess)
  4305.         goto exit;
  4306.  
  4307.     *ppifldNew = pifldNew;
  4308.  
  4309. exit:
  4310.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  4311.  
  4312.     DebugTraceResult(HrDuplicateIFLD, hr);
  4313.     return hr;
  4314. }
  4315.  
  4316. /*
  4317.  -  HrGetSortOrder
  4318.  -
  4319.  *  Purpose:
  4320.  *      returns in *lppsSortOrder the order of contents tables.
  4321.  *      Caller must use MAPIFreeBuffer to release *lppsSortOrder.
  4322.  *
  4323.  *  Parameters
  4324.  *       pifld          folder whose contents table is referred to
  4325.  *       lppsSortOrder  pointer to sort order variable
  4326.  *
  4327.  *  Returns:
  4328.  *      HRESULT
  4329.  *
  4330.  */
  4331. static HRESULT HrGetSortOrder(PIFLD pifld, LPSSortOrderSet *lppsSortOrder)
  4332. {
  4333.     ULONG cValues;              /* number of property values returned */
  4334.     LPSPropValue pval = NULL;   /* returned property array */
  4335.     HRESULT hr;
  4336.     ULONG cbSos;                /* number of bytes in sort order */
  4337.     SCODE sc;
  4338.     static SizedSPropTagArray(1, sptaSortOrder) =
  4339.     {
  4340.         1,
  4341.         {
  4342.             PR_SMS_CONTENTS_SORT_ORDER
  4343.         }
  4344.     };
  4345.  
  4346.     Assert(pifld);
  4347.     Assert(lppsSortOrder);
  4348.  
  4349.     /* get the sort order property */
  4350.     hr = pifld->lpVtbl->GetProps(pifld,
  4351.         (LPSPropTagArray) &sptaSortOrder, 0, /* ansi */
  4352.         &cValues, &pval);
  4353.  
  4354.     if (hr != hrSuccess)
  4355.     {
  4356.         Assert(HR_FAILED(hr) || GetScode(hr) == MAPI_W_ERRORS_RETURNED);
  4357.         cbSos = CbSSortOrderSet((LPSSortOrderSet) &sSortOrderContentsDefault);
  4358.  
  4359.         /* sort order is the default */
  4360.         sc = LMAlloc(&pifld->pims->lmr, cbSos, lppsSortOrder);
  4361.         if (sc != S_OK)
  4362.         {
  4363.             hr = ResultFromScode(sc);
  4364.             goto exit;
  4365.         }
  4366.  
  4367.         memcpy(*lppsSortOrder, &sSortOrderContentsDefault, (UINT) cbSos);
  4368.     }
  4369.     else
  4370.     {
  4371.         /* This property should contain a flattened array of */
  4372.         /* SSortOrder structures. */
  4373.  
  4374.         Assert(cValues == 1L);
  4375.         cbSos = sizeof(ULONG) * pval->Value.MVl.cValues;
  4376.  
  4377.         sc = LMAlloc(&pifld->pims->lmr, offsetof(SSortOrderSet, aSort) + cbSos,
  4378.             lppsSortOrder);
  4379.         if (sc != S_OK)
  4380.         {
  4381.             hr = ResultFromScode(sc);
  4382.             goto exit;
  4383.         }
  4384.  
  4385.         (*lppsSortOrder)->cSorts = pval->Value.MVl.cValues / 2;
  4386.         (*lppsSortOrder)->cCategories = 0;
  4387.         (*lppsSortOrder)->cExpanded = 0;
  4388.         memcpy((*lppsSortOrder)->aSort, pval->Value.MVl.lpl, (UINT) cbSos);
  4389.     }
  4390.     hr = hrSuccess;
  4391.  
  4392. exit:
  4393.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  4394.  
  4395.     LMFree(&pifld->pims->lmr, pval);
  4396.     DebugTraceResult(HrGetSortOrder, hr);
  4397.     return hr;
  4398. }
  4399.  
  4400. /*
  4401.  -  DestroyMessageList
  4402.  -
  4403.  *  Purpose:
  4404.  *      frees allocated memory inside lppEntryList. The cValues field
  4405.  *      inside the entrylist must contain the count of the number of
  4406.  *      entryids that are in the entrylist so that the entryids can be
  4407.  *      freed.
  4408.  *
  4409.  *  Parameters
  4410.  *       lppEntryList   pointer to a list of entryids; returned NULL
  4411.  *
  4412.  */
  4413. static void DestroyMessageList(PLMR plmr, LPENTRYLIST *lppEntryList)
  4414. {
  4415.     Assert(lppEntryList);
  4416.  
  4417.     if (*lppEntryList != NULL && (*lppEntryList)->lpbin != NULL)
  4418.     {
  4419.         ULONG cValues = (*lppEntryList)->cValues;
  4420.         LPSBinary lpbin = (*lppEntryList)->lpbin;
  4421.  
  4422.         Assert(cValues <= UINT_MAX / sizeof(SBinary));
  4423.         Assert(!IsBadReadPtr(lpbin, (UINT) cValues * sizeof(SBinary)));
  4424.  
  4425.         for (; cValues; cValues--, lpbin++)
  4426.         {
  4427.             Assert(lpbin->cb <= UINT_MAX);
  4428.             Assert(!IsBadReadPtr(lpbin->lpb, (UINT) lpbin->cb));
  4429.             LMFree(plmr, lpbin->lpb);
  4430.         }
  4431.  
  4432.         FreeNull(*lppEntryList);
  4433.     }
  4434.  
  4435.     *lppEntryList = NULL;
  4436.     return;
  4437. }
  4438.  
  4439. /*
  4440.  -  HrCreateMessageList
  4441.  -
  4442.  *  Purpose:
  4443.  *      Counts up and returns a list of entryids for every message in
  4444.  *      the given folder. If no messages are found in the folder, returns
  4445.  *      NULL instead of the list. To get the count, this routine uses
  4446.  *      FindFirstFile, FindNextFile on all files with a ".msg" extension.
  4447.  *      We use PR_CONTENT_COUNT to get an estimate of how many msgs are in
  4448.  *      the folder, but don't depend on that property being correct. If
  4449.  *      the property is incorrect, this routine updates it (SIDE EFFECT)
  4450.  *      before returning to the caller.
  4451.  *
  4452.  *  Parameters
  4453.  *       pifld          the folder object
  4454.  *       lppEntryList   [out] pointer to the location to return a list of
  4455.  *                      entryids of messages in pifld.
  4456.  *
  4457.  */
  4458. static HRESULT HrCreateMessageList(PIFLD pifld, LPENTRYLIST *lppEntryList)
  4459. {
  4460.     HRESULT hr;
  4461.     LONG cMsgsOrig;             /* value of PR_CONTENT_COUNT property */
  4462.     LONG cMsgsAlloced;          /* number of msgs allocated in pent->lpbin */
  4463.     ULONG ichLocal;             /* start of message name in full path name */
  4464.     LPSBinary lpbin;
  4465.     LPSBinary lpbinMac;
  4466.  
  4467.     PEID peidNext = NULL;       /* next entryID to be added to the list */
  4468.     WIN32_FIND_DATA ffd;
  4469.     LPTSTR szFile = NULL;
  4470.     HANDLE hFindFile = FAILED_SEARCH;
  4471.     LPENTRYLIST pent = NULL;
  4472.  
  4473.     /* get the number of messages in pifld */
  4474.     /* This value may be incorrect, so use it to simply get an idea */
  4475.     /* of how many messages are in the folder. Allocate extra space */
  4476.     /* for extra (uncounted) messages, and, if necessary, realloc */
  4477.     /* the array until we really count all of them. After we've counted */
  4478.     /* all messages, update PR_CONTENT_COUNT with the correct number. */
  4479.     /* Assuming that there are extra messages in the folder avoids having */
  4480.     /* to realloc the array as often when there actually are extra ones. */
  4481.  
  4482. #define CMSGS_EXTRA     10  /* # of extra spaces to alloc in msg array */
  4483.  
  4484.     hr = HrGetSingleProp((LPMAPIPROP) pifld, &pifld->pims->lmr,
  4485.             PR_CONTENT_COUNT, &cMsgsOrig);
  4486.  
  4487.     /* Don't allow the failure to get PR_CONTENT_COUNT to keep this call */
  4488.     /* from succeeding. Go ahead and do the call anyway. */
  4489.  
  4490.     if (hr != hrSuccess)
  4491.     {
  4492.         hr = hrSuccess;
  4493.  
  4494.         /* We failed to get the property. Set the "original value" */
  4495.         /* to an impossible value so that we will attempt to update it */
  4496.         /* at the bottom of this function. */
  4497.  
  4498.         cMsgsOrig = -1;
  4499.         cMsgsAlloced = CMSGS_EXTRA;
  4500.     }
  4501.     else
  4502.         cMsgsAlloced = cMsgsOrig + CMSGS_EXTRA;
  4503.  
  4504.     /* Allocate space for the entrylist. */
  4505.  
  4506.     hr = HrAlloc(sizeof(SBinaryArray) + (cMsgsAlloced * sizeof(SBinary)),
  4507.         &pent);
  4508.     if (hr != hrSuccess)
  4509.         goto exit;
  4510.  
  4511.     /* We allocated the space all at once; therefore, we initialize the */
  4512.     /* lpbin pointer to point just past the binary array. */
  4513.  
  4514.     pent->lpbin = (LPSBinary) ((LPBYTE) pent + sizeof(SBinaryArray));
  4515.  
  4516.     /* The cValues field of the entrylist must be kept up-to-date as we */
  4517.     /* add entryids to the entrylist. DestroyMessageList (see above) uses */
  4518.     /* that field when it frees up the entryids, and if it is wrong, the */
  4519.     /* wrong number of entryids will get freed (resulting in a crash if */
  4520.     /* it is too big, or a memory leak if it is too small). */
  4521.  
  4522.     pent->cValues = 0;
  4523.  
  4524.     lpbin = pent->lpbin;
  4525.     lpbinMac = lpbin + cMsgsAlloced;
  4526.  
  4527.     /* get the first entryID */
  4528.     hr = HrFindFirstID(pifld, szMessageTemplate, &ichLocal,
  4529.         &szFile, &hFindFile, &ffd, &peidNext);
  4530.  
  4531.     if (hr != hrSuccess)
  4532.     {
  4533.         /* MAPI_E_NOT_FOUND means that there are no more messages in */
  4534.         /* the folder. This is not an error. Because this was the first */
  4535.         /* file, there were no messages in the folder at all. Therefore, */
  4536.         /* free any allocated memory, reset hr, and get out. */
  4537.  
  4538.         if (GetScode(hr) == MAPI_E_NOT_FOUND)
  4539.         {
  4540.             DestroyMessageList(&pifld->pims->lmr, &pent);
  4541.  
  4542.             /* pent should be NULL now. We're going to return it to */
  4543.             /* our caller below. */
  4544.  
  4545.             AssertSz(pent == NULL, "DestroyMessageList is broken.");
  4546.  
  4547.             hr = hrSuccess;
  4548.         }
  4549.  
  4550.         goto exit;
  4551.     }
  4552.  
  4553.     while (TRUE)
  4554.     {
  4555.         lpbin->cb = CbEID(peidNext);
  4556.         lpbin->lpb = (LPBYTE) peidNext;
  4557.  
  4558.         ++(pent->cValues);
  4559.  
  4560.         hr = HrFindNextID(pifld, ichLocal, szFile, hFindFile, &ffd, &peidNext);
  4561.  
  4562.         if (hr != hrSuccess)
  4563.         {
  4564.             /* MAPI_E_NOT_FOUND means that there are no more messages in */
  4565.             /* the folder. This is not an error. */
  4566.  
  4567.             if (GetScode(hr) == MAPI_E_NOT_FOUND)
  4568.                 hr = hrSuccess;
  4569.  
  4570.             goto exit;
  4571.         }
  4572.  
  4573.         lpbin++;
  4574.  
  4575.         if (lpbin >= lpbinMac)
  4576.         {
  4577.             /* We need to realloc the array. */
  4578.             cMsgsAlloced += CMSGS_EXTRA;
  4579.  
  4580.             hr = HrRealloc((cMsgsAlloced * sizeof(SBinary))
  4581.                 + sizeof(SBinaryArray), pent, &pent);
  4582.             if (hr != hrSuccess)
  4583.                 goto exit;
  4584.  
  4585.             /* reset the pointer to the end of the array. */
  4586.             lpbinMac = pent->lpbin + cMsgsAlloced;
  4587.         }
  4588.     }
  4589.  
  4590. exit:
  4591.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  4592.  
  4593.     /* close the search */
  4594.     CloseIDSearch(&hFindFile, &szFile);
  4595.  
  4596.     if (hr == hrSuccess)
  4597.     {
  4598.         LONG cMsgsFound = 0;
  4599.  
  4600.         /* If PR_CONTENT_COUNT was incorrect, then update it. Note that */
  4601.         /* the number of messages we found is kept in pent->cValues unless */
  4602.         /* we didn't find any messages (in which case, pent is NULL, and */
  4603.         /* we assume no messages were found). */
  4604.  
  4605.         if (pent)
  4606.             cMsgsFound = pent->cValues;
  4607.  
  4608.         if (cMsgsFound != cMsgsOrig)
  4609.             (void) HrSetOneROFolderProp(pifld, cMsgsFound, PR_CONTENT_COUNT);
  4610.  
  4611.         *lppEntryList = pent;
  4612.     }
  4613.     else if (pent)
  4614.         DestroyMessageList(&pifld->pims->lmr, &pent);
  4615.  
  4616.     DebugTraceResult(HrCreateMessageList, hr);
  4617.     return hr;
  4618. }
  4619.  
  4620. /*
  4621.  -  HrDeleteSubDirectory
  4622.  -
  4623.  *  Purpose:
  4624.  *      Delete a subdirectory of the current directory
  4625.  *
  4626.  *  Parameters
  4627.  *       pifldParent    parent folder of folder to delete
  4628.  *       peid           entryid of folder to be deleted
  4629.  *       ulFlags        DEL_FOLDERS and/or DEL_MESSAGES
  4630.  *       fContentsOnly  BOOL. If TRUE, don't delete peid itself.
  4631.  *
  4632.  *  Side effects:
  4633.  *      If either flag is set, it deletes all that it can before returning.
  4634.  *      It is possible for the subdirectory to fail at being deleted but to
  4635.  *      have some of its contents removed.
  4636.  *
  4637.  */
  4638. static HRESULT HrDeleteSubDirectory(PIFLD pifldParent, PEID peidToDelete,
  4639.     ULONG ulFlags, BOOL fContentsOnly)
  4640. {
  4641.  
  4642.     HANDLE hFindFile = FAILED_SEARCH;
  4643.     LPTSTR szToDelete = NULL;   /* full path name of subdir to be deleted */
  4644.     PEID peidSubDir = NULL;     /* eid of subdirectory */
  4645.     PIFLD pifldToDelete = NULL; /* opened subdirectory to delete */
  4646.     ULONG ulOffset;             /* start of local name of subdir in szSubDir */
  4647.     ULONG ulObjType;
  4648.     LPENTRYLIST lpMessages = NULL;  /* list of messages to be deleted */
  4649.     LPTSTR szRelativePath = NULL;   /* relative path for id searches */
  4650.     WIN32_FIND_DATA ffd;
  4651.     HRESULT hr = hrSuccess;
  4652.     PIMS pims = pifldParent->pims;
  4653.  
  4654.     hr = HrFullPathName(pims->szStorePath, peidToDelete->szPath,
  4655.         NULL, &szToDelete);
  4656.     if (hr != hrSuccess)
  4657.         goto exit;
  4658.  
  4659.     /* open up the folder to be deleted */
  4660.     hr = pifldParent->lpVtbl->OpenEntry(pifldParent, CbEID(peidToDelete),
  4661.         (LPENTRYID) peidToDelete, NULL, MAPI_MODIFY,
  4662.         &ulObjType, (LPUNKNOWN *) &pifldToDelete);
  4663.  
  4664.     if (hr != hrSuccess)
  4665.         goto exit;
  4666.  
  4667.     if (!(ulFlags & DEL_FOLDERS))
  4668.     {
  4669.         /* The caller didn't specify DEL_FOLDERS. Check to make sure that */
  4670.         /* there aren't any subfolders before allowing the deletion to */
  4671.         /* continue. If there are subfolders, return MAPI_E_HAS_FOLDERS. */
  4672.  
  4673.         /* Look for any subfolders */
  4674.  
  4675.         hr = HrFindFirstID(pifldToDelete, szFolderTemplate, &ulOffset,
  4676.             &szRelativePath, &hFindFile, &ffd, &peidSubDir);
  4677.  
  4678.         CloseIDSearch(&hFindFile, &szRelativePath);
  4679.  
  4680.         if (hr == hrSuccess)
  4681.         {
  4682.             /* release the eid */
  4683.             LMFree(&pims->lmr, peidSubDir);
  4684.             peidSubDir = NULL;
  4685.  
  4686.             hr = ResultFromScode(MAPI_E_HAS_FOLDERS);
  4687.             goto exit;
  4688.         }
  4689.         else if (GetScode(hr) != MAPI_E_NOT_FOUND)
  4690.             goto exit;
  4691.  
  4692.         hr = hrSuccess;
  4693.     }
  4694.     else
  4695.     {
  4696.         /* set up the search for subdirectories */
  4697.         hr = HrFindFirstID(pifldToDelete, szFolderTemplate, &ulOffset,
  4698.             &szRelativePath, &hFindFile, &ffd, &peidSubDir);
  4699.     
  4700.         /* Delete each subdirectory*/
  4701.         while (hr == hrSuccess)
  4702.         {
  4703.             hr = HrDeleteSubDirectory(pifldToDelete, peidSubDir, ulFlags, FALSE);
  4704.     
  4705.             /* release the eid */
  4706.             LMFree(&pims->lmr, peidSubDir);
  4707.             peidSubDir = NULL;
  4708.     
  4709.             /* Errors returned from HrDeleteSubDirectory should be fatal. */
  4710.             /* Don't ignore them, because if we do, then we will leave a */
  4711.             /* corrupt message store behind. MAPI_E_HAS_MESSAGES can come */
  4712.             /* back from below, as well as MAPI_E_SUBMITTED, etc. */
  4713.  
  4714.             if (hr != hrSuccess)
  4715.                 goto exit;
  4716.  
  4717.             /* Delete the other subdirectories */
  4718.             hr = HrFindNextID(pifldToDelete, ulOffset, szRelativePath,
  4719.                 hFindFile, &ffd, &peidSubDir);
  4720.         }
  4721.     
  4722.         if (GetScode(hr) == MAPI_E_NOT_FOUND)
  4723.             hr = hrSuccess;
  4724.         else
  4725.             goto exit;
  4726.     
  4727.         /* end the search */
  4728.         CloseIDSearch(&hFindFile, &szRelativePath);
  4729.     }
  4730.  
  4731.     /* delete all messages if DEL_MESSAGES */
  4732.     hr = HrCreateMessageList(pifldToDelete, &lpMessages);
  4733.     if (hr != hrSuccess)
  4734.         goto exit;
  4735.  
  4736.     if (lpMessages && !(ulFlags & DEL_MESSAGES))
  4737.     {
  4738.         hr = ResultFromScode(MAPI_E_HAS_MESSAGES);
  4739.         goto exit;
  4740.     }
  4741.  
  4742.     if (lpMessages)
  4743.     {
  4744.         hr = pifldToDelete->lpVtbl->DeleteMessages(pifldToDelete,
  4745.             lpMessages, 0L, NULL, 0L);
  4746.         if (hr != hrSuccess)
  4747.             goto exit;
  4748.     }
  4749.  
  4750.     UlRelease(pifldToDelete);
  4751.     pifldToDelete = NULL;
  4752.  
  4753.     if (!fContentsOnly)
  4754.     {
  4755.         ULONG ulRowCount;
  4756.         LPMAPITABLE pmt;
  4757.  
  4758.         /* delete the files on the filesystem */
  4759.         hr = HrDestroyFolderStorage(szToDelete);
  4760.         if (hr != hrSuccess)
  4761.             goto exit;
  4762.  
  4763.         ChangeTable(pims, pifldParent->peid, peidToDelete, MAPI_FOLDER,
  4764.             TABLE_ROW_DELETED, TRUE);
  4765.  
  4766.         hr = pifldParent->lpVtbl->GetHierarchyTable(pifldParent, 0, &pmt);
  4767.  
  4768.         if (hr == hrSuccess)
  4769.         {
  4770.             hr = pmt->lpVtbl->GetRowCount(pmt, 0, &ulRowCount);
  4771.             UlRelease(pmt);
  4772.         }
  4773.  
  4774.         if (hr == hrSuccess && ulRowCount == 0)
  4775.             hr = HrSetSubFolderProp(pifldParent, FALSE);        
  4776.  
  4777.         /* Ignore errors changing and updating the hierarchy table. */
  4778.         /* The entire delete shouldn't fail simply because we couldn't */
  4779.         /* update the hierarchy table. */
  4780.  
  4781.         #ifdef DEBUG
  4782.         if (hr != hrSuccess)
  4783.         {
  4784.             TraceSz1("HrDeleteSubDirectory: Ignoring partial failure (%s)"
  4785.                 " fixing hierarchy table.", SzDecodeScode(GetScode(hr)));
  4786.         }
  4787.         #endif
  4788.  
  4789.         hr = hrSuccess;
  4790.     }
  4791.  
  4792. exit:
  4793.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  4794.  
  4795.     UlRelease(pifldToDelete);
  4796.  
  4797.     LMFree(&pims->lmr, peidSubDir);
  4798.     FreeNull(szToDelete);
  4799.  
  4800.     CloseIDSearch(&hFindFile, &szRelativePath);
  4801.  
  4802.     if (lpMessages)
  4803.         DestroyMessageList(&pims->lmr, &lpMessages);
  4804.  
  4805.     DebugTraceResult(HrDeleteSubDirectory, hr);
  4806.     return hr;
  4807. }
  4808.  
  4809. /*
  4810.  -  HrCopyFolder
  4811.  -
  4812.  *  Purpose:
  4813.  *      moves or copies one folder into another
  4814.  *
  4815.  *  Parameters
  4816.  *      peidFldSrc      pointer to the entryid of the folder to copy or move.
  4817.  *      pifldDst        the parent folder into which we copy or move
  4818.  *      ulFlags         MAPI_MOVE, MAPI_NOREPLACE
  4819.  *      ptagaExcl       List of properties to exclude from the copy.
  4820.  *
  4821.  *  Returns:
  4822.  *
  4823.  *  Errors:
  4824.  */
  4825. static HRESULT HrCopyFolder(PEID peidFldSrc, PIFLD pifldDstPar, ULONG ulFlags,
  4826.     LPSPropTagArray ptagaExcl)
  4827. {
  4828.     HRESULT hr;
  4829.     PIFLD pifldSrc = NULL;
  4830.  
  4831.     PIFLD pifldCopy = NULL;     /* copy of pifldSrc */
  4832.     LPTSTR szComment = NULL;    /* comment for copy of pifldSrc */
  4833.     PIMS pims = pifldDstPar->pims;
  4834.     ULONG ulObjType;
  4835.     LPSTR szNewFolderName;
  4836.     PIFLD pifldParent = NULL;
  4837.     ULONG cValNC;
  4838.     LPSPropValue pvalNC = NULL;
  4839.     const static SizedSPropTagArray(2, sptaNC) =
  4840.     {
  4841.         2,
  4842.         {
  4843.             PR_DISPLAY_NAME,
  4844.             PR_COMMENT
  4845.         }
  4846.     };
  4847.  
  4848.     /* Open the source folder for modification only if we are moving */
  4849.  
  4850.     hr = pims->lpVtbl->OpenEntry(pims, CbEID(peidFldSrc),
  4851.         (LPENTRYID) peidFldSrc, NULL, (ulFlags & MAPI_MOVE) ? MAPI_MODIFY : 0L,
  4852.         &ulObjType, (LPUNKNOWN *) &pifldSrc);
  4853.     if (hr != hrSuccess)
  4854.         goto exit;
  4855.  
  4856.     /* if this is a move, make sure the source folder is not in use */
  4857.     if ((ulFlags & MAPI_MOVE) && pifldSrc->cRef > 1)
  4858.     {
  4859.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  4860.         goto exit;
  4861.     }
  4862.  
  4863.     /* get the name and comment from pifldSrc */
  4864.     hr = pifldSrc->lpVtbl->GetProps(pifldSrc,
  4865.         (LPSPropTagArray) &sptaNC, 0, /* ansi */
  4866.         &cValNC, &pvalNC);
  4867.     if (hr != hrSuccess)
  4868.     {
  4869.         if (GetScode(hr) == MAPI_W_ERRORS_RETURNED)
  4870.         {
  4871.             if (PROP_TYPE(pvalNC->ulPropTag) == PT_ERROR)
  4872.             {
  4873.                 hr = ResultFromScode(pvalNC->Value.err);
  4874.                 goto exit;
  4875.             }
  4876.  
  4877.             hr = hrSuccess;
  4878.         }
  4879.         else
  4880.             goto exit;
  4881.     }
  4882.  
  4883.     /* create new folder for the copy */
  4884.     szNewFolderName = pvalNC[0].Value.LPSZ;
  4885.  
  4886.     if (PROP_TYPE(pvalNC[1].ulPropTag) != PT_ERROR)
  4887.         szComment = pvalNC[1].Value.LPSZ;
  4888.     else
  4889.         szComment = NULL;
  4890.  
  4891.     hr = HrDuplicateIFLD(pifldSrc, pifldDstPar, szNewFolderName, szComment,
  4892.         &pifldCopy);
  4893.     if (hr != hrSuccess)
  4894.         goto exit;
  4895.  
  4896.     /* move or copy the interior of this folder (recursive) */
  4897.     hr = HrCopyContents(pifldSrc, pifldCopy, ulFlags, ptagaExcl);
  4898.     if (hr != hrSuccess)
  4899.         goto exit;
  4900.  
  4901.     UlRelease(pifldSrc);
  4902.     pifldSrc = NULL;
  4903.  
  4904.     /* invalidate the source folder if this is a move */
  4905.     if (ulFlags & MAPI_MOVE)
  4906.     {
  4907.         LPMAPITABLE pmt;
  4908.         ULONG ulRowCount;
  4909.         LPTSTR szFullPathFolder;
  4910.  
  4911.         hr = HrFullPathName(pims->szStorePath,
  4912.             peidFldSrc->szPath, NULL, &szFullPathFolder);
  4913.         if (hr != hrSuccess)
  4914.             goto exit;
  4915.  
  4916.         /* delete it */
  4917.         hr = HrDestroyFolderStorage(szFullPathFolder);
  4918.  
  4919.         FreeNull(szFullPathFolder);
  4920.  
  4921.         if (hr != hrSuccess)
  4922.             goto exit;
  4923.  
  4924.         /* update the hierarchy table of the source's parent */
  4925.         hr = HrOpenParent(pims, peidFldSrc, MAPI_MODIFY, &pifldParent);
  4926.         if (hr != hrSuccess)
  4927.             goto exit;
  4928.  
  4929.         ChangeTable(pims, pifldParent->peid, peidFldSrc, MAPI_FOLDER,
  4930.             TABLE_ROW_DELETED, TRUE);
  4931.  
  4932.         hr = pifldParent->lpVtbl->GetHierarchyTable(pifldParent, 0, &pmt);
  4933.  
  4934.         if (hr == hrSuccess)
  4935.             hr = pmt->lpVtbl->GetRowCount(pmt, 0, &ulRowCount);
  4936.  
  4937.         UlRelease(pmt);
  4938.  
  4939.         if (hr == hrSuccess && ulRowCount == 0)
  4940.             hr = HrSetSubFolderProp(pifldParent, FALSE);        
  4941.     }
  4942.  
  4943. exit:
  4944.     UlRelease(pifldSrc);
  4945.  
  4946.     LMFree(&pims->lmr, pvalNC);
  4947.     UlRelease(pifldParent);
  4948.  
  4949.     UlRelease(pifldCopy);
  4950.  
  4951.     DebugTraceResult(HrCopyFolder, hr);
  4952.     return hr;
  4953. }
  4954.  
  4955. /*
  4956.  * HrCopyContents
  4957.  *
  4958.  *  Purpose         copy or move the contents of the source folder to
  4959.  *                  the destination
  4960.  *
  4961.  *  Parameters
  4962.  *      pifldSrc        source folder
  4963.  *      pifldDst        destination folder
  4964.  *      ulFlags         MAPI_MOVE, MAPI_NOREPLACE, MAPI_DIALOG
  4965.  *      ptagaExcl       List of properties to exclude from the copy.
  4966.  *
  4967.  */
  4968. static HRESULT HrCopyContents(PIFLD pifldSrc, PIFLD pifldDst, ULONG ulFlags,
  4969.     LPSPropTagArray ptagaExcl)
  4970. {
  4971.     PEID peidNext = NULL;       /* eid of next folder to move */
  4972.     ULONG ichLocal;
  4973.     PLMR plmr = &pifldSrc->pims->lmr;
  4974.  
  4975.     /* relative path name of next folder to be moved */
  4976.     LPTSTR szFile = NULL;
  4977.     HANDLE hFindFile = FAILED_SEARCH;
  4978.     HRESULT hr = hrSuccess;
  4979.     ULONG ulMessageFlags = 0L;  /* flags for the message copy */
  4980.     WIN32_FIND_DATA ffd;
  4981.  
  4982.     /* move or copy each message inside the folder */
  4983.     if (ulFlags & MAPI_MOVE)
  4984.         ulMessageFlags = MESSAGE_MOVE;
  4985.  
  4986.     if (!FContainsProp(PR_CONTAINER_CONTENTS, ptagaExcl))
  4987.     {
  4988.         LPENTRYLIST lpMessages = NULL;
  4989.  
  4990.         /* make a message list */
  4991.         hr = HrCreateMessageList(pifldSrc, &lpMessages);
  4992.         if (hr != hrSuccess)
  4993.             goto exit;
  4994.  
  4995.         /* move/copy messages */
  4996.         if (lpMessages)
  4997.         {
  4998.             hr = pifldSrc->lpVtbl->CopyMessages(pifldSrc,
  4999.                 lpMessages, 0, (LPMAPIFOLDER) pifldDst, 0,
  5000.                 NULL, ulMessageFlags);
  5001.  
  5002.             DestroyMessageList(plmr, &lpMessages);
  5003.  
  5004.             if (hr != hrSuccess)
  5005.                 goto exit;
  5006.         }
  5007.     }
  5008.  
  5009.     /* move/copy subfolders if required */
  5010.     if ((ulFlags & MAPI_MOVE)
  5011.         || (!FContainsProp(PR_CONTAINER_HIERARCHY, ptagaExcl)))
  5012.     {
  5013.         /* get the first entryID for a subfolder */
  5014.         hr = HrFindFirstID(pifldSrc, szFolderTemplate, &ichLocal,
  5015.             &szFile, &hFindFile, &ffd, &peidNext);
  5016.  
  5017.         while (hr == hrSuccess)
  5018.         {
  5019.             /* copy the eid to the destination */
  5020.             hr = HrCopyFolder(peidNext, pifldDst, ulFlags, ptagaExcl);
  5021.             if (hr != hrSuccess)
  5022.                 goto exit;
  5023.  
  5024.             LMFree(plmr, peidNext);
  5025.             peidNext = NULL;
  5026.  
  5027.             if (ulFlags & MAPI_MOVE)
  5028.             {
  5029.                 /* The folder should have been destroyed; therefore, */
  5030.                 /* start the search again. */
  5031.  
  5032.                 CloseIDSearch(&hFindFile, &szFile);
  5033.  
  5034.                 hr = HrFindFirstID(pifldSrc, szFolderTemplate, &ichLocal,
  5035.                     &szFile, &hFindFile, &ffd, &peidNext);
  5036.             }
  5037.             else
  5038.                 hr = HrFindNextID(pifldSrc, ichLocal, szFile,
  5039.                     hFindFile, &ffd, &peidNext);
  5040.         }
  5041.  
  5042.         if (GetScode(hr) != MAPI_E_NOT_FOUND)
  5043.             goto exit;          /* uh oh.  got a real error */
  5044.  
  5045.         hr = hrSuccess;
  5046.     }
  5047.  
  5048. exit:
  5049.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No warning expected");
  5050.  
  5051.     /* close the search */
  5052.     CloseIDSearch(&hFindFile, &szFile);
  5053.     LMFree(plmr, peidNext);
  5054.  
  5055.     DebugTraceResult(HrCopyContents, hr);
  5056.     return hr;
  5057. }
  5058.  
  5059. /*
  5060.  * HrEIDFromDisplayName
  5061.  *
  5062.  *  Purpose
  5063.  *      Searches the table given, looking for a matching PR_DISPLAY_NAME.
  5064.  *      If found, the routine returns the value of the corresponding
  5065.  *      PR_ENTRYID column, converted to an internal PEID. If not found, the
  5066.  *      routine returns a NULL.
  5067.  *
  5068.  *  Parameters
  5069.  *      pmt: The IMAPITable object to search for the display name.
  5070.  *      szName: The display name to search for.
  5071.  *      plmr: A pointer to the linked memory routines to allow freeing memory.
  5072.  *      ppeid: A pointer to the location to return the internal entryid of
  5073.  *          the PR_ENTRYID column that corresponds to the matching display
  5074.  *          name. If no matching display name was found, the routine returns
  5075.  *          NULL in this location.
  5076.  *  
  5077.  */
  5078. static HRESULT HrEIDFromDisplayName(LPMAPITABLE pmt, LPSTR szName, PLMR plmr,
  5079.     PEID *ppeid)
  5080. {
  5081.     HRESULT hr;
  5082.     LPSRowSet prws = NULL;
  5083.     PEID peid = NULL;
  5084.  
  5085.     SizedSPropTagArray(2, spta) =
  5086.     {
  5087.         2,
  5088.         {
  5089.             PR_ENTRYID,
  5090.             PR_DISPLAY_NAME
  5091.         }
  5092.     };
  5093.  
  5094.     hr = pmt->lpVtbl->SetColumns(pmt, (LPSPropTagArray) &spta, 0);
  5095.     if (hr != hrSuccess)
  5096.         goto exit;
  5097.  
  5098.     hr = pmt->lpVtbl->SeekRow(pmt, BOOKMARK_BEGINNING, 0, NULL);
  5099.     if (hr != hrSuccess)
  5100.         goto exit;
  5101.  
  5102.     while (TRUE)
  5103.     {
  5104.         LPSTR szCurName;
  5105.         LPSPropValue pval;
  5106.  
  5107.         hr = pmt->lpVtbl->QueryRows(pmt, 1, 0, &prws);
  5108.         if (hr != hrSuccess)
  5109.             goto exit;
  5110.  
  5111.         if (prws->cRows == 0)
  5112.             break;
  5113.  
  5114.         Assert(prws);
  5115.         Assert(prws->cRows == 1);
  5116.         Assert(prws->aRow[0].lpProps);
  5117.         Assert(prws->aRow[0].lpProps[0].ulPropTag == PR_ENTRYID);
  5118.         Assert(prws->aRow[0].lpProps[1].ulPropTag == PR_DISPLAY_NAME);
  5119.  
  5120.         pval = prws->aRow[0].lpProps;
  5121.  
  5122.         szCurName = (LPSTR) pval[1].Value.lpszA;
  5123.  
  5124.         if (lstrcmpi(szName, szCurName) == 0)
  5125.         {
  5126.             SCODE sc;
  5127.             PEID peidTemp = (PEID) pval->Value.bin.lpb;
  5128.             UINT cbEID = (UINT) pval->Value.bin.cb;
  5129.  
  5130.             sc = LMAlloc(plmr, cbEID, &peid);
  5131.             if (sc != S_OK)
  5132.             {
  5133.                 hr = ResultFromScode(sc);
  5134.                 goto exit;
  5135.             }
  5136.  
  5137.             if (cbEID)
  5138.                 memcpy(peid, peidTemp, cbEID);
  5139.  
  5140.             break;
  5141.         }
  5142.  
  5143.         FreeProws(prws);
  5144.         prws = NULL;
  5145.     }
  5146.  
  5147. exit:
  5148.     if (prws)
  5149.     {
  5150.         if (prws->cRows && prws->aRow[0].lpProps)
  5151.             LMFree(plmr, prws->aRow[0].lpProps);
  5152.  
  5153.         LMFree(plmr, prws);
  5154.     }
  5155.  
  5156.     if (hr == hrSuccess)
  5157.     {
  5158.         AssertSz(!IsBadWritePtr(ppeid, sizeof(PEID)), "Bad parameter (ppeid) "
  5159.             "passed to HrEIDFromDisplayName");
  5160.         *ppeid = peid;
  5161.     }
  5162.  
  5163.     DebugTraceResult(HrEIDFromDisplayName, hr);
  5164.     return hr;
  5165. }
  5166.  
  5167.