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