MSPRFS.C
/* 
 *  M S P R F S . C 
 * 
 *  Code for implementing Get/SetReceiveFolder for the Message 
 *  Store object. 
 * 
 *  Hungarian shorthand: 
 *      To avoid excessively long identifier names, throughout this 
 *      file, RFS is used to mean "Receive Folder Settings", and RFN 
 *      is used to mean an RFS Node. 
 * 
 *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved. 
 */ 
 
#include "msp.h" 
 
/* Manifest constants */ 
 
TCHAR szRFSStreamName[] = TEXT("RFS_STREAM"); 
 
/* GRoup of Flags (grf): 
 * 
 * grfStorageOpen:      Flags used to open a read/write OLE IStorage object. 
 * grfStorageOpenRO:    Flags used to open a read only OLE IStorage object. 
 * grfStorageCreate:    Flags used to create an OLE IStorage object. 
 * grfStreamOpen:       Flags used to open a read/write OLE IStream object. 
 * grfStreamOpenRO:     Flags used to open a read only OLE IStream object. 
 * grfStreamCreate:     Flags used to create an OLE IStream object. 
 * 
 * See the OLE 2 Programmer's Reference for details on these flags. 
 */ 
 
#define grfStorageOpen      STGM_READWRITE | STGM_SHARE_EXCLUSIVE | \ 
                                STGM_TRANSACTED 
#define grfStorageOpenRO    STGM_READ | STGM_SHARE_EXCLUSIVE | \ 
                                STGM_TRANSACTED 
#define grfStorageCreate    grfStorageOpen | STGM_FAILIFTHERE | STGM_CREATE 
 
#define grfStreamOpen       STGM_SHARE_EXCLUSIVE | STGM_READWRITE 
#define grfStreamOpenRO     STGM_SHARE_EXCLUSIVE | STGM_READ 
#define grfStreamCreate     grfStreamOpen | STGM_FAILIFTHERE 
 
/* Function prototypes */ 
 
static HRESULT OpenRFSStream(PRFS prfs, BOOL fModify, IStream **lppstream, 
    LPSTORAGE *lppstg); 
static void CloseRFSStream(IStream * lpstream, LPSTORAGE lpstg); 
 
/* 
 *  Exported functions 
 */ 
 
/* 
 *  FIsValidMessageClass 
 * 
 *  Purpose: 
 *      Checks to see if a message class is valid.  A valid message 
 *      class is defined to be a series of one or more 
 *      period-delimited tokens with each token being a series of 
 *      one or more ASCII characters in the range 32-126 
 *      (inclusive) excluding period.  Note that this definition 
 *      excludes message classes with a leading or trailing 
 *      period, or two or more consecutive periods, because this 
 *      would imply the existence of a zero-length token. 
 * 
 *      We put this function in the RFS module because dealing with 
 *      receive folders is the primary place in the Sample Store where we 
 *      care about message class. 
 * 
 *  Arguments: 
 *      szMessageClass  String identifying the message class. 
 * 
 *  Returns: 
 *      BOOL.  TRUE if szMessage is valid, FALSE if not. 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      None. 
 */ 
BOOL FIsValidMessageClass(LPTSTR szMessageClass) 
{ 
    TCHAR *pch = szMessageClass; 
    BOOL fWasPeriod = TRUE; 
 
    if (szMessageClass && IsBadStringPtr(szMessageClass, (UINT) -1)) 
        return FALSE; 
 
    /* Handle the default message class */ 
 
    if (!szMessageClass || *szMessageClass == '\0') 
        return TRUE; 
 
    /* disallow things:bad chars and cases where */ 
    /* period is not a delim(.1, 1., and 1. .1 bad) */ 
    while (*pch) 
    { 
        if (*pch < 32 || *pch > 126) 
            return FALSE; 
        if (*pch == '.') 
        { 
            if (fWasPeriod) 
                return FALSE; 
            fWasPeriod = TRUE; 
        } 
        else 
            fWasPeriod = FALSE; 
        pch++; 
    } 
 
    return !fWasPeriod; 
} 
 
/* 
 *  OpenRFS 
 * 
 *  Purpose: 
 *      Given an OLE2 storage object, opens a stream on it and 
 *      prepares it for handling receive folder settings.  OpenRFS 
 *      returns to the caller a pointer to the RFS structure which 
 *      is then used for access to the settings.  The stream format 
 *      of the receive folder settings is extremely simple:  the 
 *      first ULONG is a count of the number of RFNs in the 
 *      stream, and the nodes themselves follow sequentially 
 *      afterward.  A node on disk is not the same as an RFN in 
 *      memory.  On disk, it is a variable-sized structure 
 *      containing a ULONG which is the size (in bytes) of the 
 *      node not including this field, then a ULONG which is the 
 *      length (in bytes) of a 
 *      string containing the message class (NULL inclusive), which 
 *      immediately follows.  After that is a ULONG which is the 
 *      size (in bytes) of a string containing the relative path 
 *      name of the receive folder (NULL inclusive), which also 
 *      immediately follows.  Visually, a node looks like the 
 *      following: 
 * 
 *          +--------------------+ 
 *          | ULONG cbNode       | 
 *          +--------------------+ 
 *          | ULONG cbClass      | 
 *          +--------------------+ 
 *          | TCHAR szClass[]    | 
 *          |   .                | 
 *          |   .                | 
 *          |   .                | 
 *          +--------------------+ 
 *          | ULONG cbPath       | 
 *          +--------------------+ 
 *          | TCHAR szPath[]     | 
 *          |   .                | 
 *          |   .                | 
 *          |   .                | 
 *          +--------------------+ 
 * 
 *      The size of the message class name, cbClass, will always be 
 *      > 0 for "valid" nodes (the default message class will be a 
 *      NULL string of size 1 TCHAR).  Thus, a value of 0 for 
 *      cbClass will signify a "free" node.  Free nodes are created 
 *      in the normal use of the stream by DeleteRFN, which 
 *      SetReceiveFolder calls, and are removed at close time (see 
 *      CloseRFS, below). 
 * 
 *      Note that, because string lengths are byte-sized but the 
 *      strings themselves are made of TCHARs, translation between 
 *      BYTE and TCHAR sizes must be done. 
 * 
 *  Arguments: 
 *      szStoreRoot Full path to the sample store "root" 
 *                      directory. 
 *      szFile      Relative path name of docfile containing 
 *                      receive folder settings. 
 *      ulFlags         Flags.  The following are defined: 
 *                      RFS_CREATE  Create the docfile containing 
 *                                  receive folder settings 
 *                                  (default opens existing). 
 *      pprfs           Location in which to return a pointer to 
 *                      the newly created RFS structure. 
 * 
 *  Returns: 
 *      HRESULT 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      Various. 
 */ 
HRESULT OpenRFS(LPTSTR szStoreRoot, LPTSTR szFile, ULONG ulFlags, PRFS *pprfs) 
{ 
    HRESULT hr = hrSuccess; 
    HRESULT hrStg = hrSuccess; 
    SCODE sc; 
    LPTSTR szFullPath = NULL; 
    ULONG cRFN = 0L; 
    LPSTORAGE lpstg = NULL; 
    IStream *lpstream = NULL; 
    PRFS prfs = NULL; 
 
#ifdef _WIN32 
    OLE_CHAR szOle[MAX_PATH]; 
    int cbOle = 0L; 
 
#else 
    OLE_CHAR *szOle; 
 
#endif 
    LARGE_INTEGER liBeg; 
 
    /* initial default receive folder settings */ 
    ULONG cInitRFNs = 1;        /* number of default nodes */ 
 
#pragma pack(1) 
    struct RFN 
    { 
        ULONG cbNode; 
        ULONG cbClass; 
        TCHAR szClass; 
        ULONG cbFolder; 
        TCHAR szFolderPath; 
    } DefaultNode = 
    { 
        (2 * sizeof(ULONG)) + (2 * sizeof(TCHAR)), 
        sizeof(TCHAR), 
        '\0', 
        sizeof(TCHAR), 
        '\0' 
    }; 
#pragma pack() 
 
    LISet32(liBeg, 0);  /* This is an OLE initializer macro */ 
 
    AssertSz(szStoreRoot, "Bad szStoreRoot"); 
    AssertSz(szFile, "Bad szFile"); 
    AssertSz(pprfs, "Bad pprfs"); 
 
    hr = HrAppendPath(szStoreRoot, szFile, &szFullPath); 
    if (hr != hrSuccess) 
        goto exit; 
 
    sc = ScAllocZ(sizeof(RFS), &prfs); 
    if (sc != S_OK) 
    { 
        hr = ResultFromScode(sc); 
        goto exit; 
    } 
 
    prfs->szFile = szFullPath; 
 
#ifdef _WIN32 
    cbOle = 1 + lstrlen(szFullPath); 
    Assert(cbOle < MAX_PATH); 
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szFullPath, cbOle, szOle, cbOle); 
#else 
    szOle = szFullPath; 
#endif 
 
    if (ulFlags & RFS_CREATE) 
    { 
        hrStg = StgCreateDocfile(szOle, grfStorageCreate, 0, &lpstg); 
 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
#ifdef _WIN32 
        cbOle = 1 + lstrlen(szRFSStreamName); 
        Assert(cbOle < MAX_PATH); 
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szRFSStreamName, cbOle, 
                szOle, cbOle); 
#else 
        szOle = szRFSStreamName; 
#endif 
        hrStg = lpstg->lpVtbl->CreateStream(lpstg, szOle, grfStreamCreate, 
                0, 0, &lpstream); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        /* Initialize the newly created stream */ 
        hrStg = lpstream->lpVtbl->Seek(lpstream, liBeg, STREAM_SEEK_SET, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        /* add the default RFS setting of the root (path = '\0') */ 
        hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cInitRFNs, 
                sizeof cInitRFNs, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &DefaultNode, 
                DefaultNode.cbNode + sizeof(ULONG), NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        /* Commit docfile changes.  If we don't do this now, the file on  */ 
        /* disk will NOT be a docfile (i.e. OLE2 will not recognize it as */ 
        /* a docfile) if opened again with no other changes made to it.   */ 
 
        hrStg = lpstg->lpVtbl->Commit(lpstg, 0); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
    } 
    else        /* Open an existing stream */ 
    { 
        hr = OpenRFSStream(prfs, FALSE, &lpstream, &lpstg); 
        if (hr != hrSuccess) 
            goto exit; 
 
        hrStg = lpstream->lpVtbl->Seek(lpstream, liBeg, STREAM_SEEK_SET, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN, 
                sizeof cRFN, NULL); 
 
        /* fall through to error handler */ 
    } 
 
stg_err: 
    if (hrStg != hrSuccess) 
        hr = ResultFromScode(MapStorageSCode(GetScode(hrStg))); 
 
exit: 
    if (lpstream) 
    { 
        CloseRFSStream(lpstream, lpstg); 
        lpstg = NULL; 
    } 
 
    if (hr != hrSuccess) 
    { 
        FreeNull(szFullPath); 
        FreeNull(prfs); 
        UlRelease(lpstg); 
    } 
    else 
        *pprfs = prfs; 
 
    DebugTraceResult(OpenRFS, hr); 
    return hr; 
} 
 
/* 
 *  GetRFN 
 * 
 *  Purpose: 
 *      Returns an RFN containing the receive folder setting 
 *      for the message class that is passed in as a parameter.  If 
 *      there is not a receive folder setting for this particular 
 *      message class, the "best match" is returned, with best 
 *      match being defined in GetReceiveFolder (see msgstobj.c). 
 *      the way we measure this best match is to have a match index 
 *      which is incremented every time a section of the message 
 *      class is matched.  These values begin at 2 because 1 is 
 *      reserved for the default message class.  For example, the 
 *      message class "IPM.Note.Phone" matches against the 
 *      following receive folder settings in the following way: 
 * 
 *          "" (default)        1 
 *          "IPM"               2 
 *          "IPM.Note"          3 
 *          "IPM.Note.Phone"    4 
 *          "IPC"               0 
 * 
 *  Arguments: 
 *      prfs            Pointer to the RFS context. 
 *      szClassName     Name of the message class for which to 
 *                      search for a receive folder setting. 
 *      pprfn           Address of location in which to return a 
 *                      pointer to an RFN structure containing 
 *                      the message class and folder name of the 
 *                      "best match" receive folder setting. 
 * 
 *  Returns: 
 *      HRESULT 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      Various. 
 * 
 *  Notes: 
 *      Use FreeRFN() to release the memory of the returned 
 *      RFN structure in *pprfn. 
 */ 
HRESULT GetRFN(PRFS prfs, LPTSTR szClassName, PRFN *pprfn) 
{ 
    HRESULT hr = hrSuccess; 
    HRESULT hrStg = hrSuccess; 
    SCODE sc = S_OK; 
    LPSTORAGE lpstg = NULL; 
    LPSTREAM lpstream = NULL; 
    PRFN prfn = NULL; 
    ULONG ibNextNode = 0L; 
    ULONG ibMatchNode = 0L; 
    ULONG cbNode = 0L; 
    ULONG cbClass = 0L; 
    ULONG cRFN = 0L; 
    UINT uiMatchLvl = 0; 
    UINT ui = 0; 
    TCHAR rgch[1024]; 
    LARGE_INTEGER li; 
    ULONG cbCls = 0L; 
    ULONG cbName = 0L; 
 
    AssertSz(prfs, "Bad prfs"); 
    AssertSz(szClassName, "Bad szClassName"); 
    AssertSz(pprfn, "Bad pprfn"); 
 
    hr = OpenRFSStream(prfs, FALSE, &lpstream, &lpstg); 
    if (hr != hrSuccess) 
        goto exit; 
 
    /* Read the count of RFS nodes from the stream. */ 
 
    LISet32(li, 0);     /* This is an OLE initializer macro */ 
    hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN, 
            sizeof cRFN, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    /* Loop over nodes, looking for the best message class match */ 
 
    for (ui = 0, ibNextNode = sizeof cRFN; ui < cRFN; ui++) 
    { 
        /* Set seek pointer to beginning of this node */ 
 
        LISet32(li, ibNextNode); 
        hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        /* Need to have an absolute index to the NEXT node. */ 
        /* Remember:  cbNode is not self-inclusive, so the  */ 
        /* next node is (cbNode + sizeof cbNode) from the   */ 
        /* current node.                                    */ 
 
        hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbNode, 
                sizeof cbNode, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        ibNextNode += cbNode + sizeof cbNode; 
 
        /* Get and compare message class */ 
 
        hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbClass, 
                sizeof cbClass, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        AssertSz(sizeof rgch >= cbClass, "Message class too big!"); 
 
        if (cbClass > 0L)       /* If it's not a free node */ 
        { 
            UINT uiMatchCur = 0; 
 
            hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) rgch,  
                    cbClass, NULL); 
            if (hrStg != hrSuccess) 
                goto stg_err; 
 
            if (rgch[0] == '\0') 
            { 
                uiMatchCur = 1; 
            } 
            else 
                /* count the matching message class sections */ 
            { 
                TCHAR *pch1 = rgch; 
                TCHAR *pch1Ahead = NULL; 
                TCHAR *pch2 = szClassName; 
                TCHAR *pch2Ahead = NULL; 
 
                /* if matching against a class that is less refined than */ 
                /* what we are searching for return 0 */ 
                if (lstrlen(szClassName) >= lstrlen(rgch)) 
                { 
                    while (*pch1 && *pch2) 
                    { 
                        for (pch1Ahead = pch1 + 1; *pch1Ahead && 
                            *pch1Ahead != '.'; pch1Ahead++) 
                            ; 
                        for (pch2Ahead = pch2 + 1; *pch2Ahead && 
                            *pch2Ahead != '.'; pch2Ahead++) 
                            ; 
                        if (pch1Ahead - pch1 == pch2Ahead - pch2 && 
                            !memcmp(pch1, pch2, pch1Ahead - pch1)) 
                        { 
                            uiMatchCur++; 
                            pch1 = pch1Ahead; 
                            pch2 = pch2Ahead; 
                        } 
                        else 
                            break; 
                    } 
                } 
                /* We want to match a "real" setting higher than the */ 
                /* default, so we increment a real match to be > 1.  */ 
 
                if (uiMatchCur > 0) 
                { 
                    uiMatchCur++; 
                } 
            } 
 
            if (uiMatchCur > uiMatchLvl) 
            { 
                /* Here we set ibMatchNode to be the absolute index of */ 
                /* the cbClass member of the node (NOT the cbNode      */ 
                /* member.  When we seek back to this position, we can */ 
                /* begin reading the cbClass immediately (see below).  */ 
 
                ibMatchNode = ibNextNode - cbNode; 
                uiMatchLvl = uiMatchCur; 
            } 
        } 
    } 
 
    if (uiMatchLvl == 0) 
    { 
        hr = ResultFromScode(MAPI_E_NOT_FOUND); 
        goto exit; 
    } 
 
    /* Set the return variable w/best match */ 
 
    sc = ScAllocZ(sizeof(RFN), (PPV) &prfn); 
    if (sc != S_OK) 
        goto sc_err; 
 
    /* Goto best match node, but seek pointer will be past cbNode */ 
 
    LISet32(li, ibMatchNode); 
    hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    /* Read in class name */ 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbCls, 
            sizeof cbCls, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    sc = ScAlloc(cbCls, (PPV) &prfn->szClass); 
    if (sc != S_OK) 
        goto sc_err; 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, 
            (LPVOID) prfn->szClass, cbCls, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    /* Read in folder name */ 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbName, 
            sizeof cbName, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    sc = ScAlloc(cbName, (PPV) &prfn->szName); 
    if (sc != S_OK) 
        goto sc_err; 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, 
            (LPVOID) prfn->szName, cbName, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
sc_err: 
    if (sc != S_OK) 
    { 
        Assert(hr == hrSuccess); 
        Assert(hrStg == hrSuccess); 
        hr = ResultFromScode(sc); 
    } 
 
stg_err: 
    if (hrStg != hrSuccess) 
    { 
        Assert(sc == S_OK); 
        Assert(hr == hrSuccess); 
        hr = ResultFromScode(MapStorageSCode(GetScode(hrStg))); 
    } 
 
exit: 
    if (lpstream) 
        CloseRFSStream(lpstream, lpstg); 
 
    if (hr != hrSuccess) 
        FreeRFN(prfn); 
    else 
        *pprfn = prfn; 
 
    DebugTraceResult(GetRFN, hr); 
    return hr; 
} 
 
/* 
 -  FreeRFN 
 - 
 *  Release the memory of an RFN allocated and returned 
 *  by the GetRFN() procedure. 
 */ 
void FreeRFN(PRFN prfn) 
{ 
    if (prfn) 
    { 
        (void)FreeNull((LPVOID) prfn->szClass); 
        (void)FreeNull((LPVOID) prfn->szName); 
        (void)FreeNull((LPVOID) prfn); 
    } 
} 
 
/* 
 *  DeleteRFN 
 * 
 *  Purpose: 
 *      Delete the receive folder setting associated with a 
 *      particular message class.  We do this by "zeroing out" the 
 *      node on disk, rather than actually removing it and 
 *      compacting the stream.  We can easily zero out the node 
 *      once we've found the right one by setting the length of the 
 *      message class string contained in it to be zero (an invalid 
 *      value). 
 * 
 *  Arguments: 
 *      prfs            Pointer to the RFS context to use. 
 *      szClassName     Buffer containing the name of the message 
 *                      class for which to remove the receive 
 *                      folder setting.  We do a linear search 
 *                      through the stream to find the node on disk 
 *                      with a matching message class. 
 * 
 *  Returns: 
 *      HRESULT 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      Various. 
 */ 
HRESULT DeleteRFN(PRFS prfs, LPTSTR szClassName) 
{ 
    HRESULT hr = hrSuccess; 
    HRESULT hrStg = hrSuccess; 
    LPSTORAGE lpstg = NULL; 
    IStream *lpstream = NULL; 
    ULONG ibNextNode = 0L; 
    ULONG cbNode = 0L; 
    ULONG cbClass = 0L; 
    ULONG cRFN = 0L; 
    UINT ui = 0; 
    TCHAR rgch[1024]; 
    LARGE_INTEGER li; 
 
    AssertSz(prfs, "Bad prfs"); 
    AssertSz(szClassName, "Bad szClassName"); 
 
    hr = OpenRFSStream(prfs, TRUE, &lpstream, &lpstg); 
    if (hr != hrSuccess) 
        goto exit; 
 
    /* Read the count of RFS nodes from the stream. */ 
 
    LISet32(li, 0);     /* This is an OLE initializer macro */ 
    hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN, 
            sizeof cRFN, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    /* Loop over nodes, looking for a message class match */ 
 
    for (ui = 0, ibNextNode = sizeof cRFN; ui < cRFN; ui++) 
    { 
        /* Set seek pointer to beginning of first node */ 
 
        LISet32(li, ibNextNode); 
        hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        /* Need to have an absolute index to the NEXT node. */ 
        /* Remember:  cbNode is not self-inclusive, so the  */ 
        /* next node is (cbNode + sizeof cbNode) from the   */ 
        /* current node.                                    */ 
 
        hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbNode, 
                sizeof cbNode, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        ibNextNode += cbNode + sizeof cbNode; 
 
        /* Get and compare message class */ 
 
        hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbClass, 
                sizeof cbClass, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        AssertSz(sizeof rgch >= cbClass * sizeof(TCHAR), 
            "Message class too big!"); 
 
        if (cbClass > 0L)       /* If it's not a free node */ 
        { 
            hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) rgch, 
                    cbClass, NULL); 
            if (hrStg != hrSuccess) 
                goto stg_err; 
 
            if (cbClass == Cbtszsize(szClassName) 
                && !memcmp(szClassName, rgch, (UINT) cbClass)) 
            { 
                LONG ibClass = 0L; 
 
                /* Seek back to cbClass */ 
 
                ibClass -= (LONG) (cbClass + sizeof cbClass); 
                LISet32(li, ibClass); 
                hrStg = lpstream->lpVtbl->Seek(lpstream, li, 
                        STREAM_SEEK_CUR, NULL); 
                if (hrStg != hrSuccess) 
                    goto stg_err; 
 
                /* Zero out the node */ 
 
                cbClass = 0L; 
                hrStg = lpstream->lpVtbl->Write(lpstream, 
                        (LPVOID) &cbClass, sizeof cbClass, NULL); 
                if (hrStg != hrSuccess) 
                    goto stg_err; 
 
                /* Commit the change */ 
 
                hrStg = lpstg->lpVtbl->Commit(lpstg, 0); 
                if (hrStg != hrSuccess) 
                    goto stg_err; 
 
                break; 
            } 
        } 
    } 
 
stg_err: 
    if (hrStg) 
        hr = ResultFromScode(MapStorageSCode(GetScode(hrStg))); 
 
exit: 
    if (lpstream) 
        CloseRFSStream(lpstream, lpstg); 
 
    DebugTraceResult(DeleteRFN, hr); 
    return hr; 
} 
 
/* 
 *  AddRFN 
 * 
 *  Purpose: 
 *      Adds a node (on disk) to the stream that holds receive 
 *      folder settings for a message store.  Does this by creating 
 *      a new node at the current End-Of-Stream (at the end of all 
 *      other nodes). 
 * 
 *  Arguments: 
 *      prfs        Pointer to the receive folder storage context. 
 *      prfn    Pointer to the new node to add. 
 * 
 *  Returns: 
 *      HRESULT 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      Various. 
 */ 
HRESULT AddRFN(PRFS prfs, PRFN prfn) 
{ 
    HRESULT hr = hrSuccess; 
    HRESULT hrStg = hrSuccess; 
    LPSTORAGE lpstg = NULL; 
    IStream *lpstream = NULL; 
    UINT ui = 0; 
    ULONG cb = 0L; 
    ULONG cbNode = 0L; 
    ULONG cbClass = 0L; 
    ULONG cbName = 0L; 
    ULONG cRFN = 0L; 
    LARGE_INTEGER liEOS; 
 
    AssertSz(prfs, "Bad prfs"); 
    AssertSz(prfn, "Bad prfn"); 
    AssertSz(prfn->szClass, "Bad prfn->szClass"); 
    AssertSz(prfn->szName, "Bad prfn->szName"); 
 
    hr = OpenRFSStream(prfs, TRUE, &lpstream, &lpstg); 
    if (hr != hrSuccess) 
        goto exit; 
 
    /* Find the end of the stream.  Strictly speaking, we can't just seek */ 
    /* the current End-Of-Stream (what the IStream thinks is it's current */ 
    /* EOS), because we really want to be at the end of the last node,    */ 
    /* and there may have been stuff written after (from a failed write). */ 
    /* First, read the count of RFS nodes from the stream. */ 
 
    LISet32(liEOS, 0);      /* This is an OLE initializer macro */ 
    hrStg = lpstream->lpVtbl->Seek(lpstream, liEOS, STREAM_SEEK_SET, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN, 
            sizeof cRFN, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    for (ui = 0; ui < cRFN; ui++) 
    { 
        hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cb, 
                sizeof cb, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
 
        LISet32(liEOS, (LONG) cb); 
 
        hrStg = lpstream->lpVtbl->Seek(lpstream, liEOS, STREAM_SEEK_CUR, NULL); 
        if (hrStg != hrSuccess) 
            goto stg_err; 
    } 
 
    /* Write out the node */ 
 
    /* Size of node: length of 2 strings + 2 NULL characters + 2 times */ 
    /* the size of the space needed to hold the string lengths.        */ 
 
    cbClass = Cbtszsize(prfn->szClass); 
    cbName = Cbtszsize(prfn->szName); 
    cbNode = 2 * sizeof(ULONG) + cbClass + cbName; 
 
    hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cbNode, 
            sizeof cbNode, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cbClass, 
            sizeof cbClass, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) prfn->szClass, 
            cbClass, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cbName, 
            sizeof cbName, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) prfn->szName, 
            cbName, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    /* Keep cRFN, the in-memory and on-disk */ 
    /* copies, in sync with each other.     */ 
 
    LISet32(liEOS, 0L); 
    hrStg = lpstream->lpVtbl->Seek(lpstream, liEOS, STREAM_SEEK_SET, NULL); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    cRFN++; 
    hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cRFN, 
            sizeof cRFN, NULL); 
    if (hrStg != hrSuccess) 
    { 
        cRFN--; 
        goto stg_err; 
    } 
 
    /* Commit the change */ 
 
    hrStg = lpstg->lpVtbl->Commit(lpstg, 0); 
    /* if ( hrStg ), fall through to stg_err */ 
 
stg_err: 
    if (hrStg) 
        hr = ResultFromScode(MapStorageSCode(GetScode(hrStg))); 
 
exit: 
    if (lpstream) 
        CloseRFSStream(lpstream, lpstg); 
 
    DebugTraceResult(AddRFN, hr); 
    return hr; 
} 
 
/* 
 *  CloseRFS 
 * 
 *  Purpose: 
 *      Frees and invalidates an open context for accessing receive 
 *      folder settings. 
 * 
 *  Arguments: 
 *      prfs        Pointer to the object to close. 
 * 
 *  Returns: 
 *      HRESULT 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      Various. 
 */ 
HRESULT CloseRFS(PRFS prfs) 
{ 
    HRESULT hr = hrSuccess; 
 
    AssertSz(prfs, "Bad prfs"); 
 
    FreeNull(prfs->szFile); 
    FreeNull(prfs); 
 
    DebugTraceResult(CloseRFS, hr); 
    return hr; 
} 
 
/* 
 *  Internal functions 
 */ 
 
/* 
 *  OpenRFSStream 
 * 
 *  Purpose: 
 *      Open the stream within a docfile that contains receive 
 *      folder settings. 
 * 
 *  Arguments: 
 *      prfs        Receive folder settings context. 
 *      fModify     TRUE indicates the caller wants write access. 
 *      lppstream   Address in which to return a pointer to the 
 *                  newly opened stream. 
 * 
 *  Returns: 
 *      HRESULT 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      Various storage errors. 
 */ 
static HRESULT OpenRFSStream(PRFS prfs, BOOL fModify, IStream **lppstream, 
    LPSTORAGE *lppstg) 
{ 
    HRESULT hr = hrSuccess; 
HRESULT hrStg = hrSuccess; 
    DWORD grfMode; 
 
#ifdef _WIN32 
    OLE_CHAR szOle[MAX_PATH]; 
    int cbOle = 0L; 
#else 
    OLE_CHAR *szOle; 
#endif 
 
    LPSTORAGE lpstg = NULL; 
    IStream *lpstream = NULL; 
 
    AssertSz(prfs, "Bad prfs"); 
    AssertSz(lppstream, "Bad lppstream"); 
 
#ifdef _WIN32 
    cbOle = 1 + lstrlen(prfs->szFile); 
    Assert(cbOle < MAX_PATH); 
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, prfs->szFile, cbOle, szOle, cbOle); 
#else 
    szOle = prfs->szFile; 
#endif 
 
    if (fModify) 
        grfMode = STGM_SHARE_EXCLUSIVE | STGM_READWRITE; 
    else 
        grfMode = STGM_SHARE_EXCLUSIVE | STGM_READ; 
 
    hrStg = StgOpenStorage(szOle, NULL, grfMode | STGM_TRANSACTED, NULL, 
            0, &lpstg); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
#ifdef _WIN32 
    cbOle = 1 + lstrlen(szRFSStreamName); 
    Assert(cbOle < MAX_PATH); 
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szRFSStreamName, cbOle, szOle, cbOle); 
#else 
    szOle = szRFSStreamName; 
#endif 
 
    hrStg = lpstg->lpVtbl->OpenStream(lpstg, szOle, NULL, grfMode, 0, &lpstream); 
    if (hrStg != hrSuccess) 
        goto stg_err; 
 
    /* WARNING:  If any code is added between here and the error handler */ 
    /* that can fail, a check in the error handler must be added to free */ 
    /* the open stream in the event of an error.                         */ 
 
    *lppstream = lpstream; 
    *lppstg = lpstg; 
 
stg_err: 
    if (hrStg != hrSuccess) 
        hr = ResultFromScode(MapStorageSCode(GetScode(hrStg))); 
 
    if (hr != hrSuccess) 
        UlRelease(lpstg); 
 
    DebugTraceResult(OpenRFSStream, hr); 
    return hr; 
} 
 
/* 
 *  CloseRFSStream 
 * 
 *  Purpose: 
 *      Close the stream within a docfile that holds receive folder 
 *      settings. 
 * 
 *  Arguments: 
 *      lpstream    Pointer to the stream. 
 *      lpstg       Pointer to the storage instance in which this 
 *                  stream resides. 
 * 
 *  Returns: 
 *      void 
 * 
 *  Side effects: 
 *      None. 
 * 
 *  Errors: 
 *      None. 
 */ 
static void CloseRFSStream(IStream *lpstream, LPSTORAGE lpstg) 
{ 
    AssertSz(lpstream, "Bad lpstream"); 
    AssertSz(lpstg, "Bad lpstg"); 
 
    NFSideAssertSz(UlRelease(lpstream) == 0L, "lpstream not released"); 
    NFSideAssertSz(UlRelease(lpstg) == 0L, "lpstg not released"); 
 
    return; 
}