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

  1. /*
  2.  *  M S P M I S C . C
  3.  *
  4.  *  Utility functions needed by the MAPI Sample Store Provider.
  5.  *
  6.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  7.  */
  8.  
  9. #include "msp.h"
  10.  
  11. /*
  12.  *  Memory allocation and release functions.  They use the lpMalloc
  13.  *  interface supplied by MAPI during provider initialization.
  14.  *
  15.  *  For internal memory allocations the Sample Store Provider uses
  16.  *  the lpMalloc passed by MAPI on MSProviderInit().  For simplicity
  17.  *  of coding we keep the pointer to the lpMalloc in a
  18.  *  per-process global (which requires mucho work on Win16) instead
  19.  *  of forcing ScAlloc() calls to pass in the pointer on each and every
  20.  *  allocation.  We trade off code size with speed (on Win16) because
  21.  *  of the searching necessary in finding the per-instance globals.
  22.  *
  23.  *  When multiple MSProviderInit's are done on the same process
  24.  *  (by opening one or more stores on two different MAPI Profiles
  25.  *  on the same process) we use the lpMalloc from the first init
  26.  *  for all allocations, keeping a refcount of MSProviderInit's
  27.  *  that have been done in order to know when to free the lpMalloc.
  28.  *
  29.  *  As a result we don't necessary use the lpMalloc for store X that
  30.  *  was passed in when store X was opened, but as long as MAPI gives
  31.  *  us the Component Object's lpMalloc on each open we're fine.
  32.  */
  33.  
  34. SCODE ScAlloc(ULONG lcb, LPVOID * ppv)
  35. {
  36.     PINST pinst = (PINST) PvGetInstanceGlobals();
  37.  
  38.     Assert(pinst);
  39.     Assert(pinst->lpmalloc);
  40.  
  41.     *ppv = pinst->lpmalloc->lpVtbl->Alloc(pinst->lpmalloc, lcb);
  42.     if (*ppv != NULL)
  43.         return S_OK;
  44.  
  45.     DebugTraceSc(ScAlloc, MAPI_E_NOT_ENOUGH_MEMORY);
  46.     return (MAPI_E_NOT_ENOUGH_MEMORY);
  47. }
  48.  
  49. SCODE ScAllocZ(ULONG lcb, LPVOID * ppv)
  50. {
  51.     PINST pinst = (PINST) PvGetInstanceGlobals();
  52.  
  53.     Assert(pinst);
  54.     Assert(pinst->lpmalloc);
  55.  
  56.     *ppv = pinst->lpmalloc->lpVtbl->Alloc(pinst->lpmalloc, lcb);
  57.     if (*ppv != NULL)
  58.     {
  59.         memset(*ppv, 0, (size_t) lcb);
  60.         return S_OK;
  61.     }
  62.  
  63.     DebugTraceSc(ScAllocZ, MAPI_E_NOT_ENOUGH_MEMORY);
  64.     return (MAPI_E_NOT_ENOUGH_MEMORY);
  65. }
  66.  
  67. SCODE ScRealloc(ULONG lcb, LPVOID pvOrig, LPVOID * ppv)
  68. {
  69.     PINST pinst = (PINST) PvGetInstanceGlobals();
  70.     LPVOID pvNew;
  71.  
  72.     Assert(pinst);
  73.     Assert(pinst->lpmalloc);
  74.  
  75.     pvNew = pinst->lpmalloc->lpVtbl->Realloc(pinst->lpmalloc, pvOrig, lcb);
  76.     if (pvNew != NULL)
  77.     {
  78.         *ppv = pvNew;
  79.         return S_OK;
  80.     }
  81.  
  82.     DebugTraceSc(ScRealloc, MAPI_E_NOT_ENOUGH_MEMORY);
  83.     return (MAPI_E_NOT_ENOUGH_MEMORY);
  84. }
  85.  
  86. void FreeNull(LPVOID pv)
  87. {
  88.     if (pv)
  89.     {
  90.         PINST pinst = (PINST) PvGetInstanceGlobals();
  91.  
  92.         Assert(pinst);
  93.         Assert(pinst->lpmalloc);
  94.  
  95.         pinst->lpmalloc->lpVtbl->Free(pinst->lpmalloc, pv);
  96.     }
  97. }
  98.  
  99. /* Linked Memory Utilities ------------------------------------------------- */
  100.  
  101. SCODE LMAllocZ(PLMR plmr, ULONG lcb, LPVOID * ppv)
  102. {
  103.     SCODE sc;
  104.  
  105.     sc = LMAlloc(plmr, lcb, ppv);
  106.     if (sc != S_OK)
  107.         return (sc);
  108.  
  109.     memset(*ppv, 0, (size_t) lcb);
  110.     return (S_OK);
  111. }
  112.  
  113. /*
  114.  *  ScInitMSInstance
  115.  *
  116.  *  Remember the given lpMalloc in a per-instance variable that
  117.  *  can be looked up again in ScAlloc and FreeNull.
  118.  */
  119. SCODE ScInitMSInstance(LPMALLOC lpmalloc)
  120. {
  121.     PINST pinst = (PINST) PvGetInstanceGlobals();
  122.     SCODE sc;
  123.  
  124.     Assert(lpmalloc);
  125.  
  126.     if (pinst)
  127.     {
  128.         /* A usable allocator is already set.   */
  129.         /* Ignore the one we were passed in     */
  130.         /* and instead bump the refcount on the */
  131.         /* one we're going to use.              */
  132.         ++(pinst->cRef);
  133.         return S_OK;
  134.     }
  135.  
  136.     /* In our debugging version wrap the allocator for
  137.      * extra help locating memory leaks or other problems.
  138.      * In both debug and non-debug versions, this statement
  139.      * addrefs the allocator, and the DBGMEM_Shutdown at the
  140.      * end does a release on the allocator.
  141.      */
  142.     lpmalloc = DBGMEM_Encapsulate(lpmalloc, "Sample Store", 0);
  143.  
  144.     pinst = (PINST) lpmalloc->lpVtbl->Alloc(lpmalloc, sizeof(INST));
  145.  
  146.     if (pinst == NULL)
  147.     {
  148.         sc = MAPI_E_NOT_ENOUGH_MEMORY;
  149.         goto ret;
  150.     }
  151.  
  152.     sc = ScSetInstanceGlobals(pinst);
  153.     if (sc != S_OK)
  154.         goto ret;
  155.  
  156.     pinst->cRef = 1;
  157.     pinst->lpmalloc = lpmalloc;
  158.  
  159.     return S_OK;
  160.  
  161. ret:
  162.     if (pinst)
  163.         lpmalloc->lpVtbl->Free(lpmalloc, pinst);
  164.  
  165.     DBGMEM_Shutdown(lpmalloc);
  166.     DebugTraceSc(ScInitMSInstance, sc);
  167.     return sc;
  168. }
  169.  
  170. void DeinitMSInstance(void)
  171. {
  172.     PINST pinst = (PINST) PvGetInstanceGlobals();
  173.     LPMALLOC lpmalloc;
  174.  
  175.     if (!pinst || --(pinst->cRef) > 0)
  176.         return;
  177.  
  178.     (void)ScSetInstanceGlobals(NULL);
  179.     lpmalloc = pinst->lpmalloc;
  180.     lpmalloc->lpVtbl->Free(lpmalloc, pinst);
  181.     DBGMEM_Shutdown(lpmalloc);
  182. }
  183.  
  184. /*
  185.  *  SzBaseName
  186.  *
  187.  *  Purpose
  188.  *      return the base name of the object with the given eid
  189.  *
  190.  *  Parameters
  191.  *      peid                Pointer to entryID of object
  192.  *
  193.  *  Returns
  194.  *      LPTSTR              base name, ie sequence number and extension
  195.  *
  196.  *  Note                    no memory is allocated, returned value must be
  197.  *                          copied if it is to be saved.
  198.  */
  199. LPTSTR SzBaseName(PEID peid)
  200. {
  201.     LPTSTR szName;              /* local name of object in lpEntryID */
  202.     LPTSTR szLastSlash;         /* position of last slash in peid */
  203.  
  204.     Assert(peid);
  205.  
  206.     /* Note that the entryid pathname is always relative to the root of */
  207.     /* the store, and will therefore never contain a drive letter. */
  208.  
  209.     szLastSlash = SzFindLastCh(peid->szPath, '\\');
  210.  
  211.     if (NULL == szLastSlash)
  212.     {
  213.         szName = (LPTSTR) peid->szPath;
  214.     }
  215.     else
  216.     {
  217.         szName = szLastSlash + 1;
  218.     }
  219.  
  220.     return szName;
  221. }
  222.  
  223. /*
  224.  * FCheckEIDType
  225.  *
  226.  *  Purpose
  227.  *      This function checks the pathname extension portion of the given
  228.  *      entryid against the string given. Since files in the sample store
  229.  *      have extensions that depend on the type of object within, comparing
  230.  *      the extension will tell the caller whether the entryid is of the type
  231.  *      specified by the extension passed in.
  232.  *
  233.  *  Parameters
  234.  *      peid: A pointer to the entryid to check.
  235.  *      szExt: A pointer to the string containing the extension to compare.
  236.  *
  237.  *  Returns: TRUE if the extensions matched, FALSE otherwise.
  238.  */
  239. BOOL FCheckEIDType(PEID peid, LPSTR szExt)
  240. {
  241.     BYTE *pb;
  242.  
  243.     if (peid == NULL)
  244.         return FALSE;
  245.  
  246.     pb = (BYTE *) peid;
  247.     pb += CbEID(peid);
  248.     pb -= ((CCH_EXT + 1) * sizeof(TCHAR));
  249.  
  250.     return (lstrcmpi((LPTSTR) pb, szExt) == 0);
  251. }
  252.  
  253. /*
  254.  *  FIsRoot
  255.  *
  256.  *  Purpose     Determine if an EID is one for the root folder
  257.  *
  258.  *  Parameters  peid
  259.  *
  260.  *  Returns     TRUE or FALSE
  261.  */
  262. BOOL FIsRoot(PEID peid)
  263. {
  264.     return (NULL == peid) || *(peid->szPath) == '\0';
  265. }
  266.  
  267. /*
  268.  *  FIsFolder
  269.  *
  270.  *  Parameters  peid    pointer to entryid of object
  271.  *
  272.  *  Purpose     return TRUE if the entryID is one for a folder
  273.  *
  274.  *  Returns     TRUE If the entryID is one for a folder, FALSE otherwixe
  275.  *
  276.  */
  277. BOOL FIsFolder(PEID peid)
  278. {
  279.     /* root is a folder */
  280.     if (FIsRoot(peid))
  281.         return TRUE;
  282.  
  283.     return (FCheckEIDType(peid, FOLDER_EXT));
  284. }
  285.  
  286. /*
  287.  *  FIsUnsavedMsg
  288.  *
  289.  *  Purpose     Determine if a message has had changes saved
  290.  *
  291.  *  Parameters  pimsg: A pointer to the message object to check.
  292.  *
  293.  *  SideEffect
  294.  *              Will say that a message is saved if another open version
  295.  *              has saved it.
  296.  *
  297.  *  Returns     TRUE or FALSE
  298.  */
  299. BOOL FIsUnsavedMsg(PIMSG pimsg)
  300. {
  301.     return (FIsUnsavedEID((PEID) pimsg->peid));
  302. }
  303.  
  304. /*
  305.  *  HrDeconstructEID
  306.  *
  307.  *  Purpose:
  308.  *      Given an EID, return its component parts (MAPIUID, path to
  309.  *      file, and file name).  This is NOT a general path parser.
  310.  *      Instead, because we know the exact structure of the EIDs
  311.  *      and paths we construct, we can directly access the
  312.  *      individual components.
  313.  *
  314.  *  Parameters
  315.  *      peid        EID to deconstruct.
  316.  *      ppuid       Location in which to return a pointer to the
  317.  *                  EID's MAPIUID.
  318.  *      pszPath     Location in which to return a pointer to the
  319.  *                  EID's file path.
  320.  *      pszFile     Location in which to return a pointer to the
  321.  *                  EID's file name.
  322.  *
  323.  *  Returns:
  324.  *      HRESULT
  325.  *
  326.  *  Size effects:
  327.  *      None.
  328.  *
  329.  *  Errors:
  330.  *      MAPI_E_NOT_ENOUGH_MEMORY    Could not allocate memory for
  331.  *                                  some or all of the return
  332.  *                                  parameters.
  333.  */
  334. HRESULT HrDeconstructEID(PEID peid, LPMAPIUID *ppuid, LPTSTR *pszPath,
  335.     LPTSTR *pszFile)
  336. {
  337.     SCODE sc;
  338.     LPMAPIUID puid = NULL;
  339.     LPTSTR szPath = NULL;
  340.     LPTSTR szFile = NULL;
  341.     LPTSTR szT = NULL;
  342.     LPTSTR szEnd = NULL;
  343.  
  344.     AssertSz(!IsBadReadPtr(peid, CbNewEID(0)), "Bad peid #1");
  345.     AssertSz(!IsBadReadPtr(peid, CbEID(peid)), "Bad peid #2");
  346.     AssertSz(!IsBadWritePtr(ppuid, sizeof(LPMAPIUID)), "Bad ppuid");
  347.     AssertSz(!IsBadWritePtr(pszPath, sizeof(LPTSTR)), "Bad pszPath");
  348.     AssertSz(!IsBadWritePtr(pszFile, sizeof(LPTSTR)), "Bad pszFile");
  349.  
  350.     *ppuid = NULL;
  351.     *pszPath = NULL;
  352.     *pszFile = NULL;
  353.  
  354.     /* Get the UID out */
  355.  
  356.     sc = ScAlloc(sizeof(MAPIUID), &puid);
  357.     if (sc != S_OK)
  358.         goto exit;
  359.     *puid = peid->uidResource;
  360.  
  361.     /* Get the path and file name out */
  362.     szT = SzFindLastCh(peid->szPath, '\\');
  363.     if (szT)
  364.         szT++;
  365.     else
  366.         szT = peid->szPath;
  367.  
  368.     szEnd = (TCHAR *) ((BYTE *) peid + CbEID(peid));
  369.     sc = ScAlloc((szEnd - szT) * sizeof(TCHAR), &szFile);
  370.     if (sc != S_OK)
  371.         goto exit;
  372.  
  373.     if ((LPBYTE) szEnd - (LPBYTE) szT)
  374.         memcpy(szFile, szT, (LPBYTE) szEnd - (LPBYTE) szT);
  375.  
  376.     if (szT != peid->szPath)
  377.     {
  378.         sc = ScAlloc((LPBYTE) szT - (LPBYTE) (peid->szPath), &szPath);
  379.         if (sc != S_OK)
  380.             goto exit;
  381.  
  382.         /* We copy the trailing backslash along with the rest of   */
  383.         /* the string, then overwrite it with the NULL terminator. */
  384.         memcpy(szPath, peid->szPath, (LPBYTE) szT - (LPBYTE) (peid->szPath));
  385.         *(szPath + (szT - peid->szPath) - 1) = '\0';
  386.     }
  387.     else
  388.     {
  389.         sc = ScAlloc(sizeof(TCHAR), (PPV) &szPath);
  390.         if (sc != S_OK)
  391.             goto exit;
  392.  
  393.         *szPath = '\0';
  394.     }
  395.  
  396. exit:
  397.     if (sc)
  398.     {
  399.         (void)FreeNull((LPVOID) puid);
  400.         (void)FreeNull((LPVOID) szPath);
  401.         (void)FreeNull((LPVOID) szFile);
  402.     }
  403.     else
  404.     {
  405.         *ppuid = puid;
  406.         *pszPath = szPath;
  407.         *pszFile = szFile;
  408.     }
  409.  
  410.     DebugTraceSc(HrDeconstructEID, sc);
  411.     return ResultFromScode(sc);
  412. }
  413.  
  414. /*
  415.  *  HrAppendPath
  416.  *
  417.  *  Purpose:
  418.  *      Concatenate a path onto another, allocating space for the
  419.  *      result.
  420.  *
  421.  *  Parameters
  422.  *      szBase          Beginning of concatenated path.
  423.  *      szAppend        End of concatenated path.
  424.  *      pszFullPath     Pointer in which to place a pointer to the
  425.  *                      newly allocated concatenated path.
  426.  *
  427.  *  Returns:
  428.  *      HRESULT
  429.  *
  430.  *  Side effects:
  431.  *      None.
  432.  *
  433.  *  Errors:
  434.  *      MAPI_E_NOT_ENOUGH_MEMORY    Unable to allocate memory for
  435.  *                                  the return variable.
  436.  */
  437. HRESULT HrAppendPath(LPTSTR szBase, LPTSTR szAppend, LPTSTR * pszFullPath)
  438. {
  439.     SCODE sc = S_OK;
  440.     TCHAR rgch[512];
  441.  
  442.     AssertSz(szBase, "Bad szBase");
  443.     AssertSz(szAppend, "Bad szAppend");
  444.     AssertSz(pszFullPath, "Bad pszFullPath");
  445.     AssertSz(szAppend[0] != '\\',
  446.         "szAppend not relative path");
  447.  
  448.     if (!FAppendPathNoMem(szBase, szAppend, sizeof rgch / sizeof rgch[0],
  449.             rgch))
  450.     {
  451.         sc = MAPI_E_STRING_TOO_LONG;
  452.         goto exit;
  453.     }
  454.  
  455.     sc = ScAlloc(Cbtszsize(rgch), (PPV) pszFullPath);
  456.     if (sc != S_OK)
  457.         goto exit;
  458.  
  459.     lstrcpy(*pszFullPath, rgch);
  460.  
  461. exit:
  462.     DebugTraceSc(HrAppendPath, sc);
  463.     return ResultFromScode(sc);
  464. }
  465.  
  466. /*
  467.  *  FAppendPathNoMem
  468.  *
  469.  *  Purpose:
  470.  *      Concatenate two parts of a file path, returning result in a
  471.  *      preallocated buffer.
  472.  *
  473.  *  Parameters
  474.  *      szBase          First part of path to concatenate.
  475.  *      szAppend        Second part of path to concatenate.
  476.  *      cchFullPath     Size of return buffer.
  477.  *      szFullPath      Return buffer.
  478.  *
  479.  *  Returns:
  480.  *      BOOL.  TRUE if the return buffer is large enough to hold
  481.  *      the resultant string, FALSE if the buffer is not large
  482.  *      enough (value in szFullPath is undefined).
  483.  *
  484.  *  Side effects:
  485.  *      None.
  486.  *
  487.  *  Errors:
  488.  *      None.
  489.  */
  490. BOOL FAppendPathNoMem(LPTSTR szBase, LPTSTR szAppend, ULONG cchFullPath,
  491.     LPTSTR szFullPath)
  492. {
  493.     UINT cchBase = 0;
  494.     UINT cchAppend = 0;
  495.     UINT cchFull = 0;
  496.     BOOLEAN fPostSlash = FALSE;
  497.  
  498.     AssertSz(szBase, "Bad szBase");
  499.     AssertSz(szAppend, "Bad szAppend");
  500.     AssertSz(szFullPath, "Bad szFullPath");
  501.     AssertSz(szAppend[0] != '\\',
  502.         "szAppend not relative path");
  503.  
  504.     cchBase = lstrlen(szBase);
  505.     cchAppend = lstrlen(szAppend);
  506.  
  507.     /* Check if szBase has trailing backslash, else we'll need to add it */
  508.  
  509.     if (*(szBase + cchBase - 1) == '\\')
  510.     {
  511.         fPostSlash = TRUE;
  512.         cchFull = cchBase + cchAppend + 1;
  513.     }
  514.     else
  515.     {
  516.         fPostSlash = FALSE;
  517.         cchFull = cchBase + cchAppend + 2;
  518.     }
  519.  
  520.     if (cchFull <= cchFullPath)
  521.     {
  522.         lstrcpy(szFullPath, szBase);
  523.         if (!fPostSlash)
  524.         {
  525.             lstrcat(szFullPath, TEXT("\\"));
  526.         }
  527.         lstrcat(szFullPath, szAppend);
  528.  
  529.         return TRUE;
  530.     }
  531.  
  532.     return FALSE;
  533. }
  534.  
  535. /*
  536.  *  ReplaceExt
  537.  *
  538.  *  Purpose:
  539.  *      Substitute the filename extension in szFile with the new
  540.  *      one passed in as szExt.  It is the caller's
  541.  *      responsibility to ensure that the filename has room for the
  542.  *      new extension.  Also, the extension must include the
  543.  *      period.
  544.  *
  545.  *  Parameters
  546.  *      szFile      Filename on which to operate.
  547.  *      szExt       New extension.
  548.  *
  549.  *  Returns:
  550.  *      void.
  551.  */
  552. void ReplaceExt(LPTSTR szFile, LPTSTR szExt)
  553. {
  554.     LPTSTR szT = NULL;
  555.  
  556.     AssertSz(!IsBadStringPtr(szFile, (UINT) -1), "Bad szFile");
  557.     AssertSz(!IsBadStringPtr(szExt, (UINT) -1), "Bad szExt");
  558.  
  559.     szT = SzFindLastCh(szFile, '.');
  560.  
  561.     if (szT)
  562.         lstrcpy(szT, szExt);
  563.     #ifdef DEBUG
  564.     else
  565.         TrapSz("No extension found on szFile. Not replacing extension.");
  566.     #endif
  567.  
  568.     return;
  569. }
  570.  
  571. /*
  572.  *  HrConstructEID
  573.  *
  574.  *  Purpose     return an new EntryID for an object
  575.  *
  576.  *  Parameters
  577.  *      puidStore       UID for the store containing the object
  578.  *      plmr            Pointer to the MAPI linked memory allocators.
  579.  *      szNewName       new root relative path name of the object
  580.  *      ppeidNew        address of pointer to new entryID
  581.  *
  582.  *  Returns:
  583.  *      ULONG, PEID
  584.  */
  585. HRESULT HrConstructEID(LPMAPIUID puidStore, PLMR plmr, LPSTR szNewName,
  586.     PEID *ppeidNew)
  587. {
  588.     PEID peidNew;               /* new entry id */
  589.     ULONG cbEID;                /* number of bytes in the new entry id */
  590.     ULONG cbNewName;
  591.     SCODE sc;
  592.  
  593.     cbNewName = lstrlen(szNewName) + 1; /* we count the NULL terminator */
  594.     cbEID = CbNewEID(cbNewName);
  595.  
  596.     /* allocate space for the new entry id */
  597.     /* Use the MAPI allocator because it may be returned to the client */
  598.     /* Note that we zero-fill this allocation so that the entryid produced */
  599.     /* by identical input parameters will always be the same. If we didn't */
  600.     /* zero fill, the pad bytes after the version would be randomly filled. */
  601.  
  602.     sc = LMAllocZ(plmr, cbEID, (PPV) &peidNew);
  603.     if (sc != S_OK)
  604.         goto exit;
  605.  
  606.     *(DWORD *) (peidNew->abFlags) = (DWORD) 0;
  607.     peidNew->uidResource = *puidStore;
  608.     peidNew->bVersion = SMPMS_VERSION;
  609.  
  610.     lstrcpy(peidNew->szPath, szNewName);
  611.  
  612.     if (peidNew->szPath[0])
  613.         AnsiLowerBuff(peidNew->szPath, lstrlen(peidNew->szPath));
  614.  
  615.     *ppeidNew = peidNew;
  616.  
  617. exit:
  618.     DebugTraceSc(HrConstructEID, sc);
  619.     return ResultFromScode(sc);
  620. }
  621.  
  622. /*
  623.  * HrGetParentEID
  624.  *
  625.  *  Purpose         construct an entry id for the parent of peid. This
  626.  *                  assumes that all files in the sample message store 
  627.  *                  are CCH_NAME chars long (including NULL).
  628.  *
  629.  *  Parameters
  630.  *      plmr        Pointer to the MAPI linked memory allocators.
  631.  *      peid        entry id of object whose parent is requested
  632.  *      ppeidParent pointer to parent's peid
  633.  *
  634.  */
  635. HRESULT HrGetParentEID(PLMR plmr, PEID peid, PEID *ppeidParent)
  636. {
  637.     HRESULT hr;
  638.     ULONG cbParentName = sizeof(TCHAR); /* number of bytes in szParentName */
  639.     LPTSTR szParentName = NULL; /* name of parent */
  640.  
  641.     if (CbEIDPath(peid) > (CCH_NAME * sizeof(TCHAR)))
  642.         cbParentName = CbEIDPath(peid) - (CCH_NAME * sizeof(TCHAR));
  643.  
  644.     hr = HrAlloc(cbParentName, (PPV) &szParentName);
  645.     if (hr != hrSuccess)
  646.         goto exit;
  647.  
  648.     if (cbParentName > sizeof(TCHAR))
  649.         memcpy(szParentName, peid->szPath, (UINT) cbParentName);
  650.     szParentName[cbParentName - sizeof(TCHAR)] = 0;
  651.  
  652.     hr = HrConstructEID(&(peid->uidResource), plmr, szParentName,
  653.         ppeidParent);
  654.  
  655. exit:
  656.     FreeNull(szParentName);
  657.     DebugTraceResult(HrGetParentEID, hr);
  658.     return hr;
  659. }
  660.  
  661. /*
  662.  * HrOpenParent
  663.  *
  664.  * Purpose  open the parent folder of the given entry id
  665.  *
  666.  * Parameters
  667.  *  pims    store in which the object is
  668.  *  peid    entry id of object whose parent is to be opened
  669.  *  ulFlags MAPI_MODIFY if you want write permission on the store
  670.  *  ppifld  pointer to variable to hold open parent
  671.  */
  672. HRESULT HrOpenParent(PIMS pims, PEID peid, ULONG ulFlags, PIFLD * ppifld)
  673. {
  674.     HRESULT hr = hrSuccess;
  675.     PEID peidParent = NULL;
  676.     ULONG ulObjectType;
  677.  
  678.     Assert(pims);
  679.     Assert(peid);
  680.     Assert(ppifld);
  681.  
  682.     *ppifld = NULL;
  683.     hr = HrGetParentEID(&pims->lmr, peid, &peidParent);
  684.     if (hr != hrSuccess)
  685.         goto exit;
  686.  
  687.     hr = pims->lpVtbl->OpenEntry(pims, CbEID(peidParent),
  688.         (LPENTRYID) peidParent, NULL, ulFlags, &ulObjectType,
  689.         (LPUNKNOWN *) ppifld);
  690.     if (hr != hrSuccess)
  691.         goto exit;
  692.  
  693.     Assert(ulObjectType == MAPI_FOLDER);
  694.  
  695. exit:
  696.     LMFree(&pims->lmr, peidParent);
  697.  
  698.     DebugTraceResult(HrOpenParent, hr);
  699.     return hr;
  700. }
  701.  
  702. /*
  703.  * FreePropArrays
  704.  *
  705.  * Purpose      deallocate space for PropTag, PropValue and PropAttr arrays
  706.  *              allocated with HrAllocPropArrays
  707.  * Parameters
  708.  *  ppval       address of property value array
  709.  *  pptaga  address of the property tag array
  710.  *  ppatra  address of the property attribute array
  711.  */
  712. void FreePropArrays(LPSPropValue *ppval, LPSPropTagArray *pptaga,
  713.     LPSPropAttrArray *ppatra)
  714. {
  715.     Assert(ppval);
  716.     Assert(pptaga);
  717.     Assert(ppatra);
  718.  
  719.     FreeNull(*ppval);
  720.     FreeNull(*pptaga);
  721.     FreeNull(*ppatra);
  722.  
  723.     *ppval = NULL;
  724.     *pptaga = NULL;
  725.     *ppatra = NULL;
  726. }
  727.  
  728. /*
  729.  * HrAllocPropArrays
  730.  *
  731.  * Purpose      allocate space for PropTag, PropValue and PropAttr arrays
  732.  *              Free with FreeNull or FreePropArrays
  733.  * Parameters
  734.  *  cProps  number of properties in the arrays
  735.  *  ppval   address of property value array
  736.  *  pptaga  address of the property tag array
  737.  *  ppatra  address of the property attribute array
  738.  */
  739. HRESULT HrAllocPropArrays(ULONG cProps, LPSPropValue *ppval,
  740.     LPSPropTagArray *pptaga, LPSPropAttrArray *ppatra)
  741. {
  742.     HRESULT hr;
  743.  
  744.     /* All three pointers must be provided. */
  745.     Assert(!IsBadWritePtr(ppval, sizeof(LPSPropValue)));
  746.     Assert(!IsBadWritePtr(pptaga, sizeof(LPSPropTagArray)));
  747.     Assert(!IsBadWritePtr(ppatra, sizeof(LPSPropAttrArray)));
  748.  
  749.     /* All must be zero on entry for our cleanup mechanism. */
  750.     AssertSz(!*ppval, "bad ppval");
  751.     AssertSz(!*pptaga, "bad pptaga");
  752.     AssertSz(!*ppatra, "bad ppatra");
  753.  
  754.     hr = HrAlloc(cProps * sizeof(SPropValue), ppval);
  755.     if (hr != hrSuccess)
  756.         goto exit;
  757.  
  758.     hr = HrAlloc(CbNewSPropTagArray(cProps), pptaga);
  759.     if (hr != hrSuccess)
  760.         goto exit;
  761.  
  762.     hr = HrAlloc(CbNewSPropAttrArray(cProps), ppatra);
  763.     if (hr != hrSuccess)
  764.         goto exit;
  765.  
  766. exit:
  767.     if (hr != hrSuccess)
  768.         FreePropArrays(ppval, pptaga, ppatra);
  769.  
  770.     DebugTraceResult(HrAllocPropArrays, hr);
  771.     return hr;
  772. }
  773.  
  774. /*
  775.  * ProcessGetProps
  776.  *
  777.  *  Purpose
  778.  *      Helper routine from HrWrap_GetProps. Folder and message objects in
  779.  *      the sample store keep a few property values in memory so that when
  780.  *      we move or copy a folder or message, the entryid, parent entryid,
  781.  *      and (for copied messages) record key are correct. This routine 
  782.  *      overrides the value returned from IMessage for any in-memory 
  783.  *      properties. We keep a very small placeholder property for each
  784.  *      of the in-memory properties on disk and mark it read-only and 
  785.  *      not deletable. This keeps SetProps from succeeding on these
  786.  *      properties. All properties that we override in this function are
  787.  *      PT_BINARY property types, so HrWrap_GetProps only calls this 
  788.  *      function if the property it is looking for is a PT_BINARY.
  789.  *
  790.  *      This function tries to find the property that the client is 
  791.  *      requesting in the in-memory array of properties stored with the
  792.  *      object. If the routine finds the property, then it tries to allocate
  793.  *      and copy the correct data into the client's array. If the allocation
  794.  *      fails, the routine fills in a PT_ERROR into the client's property
  795.  *      value. Note that HrWrap_GetProps needs to watch for PT_ERROR coming
  796.  *      back and return the appropriate warning to the client. 
  797.  *      If the routine doesn't find the property, then it returns to
  798.  *      HrWrap_GetProps without modifying the client's array at all.
  799.  *
  800.  *  Parameters
  801.  *      pvalClient: A pointer to the property value to search for in our
  802.  *          in-memory property array. This function may MODIFY this value.
  803.  *      cvalInt: The number of in-memory properties on this object. If the
  804.  *          object has no in-memory properties, this value will be zero.
  805.  *      pvalInt: A pointer to the object's array of in-memory properties.
  806.  *      plmr: A pointer to the linked memory allocation routines.
  807.  *      pvOrig: The original allocated pointer (AllocateMore needs this).
  808.  *
  809.  *  Returns
  810.  *      None. May have modified the client's property value if there was
  811.  *      a matching entry in the in-memory array.
  812.  */
  813. static void ProcessGetProps(LPSPropValue pvalClient, ULONG cvalInt,
  814.     LPSPropValue pvalInt, PLMR plmr, LPVOID pvOrig)
  815. {
  816.     ULONG ulClientID = PROP_ID(pvalClient->ulPropTag);
  817.     ULONG ulClientType = PROP_TYPE(pvalClient->ulPropTag);
  818.     LPSPropValue pvalT;
  819.     LPSPropValue pvalTMac;
  820.  
  821.     pvalT = pvalInt;
  822.     pvalTMac = pvalT + cvalInt;
  823.  
  824.     for (; pvalT < pvalTMac; ++pvalT)
  825.     {
  826.         LPVOID pv;
  827.         ULONG cb;
  828.         SCODE sc;
  829.  
  830.         AssertSz(PROP_TYPE(pvalT->ulPropTag) == PT_BINARY,
  831.             "Code assumes all internal props are PT_BINARY");
  832.  
  833.         if (ulClientID == PROP_ID(pvalT->ulPropTag))
  834.         {
  835.             cb = pvalT->Value.bin.cb;
  836.             pv = pvalT->Value.bin.lpb;
  837.  
  838.             /* Link onto returned data our extra info. */
  839.             sc = LMAllocMore(plmr, cb, pvOrig, &pvalClient->Value.bin.lpb);
  840.             if (sc != S_OK)
  841.             {
  842.                 pvalClient->Value.err = sc;
  843.                 pvalClient->ulPropTag = PROP_TAG(PT_ERROR, ulClientID);
  844.             }
  845.             else
  846.             {
  847.                 pvalClient->Value.bin.cb = cb;
  848.                 if (cb)
  849.                     memcpy(pvalClient->Value.bin.lpb, pv, (UINT) cb);
  850.                 pvalClient->ulPropTag = PROP_TAG(PT_BINARY, ulClientID);
  851.             }
  852.  
  853.             break;
  854.         }
  855.     }
  856.  
  857.     return;
  858. }
  859.  
  860. /*
  861.  * HrWrap_GetProps
  862.  *
  863.  * Purpose
  864.  *  Adjust return from GetProps (Store, Folder, Message, etc) for wrapped
  865.  *  values of PR_STORE_ENTRYID, PR_STORE_RECORD_KEY and (if a store)
  866.  *  PR_ENTRYID. Doesn't allow the return of PR_ENTRYID for an unsaved
  867.  *  message. Also overwrites values for properties that the object has
  868.  *  cached in memory using the helper routine ProcessGetProps.
  869.  *              
  870.  * Parameters
  871.  *  hr              HRESULT from the original GetProps call.
  872.  *  pims            pointer to the message store object
  873.  *  cvalInt         the number of property values that are held in memory 
  874.  *                  associated with the object.
  875.  *  pvalInt         a pointer to the array of in-memory properties associated
  876.  *                  with the object. May be NULL if cvalInt is 0.
  877.  *  pcValues        The number of values in client's PropValue array
  878.  *  ppval           a pointer to the client's PropValue array
  879.  *  fStore          TRUE if the object given the GetProps call was the
  880.  *                  message store object.
  881.  *  fTagsSpecified  TRUE if the client specified a proptag array on the call;
  882.  *                  FALSE if the client passed NULL for the proptag array.
  883.  *
  884.  * Coding comments:
  885.  *  The objects must contain these properties, of the appropriate
  886.  *  type, but it isn't necessary that their values IN the objects be
  887.  *  accurate. The KEYS, for example, don't even have to be 16 bytes.
  888.  *
  889.  *  The result code may be adjusted depending on whether this routine
  890.  *  or the underlying property implementation, ran out of memory
  891.  *  while dealing with one of these.
  892.  *
  893.  *  The PR_RECORD_KEY (a UID) in a Store IS the right value,
  894.  *  and does not need to be wrapped.
  895.  */
  896. HRESULT HrWrap_GetProps(HRESULT hr, PIMS pims, ULONG cvalInt,
  897.     LPSPropValue pvalInt, ULONG * pcValues, LPSPropValue * ppval,
  898.     BOOL fStore, BOOL fTagsSpecified, POBJ pobj)
  899. {
  900.     /* Warning: pcValues and ppval parameters may not           */
  901.     /* have been validated.  Do not dereference them unless     */
  902.     /* the "hr" says everything succeeded so far.               */
  903.  
  904.     BOOL fErrors = FALSE;
  905.     LPSPropValue pval;
  906.     LPSPropValue pvalMac;
  907.  
  908.     /* No work to do unless the GetProps() generally succeeded. */
  909.     if (HR_FAILED(hr))
  910.         goto exit;
  911.  
  912.     pval = *ppval;
  913.     pvalMac = pval + *pcValues;
  914.  
  915.     for (; pval < pvalMac; ++pval)
  916.     {
  917.         UINT ulType = (UINT) PROP_TYPE(pval->ulPropTag);
  918.         UINT ulID;
  919.         LPVOID pv;
  920.         ULONG cb;
  921.         SCODE sc;
  922.  
  923.         if(pval->ulPropTag == PR_ACCESS_LEVEL)
  924.         {
  925.             pval->Value.l = OBJ_TestFlag(pobj, OBJF_MODIFY) ? MAPI_MODIFY : 0;
  926.         }
  927.         
  928.         if(pval->ulPropTag == PR_ACCESS)
  929.         {
  930.             if(OT_FOLDER == pobj->wType)
  931.             {
  932.                 pval->Value.l = MAPI_ACCESS_READ;
  933.             
  934.                 if(OBJ_TestFlag(pobj, OBJF_MODIFY))
  935.                     pval->Value.l |= MAPI_ACCESS_MODIFY |
  936.                                     MAPI_ACCESS_CREATE_CONTENTS |
  937.                                     MAPI_ACCESS_CREATE_HIERARCHY;
  938.  
  939.                 if(OBJ_TestFlag(pobj->pobjParent, OBJF_MODIFY))
  940.                     pval->Value.l |= MAPI_ACCESS_DELETE;
  941.             }
  942.             else if(OT_MESSAGE == pobj->wType)
  943.             {
  944.                 pval->Value.l = MAPI_ACCESS_READ;
  945.             
  946.                 if(OBJ_TestFlag(pobj, OBJF_MODIFY))
  947.                     pval->Value.l |= MAPI_ACCESS_MODIFY;
  948.  
  949.                 if(OBJ_TestFlag(pobj->pobjParent, OBJF_MODIFY))
  950.                     pval->Value.l |= MAPI_ACCESS_DELETE;
  951.             }
  952.             else
  953.             {
  954.                 pval->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_ACCESS));
  955.                 pval->Value.err = MAPI_E_NOT_FOUND;
  956.             }
  957.         }
  958.  
  959.         if (ulType != PT_BINARY && ulType != PT_ERROR)
  960.             continue;
  961.  
  962.         if (ulType == PT_ERROR && pval->Value.err == MAPI_E_UNEXPECTED_TYPE)
  963.         {
  964.             fErrors = TRUE;
  965.             continue;
  966.         }
  967.  
  968.         if (cvalInt != 0)
  969.         {
  970.             ProcessGetProps(pval, cvalInt, pvalInt, &pims->lmr,
  971.                 (LPVOID) *ppval);
  972.  
  973.             /* Recompute the prop type in case ProcessGetProps changed it. */
  974.             ulType = (UINT) PROP_TYPE(pval->ulPropTag);
  975.         }
  976.  
  977.         /* These values should be computed here just in case ProcessGetProps */
  978.         /* modifies pval. */
  979.  
  980.         ulID = (UINT) PROP_ID(pval->ulPropTag);
  981.  
  982.         if (ulID == PROP_ID(PR_STORE_RECORD_KEY))
  983.         {
  984.             cb = sizeof(pims->uidResource);
  985.             pv = &pims->uidResource;
  986.         }
  987.         else if (ulID == PROP_ID(PR_STORE_ENTRYID)
  988.             || (fStore && ulID == PROP_ID(PR_ENTRYID)))
  989.         {
  990.             cb = pims->eidStore.cb;
  991.             pv = pims->eidStore.lpb;
  992.         }
  993.         else if (ulID == PROP_ID(PR_ENTRYID) && ulType != PT_ERROR)
  994.         {
  995.             /* entryid of a message doesn't exist until SaveChanges(). */
  996.             if (FIsUnsavedEID((PEID) pval->Value.bin.lpb))
  997.             {
  998.                 if (fTagsSpecified)
  999.                 {
  1000.                     fErrors = TRUE;
  1001.                     pval->ulPropTag = PROP_TAG(PT_ERROR, ulID);
  1002.                     pval->Value.err = MAPI_E_NOT_FOUND;
  1003.                 }
  1004.                 else
  1005.                 {
  1006.                     /* The client wants all properties, and did not */
  1007.                     /* specify a prop tag array. The client therefore */
  1008.                     /* doesn't want NOT_FOUND errors. */
  1009.                     /* Overwrite the error entry with the last SPropValue */
  1010.                     /* in the array. */
  1011.  
  1012.                     (*pcValues)--;
  1013.                     pvalMac--;
  1014.  
  1015.                     if (pval < pvalMac)
  1016.                     {
  1017.                         memcpy(pval, pvalMac, sizeof(SPropValue));
  1018.                         --pval; /* redo this value, since we just changed it */
  1019.                     }
  1020.                 }
  1021.             }
  1022.             continue;
  1023.         }
  1024.         else
  1025.         {
  1026.             /* Remember if any errors occur in final array. */
  1027.             if (ulType == PT_ERROR)
  1028.             {
  1029.                 if (    pval->Value.err == MAPI_E_NOT_FOUND
  1030.                     &&  !fTagsSpecified)
  1031.                 {
  1032.                     /* The client wants all properties, and did not */
  1033.                     /* specify a prop tag array. The client therefore */
  1034.                     /* doesn't want NOT_FOUND errors. */
  1035.                     /* Overwrite the error entry with the last SPropValue */
  1036.                     /* in the array. */
  1037.  
  1038.                     (*pcValues)--;
  1039.                     pvalMac--;
  1040.  
  1041.                     if (pval < pvalMac)
  1042.                     {
  1043.                         memcpy(pval, pvalMac, sizeof(SPropValue));
  1044.                         --pval; /* redo this value, since we just changed it */
  1045.                     }
  1046.                 }
  1047.                 else
  1048.                     fErrors = TRUE;
  1049.             }
  1050.             continue;
  1051.         }
  1052.  
  1053.         /* Link onto returned data our extra info. */
  1054.         sc = LMAllocMore(&pims->lmr, cb, (LPVOID) *ppval,
  1055.             &pval->Value.bin.lpb);
  1056.         if (sc != S_OK)
  1057.         {
  1058.             fErrors = TRUE;
  1059.             pval->Value.err = sc;
  1060.             pval->ulPropTag = PROP_TAG(PT_ERROR, ulID);
  1061.         }
  1062.         else
  1063.         {
  1064.             pval->Value.bin.cb = cb;
  1065.             if (cb)
  1066.                 memcpy(pval->Value.bin.lpb, pv, (UINT) cb);
  1067.             pval->ulPropTag = PROP_TAG(PT_BINARY, ulID);
  1068.         }
  1069.     }
  1070.  
  1071.     /* Adjust HRESULT based on PT_ERRORs now. */
  1072.     if (!fErrors)
  1073.         hr = hrSuccess;
  1074.     else if (hr == hrSuccess)
  1075.         hr = ResultFromScode(MAPI_W_ERRORS_RETURNED);
  1076.  
  1077. exit:
  1078.     #ifdef DEBUG
  1079.     if (GetScode(hr) != MAPI_W_ERRORS_RETURNED)
  1080.         DebugTraceResult(HrWrap_GetProps, hr);
  1081.     #endif
  1082.  
  1083.     return hr;
  1084. }
  1085.  
  1086. /*
  1087.  * FIsSubmittedMessage
  1088.  *
  1089.  * Purpose      return TRUE if the message (specified by entryid) is submitted.
  1090.  *
  1091.  * Parameters
  1092.  *  pims        A pointer to the message store object.
  1093.  *  peid        The entryid of message to check.
  1094.  */
  1095. BOOL FIsSubmittedMessage(PIMS pims, PEID peid)
  1096. {
  1097.     HRESULT hr;
  1098.     PIMSG pimsgT = NULL;
  1099.     ULONG ulObjType;
  1100.  
  1101.     hr = pims->lpVtbl->OpenEntry(pims, CbEID(peid), (LPENTRYID) peid,
  1102.         NULL, MAPI_MODIFY, &ulObjType, (LPUNKNOWN *) &pimsgT);
  1103.  
  1104.     UlRelease(pimsgT);
  1105.  
  1106.     return (GetScode(hr) == MAPI_E_SUBMITTED);
  1107. }
  1108.  
  1109. /*
  1110.  *  HrOpenIMsgSession
  1111.  *
  1112.  *  Purpose:
  1113.  *      Open an IMsgSession, and return the pointer to the caller.
  1114.  *
  1115.  *  Parameters
  1116.  *      ppmsgsess: Pointer to the location to return the opened msg session.
  1117.  *
  1118.  *  Returns:
  1119.  *      HRESULT
  1120.  */
  1121. HRESULT HrOpenIMsgSession(LPMSGSESS *ppmsgsess)
  1122. {
  1123.     PINST pinst = (PINST) PvGetInstanceGlobals();
  1124.  
  1125.     Assert(pinst);
  1126.     Assert(pinst->lpmalloc);
  1127.  
  1128.     if (pinst == NULL)
  1129.         return ResultFromScode(MAPI_E_CALL_FAILED);
  1130.  
  1131.     return ResultFromScode(OpenIMsgSession(pinst->lpmalloc, 0L, ppmsgsess));
  1132. }
  1133.  
  1134. /*
  1135.  *  HrOpenIMsg
  1136.  *
  1137.  *  Purpose:
  1138.  *      Open the file given as a docfile, and then create an IMSG.DLL
  1139.  *      object on top of the storage.
  1140.  *
  1141.  *  Parameters
  1142.  *      pmsgsess    The message session to open the message within.
  1143.  *                  May be NULL (for ConfirmCred in msplogon to work).
  1144.  *      szFile      The file to open.
  1145.  *      plmr        a pointer to the linked memory routines.
  1146.  *      psup        a pointer to the MAPI support object.
  1147.  *      fCreate     TRUE means the caller wants to create the storage.
  1148.  *                  FALSE means open an existing storage.
  1149.  *      fModify     TRUE means the caller wants read/write access.
  1150.  *                  FALSE means read-only access. (This argument is 
  1151.  *                  ignored when fCreate is TRUE; in that case, the
  1152.  *                  file is always opened read/write.)
  1153.  *      fExclusive  TRUE means the caller wants exclusive access to the
  1154.  *                  storage, and to fail creation if the storage already
  1155.  *                  exists.
  1156.  *      ppmsg       Address of a location in which to return a
  1157.  *                  pointer to the newly opened IMessage instance.
  1158.  *
  1159.  *  Returns:
  1160.  *      HRESULT
  1161.  *
  1162.  *  Side effects:
  1163.  *      None.
  1164.  *
  1165.  *  Errors:
  1166.  *      IMessage on IStorage opening errors.
  1167.  */
  1168. HRESULT HrOpenIMsg(LPMSGSESS pmsgsess, LPSTR szFile, PLMR plmr, LPMAPISUP psup,
  1169.     BOOL fCreate, BOOL fModify, BOOL fExclusive, LPMESSAGE *ppmsg)
  1170. {
  1171.     HRESULT hr;
  1172.     SCODE sc;
  1173.     DWORD grfMode;
  1174.     LPSTORAGE lpstg = NULL;
  1175.     PINST pinst = (PINST) PvGetInstanceGlobals();
  1176.  
  1177. #ifdef _WIN32
  1178.     OLE_CHAR szOle[MAX_PATH];
  1179.     int cbFile = 0L;
  1180. #else
  1181.     OLE_CHAR *szOle = NULL;
  1182. #endif
  1183.  
  1184.     Assert(pinst);
  1185.     Assert(pinst->lpmalloc);
  1186.  
  1187. #ifdef _WIN32
  1188.     cbFile = 1 + lstrlen(szFile);
  1189.     Assert(cbFile < MAX_PATH);
  1190.  
  1191.     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szFile, cbFile, szOle, cbFile);
  1192. #else
  1193.     szOle = szFile;
  1194. #endif
  1195.  
  1196.     grfMode = STGM_TRANSACTED;
  1197.  
  1198.     if (fExclusive)
  1199.     {
  1200.         grfMode |= STGM_SHARE_EXCLUSIVE;
  1201.  
  1202.         if (fCreate)
  1203.             grfMode |= STGM_FAILIFTHERE;
  1204.     }
  1205.     else
  1206.         grfMode |= STGM_SHARE_DENY_NONE;
  1207.  
  1208.     if (fCreate)
  1209.     {
  1210.         grfMode |= (STGM_READWRITE | STGM_CREATE);
  1211.  
  1212.         hr = StgCreateDocfile(szOle, grfMode, 0, &lpstg);
  1213.  
  1214.         /* Commit docfile changes.  If we don't do this now, the file on  */
  1215.         /* disk will NOT be a docfile (i.e. OLE2 will not recognize it as */
  1216.         /* a docfile) if opened again with no other changes made to it.   */
  1217.  
  1218.         if (hr == hrSuccess)
  1219.             hr = lpstg->lpVtbl->Commit(lpstg, 0);
  1220.     }
  1221.     else
  1222.     {
  1223.         if (fModify)
  1224.             grfMode |= STGM_READWRITE;
  1225.         else
  1226.             grfMode |= STGM_READ;
  1227.  
  1228.         hr = StgOpenStorage(szOle, NULL, grfMode, NULL, 0, &lpstg);
  1229.     }
  1230.  
  1231.     if (hr != hrSuccess)
  1232.     {
  1233.         sc = MapStorageSCode(GetScode(hr));
  1234.         goto exit;
  1235.     }
  1236.  
  1237.     sc = OpenIMsgOnIStg(pmsgsess, plmr->lpAllocBuf, plmr->lpAllocMore,
  1238.         plmr->lpFreeBuf, pinst->lpmalloc, psup, lpstg, NULL, 0, 0, ppmsg);
  1239.  
  1240.     UlRelease(lpstg);
  1241.  
  1242. exit:
  1243.     if (sc != S_OK && fCreate)
  1244.         DeleteFile(szFile);
  1245.  
  1246.     DebugTraceSc(HrOpenIMsg, sc);
  1247.     return ResultFromScode(sc);
  1248. }
  1249.  
  1250. /*
  1251.  * HrSetOneROProp
  1252.  *
  1253.  *  Purpose
  1254.  *      The sample store needs to change properties that the client isn't
  1255.  *      allowed to change. This function allows the sample store to change
  1256.  *      a single property in the underlying IMessage object by first
  1257.  *      setting the attributes on that property to allow it to be written,
  1258.  *      then writing the property, and finally, setting the attributes back
  1259.  *      to once again only allow reading. Note that if the sample store 
  1260.  *      calls this routine on a property that is writable, this routine
  1261.  *      will make it non-writable.
  1262.  *
  1263.  *  Parameters
  1264.  *      lpmsg: A pointer to the IMessage object in which to set the property.
  1265.  *      plmr: A pointer to the linked memory allocation routines.
  1266.  *      ulPT: The property tag to set within the object.
  1267.  *      pv: A pointer to the property value to set.
  1268.  */
  1269. HRESULT HrSetOneROProp(LPMESSAGE lpmsg, PLMR plmr, ULONG ulPT, LPVOID pv)
  1270. {
  1271.     HRESULT hr;
  1272.     LPSPropAttrArray patra = NULL;
  1273.     LPSPropProblemArray pprba = NULL;
  1274.  
  1275.     SizedSPropTagArray(1, spta);
  1276.  
  1277.     /* Should be changing the pval array inside the object. */
  1278.  
  1279.     AssertSz(   ulPT != PR_ENTRYID
  1280.             &&  ulPT != PR_PARENT_ENTRYID
  1281.             &&  ulPT != PR_RECORD_KEY
  1282.             &&  ulPT != PR_INSTANCE_KEY, "Changing internal props in IMSG");
  1283.  
  1284.     spta.cValues = 1;
  1285.     spta.aulPropTag[0] = ulPT;
  1286.  
  1287.     /* Get current attributes and make the properties writable */
  1288.  
  1289.     hr = GetAttribIMsgOnIStg(lpmsg, (LPSPropTagArray) &spta, &patra);
  1290.     if (hr != hrSuccess)
  1291.         goto exit;
  1292.  
  1293.     patra->aPropAttr[0] |= PROPATTR_WRITEABLE;
  1294.  
  1295.     hr = SetAttribIMsgOnIStg(lpmsg, (LPSPropTagArray) &spta, patra, &pprba);
  1296.     if (hr != hrSuccess || pprba)
  1297.         goto exit;
  1298.  
  1299.     hr = HrSetSingleProp((LPMAPIPROP) lpmsg, plmr, ulPT, pv);
  1300.     if (hr != hrSuccess)
  1301.         goto exit;
  1302.  
  1303.     /* Restore the attribute */
  1304.  
  1305.     patra->aPropAttr[0] &= ~PROPATTR_WRITEABLE;
  1306.  
  1307.     hr = SetAttribIMsgOnIStg(lpmsg, (LPSPropTagArray) &spta, patra, &pprba);
  1308.     if (hr != hrSuccess || pprba)
  1309.         goto exit;
  1310.  
  1311. exit:
  1312.     if (pprba)
  1313.     {
  1314.         LMFree(plmr, pprba);
  1315.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  1316.     }
  1317.  
  1318.     LMFree(plmr, patra);
  1319.  
  1320.     DebugTraceResult(HrSetOneROProp, hr);
  1321.     return hr;
  1322. }
  1323.  
  1324. /*
  1325.  * HrGetSingleProp
  1326.  *
  1327.  *  Purpose
  1328.  *      Gets a property from an object, and returns the value by stuffing
  1329.  *      it into a separately passed pointer. This function is nice because
  1330.  *      it doesn't require the caller to have an SPropValue around simply
  1331.  *      to retrieve the value of a property that is a known size.
  1332.  *
  1333.  *  Parameters
  1334.  *      pmprop  The property object to get the property from.
  1335.  *      plmr    Pointer to MAPI's linked memory routines.
  1336.  *      ulPT    The property tag to get.
  1337.  *      pv      A pointer to the location to place the value of the property.
  1338.  *
  1339.  *  Returns
  1340.  *      HRESULT. No warnings are returned because this function retrieves
  1341.  *      only one property at a time.
  1342.  */
  1343. HRESULT HrGetSingleProp(LPMAPIPROP pmprop, PLMR plmr, ULONG ulPT, LPVOID pv)
  1344. {
  1345.     LPSPropValue pval = NULL;
  1346.     SCODE sc;
  1347.     HRESULT hr;
  1348.     ULONG cValues;
  1349.  
  1350.     SizedSPropTagArray(1, spta);
  1351.  
  1352.     spta.cValues = 1;
  1353.     spta.aulPropTag[0] = ulPT;
  1354.  
  1355.     hr = pmprop->lpVtbl->GetProps(pmprop, (LPSPropTagArray) &spta, 0, /* ansi */
  1356.         &cValues, &pval);
  1357.  
  1358.     sc = GetScode(hr);
  1359.  
  1360.     if ((sc != S_OK)
  1361.         && (sc != MAPI_W_ERRORS_RETURNED))
  1362.         goto exit;
  1363.  
  1364.     switch (PROP_TYPE(pval->ulPropTag))
  1365.     {
  1366.     case PT_I2:
  1367.     case PT_BOOLEAN:
  1368.         Assert(!IsBadWritePtr(pv, sizeof(short)));
  1369.         *(short *)pv = pval->Value.i;
  1370.         break;
  1371.  
  1372.     case PT_LONG:
  1373.     case PT_R4:
  1374.         Assert(!IsBadWritePtr(pv, sizeof(LONG)));
  1375.         *(LONG *) pv = pval->Value.l;
  1376.         break;
  1377.  
  1378.     case PT_DOUBLE:
  1379.     case PT_APPTIME:
  1380.     case PT_SYSTIME:
  1381.     case PT_I8:
  1382.     case PT_CURRENCY:
  1383.         Assert(!IsBadWritePtr(pv, sizeof(LARGE_INTEGER)));
  1384.         *(LARGE_INTEGER *) pv = pval->Value.li;
  1385.         break;
  1386.  
  1387.     case PT_ERROR:
  1388.         sc = pval->Value.err;
  1389.         break;
  1390.  
  1391.     default:
  1392.         TrapSz1("Unimplemented PROP_TYPE %08lX passed in.",
  1393.             PROP_TYPE(pval->ulPropTag));
  1394.     }
  1395.  
  1396.     LMFree(plmr, pval);
  1397.  
  1398. exit:
  1399.     AssertSz1(sc <= 0, "Logic error: sc %s returned from HrGetSingleProp.",
  1400.         SzDecodeScode(sc));
  1401.  
  1402.     DebugTraceSc(HrGetSingleProp, sc);
  1403.     return (ResultFromScode(sc));
  1404. }
  1405.  
  1406. /*
  1407.  *  HrSetSingleProp
  1408.  *
  1409.  *  Purpose:
  1410.  *      Sets one property and its separately passed value. This function
  1411.  *      is nice because it doesn't require the caller to have a SPropValue
  1412.  *      to pass in.
  1413.  *
  1414.  *  Parameters
  1415.  *      pmprop  The property object to set the property into.
  1416.  *      plmr    Pointer to MAPI's linked memory routines.
  1417.  *      ulPT    The property to set
  1418.  *      pv      A pointer to the value of the property
  1419.  *
  1420.  *  Returns:
  1421.  *  Any errors from SetProps. Note that no warnings or problem arrays are
  1422.  *      returned because this routine only sets one property.
  1423.  */
  1424. HRESULT HrSetSingleProp(LPMAPIPROP pmprop, PLMR plmr, ULONG ulPT, LPVOID pv)
  1425. {
  1426.     HRESULT hr;
  1427.     SPropValue sval;
  1428.     LPSPropProblemArray pprba = NULL;
  1429.  
  1430.     sval.ulPropTag = ulPT;
  1431.  
  1432.     switch (PROP_TYPE(ulPT))
  1433.     {
  1434.     case PT_I2:
  1435.     case PT_BOOLEAN:
  1436.         sval.Value.i = *(short *)pv;
  1437.         break;
  1438.  
  1439.     case PT_LONG:
  1440.     case PT_R4:
  1441.     case PT_UNICODE:
  1442.     case PT_STRING8:
  1443.     case PT_CLSID:
  1444.         AssertSz(sizeof(LPVOID) == sizeof(LONG),
  1445.             "Pointers are not the size of a long on this machine");
  1446.         sval.Value.l = *(LONG *) pv;
  1447.         break;
  1448.  
  1449.     case PT_DOUBLE:
  1450.     case PT_APPTIME:
  1451.     case PT_SYSTIME:
  1452.     case PT_I8:
  1453.     case PT_CURRENCY:
  1454.     case PT_OBJECT:
  1455.     case PT_BINARY:
  1456.         sval.Value.li = *(LARGE_INTEGER *) pv;
  1457.         break;
  1458.  
  1459.     default:
  1460.         TrapSz1("Unimplemented PROP_TYPE %08lX passed in.", PROP_TYPE(ulPT));
  1461.     }
  1462.  
  1463.     hr = pmprop->lpVtbl->SetProps(pmprop, 1, &sval, &pprba);
  1464.  
  1465.     if (hr == hrSuccess && pprba)
  1466.         hr = ResultFromScode(pprba->aProblem[0].scode);
  1467.  
  1468.     LMFree(plmr, pprba);
  1469.  
  1470.     DebugTraceResult(HrSetSingleProp, hr);
  1471.     return hr;
  1472. }
  1473.  
  1474. /*
  1475.  *  FContainsProp
  1476.  *
  1477.  *  Purpose:
  1478.  *      returns whether or not a PropTag exists in a PropTagArray
  1479.  *
  1480.  *  Parameters
  1481.  *      ulPropTag   The property to search for.
  1482.  *      ptaga       A pointer to the SPropTagArray to search. May be null,
  1483.  *                  in which case, the function will return FALSE.
  1484.  *
  1485.  *  Returns:
  1486.  *      TRUE    if ulPropTag is in ptaga
  1487.  *      FALSE   if not
  1488.  */
  1489. BOOL FContainsProp(ULONG ulPropTag, LPSPropTagArray ptaga)
  1490. {
  1491.     ULONG *pulPropTag;
  1492.     ULONG *pulPropMax;
  1493.  
  1494.     if (!ptaga)
  1495.         return FALSE;
  1496.  
  1497.     pulPropTag = ptaga->aulPropTag;
  1498.     pulPropMax = pulPropTag + ptaga->cValues;
  1499.  
  1500.     while (pulPropTag < pulPropMax)
  1501.     {
  1502.         if (ulPropTag == *pulPropTag)
  1503.             return TRUE;
  1504.  
  1505.         pulPropTag++;
  1506.     }
  1507.  
  1508.     return FALSE;
  1509. }
  1510.