home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / mapi / docfile.ms / mspms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  99.9 KB  |  3,403 lines

  1. /*
  2.  *  M S P M S . C
  3.  *
  4.  *  Code for the MAPI Sample Store Provider implementation of the
  5.  *  IMsgStore object.
  6.  *
  7.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  8.  */
  9.  
  10. #include "msp.h"
  11. #include <stdarg.h>
  12.  
  13. #define MS_ValidateParameters(pobj, intf, method, arglist)      \
  14.         OBJ_ValidateParameters(pobj, intf, method, sizeof(IMS), &vtblIMS, arglist)
  15.  
  16. static HRESULT HrGetSequenceNum(PEID peid, ULONG *pulSequenceNum);
  17. static HRESULT HrGetSMSStandardNotifKey(PIMS pims, PEID peid,
  18.     LPNOTIFKEY * lppKey);
  19.  
  20. /* Dispatch table for IMsgStore objects */
  21. IMS_Vtbl vtblIMS =
  22. {
  23.     (IMS_QueryInterface_METHOD *)   OBJ_QueryInterface,
  24.     (IMS_AddRef_METHOD *)           OBJ_AddRef,
  25.     (IMS_Release_METHOD *)          OBJ_Release,
  26.     IMS_GetLastError,
  27.     IMS_SaveChanges,
  28.     IMS_GetProps,
  29.     IMS_GetPropList,
  30.     IMS_OpenProperty,
  31.     IMS_SetProps,
  32.     IMS_DeleteProps,
  33.     IMS_CopyTo,
  34.     IMS_CopyProps,
  35.     IMS_GetNamesFromIDs,
  36.     IMS_GetIDsFromNames,
  37.  
  38.     IMS_Advise,
  39.     IMS_Unadvise,
  40.     IMS_CompareEntryIDs,
  41.     IMS_OpenEntry,
  42.     IMS_SetReceiveFolder,
  43.     IMS_GetReceiveFolder,
  44.     IMS_GetReceiveFolderTable,
  45.     IMS_StoreLogoff,
  46.     IMS_AbortSubmit,
  47.  
  48.     IMS_GetOutgoingQueue,
  49.     IMS_SetLockState,
  50.     IMS_FinishedMsg,
  51.     IMS_NotifyNewMail,
  52. };
  53.  
  54. /* definitions for outgoing queues */
  55. CALLERRELEASE OutgoingViewRelease;
  56.  
  57. /*
  58.  *  Object methods
  59.  */
  60.  
  61. /*
  62.  *  IMS_GetLastError [also called by IMSLogon, IMAPIFolder, IMessage, and IAttach]
  63.  *
  64.  *  Purpose:
  65.  *      Returns a localized text error message associated with the
  66.  *      last error which occurred on a specific object (in
  67.  *      actuality, the SCODE used is associated with the HRESULT
  68.  *      passed in, and it is not checked that this HRESULT matches
  69.  *      that of the last error on this object -- providing a
  70.  *      different HRESULT will very likely return an inconsistent
  71.  *      error message, though).
  72.  *
  73.  *  Arguments:
  74.  *      pobj            Pointer to the object.
  75.  *      hError          HRESULT containing the error code returned
  76.  *                      by the last failed call on this object.
  77.  *      ulFlags         MAPI_UNICODE, string8 is default
  78.  *      pulLLErr        location to place the low level error code
  79.  *                      (may be NULL)
  80.  *      pszMessage      Location in which to return an address to
  81.  *                      an allocated buffer containing the
  82.  *                      localized text error message.
  83.  *      pszComponent    Location in which to return an address to
  84.  *                      an allocated buffer containing the
  85.  *                      name of the component producing the error.
  86.  *      lpulContext     Location in which to return a context number
  87.  *                      for finding help within the Component's help file.
  88.  *
  89.  *  Returns:
  90.  *      HRESULT
  91.  *
  92.  *  Side effects:
  93.  *      None.
  94.  *
  95.  *  Errors:
  96.  *      MAPI_E_NOT_ENOUGH_MEMORY    Unable to allocate memory for
  97.  *                                  the return parameter.
  98.  *      MAPI_E_INVALID_PARAMETER    hError contains an unknown
  99.  *                                  SCODE.
  100.  */
  101. STDMETHODIMP IMS_GetLastError(PIMS pobj, HRESULT hError, ULONG ulFlags,
  102.     LPMAPIERROR * lppMapiError)
  103. {
  104.     HRESULT hr = hrSuccess;
  105.     SCODE   sc;
  106.     PIMS pims;
  107.     LPTSTR pszMessage   = NULL;
  108.     
  109.     #ifdef VALIDATE
  110.     if (IsBadWritePtr(pobj, sizeof(OBJ))
  111.         || (pobj->lpVtbl != (IMS_Vtbl *) &vtblIMS
  112.             && pobj->lpVtbl != (IMS_Vtbl *) &vtblMSL
  113.             && pobj->lpVtbl != (IMS_Vtbl *) &vtblIFLD
  114.             && pobj->lpVtbl != (IMS_Vtbl *) &vtblIMSG
  115.             && pobj->lpVtbl != (IMS_Vtbl *) &vtblIATCH))
  116.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  117.         
  118.     Validate_IMAPIProp_GetLastError(pobj, hError, ulFlags, lppMapiError);
  119.  
  120.     if (ulFlags & MAPI_UNICODE)
  121.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  122.     #endif
  123.  
  124.     OBJ_EnterCriticalSection((POBJ) pobj);
  125.  
  126.     pims = pobj->pims;
  127.  
  128.     /* //$ Add param checking and correctly return Component & Context */
  129.     *lppMapiError = NULL;
  130.     
  131.     sc = LMAlloc( &pims->lmr, sizeof( MAPIERROR ), lppMapiError );
  132.     if ( FAILED( sc ) )
  133.     {
  134.         hr = ResultFromScode( sc );
  135.         goto ret;
  136.     }
  137.     
  138.     memset( *lppMapiError, 0, sizeof( MAPIERROR ) );
  139.     
  140.     (*lppMapiError)->ulVersion = MAPI_ERROR_VERSION;    
  141.  
  142.     hr = MapScodeSz(GetScode(hError), pims, &pszMessage);
  143.     if ( HR_FAILED( hr ) )
  144.         goto ret;
  145.     
  146.     sc = LMAllocMore( &pims->lmr, Cbtszsize( pszMessage ), *lppMapiError,
  147.             &(*lppMapiError)->lpszError );
  148.     
  149.     if ( FAILED( sc ) )
  150.     {
  151.         hr = ResultFromScode( sc );
  152.         goto ret;
  153.     }
  154.     
  155.     lstrcpy( (*lppMapiError)->lpszError, pszMessage );
  156.  
  157. ret:
  158.     
  159.     if ( hr )   
  160.     {
  161.         LMFree( &pims->lmr, *lppMapiError );
  162.         *lppMapiError = NULL;
  163.     }
  164.     
  165.     LMFree( &pims->lmr, pszMessage );
  166.     
  167.     OBJ_LeaveCriticalSection(pobj);
  168.  
  169.     DebugTraceResult(IMS_GetLastError, hr);
  170.     return HrCheckHr(hr, IUnknown_GetLastError);
  171. }
  172.  
  173. /*
  174.  *  IMS_SaveChanges [Also used by IMAPIFolder]
  175.  *
  176.  *  Purpose:
  177.  *      Saves changes made to the message store object properties
  178.  *      (does not propagate to sub-objects).  Because changes to
  179.  *      message store object properties show up immediately,
  180.  *      however, this call does nothing (but returns success).
  181.  *
  182.  *  Arguments:
  183.  *      pims        Pointer to the object.
  184.  *      ulFlags     Flags.  The following are defined:
  185.  *                  KEEP_OPEN_READONLY  Do not invalidate the
  186.  *                                      object, make it read-only.
  187.  *                  KEEP_OPEN_READWRITE Don't invalidate the
  188.  *                                      object, keep it open
  189.  *                                      read/write.
  190.  *                  FORCE_SAVE          Overwrite any changes made by
  191.  *                                      others since store was opened
  192.  *
  193.  *  Returns:
  194.  *      HRESULT
  195.  *
  196.  *  Side effects:
  197.  *      None.
  198.  *
  199.  *  Errors:
  200.  *      None.
  201.  */
  202. STDMETHODIMP IMS_SaveChanges(PIMS pims, ULONG ulFlags)
  203. {
  204.     #ifdef VALIDATE
  205.     if (    IsBadWritePtr(pims, sizeof(OBJ))
  206.         ||  (   pims->lpVtbl != (IMS_Vtbl *) &vtblIMS
  207.             &&  pims->lpVtbl != (IMS_Vtbl *) &vtblIFLD))
  208.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  209.  
  210.     Validate_IMAPIProp_SaveChanges(pims, ulFlags);
  211.     #endif
  212.  
  213.     return hrSuccess;
  214. }
  215.  
  216. /*
  217.  *  IMS_GetProps
  218.  *
  219.  *  Purpose:
  220.  *      Returns to the caller the value(s) of one or more
  221.  *      properties existent on an IMS object.  The order of the
  222.  *      properties in the returned ppval structure exactly
  223.  *      matches the order in which the properties were requested in
  224.  *      ptaga.  The caller must free the returned
  225.  *      structure by calling MAPIFreeBuffer(*ppval), but
  226.  *      only if the function returns zero or the error
  227.  *      MAPI_W_ERRORS_RETURNED.  Uses the IMessage on IStorage
  228.  *      property interface implementation.
  229.  *
  230.  *  Arguments:
  231.  *      pims            Pointer to the object.
  232.  *      ptaga           Pointer to a counted array of property tags
  233.  *                      ("names") that identify the values to be
  234.  *                      returned.
  235.  *      ulFlags         UNICODE / String8
  236.  *      pcval       Location in which to return the count of
  237.  *                      elements in *ppval.
  238.  *      ppval   Location in which to return an allocated
  239.  *                      array of property values (the caller frees
  240.  *                      by calling MAPIFreeBuffer).
  241.  *
  242.  *  Returns:
  243.  *      HRESULT
  244.  *
  245.  *  Side effects:
  246.  *      None.
  247.  *
  248.  *  Errors:
  249.  *      If the call succeeded overall but access to one or more
  250.  *      properties failed, the function returns the warning
  251.  *      MAPI_W_ERRORS_RETURNED.  The calling application should
  252.  *      then check the Property Tag of each of the returned
  253.  *      properties to determine which ones failed.  Those that fail
  254.  *      have their Property Type set to PT_ERROR and their value (a
  255.  *      ULONG) indicates which error occurred.
  256.  *
  257.  *      MAPI_E_NO_ACCESS    The caller does not have access
  258.  *                                  to the requested properties.
  259.  *      MAPI_W_ERRORS_RETURNED      See above.
  260.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  261.  *                                  call to the service provider
  262.  *                                  failed.
  263.  */
  264. STDMETHODIMP IMS_GetProps(PIMS pims, LPSPropTagArray ptaga, ULONG ulFlags,
  265.     ULONG *pcval, LPSPropValue *ppval)
  266. {
  267.     HRESULT hr = hrSuccess;
  268.     LPMESSAGE lpmsg = NULL;
  269.  
  270.     MS_ValidateParameters(
  271.             pims, 
  272.             IMAPIProp,
  273.             GetProps,
  274.             (pims, 
  275.             ptaga, 
  276.             ulFlags, 
  277.             pcval, 
  278.             ppval));
  279.  
  280.     #ifdef VALIDATE
  281.     if (ulFlags & MAPI_UNICODE)
  282.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  283.     #endif
  284.         
  285.     IMS_EnterCriticalSection(pims);
  286.  
  287.     /* If input parameters are okay, make GetProps call on lpmsgProps. */
  288.  
  289.     *pcval = 0L;
  290.     *ppval = NULL;
  291.  
  292.     hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps, &pims->lmr,
  293.         pims->psup, FALSE, &lpmsg);
  294.     if (hr != hrSuccess)
  295.         goto exit;
  296.  
  297.     hr = lpmsg->lpVtbl->GetProps(lpmsg, ptaga, ulFlags, pcval, ppval);
  298.  
  299.     {if(HR_SUCCEEDED(hr))
  300.     {
  301.         LPSPropValue pvalStoreSupMask = PpropFindProp(*ppval, *pcval, 
  302.                                 PROP_TAG(PT_UNSPECIFIED, PROP_ID(PR_STORE_SUPPORT_MASK)));
  303.         if(pvalStoreSupMask)
  304.         {
  305.             pvalStoreSupMask->ulPropTag = PR_STORE_SUPPORT_MASK;
  306.             pvalStoreSupMask->Value.l = SMS_SUPPORTMASK;
  307.  
  308.             /* fix up hr */
  309.             if(ptaga->cValues == 1)
  310.                 hr = hrSuccess;
  311.         }
  312.     }
  313.     }
  314.     /* Wrap the store entryids. Note that this function takes as an */
  315.     /* argument the HRESULT from the previous GetProps call. */
  316.     /* We aren't ignoring the error. */
  317.  
  318.     hr = HrWrap_GetProps(hr, pims, 0, NULL, pcval, ppval, TRUE,
  319.         (ptaga != NULL), (POBJ)pims);
  320.  
  321. exit:
  322.     UlRelease(lpmsg);
  323.  
  324.     IMS_LeaveCriticalSection(pims);
  325.  
  326.     #ifdef DEBUG
  327.     if (GetScode(hr) != MAPI_W_ERRORS_RETURNED)
  328.         DebugTraceResult(IMS_GetProps, hr);
  329.     #endif
  330.  
  331.     return HrCheckHr(hr, IMAPIProp_GetProps);
  332. }
  333.  
  334. /*
  335.  *  IMS_GetPropList
  336.  *
  337.  *  Purpose:
  338.  *      Returns a list of all the properties currently accessible.
  339.  *      Uses the IMessage on IStorage property implementation.
  340.  *
  341.  *  Arguments:
  342.  *      pims        Pointer to the object.
  343.  *      ulFlags     UNICODE / String8
  344.  *      pptaga      Location in which to return a pointer
  345.  *                  to a counted array of property tags.
  346.  *
  347.  *  Returns:
  348.  *      HRESULT
  349.  *
  350.  *  Side effects:
  351.  *      None.
  352.  *
  353.  *  Errors:
  354.  *      MAPI_E_NO_ACCESS    The caller does not have access
  355.  *                          to the requested properties.
  356.  *      MAPI_E_CALL_FAILED  The mechanism for making the
  357.  *                          call to the service provider
  358.  *                          failed.
  359.  */
  360. STDMETHODIMP IMS_GetPropList(PIMS pims, ULONG ulFlags, LPSPropTagArray * pptaga)
  361. {
  362.     HRESULT hr = hrSuccess;
  363.     LPMESSAGE lpmsg = NULL;
  364.  
  365.     MS_ValidateParameters(
  366.             pims, 
  367.             IMAPIProp,
  368.             GetPropList,
  369.             (pims, 
  370.             ulFlags, 
  371.             pptaga));
  372.  
  373.     #ifdef VALIDATE
  374.     if (ulFlags & MAPI_UNICODE)
  375.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  376.     #endif
  377.         
  378.     IMS_EnterCriticalSection(pims);
  379.  
  380.     /* If input parameters are okay, make GetPropList call on lpmsgProps. */
  381.  
  382.     hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps, &pims->lmr,
  383.         pims->psup, FALSE, &lpmsg);
  384.     if (hr != hrSuccess)
  385.         goto exit;
  386.  
  387.     hr = lpmsg->lpVtbl->GetPropList(lpmsg, ulFlags, pptaga);
  388.     /* if ( hr ) fall through to exit */
  389.  
  390. exit:
  391.     UlRelease(lpmsg);
  392.  
  393.     IMS_LeaveCriticalSection(pims);
  394.  
  395.     DebugTraceResult(IMS_GetPropList, hr);
  396.     return HrCheckHr(hr, IMAPIProp_GetPropList);
  397. }
  398.  
  399. /*
  400.  *  IMS_OpenProperty
  401.  *
  402.  *  Purpose:
  403.  *      Open a requested interface on a property for further
  404.  *      access.  Commonly used for stream access to a large binary
  405.  *      or text property.  This is the only way to access a
  406.  *      property of type PT_OBJECT, and may be used on other
  407.  *      properties depending on the implementation.  Uses the
  408.  *      IMessage on IStorage property implementation, but since the
  409.  *      transaction model for IMsgOnIStg does not match that for
  410.  *      IMsgStore, we fail this call.  (We could do a lot of work
  411.  *      to wrap the interface that IMsgOnIStg returns to us before
  412.  *      returning it to the client, but we don't.)
  413.  *
  414.  *  Arguments:
  415.  *      pims        Pointer to the object.
  416.  *      ulPropTag   Property tag for the desired property.  Only
  417.  *                  the ID bits of the tag are used; the type bits
  418.  *                  are ignored.
  419.  *      lpiid       Pointer to the GUID identifying which interface
  420.  *                  is desired.
  421.  *      lppUnk      Location in which to return a pointer to the
  422.  *                  newly created interface pointer.
  423.  *
  424.  *  Returns:
  425.  *      HRESULT
  426.  *
  427.  *  Errors:
  428.  *      MAPI_E_INVALID_PARAMETER
  429.  *      MAPI_E_NO_SUPPORT   The requested interface is not
  430.  *                              available on the given property.
  431.  */
  432. STDMETHODIMP IMS_OpenProperty(PIMS pims, ULONG ulPropTag, LPCIID lpiid,
  433.     ULONG ulInterfaceOptions, ULONG ulFlags, LPUNKNOWN * lppUnk)
  434. {
  435.     SCODE sc;
  436.  
  437.     MS_ValidateParameters(
  438.             pims, 
  439.             IMAPIProp,
  440.             OpenProperty,
  441.             (pims, 
  442.             ulPropTag, 
  443.             lpiid, 
  444.             ulInterfaceOptions, 
  445.             ulFlags, 
  446.             lppUnk));
  447.  
  448.     sc = MAPI_E_NO_SUPPORT;
  449.     
  450.     DebugTraceSc(IFLD_OpenProperty, sc);
  451.     return ResultFromScode(sc);
  452. }
  453.  
  454. /*
  455.  *  IMS_SetProps
  456.  *
  457.  *  Purpose:
  458.  *      Sets the value of one or more properties.  This call passes
  459.  *      a number of Property Value structures.  The Property Tag in
  460.  *      each indicates which property is having its values set and
  461.  *      the value indicates what should be stored.  The caller must
  462.  *      free the returned property problem structure by calling
  463.  *      MAPIFreeBuffer(*lppProblems), but only if the call
  464.  *      succeeded overall.  Uses the IMessage on IStorage property
  465.  *      implementation.
  466.  *
  467.  *  Arguments:
  468.  *      pims            Pointer to the object.
  469.  *      cValues         Number of values in lpPropArray.
  470.  *      lpPropArray     Pointer to a Property Value array.
  471.  *      lppProblems     Location in which to return a pointer to a
  472.  *                      counted array of property problem
  473.  *                      structures.
  474.  *
  475.  *  Returns:
  476.  *      HRESULT.  If the call succeeds overall, a zero is returned.
  477.  *      If there are problems with setting some or all of the
  478.  *      selected values, and a non-NULL is passed for lppProblems,
  479.  *      then a SPropProblemArray structure is returned with details
  480.  *      about each problem.  The value returned in lppProblems is
  481.  *      only valid if zero is returned in the HRESULT.  If an error
  482.  *      occurs on the call such that a non-zero value is returned
  483.  *      for the HRESULT then the contents of *lppProblems are
  484.  *      undefined.  In particular, do not use or free the structure
  485.  *      if an error occurs on the call.
  486.  *
  487.  *  Side effects:
  488.  *      None.
  489.  *
  490.  *  Errors:
  491.  *      MAPI_E_NO_ACCESS    The caller does not have access
  492.  *                                  to the requested properties.
  493.  *      MAPI_E_CALL_FAILED      A general problem affecting
  494.  *                                  access to all of the object's
  495.  *                                  properties occurred.
  496.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  497.  *                                  call to the service provider
  498.  *                                  failed.
  499.  */
  500. STDMETHODIMP IMS_SetProps(PIMS pims, ULONG cValues, LPSPropValue lpPropArray,
  501.     LPSPropProblemArray * lppProblems)
  502. {
  503.     HRESULT hr = hrSuccess;
  504.     LPMESSAGE lpmsg = NULL;
  505.  
  506.     MS_ValidateParameters(
  507.             pims, 
  508.             IMAPIProp,
  509.             SetProps,
  510.             (pims, 
  511.             cValues, 
  512.             lpPropArray, 
  513.             lppProblems));
  514.  
  515.     IMS_EnterCriticalSection(pims);
  516.  
  517.     if (!OBJ_TestFlag(pims, OBJF_MODIFY))
  518.     {
  519.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  520.         goto exit;
  521.     }
  522.  
  523.     /* If input parameters are okay, make SetProps call on lpmsgProps. */
  524.  
  525.     hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps, &pims->lmr,
  526.         pims->psup, TRUE, &lpmsg);
  527.     if (hr != hrSuccess)
  528.         goto exit;
  529.  
  530.     hr = lpmsg->lpVtbl->SetProps(lpmsg, cValues, lpPropArray, lppProblems);
  531.     if (hr != hrSuccess)
  532.         goto exit;
  533.  
  534.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  535.     /* if ( hr ), fall through to exit */
  536.  
  537. exit:
  538.     UlRelease(lpmsg);
  539.  
  540.     IMS_LeaveCriticalSection(pims);
  541.  
  542.     DebugTraceResult(IMS_SetProps, hr);
  543.     return HrCheckHr(hr, IMAPIProp_SetProps);
  544. }
  545.  
  546. /*
  547.  *  IMS_DeleteProps
  548.  *
  549.  *  Purpose:
  550.  *      Deletes the list of properties given in ptaga.
  551.  *      The caller must free the returned property problem
  552.  *      structure by calling MAPIFreeBuffer(*pprba), but only
  553.  *      if the call succeeded overall.  Uses the IMessage on
  554.  *      IStorage property implementation.
  555.  *
  556.  *  Arguments:
  557.  *      pims    Pointer to the object.
  558.  *      ptaga   Pointer to an array of Property Tags
  559.  *              identifying the properties to delete.
  560.  *      pprba   Location in which to return a pointer to a
  561.  *              counted array of property problem
  562.  *              structures.
  563.  *
  564.  *  Returns:
  565.  *      HRESULT.  If the call succeeds overall, a zero is returned.
  566.  *      If there are problems with deleting some or all of the
  567.  *      selected values, and a non-NULL is passed for pprba,
  568.  *      then a SPropProblemArray structure is returned with details
  569.  *      about each problem.  The value returned in pprba is
  570.  *      only valid if zero is returned in the HRESULT.  If an error
  571.  *      occurs on the call such that a non-zero value is returned
  572.  *      for the HRESULT then the contents of *pprba are
  573.  *      undefined.  In particular, do not use or free the structure
  574.  *      if an error occurs on the call.
  575.  *
  576.  *  Side effects:
  577.  *      None.
  578.  *
  579.  *  Errors:
  580.  *      MAPI_E_NO_ACCESS    The caller does not have access
  581.  *                                  to the requested properties.
  582.  *      MAPI_E_CALL_FAILED      A general problem affecting
  583.  *                                  access to all of the object's
  584.  *                                  properties occurred.
  585.  *      MAPI_E_CALL_FAILED          The mechanism for making the
  586.  *                                  call to the service provider
  587.  *                                  failed.
  588.  */
  589. STDMETHODIMP IMS_DeleteProps(PIMS pims, LPSPropTagArray ptaga,
  590.     LPSPropProblemArray *pprba)
  591. {
  592.     HRESULT hr = hrSuccess;
  593.     LPMESSAGE lpmsg = NULL;
  594.  
  595.     MS_ValidateParameters(
  596.             pims, 
  597.             IMAPIProp,
  598.             DeleteProps,
  599.             (pims, 
  600.             ptaga, 
  601.             pprba));
  602.  
  603.     IMS_EnterCriticalSection(pims);
  604.  
  605.     if (!OBJ_TestFlag(pims, OBJF_MODIFY))
  606.     {
  607.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  608.         goto exit;
  609.     }
  610.  
  611.     /* If input parameters are okay, make DeleteProps call on lpmsg. */
  612.  
  613.     hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps, &pims->lmr,
  614.         pims->psup, TRUE, &lpmsg);
  615.     if (hr != hrSuccess)
  616.         goto exit;
  617.  
  618.     hr = lpmsg->lpVtbl->DeleteProps(lpmsg, ptaga, pprba);
  619.     if (hr != hrSuccess)
  620.         goto exit;
  621.  
  622.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  623.     /* if ( hr ), fall through to exit */
  624.  
  625. exit:
  626.     UlRelease(lpmsg);
  627.  
  628.     IMS_LeaveCriticalSection(pims);
  629.  
  630.     DebugTraceResult(IMS_DeleteProps, hr);
  631.     return HrCheckHr(hr, IMAPIProp_DeleteProps);
  632. }
  633.  
  634. /*
  635.  *  IMS_CopyTo
  636.  *
  637.  *  Purpose:
  638.  *      Copies the contents of the current object to a destination
  639.  *      object.  The entire contents, including contained objects,
  640.  *      are copied, or optionally the caller can provide a list of
  641.  *      properties that are not to be copied.  Previous information
  642.  *      in the destination object which is not overwritten by
  643.  *      copied data is neither deleted nor modified.  It is not a
  644.  *      requirement in MAPI 1.0, however, that CopyTo semantics
  645.  *      understood by the message store object or folder objects,
  646.  *      and so this method always returns MAPI_E_NO_SUPPORT.
  647.  *
  648.  *  Arguments:
  649.  *      pims            Pointer to the source object.
  650.  *      ciidExclude     Count of the excluded interfaces in
  651.  *                      rgiidExclude.
  652.  *      rgiidExclude    Array of interface IDs specifying
  653.  *                      interfaces not to be attempted in trying to
  654.  *                      copy supplemental information to the
  655.  *                      destination object.
  656.  *      ptagaExcl   Counted array of property tags of
  657.  *                      properties that are not to be copied to the
  658.  *                      destination object.  NULL indicates all
  659.  *                      properties are to be copied.
  660.  *      ulUIParam       Handle of parent window cast to ULONG.
  661.  *      lpProgress      Callback for doing progress UI.
  662.  *      piidDst     Interface ID of the interface of lpDestObj,
  663.  *                      the destination object.
  664.  *      lpDestObj       Pointer to the open destination object.
  665.  *      ulFlags         Flags.  Defined as follows:
  666.  *                      MAPI_MOVE       Indicates a move operation.
  667.  *                                      The default is to copy.
  668.  *                      MAPI_NOREPLACE  Indicates that existing
  669.  *                                      properties should not be
  670.  *                                      overridden.  The default is
  671.  *                                      to overwrite existing
  672.  *                                      properties.
  673.  *                      MAPI_DIALOG     Display a progress dialog
  674.  *                                      as the operation proceeds.
  675.  *      pprba       Pointer to a variable that is filled in
  676.  *                      with a pointer to a set of property
  677.  *                      problems.  If NULL, no problem set is
  678.  *                      returned on an error.
  679.  *
  680.  *  Returns:
  681.  *      HRESULT
  682.  *
  683.  *  Side effects:
  684.  *      None.
  685.  *
  686.  *  Errors:
  687.  *      MAPI_E_NO_SUPPORT   This method is not supported by
  688.  *                              this type of object.
  689.  */
  690. STDMETHODIMP IMS_CopyTo(PIMS pims, ULONG ciidExcl, LPCIID rgiidExcl,
  691.     LPSPropTagArray ptagaExcl, ULONG ulUIParam, LPMAPIPROGRESS
  692.     lpProgress, LPCIID piidDst, LPVOID lpDestObj, ULONG ulFlags,
  693.     LPSPropProblemArray *pprba)
  694. {
  695.     SCODE sc;
  696.  
  697.     MS_ValidateParameters(
  698.             pims, 
  699.             IMAPIProp,
  700.             CopyTo,
  701.             (pims, 
  702.             ciidExcl, 
  703.             rgiidExcl, 
  704.             ptagaExcl, 
  705.             ulUIParam,  
  706.             lpProgress, 
  707.             piidDst, 
  708.             lpDestObj, 
  709.             ulFlags, 
  710.             pprba));
  711.  
  712.     sc = MAPI_E_NO_SUPPORT;
  713.  
  714.     DebugTraceSc(IMS_CopyTo, sc);
  715.     return ResultFromScode(sc);
  716. }
  717.  
  718. /*
  719.  *  IMS_CopyProps
  720.  *
  721.  *  Purpose:
  722.  *      Copies the specified properties of the current object to a destination
  723.  *      object.
  724.  *
  725.  *  Arguments:
  726.  *      pims            Pointer to the source object.
  727.  *      ptagaIncl       Counted array of property tags of
  728.  *                      properties that are to be copied to the
  729.  *                      destination object.
  730.  *      ulUIParam       Handle of parent window cast to ULONG.
  731.  *      lpProgress      Callback for doing progress UI.
  732.  *      piidDst         Interface ID of the interface of lpDestObj,
  733.  *                      the destination object.
  734.  *      lpDestObj       Pointer to the open destination object.
  735.  *      ulFlags         Flags.  Defined as follows:
  736.  *                      MAPI_MOVE       Indicates a move operation.
  737.  *                                      The default is to copy.
  738.  *                      MAPI_NOREPLACE  Indicates that existing
  739.  *                                      properties should not be
  740.  *                                      overridden.  The default is
  741.  *                                      to overwrite existing
  742.  *                                      properties.
  743.  *                      MAPI_DIALOG     Display a progress dialog
  744.  *                                      as the operation proceeds.
  745.  *                      MAPI_DECLINE_OK
  746.  *      pprba       Pointer to a variable that is filled in
  747.  *                      with a pointer to a set of property
  748.  *                      problems.  If NULL, no problem set is
  749.  *                      returned on an error.
  750.  *
  751.  *  Returns:
  752.  *      HRESULT
  753.  *
  754.  *  Side effects:
  755.  *      None.
  756.  *
  757.  *  Errors:
  758.  *      MAPI_E_NO_SUPPORT   This method is not supported by
  759.  *                              this type of object.
  760.  */
  761. STDMETHODIMP IMS_CopyProps(PIMS pims,
  762.     LPSPropTagArray ptagaIncl, ULONG ulUIParam, LPMAPIPROGRESS
  763.     lpProgress, LPCIID piidDst, LPVOID lpDestObj, ULONG ulFlags,
  764.     LPSPropProblemArray *pprba)
  765. {
  766.     SCODE sc;
  767.  
  768.     MS_ValidateParameters(
  769.             pims, 
  770.             IMAPIProp,
  771.             CopyProps,
  772.             (pims, 
  773.             ptagaIncl, 
  774.             ulUIParam,  
  775.             lpProgress, 
  776.             piidDst, 
  777.             lpDestObj, 
  778.             ulFlags, 
  779.             pprba));
  780.  
  781.     sc = MAPI_E_NO_SUPPORT;
  782.  
  783.     DebugTraceSc(IMS_CopyProps, sc);
  784.     return ResultFromScode(sc);
  785. }
  786.  
  787. /*
  788.  *  IMS_GetNamesFromIDs [also used by IFLD, IMSG, IATCH]
  789.  *
  790.  *  Purpose:
  791.  *
  792.  *  Arguments:
  793.  *
  794.  *  Returns:
  795.  *      HRESULT
  796.  *
  797.  *  Side effects:
  798.  *
  799.  *  Errors:
  800.  *      MAPI_E_NO_SUPPORT   This method is not yet supported in the sample ms.
  801.  */
  802. STDMETHODIMP IMS_GetNamesFromIDs(PIMS pobj, LPSPropTagArray * pptaga,
  803.     LPGUID lpguid, ULONG ulFlags, ULONG * pcNames, LPMAPINAMEID ** pppNames)
  804. {
  805.     HRESULT hr = hrSuccess;
  806.     LPMAPIPROP pmp = NULL;
  807.     BOOL fReleasePMP = FALSE;
  808.  
  809.     /* Check input parameters. */
  810.  
  811.     #ifdef VALIDATE
  812.     if (    IsBadWritePtr(pobj, sizeof(OBJ))
  813.         || (    pobj->lpVtbl != (IMS_Vtbl *) &vtblIMS
  814.             &&  pobj->lpVtbl != (IMS_Vtbl *) &vtblIFLD
  815.             &&  pobj->lpVtbl != (IMS_Vtbl *) &vtblIMSG
  816.             &&  pobj->lpVtbl != (IMS_Vtbl *) &vtblIATCH))
  817.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  818.  
  819.     Validate_IMAPIProp_GetNamesFromIDs(
  820.                 pobj, 
  821.                 pptaga, 
  822.                 lpguid, 
  823.                 ulFlags, 
  824.                 pcNames, 
  825.                 pppNames);
  826.     #endif
  827.  
  828.     OBJ_EnterCriticalSection(pobj);
  829.  
  830.     switch(pobj->wType)
  831.     {
  832.         case OT_MSGSTORE:
  833.         {
  834.             PIMS pims = pobj->pims;
  835.     
  836.             hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps,
  837.                 &pims->lmr, pims->psup, FALSE, (LPMESSAGE *) &pmp);
  838.             if (hr != hrSuccess)
  839.                 goto exit;
  840.     
  841.             fReleasePMP = TRUE;
  842.             break;
  843.         }
  844.     
  845.         case OT_FOLDER:
  846.         {
  847.             PIFLD pifld = (PIFLD) pobj;
  848.     
  849.             hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims, FALSE,
  850.                 (LPMESSAGE *) &pmp);
  851.             if (hr != hrSuccess)
  852.                 goto exit;
  853.     
  854.             fReleasePMP = TRUE;
  855.             break;
  856.         }
  857.     
  858.         case OT_MESSAGE:
  859.             pmp = (LPMAPIPROP) ((PIMSG) pobj)->lpmsg;
  860.             break;
  861.     
  862.         case OT_ATTACH:
  863.             pmp = (LPMAPIPROP) ((PIATCH) pobj)->lpattach;
  864.             break;
  865.     
  866.         default:
  867.             TrapSz1("Invalid Object Type %08lX found", pobj->wType);
  868.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  869.             break;
  870.     }
  871.  
  872.     hr = pmp->lpVtbl->GetNamesFromIDs(pmp, pptaga, lpguid, ulFlags, pcNames,
  873.         pppNames);
  874.  
  875. exit:
  876.     if (fReleasePMP)
  877.         UlRelease(pmp);
  878.  
  879.     OBJ_LeaveCriticalSection(pobj);
  880.  
  881.     DebugTraceResult(IMS_GetNamesFromIDs, hr);
  882.     return hr;
  883. }
  884.  
  885. /*
  886.  *  IMS_GetIDsFromNames [also used by IFLD, IMSG, IATCH]
  887.  *
  888.  *  Purpose:
  889.  *
  890.  *  Arguments:
  891.  *
  892.  *  Returns:
  893.  *      HRESULT
  894.  *
  895.  *  Side effects:
  896.  *      None.
  897.  *
  898.  *  Errors:
  899.  */
  900. STDMETHODIMP IMS_GetIDsFromNames(PIMS pobj, ULONG cNames,
  901.     LPMAPINAMEID * ppNames, ULONG ulFlags, LPSPropTagArray * pptaga)
  902. {
  903.     HRESULT hr = hrSuccess;
  904.     LPMAPIPROP pmp = NULL;
  905.     BOOL fSaveReleasePMP = FALSE;
  906.     BOOL fModifyAccess;
  907.  
  908.     /* Check input parameters. */
  909.     #ifdef VALIDATE
  910.     if (    IsBadWritePtr(pobj, sizeof(OBJ))
  911.         || (    pobj->lpVtbl != (IMS_Vtbl *) &vtblIMS
  912.             &&  pobj->lpVtbl != (IMS_Vtbl *) &vtblIFLD
  913.             &&  pobj->lpVtbl != (IMS_Vtbl *) &vtblIMSG
  914.             &&  pobj->lpVtbl != (IMS_Vtbl *) &vtblIATCH))
  915.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  916.  
  917.     Validate_IMAPIProp_GetIDsFromNames(pobj, cNames, ppNames, ulFlags, pptaga);
  918.     #endif
  919.  
  920.     OBJ_EnterCriticalSection(pobj);
  921.  
  922.     fModifyAccess = !!(ulFlags & MAPI_CREATE);
  923.  
  924.     // Check for correct access mode
  925.     if (    fModifyAccess
  926.         &&  !OBJ_TestFlag(pobj, OBJF_MODIFY))
  927.     {
  928.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  929.         goto exit;
  930.     }
  931.  
  932.     switch(pobj->wType)
  933.     {
  934.         case OT_MSGSTORE:
  935.         {
  936.             PIMS pims = pobj->pims;
  937.     
  938.             hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps,
  939.                 &pims->lmr, pims->psup, fModifyAccess, (LPMESSAGE *) &pmp);
  940.             if (hr != hrSuccess)
  941.                 goto exit;
  942.     
  943.             fSaveReleasePMP = TRUE;
  944.             break;
  945.         }
  946.     
  947.         case OT_FOLDER:
  948.         {
  949.             PIFLD pifld = (PIFLD) pobj;
  950.     
  951.             hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims,
  952.                 fModifyAccess, (LPMESSAGE *) &pmp);
  953.             if (hr != hrSuccess)
  954.                 goto exit;
  955.     
  956.             fSaveReleasePMP = TRUE;
  957.             break;
  958.         }
  959.     
  960.         case OT_MESSAGE:
  961.             pmp = (LPMAPIPROP) ((PIMSG) pobj)->lpmsg;
  962.             break;
  963.     
  964.         case OT_ATTACH:
  965.             pmp = (LPMAPIPROP) ((PIATCH) pobj)->lpattach;
  966.             break;
  967.     
  968.         default:
  969.             TrapSz1("Invalid Object Type %08lX found", pobj->wType);
  970.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  971.             break;
  972.     }
  973.  
  974.     // CAN RETURN WARNINGS!!! Should still continue even when a warning
  975.     // is returned.
  976.  
  977.     hr = pmp->lpVtbl->GetIDsFromNames(pmp, cNames, ppNames, ulFlags, pptaga);
  978.     if (HR_FAILED(hr))
  979.         goto exit;
  980.  
  981.     if (fSaveReleasePMP && fModifyAccess)
  982.     {
  983.         HRESULT hrT;
  984.  
  985.         hrT = pmp->lpVtbl->SaveChanges(pmp, 0);
  986.         if (HR_FAILED(hrT))
  987.         {
  988.             LMFree(&pobj->lmr, *pptaga);
  989.             *pptaga = NULL;
  990.             hr = hrT;
  991.         }
  992.     }
  993.  
  994. exit:
  995.     if (fSaveReleasePMP)
  996.         UlRelease(pmp);
  997.  
  998.     OBJ_LeaveCriticalSection(pobj);
  999.  
  1000.     #ifdef DEBUG
  1001.     if (GetScode(hr) != MAPI_W_ERRORS_RETURNED)
  1002.         DebugTraceResult(IMS_GetIDsFromNames, hr);
  1003.     #endif
  1004.  
  1005.     return hr;
  1006. }
  1007.  
  1008. /*
  1009.  *  IMS_Advise [Also used by IMSLogon]
  1010.  *
  1011.  *  Purpose:
  1012.  *      Register a client's interest in a set of events that could
  1013.  *      occur to an object in this store.  The client's particular
  1014.  *      interest is expressed in a ulEventMask, and he is told of
  1015.  *      changes through an Advise object which he gives us.
  1016.  *      The Sample Store uses the MAPI
  1017.  *      Notification Engine to handle notifications, so the
  1018.  *      Advise call is translated into a notification
  1019.  *      subscription with MAPI.  When events occur in the store
  1020.  *      which would cause interested clients to be notified, the
  1021.  *      store calls back to MAPI to request that the occurrence of
  1022.  *      an event be broadcast.
  1023.  *
  1024.  *  Arguments:
  1025.  *      pims                Pointer to the message store object.
  1026.  *      cbEntryID           Size of lpEntryID.
  1027.  *      lpEntryID           Pointer to the ID for the object for
  1028.  *                          which interest is being registered.
  1029.  *                          if NULL then registration is for all changes.
  1030.  *      ulEventMask         Indicates the events of interest and
  1031.  *                          how to see them.
  1032.  *      lpAdviseSink        Pointer to client's Advise Sink object.
  1033.  *      lpulConnection      Pointer to a variable in which the
  1034.  *                          client gets a cookie for cancelling
  1035.  *                          the notifications.
  1036.  *
  1037.  *  Returns:
  1038.  *      HRESULT
  1039.  *
  1040.  *  Side effects:
  1041.  *      None.
  1042.  *
  1043.  *  Errors:
  1044.  *      MAPI_E_INVALID_ENTRYID      The EntryID is not recognized
  1045.  *                                  as belonging to this store.
  1046.  *      MAPI_E_NO_SUPPORT       The implementation does not
  1047.  *                                  support notification on this
  1048.  *                                  object.
  1049.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for a
  1050.  *                                  needed buffer.
  1051.  */
  1052. STDMETHODIMP IMS_Advise(PIMS pobj, ULONG cbEntryID, LPENTRYID lpEntryID,
  1053.     ULONG ulEventMask, LPMAPIADVISESINK lpAdviseSink,
  1054.     ULONG *lpulConnection)
  1055. {
  1056.     HRESULT hr = hrSuccess;
  1057.     LPNOTIFKEY lpnotifkey = NULL;
  1058.     PIMS pims;
  1059.     PEID peid;
  1060.  
  1061.     /* Check input parameters. */
  1062.  
  1063.     #ifdef VALIDATE
  1064.     if (    IsBadWritePtr(pobj, sizeof(OBJ))
  1065.         || (    pobj->lpVtbl != (IMS_Vtbl *)&vtblIMS
  1066.             &&  pobj->lpVtbl != (IMS_Vtbl *)&vtblMSL))
  1067.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1068.  
  1069.     Validate_IMsgStore_Advise(
  1070.             pobj, 
  1071.             cbEntryID, 
  1072.             lpEntryID, 
  1073.             ulEventMask, 
  1074.             lpAdviseSink, 
  1075.             lpulConnection);
  1076.     #endif
  1077.  
  1078.     /* If this is an EntryID for which we support notifications, call the */
  1079.     /* MAPI registration function and return the notification object.     */
  1080.  
  1081.     OBJ_EnterCriticalSection(pobj);
  1082.  
  1083.     pims = pobj->pims;
  1084.     peid = (PEID) lpEntryID;
  1085.     *lpulConnection = 0;
  1086.  
  1087.     if (cbEntryID && FIsInvalidEID(cbEntryID, peid, pims))
  1088.     {
  1089.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1090.         goto exit;
  1091.     }
  1092.  
  1093.     if (cbEntryID && !FIsFolder(peid) && !FIsMessage(peid))
  1094.     {
  1095.         hr = ResultFromScode(MAPI_E_NO_SUPPORT);
  1096.         goto exit;
  1097.     }
  1098.  
  1099.     if (cbEntryID == 0)
  1100.         peid = NULL;
  1101.  
  1102.     hr = HrGetSMSStandardNotifKey(pims, peid, &lpnotifkey);
  1103.     if (hr != hrSuccess)
  1104.         goto exit;
  1105.  
  1106.     if (pims->psup)
  1107.     {
  1108.         hr = pims->psup->lpVtbl->Subscribe(pims->psup,
  1109.             lpnotifkey, ulEventMask, 0L, lpAdviseSink, lpulConnection);
  1110.         if (hr != hrSuccess)
  1111.             goto exit;
  1112.     }
  1113.     else
  1114.     {
  1115.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1116.         /* fall through to exit */
  1117.     }
  1118.  
  1119. exit:
  1120.     OBJ_LeaveCriticalSection(pobj);
  1121.  
  1122.     FreeNull(lpnotifkey);
  1123.  
  1124.     DebugTraceResult(IMS_Advise, hr);
  1125.     return HrCheckHr(hr, IMsgStore_Advise);
  1126. }
  1127.  
  1128. /*
  1129.  *  IMS_Unadvise [Also used by IMSLogon]
  1130.  *
  1131.  *  Purpose:
  1132.  *      Deregister a previous notification.
  1133.  *
  1134.  *  Arguments:
  1135.  *      pims                Pointer to the message store object.
  1136.  *      ulConnection        Cookie given out at Advise time.
  1137.  *
  1138.  *  Returns:
  1139.  *      HRESULT
  1140.  *
  1141.  *  Side effects:
  1142.  *      None.
  1143.  *
  1144.  *  Errors:
  1145.  */
  1146. STDMETHODIMP IMS_Unadvise(PIMS pobj, ULONG ulConnection)
  1147. {
  1148.     HRESULT hr = hrSuccess;
  1149.     LPMAPISUP psup;
  1150.  
  1151.     /* Check input parameters. */
  1152.  
  1153.     #ifdef VALIDATE
  1154.     if (    IsBadWritePtr(pobj, sizeof(OBJ))
  1155.         ||  (   pobj->lpVtbl != (IMS_Vtbl *)&vtblIMS
  1156.             &&  pobj->lpVtbl != (IMS_Vtbl *)&vtblMSL))
  1157.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1158.  
  1159.     Validate_IMsgStore_Unadvise(pobj, ulConnection);
  1160.     #endif
  1161.  
  1162.     OBJ_EnterCriticalSection(pobj);
  1163.  
  1164.     psup = pobj->pims->psup;
  1165.  
  1166.     if (psup)
  1167.         hr = psup->lpVtbl->Unsubscribe(psup, ulConnection);
  1168.     else
  1169.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1170.  
  1171.     OBJ_LeaveCriticalSection(pobj);
  1172.  
  1173.     DebugTraceResult(IMS_Unadvise, hr);
  1174.     return HrCheckHr(hr, IMsgStore_Unadvise);
  1175. }
  1176.  
  1177. /*
  1178.  *  IMS_CompareEntryIDs
  1179.  *
  1180.  *  Purpose:
  1181.  *      Compares two EntryIDs to determine if they refer to the
  1182.  *      same object.  This is useful because, in general, an object
  1183.  *      may have more than one valid EntryID.  For the Sample
  1184.  *      Store, however, an object only has one valid EntryID at any
  1185.  *      time and so this function reduces to a check for binary
  1186.  *      equality of the EntryIDs.
  1187.  *
  1188.  *  Arguments:
  1189.  *      pims        Pointer to the Message Store Object.
  1190.  *      cbEntryID1  Size of first EntryID.
  1191.  *      lpEntryID1  Pointer to the first EntryID.
  1192.  *      cbEntryID2  Size of second EntryID.
  1193.  *      lpEntryID2  Pointer to the second EntryID.
  1194.  *      ulFlags     Flags.  Reserved.  Must be zero.
  1195.  *      lpulResult  Pointer to a variable in which the result of
  1196.  *                  the comparison (TRUE or FALSE) is placed.
  1197.  *
  1198.  *  Returns:
  1199.  *      HRESULT
  1200.  *
  1201.  *  Side effects:
  1202.  *      None.
  1203.  *
  1204.  *  Errors:
  1205.  *      MAPI_E_INVALID_ENTRYID  An EntryID is not recognized as
  1206.  *                              belonging to this store.
  1207.  */
  1208. STDMETHODIMP IMS_CompareEntryIDs(PIMS pobj, ULONG cbEntryID1,
  1209.     LPENTRYID lpEntryID1, ULONG cbEntryID2, LPENTRYID lpEntryID2, ULONG ulFlags,
  1210.     ULONG *lpulResult)
  1211. {
  1212.     HRESULT hr = hrSuccess;
  1213.     PEID peid1;
  1214.     PEID peid2;
  1215.     PIMS pims;
  1216.     BOOL fEID1IsRoot = FALSE;
  1217.     BOOL fEID2IsRoot = FALSE;
  1218.  
  1219.     /* Check input parameters. */
  1220.  
  1221.     #ifdef VALIDATE
  1222.     if (    IsBadWritePtr(pobj, sizeof(OBJ))
  1223.         || (    pobj->lpVtbl != (IMS_Vtbl *)&vtblIMS
  1224.             &&  pobj->lpVtbl != (IMS_Vtbl *)&vtblMSL))
  1225.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1226.  
  1227.     Validate_IMsgStore_CompareEntryIDs(
  1228.                 pobj, 
  1229.                 cbEntryID1, 
  1230.                 lpEntryID1, 
  1231.                 cbEntryID2, 
  1232.                 lpEntryID2, 
  1233.                 ulFlags, 
  1234.                 lpulResult);
  1235.     #endif
  1236.  
  1237.     OBJ_EnterCriticalSection(pobj);
  1238.  
  1239.     /* Do a binary comparison of the EIDs, if they're the same size AND */
  1240.     /* if we recognize one of them as belonging to this store.          */
  1241.  
  1242.     peid1 = (PEID) lpEntryID1;
  1243.     peid2 = (PEID) lpEntryID2;
  1244.     pims = pobj->pims;
  1245.  
  1246.     if ((cbEntryID1 && FIsInvalidEID(cbEntryID1, peid1, pims))
  1247.         || (cbEntryID2 && FIsInvalidEID(cbEntryID2, peid2, pims)))
  1248.     {
  1249.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1250.         goto exit;
  1251.     }
  1252.  
  1253.     if (cbEntryID1 == 0 || FIsRoot(peid1))
  1254.         fEID1IsRoot = TRUE;
  1255.  
  1256.     if (cbEntryID2 == 0 || FIsRoot(peid2))
  1257.         fEID2IsRoot = TRUE;
  1258.  
  1259.     *lpulResult = FALSE;
  1260.  
  1261.     if (fEID1IsRoot == fEID2IsRoot && fEID1IsRoot == TRUE)
  1262.         *lpulResult = TRUE;
  1263.     else if (cbEntryID1 == cbEntryID2
  1264.             && memcmp(peid1, peid2, offsetof(EID, szPath)) == 0
  1265.             && peid1->bVersion == SMPMS_VERSION
  1266.         && lstrcmpi(peid1->szPath, peid2->szPath) == 0)
  1267.         *lpulResult = TRUE;
  1268.  
  1269. exit:
  1270.     OBJ_LeaveCriticalSection(pobj);
  1271.  
  1272.     DebugTraceResult(IMS_CompareEntryIDs, hr);
  1273.     return hr;
  1274. }
  1275.  
  1276. /*
  1277.  *  IMS_OpenEntry [Also used by IMSLogon and IMAPIFolder]
  1278.  *
  1279.  *  Purpose:
  1280.  *      Opens an object in this message store.
  1281.  *
  1282.  *  Arguments:
  1283.  *      pobj            Message store on which this function was
  1284.  *                      called.
  1285.  *      cbEntryID       Size of lpEntryID.
  1286.  *      lpEntryID       EntryID of object to open.
  1287.  *      piid            IID of interface requested for the
  1288.  *                      newly-opened object.  NULL or IID_IMAPIProp
  1289.  *                      means to open the object using the standard
  1290.  *                      MAPI 1.0 interface for the object.
  1291.  *                      IID_IUnknown means to open it using
  1292.  *                      the easiest interface you can open.
  1293.  *      ulFlags         Flags.  The following are defined:
  1294.  *                      MAPI_MODIFY             Write access desired.
  1295.  *                      MAPI_DEFERRED_ERRORS    Delayed "open" errors OKAY.
  1296.  *                      MAPI_BEST_ACCESS        Open for writing if possible,
  1297.  *                                              otherwise, open for reading.
  1298.  *      lpulObjType     Address in which to place the type of the
  1299.  *                      opened object.
  1300.  *      lppUnk          Address in which to place a pointer to the
  1301.  *                      opened object.
  1302.  *
  1303.  *  Returns:
  1304.  *      HRESULT
  1305.  *
  1306.  *  Side effects:
  1307.  *      None.
  1308.  *
  1309.  *  Errors:
  1310.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate memory for
  1311.  *                                  the to-be-opened object.
  1312.  *      MAPI_E_INVALID_ENTRYID      This message store does not
  1313.  *                                  recognize this EntryID.
  1314.  *      MAPI_E_UNKNOWN_FLAGS
  1315.  *      MAPI_E_INVALID_PARAMETER
  1316.  */
  1317. STDMETHODIMP IMS_OpenEntry(PIMS pobj, ULONG cbEntryID, LPENTRYID lpEntryID,
  1318.     LPCIID piid, ULONG ulFlags, ULONG *lpulObjType, LPUNKNOWN *lppUnk)
  1319. {
  1320.     HRESULT hr = hrSuccess;
  1321.     PIMSG pimsg = NULL;
  1322.     PIFLD pifld = NULL;
  1323.     PEID peidParent = NULL;
  1324.     BOOL fEIDIsRoot = FALSE;
  1325.     BOOL fModify;
  1326.     BOOL fBestAcc;
  1327.     PIMS pims;
  1328.     PEID peid;
  1329.  
  1330.     /* Check input parameters. */
  1331.  
  1332.     #ifdef VALIDATE
  1333.     if (    IsBadWritePtr(pobj, sizeof(OBJ))
  1334.         ||  (   pobj->lpVtbl != (IMS_Vtbl *)&vtblIFLD
  1335.              && pobj->lpVtbl != (IMS_Vtbl *)&vtblIMS
  1336.              && pobj->lpVtbl != (IMS_Vtbl *)&vtblMSL))
  1337.         return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1338.  
  1339.     Validate_IMAPIContainer_OpenEntry(
  1340.                 pobj, 
  1341.                 cbEntryID, 
  1342.                 lpEntryID, 
  1343.                 piid, 
  1344.                 ulFlags, 
  1345.                 lpulObjType, 
  1346.                 lppUnk);
  1347.     #endif
  1348.  
  1349.     OBJ_EnterCriticalSection(pobj);
  1350.  
  1351.     /* Get the real pims object in case pobj is a PIFLD or PMSL */
  1352.     pims = pobj->pims;
  1353.  
  1354.     peid = (PEID) lpEntryID;
  1355.  
  1356.     /* If parameters are okay, see if this is an EntryID we understand. */
  1357.  
  1358.     if (cbEntryID && FIsInvalidEID(cbEntryID, peid, pims))
  1359.     {
  1360.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1361.         goto exit;
  1362.     }
  1363.  
  1364.     if (cbEntryID == 0 || FIsRoot(peid))
  1365.         fEIDIsRoot = TRUE;
  1366.  
  1367.     /* determine if the caller wants modification access */
  1368.     fModify = (ulFlags & (MAPI_MODIFY | MAPI_BEST_ACCESS)) != 0;
  1369.     fBestAcc = (ulFlags & MAPI_BEST_ACCESS) != 0;
  1370.  
  1371.     /* Fail if attempting to open an object for */
  1372.     /* modification in a read-only store.       */
  1373.  
  1374.     if (fModify
  1375.         && !OBJ_TestFlag(pims, OBJF_MODIFY))
  1376.     {
  1377.         if (fBestAcc)
  1378.             fModify = FALSE;
  1379.         else
  1380.         {
  1381.             hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1382.             goto exit;
  1383.         }
  1384.     }
  1385.  
  1386.     /* Open the object */
  1387.  
  1388.     if (fEIDIsRoot)
  1389.     {
  1390.         PEID peidRoot = NULL;
  1391.         CHAR ch = '\0';
  1392.         MAPIUID uid;
  1393.  
  1394.         if (piid && !FQueryInterface(OT_FOLDER, piid))
  1395.         {
  1396.             hr = ResultFromScode(E_NOINTERFACE);
  1397.             goto exit;
  1398.         }
  1399.  
  1400.         GetResourceUID(pims, &uid);
  1401.  
  1402.         hr = HrConstructEID(&uid, &pims->lmr, (LPSTR) &ch, &peidRoot);
  1403.         if (hr != hrSuccess)
  1404.             goto exit;
  1405.  
  1406.         hr = HrNewIFLD(peidRoot, pims, fModify, &pifld);
  1407.  
  1408.         if (hr == hrSuccess)
  1409.             hr = HrSetInternalProps(&pims->lmr, cpropIFLDInternal,
  1410.                 &(pifld->pval), &(pifld->cval), peidRoot, peidRoot, 0);
  1411.  
  1412.         LMFree(&pims->lmr, peidRoot);
  1413.  
  1414.         if (hr != hrSuccess)
  1415.             goto exit;
  1416.  
  1417.         *lppUnk = (LPUNKNOWN) pifld;
  1418.         *lpulObjType = MAPI_FOLDER;
  1419.     }
  1420.     else
  1421.     {
  1422.         hr = HrGetParentEID(&pims->lmr, peid, &peidParent);
  1423.         if (hr != hrSuccess)
  1424.             goto exit;
  1425.  
  1426.         if (FIsMessage(peid))
  1427.         {
  1428.             ULONG ulSeqNum;
  1429.  
  1430.             if (piid && !FQueryInterface(OT_MESSAGE, piid))
  1431.             {
  1432.                 hr = ResultFromScode(E_NOINTERFACE);
  1433.                 goto exit;
  1434.             }
  1435.  
  1436.             hr = HrGetSequenceNum(peid, &ulSeqNum);
  1437.  
  1438.             hr = HrNewIMSG(peid, pims, FALSE, fModify, ulSeqNum, NULL, &pimsg);
  1439.  
  1440.             if (GetScode(hr) == MAPI_E_SUBMITTED && fBestAcc && fModify)
  1441.             {
  1442.                 fModify = FALSE;
  1443.                 hr = HrNewIMSG(peid, pims, FALSE, fModify, ulSeqNum, NULL, &pimsg);
  1444.             }
  1445.  
  1446.             if (hr != hrSuccess)
  1447.                 goto exit;
  1448.  
  1449.             *lppUnk = (LPUNKNOWN) pimsg;
  1450.             *lpulObjType = MAPI_MESSAGE;
  1451.         }
  1452.         else /* a folder */
  1453.         {
  1454.             if (!FIsFolder(peid))
  1455.             {
  1456.                 TrapSz("Logic error");
  1457.                 hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1458.                 goto exit;
  1459.             }
  1460.  
  1461.             if (piid && !FQueryInterface(OT_FOLDER, piid))
  1462.             {
  1463.                 hr = ResultFromScode(E_NOINTERFACE);
  1464.                 goto exit;
  1465.             }
  1466.  
  1467.             hr = HrNewIFLD(peid, pims, fModify, &pifld);
  1468.             if (hr != hrSuccess)
  1469.                 goto exit;
  1470.  
  1471.             hr = HrSetInternalProps(&pims->lmr, cpropIFLDInternal,
  1472.                 &(pifld->pval), &(pifld->cval), peid, peidParent, 0);
  1473.             if (hr != hrSuccess)
  1474.                 goto exit;
  1475.  
  1476.             *lppUnk = (LPUNKNOWN) pifld;
  1477.             *lpulObjType = MAPI_FOLDER;
  1478.         }
  1479.     }
  1480.  
  1481. exit:
  1482.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No Warnings expected");
  1483.  
  1484.     LMFree(&pims->lmr, peidParent);
  1485.  
  1486.     if (hr != hrSuccess)
  1487.     {
  1488.         UlRelease(pimsg);
  1489.         UlRelease(pifld);
  1490.     }
  1491.  
  1492.     OBJ_LeaveCriticalSection(pobj);
  1493.  
  1494.     DebugTraceResult(IMS_OpenEntry, hr);
  1495.     return HrCheckHr(hr, IMsgStore_OpenEntry);
  1496. }
  1497.  
  1498. /*
  1499.  *  IMS_SetReceiveFolder
  1500.  *
  1501.  *  Purpose:
  1502.  *      Sets the receive folder for a particular message class.  A
  1503.  *      message class is a string with "." delimiters, e.g
  1504.  *      "IPM.Note".  This method also removes a setting if the
  1505.  *      EntryID parameter (see below) is NULL.  Because there must
  1506.  *      be only one set of receive folder settings per store (NOT
  1507.  *      per logon), the settings are stored on disk in an OLE2
  1508.  *      docfile, and all access to them is done on disk, not in
  1509.  *      memory (see recfldr.c, recfldr.h for details).
  1510.  *
  1511.  *  Arguments:
  1512.  *      pims                Pointer to the object.
  1513.  *      szMessageClass  String identifying a message class.  If
  1514.  *                          NULL, then the default receive folder
  1515.  *                          is set.
  1516.  *      ulFlags             Flags.
  1517.  *      cbEntryID           Size of lpEntryID.
  1518.  *      lpEntryID           Pointer to the identifier of a
  1519.  *                          particular folder in the store.  If
  1520.  *                          this pointer is NULL, the receive
  1521.  *                          folder setting is removed.
  1522.  *
  1523.  *  Returns:
  1524.  *      HRESULT
  1525.  *
  1526.  *  Side effects:
  1527.  *      IMS_SetReceiveFolder will not actually check to see if the
  1528.  *      EntryID it is given exists in the store at this time, so
  1529.  *      will go ahead and create an invalid receive folder setting
  1530.  *      for that particular message class.
  1531.  *
  1532.  *  Errors:
  1533.  *      MAPI_E_INVALID_ENTRYID  The store doesn't recognize this
  1534.  *                              EntryID as belonging to it.
  1535.  */
  1536. STDMETHODIMP IMS_SetReceiveFolder(PIMS pims, LPTSTR szMessageClass,
  1537.     ULONG ulFlags, ULONG cbEntryID, LPENTRYID lpEntryID)
  1538. {
  1539.     SCODE sc;
  1540.     HRESULT hr = hrSuccess;
  1541.     TCHAR rgchDefMsgClass[] = TEXT("");
  1542.     LPTSTR szNormalizedClass = NULL;
  1543.     RFN rfn;
  1544.     PEID peid;
  1545.  
  1546.     /* Check input parameters. */
  1547.  
  1548.     MS_ValidateParameters(
  1549.             pims, 
  1550.             IMsgStore,
  1551.             SetReceiveFolder,
  1552.             (pims, 
  1553.             szMessageClass, 
  1554.             ulFlags, 
  1555.             cbEntryID, 
  1556.             lpEntryID));
  1557.  
  1558.     #ifdef VALIDATE
  1559.     if (ulFlags & MAPI_UNICODE)
  1560.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1561.     #endif
  1562.  
  1563.     IMS_EnterCriticalSection(pims);
  1564.  
  1565.     if (!OBJ_TestFlag(pims, OBJF_MODIFY))
  1566.     {
  1567.         hr = ResultFromScode(MAPI_E_NO_ACCESS);
  1568.         goto exit;
  1569.     }
  1570.  
  1571.     peid = (PEID) lpEntryID;
  1572.  
  1573.     /* don't allow removal of the default settings */
  1574.     if (peid == NULL &&
  1575.         (szMessageClass == NULL || *szMessageClass == '\0'))
  1576.     {
  1577.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1578.         goto exit;
  1579.     }
  1580.  
  1581.     /* If parameters are okay, see if this is an EntryID we understand. */
  1582.  
  1583.     if (cbEntryID && FIsInvalidEID(cbEntryID, peid, pims))
  1584.     {
  1585.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1586.         goto exit;
  1587.     }
  1588.  
  1589.     /* The RFS module always expects a valid string */
  1590.     if (!szMessageClass)
  1591.         szMessageClass = rgchDefMsgClass;
  1592.  
  1593.     /* Make a copy of the szMessageClass that is normalized */
  1594.  
  1595.     sc = LMAlloc(&pims->lmr, Cbtszsize(szMessageClass), &szNormalizedClass);
  1596.     if (sc != S_OK)
  1597.     {
  1598.         hr = ResultFromScode(sc);
  1599.         goto exit;
  1600.     }
  1601.     lstrcpy (szNormalizedClass, szMessageClass);
  1602.     CharUpper (szNormalizedClass);
  1603.         
  1604.     if (cbEntryID)
  1605.     {
  1606.         PRFN prfnExisting = NULL;
  1607.  
  1608.         rfn.szClass = szNormalizedClass;
  1609.         rfn.szName = peid->szPath;
  1610.  
  1611.         hr = GetRFN(pims->prfs, szNormalizedClass, &prfnExisting);
  1612.         if (hr != hrSuccess)
  1613.         {
  1614.             if (GetScode(hr) == MAPI_E_NOT_FOUND)
  1615.                 hr = hrSuccess;
  1616.             else
  1617.                 goto exit;
  1618.         }
  1619.         else
  1620.         {
  1621.             /* Remove the existing receive folder setting, but only if it */
  1622.             /* _exactly_ matches the one that we're adding.               */
  1623.             /*  //$ Are message classes case-sensitive? */
  1624.  
  1625.             if (!lstrcmp(szMessageClass, prfnExisting->szClass))
  1626.                 hr = DeleteRFN(pims->prfs, szNormalizedClass);
  1627.  
  1628.             FreeRFN(prfnExisting);
  1629.  
  1630.             if (hr != hrSuccess)
  1631.                 goto exit;
  1632.         }
  1633.  
  1634.         /* WARNING:  If the addition fails, we can't easily  */
  1635.         /* revert to a previous version of the RFS settings. */
  1636.  
  1637.         hr = AddRFN(pims->prfs, &rfn);
  1638.         /* if ( hr ), fall through to exit */
  1639.     }
  1640.     else
  1641.     {
  1642.         hr = DeleteRFN(pims->prfs, szNormalizedClass);
  1643.         /* if ( hr ), fall through to exit */
  1644.     }
  1645.  
  1646. exit:
  1647.     LMFree(&pims->lmr, szNormalizedClass);
  1648.     IMS_LeaveCriticalSection(pims);
  1649.  
  1650.     DebugTraceResult(IMS_SetReceiveFolder, hr);
  1651.     return HrCheckHr(hr, IMsgStore_SetReceiveFolder);
  1652. }
  1653.  
  1654. /*
  1655.  *  IMS_GetReceiveFolder
  1656.  *
  1657.  *  Purpose:
  1658.  *      Obtains the receive folder setting for a particular message
  1659.  *      class and other information about the receive behavior of
  1660.  *      that message class.  This function obtains the EntryID of
  1661.  *      the folder where messages of a specific class are placed.
  1662.  *      If szMessageClass does not explicitly set a receive
  1663.  *      folder, then the receive folder of the first superclass of
  1664.  *      szMessageClass which does explicitly set a receive folder
  1665.  *      is returned.  Whichever message class that explicitly sets
  1666.  *      the receive folder is returned in pszExplicitClass.  For
  1667.  *      example, if the receive folder of the message class
  1668.  *      "IPM.Note" has been set to the EntryID of the Inbox and an
  1669.  *      application calls GetReceiveFolder() on the message class
  1670.  *      "IPM.Note.Phone," the EntryID of the Inbox is returned as
  1671.  *      the lppEntryID, and "IPM.Note" is retuned in
  1672.  *      pszExplicitClass.  The converse is not true:  if the
  1673.  *      receive folder setting of "IPM.Note.Phone" is the Inbox and
  1674.  *      the client asks for the setting of "IPM.Note", NULL is
  1675.  *      returned.
  1676.  *
  1677.  *  Arguments:
  1678.  *      pims                Pointer to the object.
  1679.  *      szMessageClass  Identifies the particular message
  1680.  *                          class.  If this pointer is NULL, then
  1681.  *                          the default is returned.
  1682.  *      ulFlags             Flags.
  1683.  *      lpcbEntryID         Address of the location in which to
  1684.  *                          return the size of the EntryID in
  1685.  *                          *lppEntryID.
  1686.  *      lppEntryID          Address of the location in which to
  1687.  *                          return a pointer to an EntryID which is
  1688.  *                          the identifier of the receive folder.
  1689.  *      pszExplicitClass    Address of the location in which to
  1690.  *                          return a pointer a buffer containing
  1691.  *                          the message class that explicitly sets
  1692.  *                          its receive folder to *lppEntryID.  If
  1693.  *                          NULL indicates that no class name
  1694.  *                          should be returned.  If it is exactly
  1695.  *                          equal to szMessageClass, nothing is
  1696.  *                          returned (*pszExplicitClass == NULL).
  1697.  *
  1698.  *  Returns:
  1699.  *      HRESULT
  1700.  *
  1701.  *  Side effects:
  1702.  *      None.
  1703.  *
  1704.  *  Errors:
  1705.  *      None.
  1706.  */
  1707. STDMETHODIMP IMS_GetReceiveFolder(PIMS pims, LPTSTR szMessageClass,
  1708.     ULONG ulFlags, ULONG *lpcbEntryID, LPENTRYID *lppEntryID,
  1709.     LPTSTR *pszExplicitClass)
  1710. {
  1711.     HRESULT hr = hrSuccess;
  1712.     ULONG cbeid = 0L;
  1713.     PEID peid = NULL;
  1714.     LPTSTR szExCls = NULL;
  1715.     PRFN prfn = NULL;
  1716.     TCHAR rgchDefMsgClass[] = TEXT("");
  1717.     LPTSTR szNormalizedClass = NULL;
  1718.     MAPIUID uid;
  1719.     SCODE sc = S_OK;
  1720.  
  1721.     /* Check input parameters. */
  1722.  
  1723.     MS_ValidateParameters(
  1724.             pims, 
  1725.             IMsgStore,
  1726.             GetReceiveFolder,
  1727.             (pims, 
  1728.             szMessageClass, 
  1729.             ulFlags, 
  1730.             lpcbEntryID, 
  1731.             lppEntryID, 
  1732.             pszExplicitClass));
  1733.  
  1734.     #ifdef VALIDATE
  1735.     if (ulFlags & MAPI_UNICODE)
  1736.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1737.     #endif
  1738.  
  1739.     IMS_EnterCriticalSection(pims);
  1740.  
  1741.     *lpcbEntryID = 0L;
  1742.     *lppEntryID = NULL;
  1743.  
  1744.     if (pszExplicitClass)
  1745.         *pszExplicitClass = NULL;
  1746.  
  1747.     /* The RFS module always expects a valid string */
  1748.  
  1749.     if (!szMessageClass)
  1750.         szMessageClass = rgchDefMsgClass;
  1751.  
  1752.     /* Make a copy of the szMessageClass that is normalized */
  1753.  
  1754.     sc = LMAlloc(&pims->lmr, Cbtszsize(szMessageClass), &szNormalizedClass);
  1755.     if (sc != S_OK)
  1756.     {
  1757.         hr = ResultFromScode(sc);
  1758.         goto exit;
  1759.     }
  1760.     lstrcpy (szNormalizedClass, szMessageClass);
  1761.     CharUpper (szNormalizedClass);
  1762.  
  1763.     hr = GetRFN(pims->prfs, szNormalizedClass, &prfn);
  1764.     if (hr != hrSuccess)
  1765.         goto exit;
  1766.  
  1767.     /* Allocate and set return variables. */
  1768.  
  1769.     GetResourceUID(pims, &uid);
  1770.  
  1771.     hr = HrConstructEID(&uid, &pims->lmr, prfn->szName, &peid);
  1772.     if (hr != hrSuccess)
  1773.         goto exit;
  1774.  
  1775.     cbeid = CbEID(peid);
  1776.  
  1777.     if (pszExplicitClass)
  1778.     {
  1779.         sc = LMAlloc(&pims->lmr, Cbtszsize(prfn->szClass), &szExCls);
  1780.         if (sc != S_OK)
  1781.         {
  1782.             hr = ResultFromScode(sc);
  1783.             goto exit;
  1784.         }
  1785.  
  1786.         lstrcpy(szExCls, prfn->szClass);
  1787.         *pszExplicitClass = szExCls;
  1788.     }
  1789.  
  1790.     *lpcbEntryID = cbeid;
  1791.     *lppEntryID = (LPENTRYID) peid;
  1792.  
  1793. exit:
  1794.     FreeRFN(prfn);
  1795.     LMFree(&pims->lmr, szNormalizedClass);
  1796.     if (hr != hrSuccess)
  1797.     {
  1798.         LMFree(&pims->lmr, peid);
  1799.         LMFree(&pims->lmr, szExCls);
  1800.     }
  1801.  
  1802.     IMS_LeaveCriticalSection(pims);
  1803.  
  1804.     DebugTraceResult(IMS_GetReceiveFolder, hr);
  1805.     return HrCheckHr(hr, IMsgStore_GetReceiveFolder);
  1806. }
  1807.  
  1808. STDMETHODIMP IMS_GetReceiveFolderTable(PIMS pims, ULONG ulFlags,
  1809.     LPMAPITABLE *lppTable)
  1810. {
  1811.     MS_ValidateParameters(
  1812.             pims, 
  1813.             IMsgStore,
  1814.             GetReceiveFolderTable,
  1815.             (pims, 
  1816.             ulFlags, 
  1817.             lppTable));
  1818.  
  1819.     #ifdef VALIDATE
  1820.     if (ulFlags & MAPI_UNICODE)
  1821.         return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
  1822.     #endif
  1823.  
  1824.     DebugTraceSc(IMS_GetReceiveFolderTable, MAPI_E_NO_SUPPORT);
  1825.     return ResultFromScode(MAPI_E_NO_SUPPORT);
  1826. }
  1827.  
  1828. /*
  1829.  *  IMS_StoreLogoff
  1830.  *
  1831.  *  Purpose:
  1832.  *      Allows the orderly release of a store under client control.
  1833.  *      Use of this function to release a store allows some client
  1834.  *      control over what MAPI will do about transport activity.
  1835.  *      The client can either put itself in the loop by setting the
  1836.  *      appropriate flags, or it can allow MAPI to either abort the
  1837.  *      sending of mail or to complete it prior to invalidating the
  1838.  *      object.  This behavior will only occur when the client is
  1839.  *      the only application which is using the message store, and 
  1840.  *      will happen during release of the store object.  If
  1841.  *      another client is still using the store, the store object
  1842.  *      will remember the flags and will issue the call during the 
  1843.  *      final release.
  1844.  *
  1845.  *  Arguments:
  1846.  *      pims        Pointer to the object.
  1847.  *      pulFlags    Flags.  The following are defined as input:
  1848.  *                  LOGOFF_NO_WAIT  Don't wait for the transports.
  1849.  *                                  All outboutnd mail that is
  1850.  *                                  ready to be sent will be sent.
  1851.  *                                  Control is returned to the
  1852.  *                                  client immediately.
  1853.  *                  LOGOFF_ORDERLY  Don't wait for the transports.
  1854.  *                                  Any currently in-process
  1855.  *                                  message on the store is
  1856.  *                                  completed; no new ones are
  1857.  *                                  started.  Control is returned
  1858.  *                                  to the client immediately.
  1859.  *                  LOGOFF_PURGE    Same as LOGOFF_NO_WAIT but
  1860.  *                                  PurgeQueues() called for
  1861.  *                                  appropriate transports and
  1862.  *                                  client waits for completion.
  1863.  *                  LOGOFF_ABORT    Any transport activity on this
  1864.  *                                  store should be aborted.
  1865.  *                                  Control is returned to the
  1866.  *                                  client when abort completes.
  1867.  *                  LOGOFF_QUIET    If any transport activity is
  1868.  *                                  taking place, the logoff will
  1869.  *                                  not occur.
  1870.  *                  The following are defined as output flags:
  1871.  *                  LOGOFF_COMPLETE         All resources
  1872.  *                                          associated with the
  1873.  *                                          store have been
  1874.  *                                          released and the object
  1875.  *                                          invalidated.
  1876.  *                  LOGOFF_INBOUND          A message is currently
  1877.  *                                          coming into the store
  1878.  *                                          from one or more
  1879.  *                                          transports.
  1880.  *                  LOGOFF_OUTBOUND         A message is currently
  1881.  *                                          being sent from the
  1882.  *                                          store by one or more
  1883.  *                                          transports.
  1884.  *                  LOGOFF_OUTBOUND_QUEUE   Messages are currently
  1885.  *                                          in the outbound queue
  1886.  *                                          for the store.
  1887.  *
  1888.  *  Returns:
  1889.  *      HRESULT
  1890.  *
  1891.  *  Side effects:
  1892.  *      See flag description for possible side effects.
  1893.  *
  1894.  *  Errors:
  1895.  *      Various.
  1896.  */
  1897.  
  1898. STDMETHODIMP IMS_StoreLogoff(PIMS pims, ULONG * pulFlags)
  1899. {
  1900.     HRESULT hr = 0;
  1901.     LPMAPISUP psup = NULL;
  1902.  
  1903.     MS_ValidateParameters(
  1904.             pims, 
  1905.             IMsgStore,
  1906.             StoreLogoff,
  1907.             (pims,
  1908.             pulFlags));
  1909.  
  1910.     IMS_EnterCriticalSection(pims);
  1911.  
  1912.     pims->ulFlagsSLT = *pulFlags;
  1913.     *pulFlags = LOGOFF_COMPLETE;
  1914.  
  1915.     IMS_LeaveCriticalSection(pims);
  1916.  
  1917.     DebugTraceResult(IMS_StoreLogoff, hr);
  1918.     return HrCheckHr(hr, IMsgStore_StoreLogoff);
  1919. }
  1920.  
  1921. /*
  1922.  *  IMS_AbortSubmit
  1923.  *
  1924.  *  Purpose:
  1925.  *      Removes the current message from the submission queue.
  1926.  *      Since this is not implemented in IMessage on IStorage, we
  1927.  *      must do it ourselves.
  1928.  *
  1929.  *  Arguments:
  1930.  *      lpMS        Pointer to the message store.
  1931.  *      cbEntryID   the size of the entry ID
  1932.  *      lpEntryID   the entry ID of the message to abort
  1933.  *      ulFlags     Flags.  Reserved for future use.  Must be zero.
  1934.  *
  1935.  *  Returns:
  1936.  *      HRESULT
  1937.  *
  1938.  *  Side effects:
  1939.  *      None.
  1940.  *
  1941.  *  Errors:
  1942.  *      MAPI_E_NOT_IN_QUEUE     The message was never successfully
  1943.  *                              submitted (and is thus not in the
  1944.  *                              outgoing queue).
  1945.  *      MAPI_E_UNABLE_TO_ABORT  The underlying messaging system no
  1946.  *                              longer allows the submission to be
  1947.  *                              cancelled.
  1948.  */
  1949. STDMETHODIMP IMS_AbortSubmit(PIMS pims, ULONG cbEntryID, LPENTRYID lpEntryID,
  1950.     ULONG ulFlags)
  1951. {
  1952.     HRESULT hr = hrSuccess;
  1953.     PIMSG pimsg = NULL;
  1954.     ULONG ulObjType;
  1955.     BOOL fClearSpooler = FALSE;
  1956.     ULONG ulSF;
  1957.     PEID peid;
  1958.  
  1959.     MS_ValidateParameters(
  1960.             pims, 
  1961.             IMsgStore,
  1962.             AbortSubmit,
  1963.             (pims, 
  1964.             cbEntryID, 
  1965.             lpEntryID, 
  1966.             ulFlags));
  1967.  
  1968.     IMS_EnterCriticalSection(pims);
  1969.  
  1970.     peid = (PEID) lpEntryID;
  1971.  
  1972.     /* Note that we don't allow a NULL entryid here, because the root */
  1973.     /* folder is not a valid input to AbortSubmit. */
  1974.  
  1975.     if (FIsInvalidEID(cbEntryID, peid, pims)
  1976.         && !FIsMessage(peid))
  1977.     {
  1978.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  1979.         goto exit;
  1980.     }
  1981.  
  1982.     /* unlock the message */
  1983.     /* make this store look like the spooler so we can open it if locked */
  1984.  
  1985.     if (!OBJ_TestFlag(pims, MSF_SPOOLER))
  1986.     {
  1987.         OBJ_SetFlag(pims, MSF_SPOOLER);
  1988.         fClearSpooler = TRUE;
  1989.     }
  1990.  
  1991.     hr = pims->lpVtbl->OpenEntry(pims, cbEntryID, lpEntryID,
  1992.         NULL, MAPI_MODIFY, &ulObjType, (LPUNKNOWN *) &pimsg);
  1993.     if (hr != hrSuccess)
  1994.         goto exit;
  1995.  
  1996.     /* If we can't get PR_SUBMIT_FLAGS from the message, then either */
  1997.     /* the message hasn't been submitted, or something else is broken. */
  1998.     /* In any case, we can't abort the submit. */
  1999.     /* If the message is locked already by the spooler, then we also */
  2000.     /* can't abort the submit. */
  2001.  
  2002.     hr = HrGetSingleProp((LPMAPIPROP) pimsg, &pims->lmr, PR_SUBMIT_FLAGS, &ulSF);
  2003.     if ((hr != hrSuccess) || (ulSF & SUBMITFLAG_LOCKED))
  2004.     {
  2005.         hr = ResultFromScode(MAPI_E_UNABLE_TO_ABORT);
  2006.         goto exit;
  2007.     }
  2008.  
  2009.     hr = HrSetFlags(pimsg, UNSET, PR_MESSAGE_FLAGS, MSGFLAG_SUBMIT);
  2010.     if (hr != hrSuccess)
  2011.         goto exit;
  2012.  
  2013.     hr = HrSetFlags(pimsg, UNSET, PR_SUBMIT_FLAGS, SUBMITFLAG_LOCKED);
  2014.     if (hr != hrSuccess)
  2015.         goto exit;
  2016.  
  2017.     hr = pimsg->lpVtbl->SaveChanges(pimsg, KEEP_OPEN_READWRITE);
  2018.     if (hr != hrSuccess)
  2019.         goto exit;
  2020.  
  2021.     hr = HrUpdateOutgoingQueue(pims, NULL, (PEID) lpEntryID,
  2022.         TABLE_ROW_DELETED);
  2023.  
  2024. exit:
  2025.     UlRelease(pimsg);
  2026.  
  2027.     if (fClearSpooler)
  2028.         OBJ_ClearFlag(pims, MSF_SPOOLER);
  2029.  
  2030.     IMS_LeaveCriticalSection(pims);
  2031.  
  2032.     DebugTraceResult(IMS_AbortSubmit, hr);
  2033.     return HrCheckHr(hr, IMsgStore_AbortSubmit);
  2034. }
  2035.  
  2036. /*
  2037.  *  IMS_GetOutgoingQueue
  2038.  *
  2039.  *  Purpose:
  2040.  *      Returns a MAPI Table Object of the queue of messages
  2041.  *      waiting to be sent.
  2042.  *
  2043.  *  Arguments:
  2044.  *      pims        Pointer to a Spooler Message Store Object.
  2045.  *      ulFlags     Reserved for future use.  Must be zero.
  2046.  *      lppTable    Location to return the new table object.
  2047.  *
  2048.  *  Returns:
  2049.  *      HRESULT
  2050.  *
  2051.  *  Side effects:
  2052.  *      None.
  2053.  *
  2054.  *  Errors:
  2055.  *      None.
  2056.  */
  2057. STDMETHODIMP IMS_GetOutgoingQueue(PIMS pims, ULONG ulFlags,
  2058.     LPMAPITABLE *lppTable)
  2059. {
  2060.     SCODE sc = S_OK;
  2061.     HRESULT hr = hrSuccess;
  2062.     LPMAPITABLE ptbl = NULL;
  2063.     BOOL fInMutex = FALSE;
  2064.  
  2065.     MS_ValidateParameters(
  2066.             pims, 
  2067.             IMsgStore,
  2068.             GetOutgoingQueue,
  2069.             (pims, 
  2070.             ulFlags, 
  2071.             lppTable));
  2072.  
  2073.     IMS_EnterCriticalSection(pims);
  2074.  
  2075.     if (!OBJ_TestFlag(pims, MSF_SPOOLER))
  2076.     {
  2077.         hr = ResultFromScode(MAPI_E_NO_SUPPORT);
  2078.         goto exit;
  2079.     }
  2080.  
  2081.     /* If the file mutex doesn't yet exist on this process, create it. */
  2082.  
  2083.     if (pims->hOGQueueMutex == NULL)
  2084.     {
  2085.         hr = HrCreateOGQueueMutex(&pims->hOGQueueMutex);
  2086.         if (hr != hrSuccess)
  2087.             goto exit;
  2088.     }
  2089.  
  2090.     /* Get the file mutex so that we can use the file (and change it) */
  2091.     /* without crossing paths with another process. */
  2092.  
  2093.     WaitForSingleObject(pims->hOGQueueMutex, INFINITE);
  2094.     fInMutex = TRUE;
  2095.  
  2096.     /* Create a new table if there currently is none */
  2097.     if (pims->lptblOutgoing == NULL)
  2098.     {
  2099.         hr = HrNewOutgoingTableData(pims);
  2100.         if (hr != hrSuccess)
  2101.             goto exit;
  2102.     }
  2103.  
  2104.     /* open a view on the table */
  2105.     hr = pims->lptblOutgoing->lpVtbl->HrGetView(pims->lptblOutgoing,
  2106.         NULL, OutgoingViewRelease, (ULONG) pims, &ptbl);
  2107.     if (hr != hrSuccess)
  2108.         goto exit;
  2109.  
  2110.     pims->cOutgoingViews++;
  2111.  
  2112.     *lppTable = ptbl;
  2113.  
  2114.     Assert(hrSuccess == hr);
  2115.  
  2116. exit:
  2117.     AssertSz(hr == hrSuccess || HR_FAILED(hr),
  2118.         "Unexpected warning return");
  2119.  
  2120.     if (fInMutex)
  2121.         ReleaseMutex(pims->hOGQueueMutex);
  2122.  
  2123.     IMS_LeaveCriticalSection(pims);
  2124.  
  2125.     DebugTraceResult(IMS_GetOutgoingQueue, hr);
  2126.     return HrCheckHr(hr, IMsgStore_GetOutgoingQueue);
  2127. }
  2128.  
  2129. /*
  2130.  *  IMS_SetLockState
  2131.  *
  2132.  *  Purpose:
  2133.  *      Allows the spooler to lock a message so that no one else
  2134.  *      can modify it while the spooler processes it.
  2135.  *
  2136.  *  Arguments:
  2137.  *      pims        Pointer to the Message Store Object.
  2138.  *      lpMessage   The message object to be locked
  2139.  *      ulFlags     control flags
  2140.  *
  2141.  *  Returns:
  2142.  *      HRESULT
  2143.  *
  2144.  *  Side effects:
  2145.  *      None.
  2146.  *
  2147.  *  Errors:
  2148.  *      None.
  2149.  */
  2150. STDMETHODIMP IMS_SetLockState(PIMS pims, LPMESSAGE lpMessage, ULONG ulFlags)
  2151. {
  2152.     SCODE sc = S_OK;
  2153.     HRESULT hr = hrSuccess;
  2154.     PIMSG pimsg = (PIMSG) lpMessage;
  2155.     ULONG ulSF;
  2156.     LPSPropValue pval = NULL;
  2157.  
  2158.     MS_ValidateParameters(
  2159.             pims, 
  2160.             IMsgStore,
  2161.             SetLockState,
  2162.             (pims, 
  2163.             lpMessage, 
  2164.             ulFlags));
  2165.  
  2166.     IMS_EnterCriticalSection(pims);
  2167.  
  2168.     if (!OBJ_TestFlag(pims, MSF_SPOOLER))
  2169.     {
  2170.         hr = ResultFromScode(MAPI_E_NO_SUPPORT);
  2171.         goto exit;
  2172.     }
  2173.  
  2174.     hr = HrGetSingleProp((LPMAPIPROP) pimsg, &pims->lmr, PR_SUBMIT_FLAGS, &ulSF);
  2175.     if (hr != hrSuccess)
  2176.     {
  2177.         if (GetScode(hr) == MAPI_E_NOT_FOUND)
  2178.         {
  2179.             ULONG ulMF;
  2180.  
  2181.             hr = HrGetSingleProp((LPMAPIPROP) pimsg, &pims->lmr, PR_MESSAGE_FLAGS, &ulMF);
  2182.             if (hr != hrSuccess)
  2183.             {
  2184.                 if (GetScode(hr) == MAPI_E_NOT_FOUND)
  2185.                     hr = ResultFromScode(MAPI_E_CORRUPT_STORE);
  2186.  
  2187.                 goto exit;
  2188.             }
  2189.  
  2190.             if (ulMF & MSGFLAG_SUBMIT)
  2191.                 hr = ResultFromScode(MAPI_E_CORRUPT_STORE);
  2192.             else
  2193.                 hr = ResultFromScode(MAPI_E_NOT_IN_QUEUE);
  2194.         }
  2195.         goto exit;
  2196.     }
  2197.  
  2198.     /* set the lock state, if the message is already in the correct state */
  2199.     /* just get outta here */
  2200.  
  2201.     if (ulFlags & MSG_LOCKED)
  2202.     {
  2203.         if (!(ulSF & SUBMITFLAG_LOCKED))
  2204.             ulSF |= SUBMITFLAG_LOCKED;
  2205.         else
  2206.             goto exit;
  2207.     }
  2208.     else
  2209.         /* unlock */
  2210.     {
  2211.         if (ulSF & SUBMITFLAG_LOCKED)
  2212.             ulSF &= ~SUBMITFLAG_LOCKED;
  2213.         else
  2214.             goto exit;
  2215.     }
  2216.  
  2217.     hr = HrSetSingleProp((LPMAPIPROP) pimsg, &pims->lmr, PR_SUBMIT_FLAGS, &ulSF);
  2218.     if (hr != hrSuccess)
  2219.         goto exit;
  2220.  
  2221.     hr = pimsg->lpVtbl->SaveChanges(pimsg, KEEP_OPEN_READWRITE);
  2222.     if (hr != hrSuccess)
  2223.         goto exit;
  2224.  
  2225.     /* No need to call ChangeTable to update tables because the SaveChanges
  2226.      * call above just did that.
  2227.      */
  2228.  
  2229. exit:
  2230.     IMS_LeaveCriticalSection(pims);
  2231.  
  2232.     DebugTraceResult(IMS_SetLockState, hr);
  2233.     return HrCheckHr(hr, IMsgStore_SetLockState);
  2234. }
  2235.  
  2236. /*
  2237.  *  IMS_FinishedMsg
  2238.  *
  2239.  *  Purpose:
  2240.  *      Allows the Spooler to inform the message store that it has
  2241.  *      finished processing a message (cancels a previous
  2242.  *      MDBLockMsg).
  2243.  *
  2244.  *  Arguments:
  2245.  *      pims        Pointer to a Message Store Object.
  2246.  *      ulFlags     Reserved for future use.  Ignored.
  2247.  *      lpEntryID   EntryID of message that was locked.
  2248.  *
  2249.  *  Returns:
  2250.  *      HRESULT
  2251.  *
  2252.  *  Side effects:
  2253.  *      None.
  2254.  *
  2255.  *  Errors:
  2256.  *      None.
  2257.  */
  2258. STDMETHODIMP IMS_FinishedMsg(PIMS pims, ULONG ulFlags, ULONG cbEntryID,
  2259.     LPENTRYID lpEntryID)
  2260. {
  2261.     PIMSG pimsg = NULL;         /* opened message */
  2262.     PIFLD pifldParent = NULL;   /* parent folder of this message */
  2263.     ULONG ulObjectType;
  2264.     HRESULT hr = hrSuccess;
  2265.     SCODE sc = S_OK;
  2266.     PEID peid = (PEID) lpEntryID;
  2267.  
  2268.  
  2269.     MS_ValidateParameters(
  2270.             pims, 
  2271.             IMsgStore,
  2272.             FinishedMsg,
  2273.             (pims, 
  2274.             ulFlags, 
  2275.             cbEntryID, 
  2276.             lpEntryID));
  2277.  
  2278.     IMS_EnterCriticalSection(pims);
  2279.  
  2280.     if (!OBJ_TestFlag(pims, MSF_SPOOLER))
  2281.     {
  2282.         hr = ResultFromScode(MAPI_E_NO_SUPPORT);
  2283.         goto exit;
  2284.     }
  2285.  
  2286.     if (FIsInvalidEID(cbEntryID, peid, pims))
  2287.     {
  2288.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  2289.         goto exit;
  2290.     }
  2291.  
  2292.     hr = pims->lpVtbl->OpenEntry(pims, cbEntryID, lpEntryID,
  2293.         NULL, MAPI_MODIFY, &ulObjectType, (LPUNKNOWN *) &pimsg);
  2294.     if (hr != hrSuccess)
  2295.         goto exit;
  2296.  
  2297.     Assert(ulObjectType == MAPI_MESSAGE);
  2298.  
  2299.     /* update the parent folder */
  2300.     hr = HrOpenParent(pims, peid, MAPI_MODIFY, &pifldParent);
  2301.     if (hr != hrSuccess)
  2302.         goto exit;
  2303.  
  2304.     /* unlock the message */
  2305.     hr = HrSetFlags(pimsg, UNSET, PR_MESSAGE_FLAGS,
  2306.         MSGFLAG_UNSENT | MSGFLAG_SUBMIT);
  2307.     if (hr != hrSuccess)
  2308.         goto exit;
  2309.  
  2310.     /* Mark the message read */
  2311.     hr = HrSetFlags(pimsg, SET, PR_MESSAGE_FLAGS, MSGFLAG_READ);
  2312.     if (hr != hrSuccess)
  2313.         goto exit;
  2314.  
  2315.     /* Clear submitflag_locked and save the message */
  2316.     hr = HrSetFlags(pimsg, UNSET, PR_SUBMIT_FLAGS, SUBMITFLAG_LOCKED);
  2317.     if (hr != hrSuccess)
  2318.         goto exit;
  2319.  
  2320.     hr = pimsg->lpVtbl->SaveChanges(pimsg, KEEP_OPEN_READWRITE);
  2321.     if (hr != hrSuccess)
  2322.         goto exit;
  2323.  
  2324.     hr = pims->psup->lpVtbl->DoSentMail(pims->psup, 0L, (LPMESSAGE) pimsg);
  2325.     if (hr != hrSuccess)
  2326.         goto exit;
  2327.     pimsg = NULL;
  2328.  
  2329.     Assert(pims->lptblOutgoing);
  2330.  
  2331.     hr = HrUpdateOutgoingQueue(pims, NULL, (PEID) lpEntryID,
  2332.         TABLE_ROW_DELETED);
  2333.  
  2334. exit:
  2335.     UlRelease(pifldParent);
  2336.     UlRelease(pimsg);
  2337.  
  2338.     IMS_LeaveCriticalSection(pims);
  2339.  
  2340.     DebugTraceResult(IMS_FinishedMsg, hr);
  2341.     return HrCheckHr(hr, IMsgStore_FinishedMsg);
  2342. }
  2343.  
  2344. /*
  2345.  *  IMS_NotifyNewMail
  2346.  *
  2347.  *  Purpose:
  2348.  *      Spooler tells us when to tell clients about a new message.
  2349.  *      Passed in on the call is the exact notification structure
  2350.  *      we need to give the client.
  2351.  *
  2352.  *      Stubbed for now.
  2353.  *
  2354.  *  Arguments:
  2355.  *      pims        Pointer to the object.
  2356.  *      pntf        Pointer to the newmail notification structure.
  2357.  *                                      read/write.
  2358.  *
  2359.  *  Returns:
  2360.  *      HRESULT
  2361.  *
  2362.  *  Side effects:
  2363.  *      None.
  2364.  *
  2365.  *  Errors:
  2366.  *      None.
  2367.  */
  2368. STDMETHODIMP IMS_NotifyNewMail(PIMS pims, LPNOTIFICATION pntf)
  2369. {
  2370.     HRESULT hr = hrSuccess;
  2371.     LPNOTIFKEY lpKey = NULL;
  2372.     ULONG ulFlags = 0;
  2373.     PEID peidMsg;
  2374.     PEID peidFld;
  2375.     ULONG cbEIDMsg;
  2376.     ULONG cbEIDFld;
  2377.  
  2378.     MS_ValidateParameters(
  2379.             pims, 
  2380.             IMsgStore,
  2381.             NotifyNewMail,
  2382.             (pims, 
  2383.             pntf));
  2384.  
  2385.     IMS_EnterCriticalSection(pims);
  2386.  
  2387.     if (!OBJ_TestFlag(pims, MSF_SPOOLER))
  2388.     {
  2389.         hr = ResultFromScode(MAPI_E_NO_SUPPORT);
  2390.         goto exit;
  2391.     }
  2392.  
  2393.     if (!pims->psup)
  2394.     {
  2395.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  2396.         goto exit;
  2397.     }
  2398.  
  2399.     cbEIDMsg = pntf->info.newmail.cbEntryID;
  2400.     peidMsg = (PEID) pntf->info.newmail.lpEntryID;
  2401.  
  2402.     cbEIDFld = pntf->info.newmail.cbParentID;
  2403.     peidFld = (PEID) pntf->info.newmail.lpParentID;
  2404.  
  2405.     if (    cbEIDMsg == 0
  2406.         ||  FIsInvalidEID(cbEIDMsg, peidMsg, pims)
  2407.         ||  !FIsMessage(peidMsg)
  2408.         ||  cbEIDFld == 0
  2409.         ||  FIsInvalidEID(cbEIDFld, peidFld, pims)
  2410.         ||  !FIsFolder(peidFld))
  2411.     {
  2412.         TraceSz("SMS: Bad entryid given to NotifyNewMail");
  2413.         hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
  2414.         goto exit;
  2415.     }
  2416.  
  2417.     /* Generate the key we use for notifications. First, get the key */
  2418.     /* for the folder that the message arrived into. */
  2419.  
  2420.     hr = HrGetSMSStandardNotifKey(pims, peidFld, &lpKey);
  2421.     if (hr != hrSuccess)
  2422.         goto exit;
  2423.  
  2424.     hr = pims->psup->lpVtbl->Notify(pims->psup, lpKey, 1, pntf, &ulFlags);
  2425.     if (hr != hrSuccess)
  2426.         goto exit;
  2427.     
  2428.     FreeNull(lpKey);
  2429.     lpKey = NULL;
  2430.     ulFlags = 0;
  2431.  
  2432.     /* Now, get the key for the entire store, and send to that key also. */
  2433.  
  2434.     hr = HrGetSMSStandardNotifKey(pims, NULL, &lpKey);
  2435.     if (hr != hrSuccess)
  2436.         goto exit;
  2437.  
  2438.     hr = pims->psup->lpVtbl->Notify(pims->psup, lpKey, 1, pntf, &ulFlags);
  2439.     if (hr != hrSuccess)
  2440.         goto exit;
  2441.  
  2442. exit:
  2443.     FreeNull(lpKey);
  2444.  
  2445.     IMS_LeaveCriticalSection(pims);
  2446.  
  2447.     DebugTraceResult(IMS_NotifyNewMail, hr);
  2448.     return HrCheckHr(hr, IMsgStore_NotifyNewMail);
  2449. }
  2450.  
  2451. /* Internal Functions */
  2452.  
  2453. BOOL IMS_IsInvalid(PIMS pims)
  2454. {
  2455.     return (IsBadWritePtr(pims, sizeof(IMS)) || pims->lpVtbl != &vtblIMS);
  2456. }
  2457.  
  2458. /***************************************************************************
  2459.  -  OutgoingViewRelease
  2460.  -
  2461.  *  Purpose:
  2462.  *      Call back function from itable on release of a view
  2463.  *      removes the view from the list of open views
  2464.  *      releases the table if there are no more open views on it
  2465.  *
  2466.  *  Arguments:
  2467.  *       ulCallerData   pointer to folder object
  2468.  *       lptbl      pointer to the table on which this is a view
  2469.  *       lpvtView       pointer to the view that was released
  2470.  *
  2471.  *
  2472.  */
  2473. STDAPI_(void) OutgoingViewRelease(ULONG ulCallerData, LPTABLEDATA lptbl,
  2474.     LPMAPITABLE lpvtView)
  2475. {
  2476.     PIMS pims;                  /* store who owns this view */
  2477.     ULONG ulViewsLeft;          /* number of open views left */
  2478.  
  2479.     pims = (PIMS) ulCallerData;
  2480.  
  2481.     /* do nothing if the message store is gone */
  2482.     if (IMS_IsInvalid(pims))
  2483.         return;
  2484.  
  2485.     IMS_EnterCriticalSection(pims);
  2486.  
  2487.     AssertSz(pims->lptblOutgoing == lptbl,
  2488.         "Different table data given to OutgoingViewRelease");
  2489.  
  2490.     ulViewsLeft = --(pims->cOutgoingViews);
  2491.  
  2492.     /* release the table data if the viewlist is empty */
  2493.     if (ulViewsLeft == 0)
  2494.     {
  2495.         UlRelease(lptbl);
  2496.         pims->lptblOutgoing = NULL;
  2497.     }
  2498.  
  2499.     IMS_LeaveCriticalSection(pims);
  2500.  
  2501.     return;
  2502. }
  2503.  
  2504. /*
  2505.  *  HrNewIMS
  2506.  *
  2507.  *  Purpose:
  2508.  *      Allocates and initializes an IMS object (internal
  2509.  *      implementation of IMsgStore).
  2510.  *
  2511.  *  Arguments:
  2512.  *      szStorePath         Path name of directory which is the
  2513.  *                          "root" of this message store.
  2514.  *      szStoreProps        Filename of IMsgStore properties
  2515.  *                          docfile in the store root.
  2516.  *      pmsp                pointer to the MS provider object.
  2517.  *      pmsl                Pointer to the MS logon object given to
  2518.  *                          MAPI when this store is created.
  2519.  *      prfs                Pointer to a context for accessing
  2520.  *                          receive folder settings.
  2521.  *      pps                 pointer to our profile section.
  2522.  *      psup                Pointer to MAPI Support Object.
  2523.  *      fCreate             TRUE if the function should create the docfile
  2524.  *                          containing IMS properties (FALSE opens existing).
  2525.  *      ppims               Location to return the address of the
  2526.  *                          newly created IMS object.
  2527.  *
  2528.  *  Returns:
  2529.  *      HRESULT
  2530.  *
  2531.  *  Side effects:
  2532.  *      Does AddRef() of support object.
  2533.  *
  2534.  *  Errors:
  2535.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate memory for
  2536.  *                                  the object.
  2537.  */
  2538. HRESULT HrNewIMS(LPTSTR szStorePath, LPTSTR szStoreProps, PMSP pmsp, PMSL pmsl,
  2539.     PRFS prfs, LPPROFSECT pps, LPMAPISUP psup, BOOL fCreate, PIMS *ppims)
  2540. {
  2541.     SCODE sc = S_OK;
  2542.     HRESULT hr;
  2543.     HRESULT hrStg = hrSuccess;
  2544.     LPTSTR szPropFull = NULL;
  2545.     PIMS pimsNew = NULL;
  2546.     PEID peid = NULL;
  2547.     LPMESSAGE lpmsg = NULL;
  2548.     BOOL fDoneCreate = FALSE;
  2549.     LPMSGSESS pmsgsess = NULL;
  2550.     LPSPropValue pvalDLLName = NULL;
  2551.  
  2552.     AssertSz(szStorePath, "Bad szStorePath");
  2553.     AssertSz(szStoreProps, "Bad szStoreProps");
  2554.     AssertSz(pmsl, "Bad pmsl");
  2555.     AssertSz(prfs, "Bad prfs");
  2556.     AssertSz(ppims, "Bad ppims");
  2557.  
  2558.     *ppims = NULL;
  2559.  
  2560.     /* Begin by creating or opening the message store's property file. */
  2561.     /* This implementation of the sample message store puts its message */
  2562.     /* store properties in a file called MSGSTORE.PRP in the root folder */
  2563.     /* of the store. If we're opening an existing store, then we'll read */
  2564.     /* PR_RECORD_KEY from this file below. If we're creating the file, we */
  2565.     /* don't actually use it in this function; however, we still need to */
  2566.     /* create the file. */
  2567.  
  2568.     hr = HrOpenIMsgSession(&pmsgsess);
  2569.     if (hr != hrSuccess)
  2570.         goto hr_err;
  2571.  
  2572.     hr = HrAppendPath(szStorePath, szStoreProps, &szPropFull);
  2573.     if (hr != hrSuccess)
  2574.         goto hr_err;
  2575.     
  2576.     hr = HrOpenIMsg(pmsgsess, szPropFull, &pmsp->lmr, psup, fCreate, FALSE,
  2577.         TRUE, &lpmsg);
  2578.     if (hr != hrSuccess)
  2579.         goto hr_err;
  2580.  
  2581.     if (fCreate)
  2582.         fDoneCreate = TRUE;
  2583.  
  2584.     /* Allocate and fill in the new object. */
  2585.  
  2586.     sc = LMAllocZ(&pmsp->lmr, sizeof(IMS), &pimsNew);
  2587.     if (sc != S_OK)
  2588.         goto sc_err;
  2589.  
  2590.     OBJ_Initialize(pimsNew, &vtblIMS, OT_MSGSTORE, pimsNew, &pmsl->cs);
  2591.  
  2592.     pimsNew->pmsl = pmsl;
  2593.     pimsNew->pmsp = pmsp;
  2594.     pimsNew->prfs = prfs;
  2595.     pimsNew->psup = psup;
  2596.     pimsNew->lmr = pmsl->lmr;
  2597.     pimsNew->eidStore.cb = 0L;
  2598.     pimsNew->eidStore.lpb = NULL;
  2599.     pimsNew->lptblOutgoing = NULL;
  2600.     pimsNew->cOutgoingViews = 0L;
  2601.     pimsNew->ulOQConn = 0L;
  2602.     pimsNew->ulTblConn = 0L;
  2603.     pimsNew->pmsgsess = pmsgsess;
  2604.     pimsNew->ulFlagsSLT = LOGOFF_ABORT;
  2605.  
  2606.     sc = ScAlloc(Cbtszsize(szStorePath), (PPV) &pimsNew->szStorePath);
  2607.     if (sc != S_OK)
  2608.         goto sc_err;
  2609.  
  2610.     lstrcpy(pimsNew->szStorePath, szStorePath);
  2611.  
  2612.     pimsNew->szProps = szPropFull;
  2613.  
  2614.     /* Fill in the uidResource.
  2615.      * This is used as the PR_STORE_RECORD_KEY and
  2616.      * as the UID contained in EntryIDs.
  2617.      */
  2618.     if (fCreate)
  2619.     {
  2620.         hr = psup->lpVtbl->NewUID(psup, &pimsNew->uidResource);
  2621.         if (hr != hrSuccess)
  2622.             goto hr_err;
  2623.     }
  2624.     else
  2625.     {
  2626.         LPSPropValue pval;
  2627.  
  2628.         /* Read out the PR_RECORD_KEY from the store.   */
  2629.         /* DO NOT READ THE PR_STORE_RECORD_KEY as that  */
  2630.         /* gets wrapped back to pimsNew->uidResource and    */
  2631.         /* HAS NOT BEEN FILLED IN YET!                      */
  2632.  
  2633.         hr = HrGetOneProp((LPMAPIPROP) lpmsg, PR_RECORD_KEY, &pval);
  2634.         if (hr != hrSuccess)
  2635.             goto hr_err;
  2636.  
  2637.         AssertSz(pval[0].Value.bin.cb == sizeof(MAPIUID),
  2638.             "Corrupted data returned from GetProps");
  2639.  
  2640.         pimsNew->uidResource = *((LPMAPIUID) pval[0].Value.bin.lpb);
  2641.  
  2642.         LMFree(&pmsp->lmr, pval);
  2643.     }
  2644.  
  2645.     /* Generate the PR_STORE_ENTRYID property in memory. */
  2646.  
  2647.     hr = HrConstructEID(&pimsNew->uidResource, &pmsp->lmr,
  2648.             pimsNew->szStorePath, &peid);
  2649.     if (hr != hrSuccess)
  2650.         goto hr_err;
  2651.  
  2652.     hr = HrGetOneProp((LPMAPIPROP)pps, PR_PROVIDER_DLL_NAME, &pvalDLLName);
  2653.     if (hr != hrSuccess)
  2654.         goto hr_err;
  2655.  
  2656.     hr = WrapStoreEntryID(0, pvalDLLName->Value.lpszA, CbEID(peid),
  2657.             (LPENTRYID)peid, &pimsNew->eidStore.cb,
  2658.             (LPENTRYID *) &pimsNew->eidStore.lpb);
  2659.     if (hr != hrSuccess)
  2660.         goto hr_err;
  2661.  
  2662. sc_err:
  2663.     if (sc != S_OK)
  2664.         hr = ResultFromScode(sc);
  2665.  
  2666. hr_err:
  2667.     LMFree(&pmsp->lmr, pvalDLLName);
  2668.     UlRelease(lpmsg);
  2669.     LMFree(&pmsp->lmr, peid);
  2670.  
  2671.     if (hr != hrSuccess)
  2672.     {
  2673.         if (fDoneCreate)
  2674.             DeleteFile(szPropFull);
  2675.  
  2676.         FreeNull(szPropFull);
  2677.         if (pimsNew)
  2678.         {
  2679.             FreeNull(pimsNew->szStorePath);
  2680.             LMFree(&pmsp->lmr, pimsNew->eidStore.lpb);
  2681.             LMFree(&pmsp->lmr, pimsNew);
  2682.         }
  2683.  
  2684.         if (pmsgsess)
  2685.             CloseIMsgSession(pmsgsess);
  2686.     }
  2687.     else
  2688.     {
  2689.         /* SUCCESS! */
  2690.         *ppims = pimsNew;
  2691.     }
  2692.  
  2693.     DebugTraceResult(HrNewIMS, hr);
  2694.     return hr;
  2695. }
  2696.  
  2697. /*
  2698.  *  HrInitIMSProps
  2699.  *
  2700.  *  Purpose:
  2701.  *      Sets the initial (and for read-only properties, the only)
  2702.  *      values for the base properties of the Message Store Object:
  2703.  *      takes as input parameters the values of those properties
  2704.  *      that are specific to this store and calculates (hard-coded)
  2705.  *      the values of those properties that are the same for all
  2706.  *      stores created by the Microsoft Sample Store Provider.
  2707.  *      Also sets attributes.
  2708.  *
  2709.  *  Arguments:
  2710.  *      pims        Internal IMsgStore object instance.
  2711.  *      szPswd      Logon Account Password.
  2712.  *
  2713.  *  Returns:
  2714.  *      HRESULT
  2715.  *
  2716.  *  Side effects:
  2717.  *      None.
  2718.  *
  2719.  *  Errors:
  2720.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  2721.  *                                  the property arrays.
  2722.  */
  2723. HRESULT HrInitIMSProps(PIMS pims, LPTSTR szPswd)
  2724. {
  2725.     HRESULT hr;
  2726.     LPMESSAGE lpmsg = NULL;
  2727.     LPSPropValue pval = NULL;
  2728.     LPSPropProblemArray pprba = NULL;
  2729.     LPSPropTagArray ptaga = NULL;
  2730.     LPSPropAttrArray patra = NULL;
  2731.     SCODE sc = S_OK;
  2732.     LPSPropProblem pProbl = NULL;
  2733.  
  2734. #define cInitIMSProps       10
  2735. #define grfInitIMSProps     ( PROPATTR_MANDATORY | PROPATTR_READABLE )
  2736.  
  2737.     AssertSz(pims, "Bad pims");
  2738.     NFAssertSz(pims->psup, "Bad support object");
  2739.     AssertSz(szPswd, "Bad szPswd");
  2740.  
  2741.     hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps, &pims->lmr,
  2742.         pims->psup, TRUE, &lpmsg);
  2743.     if (hr != hrSuccess)
  2744.         goto exit;
  2745.  
  2746.     /* Allocate the property arrays. */
  2747.     hr = HrAllocPropArrays(cInitIMSProps, &pval, &ptaga, &patra);
  2748.     if (hr != hrSuccess)
  2749.         goto exit;
  2750.  
  2751.     /* Initialize property value array and all property tags. */
  2752.  
  2753.     ptaga->cValues = patra->cValues = cInitIMSProps;
  2754.  
  2755.     pval[0].ulPropTag = ptaga->aulPropTag[0] = PR_STORE_ENTRYID;
  2756.     pval[0].Value.bin.cb = pims->eidStore.cb;
  2757.     pval[0].Value.bin.lpb = pims->eidStore.lpb;
  2758.     pval[1].ulPropTag = ptaga->aulPropTag[1] = PR_STORE_RECORD_KEY;
  2759.     pval[1].Value.bin.cb = sizeof(pims->uidResource);
  2760.     pval[1].Value.bin.lpb = (LPBYTE) &pims->uidResource;
  2761.     pval[2].ulPropTag = ptaga->aulPropTag[2] = PR_ENTRYID;
  2762.     pval[2].Value.bin.cb = pims->eidStore.cb;
  2763.     pval[2].Value.bin.lpb = pims->eidStore.lpb;
  2764.     pval[3].ulPropTag = ptaga->aulPropTag[3] = PR_RECORD_KEY;
  2765.     pval[3].Value.bin.cb = sizeof(pims->uidResource);
  2766.     pval[3].Value.bin.lpb = (LPBYTE) &pims->uidResource;
  2767.     pval[4].ulPropTag = ptaga->aulPropTag[4] = PR_DISPLAY_NAME;
  2768.     pval[4].Value.LPSZ = pims->szStorePath;
  2769.     pval[5].ulPropTag = ptaga->aulPropTag[5] = PR_SMS_PASSWORD;
  2770.     pval[5].Value.LPSZ = szPswd;
  2771.  
  2772.     /* We don't set support properties that are changed by READONLY */
  2773.     /* when the store is open. Those are or'ed in during GetProps. */
  2774.     /* See HrWrap_GetProps for details. */
  2775.  
  2776.     pval[6].ulPropTag = ptaga->aulPropTag[6] = PR_STORE_SUPPORT_MASK;
  2777.     pval[6].Value.ul = SMS_SUPPORTMASK;
  2778.         
  2779.     pval[7].ulPropTag = ptaga->aulPropTag[7] = PR_OBJECT_TYPE;
  2780.     pval[7].Value.l = MAPI_STORE;
  2781.     pval[8].ulPropTag = ptaga->aulPropTag[8] = PR_FILENAME_SEQUENCE_NUMBER;
  2782.     pval[8].Value.ul = 0x10000000;
  2783.     pval[9].ulPropTag = ptaga->aulPropTag[9] = PR_MDB_PROVIDER;
  2784.     pval[9].Value.bin.cb = sizeof(MAPIUID);
  2785.     pval[9].Value.bin.lpb = (LPBYTE) &uidProvider;
  2786.  
  2787.     /* Initialize the property attribute array. */
  2788.  
  2789.     patra->aPropAttr[0] = grfInitIMSProps;
  2790.     patra->aPropAttr[1] = grfInitIMSProps;
  2791.     patra->aPropAttr[2] = grfInitIMSProps;
  2792.     patra->aPropAttr[3] = grfInitIMSProps;
  2793.     patra->aPropAttr[4] = grfInitIMSProps | PROPATTR_WRITEABLE;
  2794.     patra->aPropAttr[5] = grfInitIMSProps;
  2795.     patra->aPropAttr[6] = grfInitIMSProps;
  2796.     patra->aPropAttr[7] = grfInitIMSProps;
  2797.     patra->aPropAttr[8] = grfInitIMSProps;
  2798.     patra->aPropAttr[9] = grfInitIMSProps;
  2799.  
  2800.     /* Set the property values. */
  2801.  
  2802.     hr = lpmsg->lpVtbl->SetProps(lpmsg, cInitIMSProps, pval, &pprba);
  2803.     if (hr != hrSuccess) /* || pprba)*/
  2804.         goto exit;
  2805.     if(pprba)
  2806.     {
  2807.         for(pProbl = pprba->aProblem; pProbl < pprba->aProblem + pprba->cProblem; ++pProbl)
  2808.         {
  2809.             if(pProbl->ulPropTag != PR_STORE_SUPPORT_MASK)
  2810.                 goto exit;
  2811.         }
  2812.         LMFree(&pims->lmr, pprba);
  2813.         pprba = NULL;
  2814.     }
  2815.  
  2816.  
  2817.     /* Set the property attributes. */
  2818.  
  2819.     hr = SetAttribIMsgOnIStg(lpmsg, ptaga, patra, &pprba);
  2820.     if (hr != hrSuccess) /* || pprba)*/
  2821.         goto exit;
  2822.     if(pprba)
  2823.     {
  2824.         for(pProbl = pprba->aProblem; pProbl < pprba->aProblem + pprba->cProblem; ++pProbl)
  2825.         {
  2826.             if(pProbl->ulPropTag != PR_STORE_SUPPORT_MASK)
  2827.                 goto exit;
  2828.         }
  2829.         LMFree(&pims->lmr, pprba);
  2830.         pprba = NULL;
  2831.     }
  2832.  
  2833.  
  2834.  
  2835.     /* If we succeeded up to this point, commit the properties. */
  2836.  
  2837.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  2838.     if (hr != hrSuccess)
  2839.         goto exit;
  2840.  
  2841. exit:
  2842.     AssertSz(hr == hrSuccess || HR_FAILED(hr), "No Warnings expected");
  2843.  
  2844.     if (pprba)
  2845.     {
  2846.         LMFree(&pims->lmr, pprba);
  2847.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  2848.     }
  2849.  
  2850.     FreePropArrays(&pval, &ptaga, &patra);
  2851.  
  2852.     UlRelease(lpmsg);
  2853.  
  2854.     DebugTraceResult(InitIMSProps, hr);
  2855.     return hr;
  2856. }
  2857.  
  2858. /*
  2859.  *  GetResourceUID
  2860.  *
  2861.  *  Purpose:
  2862.  *      Returns the one UID that identifies this MAPI resource
  2863.  *      (message store).
  2864.  *
  2865.  *  Arguments:
  2866.  *      pims    Internal IMsgStore object instance.
  2867.  *      lpuid   Location in which to return the value of the
  2868.  *              Resource UID.
  2869.  *
  2870.  *  Returns:
  2871.  *      VOID
  2872.  *
  2873.  *  Side effects:
  2874.  *      None.
  2875.  *
  2876.  *  Errors:
  2877.  *      None.
  2878.  */
  2879. VOID GetResourceUID(PIMS pims, LPMAPIUID lpuid)
  2880. {
  2881.     AssertSz(pims, "Bad pims");
  2882.     AssertSz(lpuid, "Bad lpuid");
  2883.  
  2884.     *lpuid = pims->uidResource;
  2885.  
  2886.     return;
  2887. }
  2888.  
  2889. /*********************************************************
  2890.  * FIsInvalidEID
  2891.  *
  2892.  *  Purpose         check if the peid points to an invalid EID
  2893.  *                  Returns TRUE if it is invalid. This routine
  2894.  *                  considers EIDs of length 0 invalid.
  2895.  *
  2896.  *  Parameter
  2897.  *      cb          number of bytes believed to be in peid
  2898.  *      peid        pointer to the entryid
  2899.  *      pims        store in which the object should be. May be NULL,
  2900.  *                  in which case, no check of the uid is performed.
  2901.  */
  2902. BOOL FIsInvalidEID(ULONG cb, PEID peid, PIMS pims)
  2903. {
  2904.     BOOL fInvalid;
  2905.  
  2906.     fInvalid = (cb < CbNewEID(0)
  2907.         || cb > CbNewEID(MAX_PATH)
  2908.         || IsBadReadPtr(peid, (UINT) cb)
  2909.         || IsBadStringPtr(peid->szPath, (UINT) -1)
  2910.         || cb != CbEID(peid)
  2911.         || peid->bVersion != SMPMS_VERSION);
  2912.  
  2913.     /* If the eid still looks good, and we were given the message store */
  2914.     /* object, then do one final check of the uid in the eid versus the */
  2915.     /* uid of the store. */
  2916.  
  2917.     if (!fInvalid && pims)
  2918.     {
  2919.         MAPIUID uid;
  2920.  
  2921.         GetResourceUID(pims, &uid);
  2922.         fInvalid = !IsEqualMAPIUID(&uid, &peid->uidResource);
  2923.     }
  2924.  
  2925.     return fInvalid;
  2926. }
  2927.  
  2928. /*
  2929.  *  HrUniqueFileName
  2930.  *
  2931.  *  Purpose:
  2932.  *      Returns a unique file name base that can be used by other
  2933.  *      parts of the store when a file needs to be created.  Gets
  2934.  *      the PR_FILENAME_SEQUENCE_NUMBER property out of the message
  2935.  *      store object, uses its textized form as the unique name,
  2936.  *      increments it, and stores it back in the object.  Sequence
  2937.  *      numbers begin at 0x10000000 and increment so that every
  2938.  *      file name returned is the same 8-character length.  (See
  2939.  *      HrInitIMSProps.)
  2940.  *
  2941.  *  Arguments:
  2942.  *      pims            Message Store Object.
  2943.  *      lpulSeqNumber   pointer to sequence number of this file name
  2944.  *      lppstrNewName   Location in which to return a pointer to a
  2945.  *                      buffer containing the unique file name.
  2946.  *
  2947.  *  Returns:
  2948.  *      HRESULT
  2949.  *
  2950.  *  Side effects:
  2951.  *      Increments the PR_FILENAME_SEQUENCE_NUMBER property of the
  2952.  *      message store object.
  2953.  *
  2954.  *  Errors:
  2955.  *      All SetProps and SaveChanges errors.  Also:
  2956.  *
  2957.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate space for
  2958.  *                                  the return parameter.
  2959.  */
  2960. HRESULT HrUniqueFileName(PIMS pims, ULONG *lpulSeqNumber,
  2961.     LPTSTR *pszNewName)
  2962. {
  2963.     HRESULT hr = hrSuccess;
  2964.     LPTSTR szFileName = NULL;
  2965.     ULONG ulSeq = 0L;
  2966.     LPMESSAGE lpmsg = NULL;
  2967.  
  2968.     AssertSz(pims, "Bad pims");
  2969.     AssertSz(pszNewName, "Bad pszNewName");
  2970.  
  2971.     /* Allocate space for the return string */
  2972.  
  2973.     hr = HrAlloc((CCH_NAME - CCH_EXT) * sizeof(TCHAR), (PPV) &szFileName);
  2974.     if (hr != hrSuccess)
  2975.         goto exit;
  2976.  
  2977.     /* Get sequence number out of object, increment */
  2978.     /* sequence number, and turn it into a string.  */
  2979.  
  2980.     hr = HrGetSingleProp((LPMAPIPROP) pims, &pims->lmr,
  2981.         PR_FILENAME_SEQUENCE_NUMBER, &ulSeq);
  2982.     if (hr != hrSuccess)
  2983.         goto exit;
  2984.  
  2985.     ulSeq++;
  2986.  
  2987.     hr = HrOpenIMSPropsFileRetry(pims->pmsgsess, pims->szProps, &pims->lmr,
  2988.         pims->psup, TRUE, &lpmsg);
  2989.     if (hr != hrSuccess)
  2990.         goto exit;
  2991.  
  2992.     hr = HrSetOneROProp(lpmsg, &pims->lmr, PR_FILENAME_SEQUENCE_NUMBER, &ulSeq);
  2993.     if (hr != hrSuccess)
  2994.         goto exit;
  2995.  
  2996.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, KEEP_OPEN_READWRITE);
  2997.     if (hr != hrSuccess)
  2998.         goto exit;
  2999.  
  3000.     /* If this wsprintf statement changes, fix HrConvertSzToHex below. */
  3001.  
  3002.     wsprintf(szFileName, TEXT("%08lx"), ulSeq);
  3003.  
  3004.     *pszNewName = szFileName;
  3005.     *lpulSeqNumber = ulSeq;
  3006.  
  3007. exit:
  3008.     UlRelease(lpmsg);
  3009.  
  3010.     if (hr != hrSuccess)
  3011.         FreeNull(szFileName);
  3012.  
  3013.     DebugTraceResult(HrUniqueFileName, hr);
  3014.     return hr;
  3015. }
  3016.  
  3017. /*
  3018.  * HrConvertSzToHex
  3019.  *
  3020.  * Converts the string given into an equivalent number. The string must
  3021.  * contain characters in the range 0-9, A-F, or a-f. If the routine finds
  3022.  * characters outside these ranges in the string, it will terminate with
  3023.  * the error MAPI_E_CALL_FAILED. The string must be at least 8 characters
  3024.  * long (a 32-bit number is fully-specified by a 8 hex characters). The
  3025.  * routine will use only the first 8 characters in the string, even if the
  3026.  * string is longer than 8 characters. Note that only the first 8 characters
  3027.  * must be within the proper range. Characters after the first 8 will be
  3028.  * ignored.
  3029.  *
  3030.  * Parameters:
  3031.  *  szName: The string to convert.
  3032.  *  pulAnswer: A pointer to the location to return the converted answer.
  3033.  *
  3034.  * Errors:
  3035.  *  MAPI_E_CALL_FAILED -- when a character is out of range.
  3036.  */
  3037. static HRESULT HrConvertSzToHex(LPSTR szName, ULONG *pulAnswer)
  3038. {
  3039.     HRESULT hr = hrSuccess;
  3040.     CHAR *pch;
  3041.     CHAR *pchMax;
  3042.     ULONG ulAns = 0;
  3043.     ULONG ichConv = 0;
  3044.  
  3045.     /* String must be at least 8 hex chars long. It should have come from */
  3046.     /* the HrUniqueFileName function above, which uses wsprintf to generate */
  3047.     /* the string. */
  3048.  
  3049.     if (    IsBadStringPtr(szName, (UINT) -1)
  3050.         ||  lstrlen(szName) < 8)
  3051.     {
  3052.         TrapSz("Bad input to function");
  3053.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  3054.         goto exit;
  3055.     }
  3056.  
  3057.     pch = szName;
  3058.     pchMax = szName + lstrlen(szName);
  3059.  
  3060.     /* Only convert the first 8 characters, no matter how long the string is. */
  3061.     while(pch < pchMax && ichConv < 8)
  3062.     {
  3063.         ulAns <<= 4;
  3064.  
  3065.         if (*pch >= '0' && *pch <= '9')
  3066.             ulAns += (*pch - '0');
  3067.         else if (*pch >= 'a' && *pch <= 'f')
  3068.             ulAns += (*pch - 'a' + 0xA);
  3069.         else if (*pch >= 'A' && *pch <= 'F')
  3070.             ulAns += (*pch - 'A'+ 0xA);
  3071.         else
  3072.         {
  3073.             TraceSz2("SampleMS: HrConvertSztoHex: char %c(%#x) out of range.\n",
  3074.                 *pch, *pch);
  3075.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  3076.             goto exit;
  3077.         }
  3078.  
  3079.         ++pch;
  3080.         ++ichConv;
  3081.     }
  3082.  
  3083.     *pulAnswer = ulAns;
  3084.  
  3085. exit:
  3086.     DebugTraceResult(HrConvertSzToHex, hr);
  3087.     return hr;
  3088. }
  3089.  
  3090. /*
  3091.  * HrGetSequenceNum
  3092.  *
  3093.  * This routine takes a message eid and gets the
  3094.  * numerical equivalent of the base file name in the entryid. So, if the
  3095.  * base name of the message was "00000005.msg", the function would return
  3096.  * 0x00000005.
  3097.  *
  3098.  * Parameters:
  3099.  *  pimsg: the message object to update.
  3100.  *  pulSequenceNum: a pointer to the location to place the generated number.
  3101.  *
  3102.  */
  3103. static HRESULT HrGetSequenceNum(PEID peid, ULONG *pulSequenceNum)
  3104. {
  3105.     HRESULT hr;
  3106.     LPSTR szBaseName;
  3107.     ULONG ulSeqNum;
  3108.  
  3109.     szBaseName = SzBaseName(peid);
  3110.  
  3111.     hr = HrConvertSzToHex(szBaseName, &ulSeqNum);
  3112.     if (hr != hrSuccess)
  3113.         goto exit;
  3114.  
  3115.     *pulSequenceNum = ulSeqNum;
  3116.  
  3117. exit:
  3118.     DebugTraceResult(HrGetSequenceNum, hr);
  3119.     return hr;
  3120. }
  3121.  
  3122. /*
  3123.  *  IMS_NeuterChildren, IMS_Neuter
  3124.  *
  3125.  *  Purpose:
  3126.  *      Free all memory and resources associated with a logon.  We
  3127.  *      cannot, however, remove the critical section:  that must be
  3128.  *      done by the caller of this routine.
  3129.  *
  3130.  *  Arguments:
  3131.  *      pims            Pointer to the MS object.
  3132.  *
  3133.  *  Returns:
  3134.  *      None.
  3135.  *
  3136.  *  Side effects:
  3137.  *      None.
  3138.  *
  3139.  *  Errors:
  3140.  *      None.
  3141.  */
  3142. void IMS_NeuterChildren(POBJ pobj, int iLevel)
  3143. {
  3144. #ifdef DEBUG
  3145.     int i;
  3146.  
  3147.     DebugTrace("SMS:   ");
  3148.     for (i = 0; i < iLevel; ++i)
  3149.         DebugTrace("  ");
  3150.  
  3151.     switch (pobj->wType)
  3152.     {
  3153.     case OT_MSGSTORE:
  3154.         DebugTrace("IMsgStore (%s)", ((PIMS) pobj)->szStorePath);
  3155.         break;
  3156.     case OT_FOLDER:
  3157.         DebugTrace("IMAPIFolder");
  3158.         break;
  3159.     case OT_MESSAGE:
  3160.         DebugTrace("IMessage");
  3161.         break;
  3162.     case OT_ATTACH:
  3163.         DebugTrace("IAttach");
  3164.         break;
  3165.     case OT_STREAM:
  3166.         DebugTrace("IStream");
  3167.         break;
  3168.     }
  3169.     DebugTrace(" (@%08lX,cRef=%ld)\n", pobj, pobj->cRef);
  3170. #endif
  3171.  
  3172.     while (TRUE)
  3173.     {
  3174.         POBJ pobjChild;
  3175.  
  3176.         pobjChild = pobj->pobjHead;
  3177.  
  3178.         if (pobjChild == NULL)
  3179.             break;
  3180.  
  3181.         pobj->pobjHead = pobjChild->pobjNext;
  3182.         IMS_NeuterChildren(pobjChild, iLevel + 1);
  3183.     }
  3184.  
  3185.     if (pobj->wType != OT_MSGSTORE)
  3186.     {
  3187.         LPFNNEUTER lpfnNeuter;
  3188.  
  3189.         lpfnNeuter = rgfnNeuter[pobj->wType];
  3190.  
  3191.         if (lpfnNeuter != 0)
  3192.             lpfnNeuter(pobj);
  3193.  
  3194.         pobj->lpVtbl = 0;
  3195.         LMFree(&pobj->pims->lmr, pobj);
  3196.     }
  3197. }
  3198.  
  3199. void IMS_Neuter(PIMS pims)
  3200. {
  3201.     HRESULT hr;
  3202.     ULONG ulFlags;
  3203.  
  3204.  
  3205.     if (pims->ulOQConn)
  3206.     {
  3207.         pims->psup->lpVtbl->Unsubscribe(pims->psup, pims->ulOQConn);
  3208.         pims->ulOQConn = 0;
  3209.     }
  3210.  
  3211.     if (pims->ulTblConn)
  3212.     {
  3213.         pims->psup->lpVtbl->Unsubscribe(pims->psup, pims->ulTblConn);
  3214.         pims->ulTblConn = 0;
  3215.     }
  3216.         
  3217.     OBJ_SetFlag(pims, MSF_BEINGDESTROYED);
  3218.     
  3219.     ulFlags = pims->ulFlagsSLT;
  3220.  
  3221.     hr = pims->psup->lpVtbl->StoreLogoffTransports(pims->psup, &ulFlags);
  3222.  
  3223. #ifdef DEBUG
  3224.     /* The support object given us during config doesn't support */
  3225.     /* StoreLogoffTransports. It isn't an error to get no support */
  3226.     /* in that case. */
  3227.     if (hr != hrSuccess && GetScode(hr) != MAPI_E_NO_SUPPORT)
  3228.         TraceSz1("SMS: IMS_Neuter: StoreLogoffTransports(LOGOFF_ABORT) "
  3229.             "returned unexpected error %s", SzDecodeScode(GetScode(hr)));
  3230. #endif
  3231.  
  3232.     if (pims->cRef != 0 || pims->pobjHead != 0)
  3233.     {
  3234.         TraceSz("\n---------------------------------------------------------"
  3235.             "-------");
  3236.         TraceSz("SMS: The following objects were not released before Logoff:");
  3237.         IMS_NeuterChildren((POBJ) pims, 0);
  3238.         TraceSz("-----------------------------------------------------------"
  3239.             "-----\n");
  3240.     }
  3241.  
  3242.     /* Free MS object's resources */
  3243.     FreeNull(pims->szStorePath);
  3244.     FreeNull(pims->szProps);
  3245.     LMFree(&pims->lmr, pims->eidStore.lpb);
  3246.     CloseRFS(pims->prfs);
  3247.  
  3248.     if (pims->lptblOutgoing)
  3249.     {
  3250.         if (pims->cOutgoingViews)
  3251.         {
  3252.             TraceSz1("Sample MS: IMS_Neuter: Leaked outgoing queue table "
  3253.                 "(# views left = %08lX)\n", pims->cOutgoingViews);
  3254.             pims->cOutgoingViews = 0;
  3255.         }
  3256.  
  3257.         UlRelease(pims->lptblOutgoing);
  3258.     }
  3259.  
  3260.  
  3261.     CloseIMsgSession(pims->pmsgsess);
  3262.     pims->pmsgsess = NULL;
  3263.  
  3264.     /* Make the logon object forget about us */
  3265.     if (pims->pmsl)
  3266.         pims->pmsl->pims = NULL;
  3267. }
  3268.  
  3269. /*
  3270.  *  HrOpenIMSPropsFileRetry
  3271.  *
  3272.  *  Purpose:
  3273.  *      Open the IMsgStore properties docfile as an IMessage
  3274.  *      instance to get/set properties.
  3275.  *      This retries up to NUM_RETRIES times on MAPI_E_NO_ACCESS
  3276.  *
  3277.  *  Arguments:
  3278.  *      szFile      The file to open.
  3279.  *      plmr        a pointer to the linked memory routines.
  3280.  *      psup        a pointer to the MAPI support object.
  3281.  *      fModify     TRUE means the caller wants read/write access.
  3282.  *                  FALSE means read-only access.
  3283.  *      lppmsg      Address of a location in which to return a
  3284.  *                  pointer to the newly opened IMessage instance.
  3285.  *
  3286.  *  Returns:
  3287.  *      HRESULT
  3288.  *
  3289.  *  Side effects:
  3290.  *      None.
  3291.  *
  3292.  *  Errors:
  3293.  *      IMessage on IStorage opening errors.
  3294.  */
  3295. HRESULT HrOpenIMSPropsFileRetry(LPMSGSESS pmsgsess, LPTSTR szFile, PLMR plmr,
  3296.     LPMAPISUP psup, BOOL fModify, LPMESSAGE * lppmsg)
  3297. {
  3298.     UINT iRetry;                /* number of attempts to open */
  3299.     HRESULT hr;
  3300.  
  3301.     iRetry = 0;
  3302.  
  3303.     while (TRUE)
  3304.     {
  3305.         hr = HrOpenIMsg(pmsgsess, szFile, plmr, psup, FALSE, fModify, TRUE,
  3306.             lppmsg);
  3307.  
  3308.         if (GetScode(hr) != MAPI_E_NO_ACCESS || ++iRetry >= NUM_RETRIES)
  3309.             break;
  3310.  
  3311.         Sleep(500);
  3312.     }
  3313.  
  3314.     #ifdef DEBUG
  3315.     if (iRetry >= NUM_RETRIES)
  3316.         TraceSz("HrOpenIMSPropsFileRetry: Failing open. Too many tries.");
  3317.     #endif
  3318.  
  3319.     DebugTraceResult(HrOpenIMSPropsFileRetry, hr);
  3320.     return hr;
  3321. }
  3322.  
  3323. /*
  3324.  * HrGetSMSStandardNotifKey
  3325.  *
  3326.  * Purpose
  3327.  *
  3328.  * return the notification key for standard notifications (everything
  3329.  * except outgoing queue notifications).
  3330.  * memory should be freed with FreeNull
  3331.  *
  3332.  * In order to call the MAPI registration function, we need to turn
  3333.  * an EntryID into a "notification key" (something unique to an
  3334.  * object in the store that will remain constant throughout this
  3335.  * logon session).  In the case of the Sample Store, we can just
  3336.  * use the local file name of the object (folder or message).
  3337.  * If the user is interested in notifications about all objects in the
  3338.  * store, we use the store's uid for the key.
  3339.  *
  3340.  * Parameters
  3341.  * pims         pointer to the message store object
  3342.  * peid         the entryid of the object 
  3343.  * lppKey       pointer to the location to return the key
  3344.  */
  3345. static HRESULT HrGetSMSStandardNotifKey(PIMS pims, PEID peid,
  3346.     LPNOTIFKEY * lppKey)
  3347. {
  3348.     HRESULT hr;
  3349.     LPNOTIFKEY lpKey;
  3350.     ULONG cb;           /* number of bytes in the key */
  3351.  
  3352.     if (peid)
  3353.     {
  3354.         LPMAPIUID lpuidEID = NULL;
  3355.         LPTSTR szPathEID = NULL;
  3356.         LPTSTR szFileEID = NULL;
  3357.  
  3358.         hr = HrDeconstructEID(peid, &lpuidEID, &szPathEID, &szFileEID);
  3359.         if (hr == hrSuccess)
  3360.         {
  3361.             cb = CbNewNOTIFKEY(Cbtszsize(szFileEID));
  3362.             hr = HrAlloc(cb, (PPV) &lpKey);
  3363.         }
  3364.  
  3365.         if (hr == hrSuccess)
  3366.         {
  3367.             lstrcpy((LPTSTR) &lpKey->ab, szFileEID);
  3368.             lpKey->cb = Cbtszsize(szFileEID);
  3369.         }
  3370.  
  3371.         FreeNull(lpuidEID);
  3372.         FreeNull(szPathEID);
  3373.         FreeNull(szFileEID);
  3374.  
  3375.         if (hr != hrSuccess)
  3376.             goto exit;
  3377.     }
  3378.     else
  3379.     {
  3380.         /* The caller is interested in notifications on all objects in the */
  3381.         /* store. Generate a key from our uid. */
  3382.  
  3383.         cb = CbNewNOTIFKEY(sizeof(MAPIUID));
  3384.         hr = HrAlloc(cb, (PPV) &lpKey);
  3385.         if (hr != hrSuccess)
  3386.             goto exit;
  3387.  
  3388.         GetResourceUID(pims, (MAPIUID *) &(lpKey->ab[0]));
  3389.         lpKey->cb = sizeof(MAPIUID);
  3390.     }
  3391.  
  3392. exit:
  3393.     if (hr != hrSuccess)
  3394.         FreeNull(lpKey);
  3395.     else
  3396.         *lppKey = lpKey;
  3397.  
  3398.     DebugTraceResult(HrGetSMSStandardNotifKey, hr);
  3399.     return hr;
  3400. }
  3401.  
  3402.  
  3403.