MSPMS.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:100k
源码类别:

Windows编程

开发平台:

Visual C++

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