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 / msprfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  30.7 KB  |  1,067 lines

  1. /*
  2.  *  M S P R F S . C
  3.  *
  4.  *  Code for implementing Get/SetReceiveFolder for the Message
  5.  *  Store object.
  6.  *
  7.  *  Hungarian shorthand:
  8.  *      To avoid excessively long identifier names, throughout this
  9.  *      file, RFS is used to mean "Receive Folder Settings", and RFN
  10.  *      is used to mean an RFS Node.
  11.  *
  12.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  13.  */
  14.  
  15. #include "msp.h"
  16.  
  17. /* Manifest constants */
  18.  
  19. TCHAR szRFSStreamName[] = TEXT("RFS_STREAM");
  20.  
  21. /* GRoup of Flags (grf):
  22.  *
  23.  * grfStorageOpen:      Flags used to open a read/write OLE IStorage object.
  24.  * grfStorageOpenRO:    Flags used to open a read only OLE IStorage object.
  25.  * grfStorageCreate:    Flags used to create an OLE IStorage object.
  26.  * grfStreamOpen:       Flags used to open a read/write OLE IStream object.
  27.  * grfStreamOpenRO:     Flags used to open a read only OLE IStream object.
  28.  * grfStreamCreate:     Flags used to create an OLE IStream object.
  29.  *
  30.  * See the OLE 2 Programmer's Reference for details on these flags.
  31.  */
  32.  
  33. #define grfStorageOpen      STGM_READWRITE | STGM_SHARE_EXCLUSIVE | \
  34.                                 STGM_TRANSACTED
  35. #define grfStorageOpenRO    STGM_READ | STGM_SHARE_EXCLUSIVE | \
  36.                                 STGM_TRANSACTED
  37. #define grfStorageCreate    grfStorageOpen | STGM_FAILIFTHERE | STGM_CREATE
  38.  
  39. #define grfStreamOpen       STGM_SHARE_EXCLUSIVE | STGM_READWRITE
  40. #define grfStreamOpenRO     STGM_SHARE_EXCLUSIVE | STGM_READ
  41. #define grfStreamCreate     grfStreamOpen | STGM_FAILIFTHERE
  42.  
  43. /* Function prototypes */
  44.  
  45. static HRESULT OpenRFSStream(PRFS prfs, BOOL fModify, IStream **lppstream,
  46.     LPSTORAGE *lppstg);
  47. static void CloseRFSStream(IStream * lpstream, LPSTORAGE lpstg);
  48.  
  49. /*
  50.  *  Exported functions
  51.  */
  52.  
  53. /*
  54.  *  FIsValidMessageClass
  55.  *
  56.  *  Purpose:
  57.  *      Checks to see if a message class is valid.  A valid message
  58.  *      class is defined to be a series of one or more
  59.  *      period-delimited tokens with each token being a series of
  60.  *      one or more ASCII characters in the range 32-126
  61.  *      (inclusive) excluding period.  Note that this definition
  62.  *      excludes message classes with a leading or trailing
  63.  *      period, or two or more consecutive periods, because this
  64.  *      would imply the existence of a zero-length token.
  65.  *
  66.  *      We put this function in the RFS module because dealing with
  67.  *      receive folders is the primary place in the Sample Store where we
  68.  *      care about message class.
  69.  *
  70.  *  Arguments:
  71.  *      szMessageClass  String identifying the message class.
  72.  *
  73.  *  Returns:
  74.  *      BOOL.  TRUE if szMessage is valid, FALSE if not.
  75.  *
  76.  *  Side effects:
  77.  *      None.
  78.  *
  79.  *  Errors:
  80.  *      None.
  81.  */
  82. BOOL FIsValidMessageClass(LPTSTR szMessageClass)
  83. {
  84.     TCHAR *pch = szMessageClass;
  85.     BOOL fWasPeriod = TRUE;
  86.  
  87.     if (szMessageClass && IsBadStringPtr(szMessageClass, (UINT) -1))
  88.         return FALSE;
  89.  
  90.     /* Handle the default message class */
  91.  
  92.     if (!szMessageClass || *szMessageClass == '\0')
  93.         return TRUE;
  94.  
  95.     /* disallow things:bad chars and cases where */
  96.     /* period is not a delim(.1, 1., and 1. .1 bad) */
  97.     while (*pch)
  98.     {
  99.         if (*pch < 32 || *pch > 126)
  100.             return FALSE;
  101.         if (*pch == '.')
  102.         {
  103.             if (fWasPeriod)
  104.                 return FALSE;
  105.             fWasPeriod = TRUE;
  106.         }
  107.         else
  108.             fWasPeriod = FALSE;
  109.         pch++;
  110.     }
  111.  
  112.     return !fWasPeriod;
  113. }
  114.  
  115. /*
  116.  *  OpenRFS
  117.  *
  118.  *  Purpose:
  119.  *      Given an OLE2 storage object, opens a stream on it and
  120.  *      prepares it for handling receive folder settings.  OpenRFS
  121.  *      returns to the caller a pointer to the RFS structure which
  122.  *      is then used for access to the settings.  The stream format
  123.  *      of the receive folder settings is extremely simple:  the
  124.  *      first ULONG is a count of the number of RFNs in the
  125.  *      stream, and the nodes themselves follow sequentially
  126.  *      afterward.  A node on disk is not the same as an RFN in
  127.  *      memory.  On disk, it is a variable-sized structure
  128.  *      containing a ULONG which is the size (in bytes) of the
  129.  *      node not including this field, then a ULONG which is the
  130.  *      length (in bytes) of a
  131.  *      string containing the message class (NULL inclusive), which
  132.  *      immediately follows.  After that is a ULONG which is the
  133.  *      size (in bytes) of a string containing the relative path
  134.  *      name of the receive folder (NULL inclusive), which also
  135.  *      immediately follows.  Visually, a node looks like the
  136.  *      following:
  137.  *
  138.  *          +--------------------+
  139.  *          | ULONG cbNode       |
  140.  *          +--------------------+
  141.  *          | ULONG cbClass      |
  142.  *          +--------------------+
  143.  *          | TCHAR szClass[]    |
  144.  *          |   .                |
  145.  *          |   .                |
  146.  *          |   .                |
  147.  *          +--------------------+
  148.  *          | ULONG cbPath       |
  149.  *          +--------------------+
  150.  *          | TCHAR szPath[]     |
  151.  *          |   .                |
  152.  *          |   .                |
  153.  *          |   .                |
  154.  *          +--------------------+
  155.  *
  156.  *      The size of the message class name, cbClass, will always be
  157.  *      > 0 for "valid" nodes (the default message class will be a
  158.  *      NULL string of size 1 TCHAR).  Thus, a value of 0 for
  159.  *      cbClass will signify a "free" node.  Free nodes are created
  160.  *      in the normal use of the stream by DeleteRFN, which
  161.  *      SetReceiveFolder calls, and are removed at close time (see
  162.  *      CloseRFS, below).
  163.  *
  164.  *      Note that, because string lengths are byte-sized but the
  165.  *      strings themselves are made of TCHARs, translation between
  166.  *      BYTE and TCHAR sizes must be done.
  167.  *
  168.  *  Arguments:
  169.  *      szStoreRoot Full path to the sample store "root"
  170.  *                      directory.
  171.  *      szFile      Relative path name of docfile containing
  172.  *                      receive folder settings.
  173.  *      ulFlags         Flags.  The following are defined:
  174.  *                      RFS_CREATE  Create the docfile containing
  175.  *                                  receive folder settings
  176.  *                                  (default opens existing).
  177.  *      pprfs           Location in which to return a pointer to
  178.  *                      the newly created RFS structure.
  179.  *
  180.  *  Returns:
  181.  *      HRESULT
  182.  *
  183.  *  Side effects:
  184.  *      None.
  185.  *
  186.  *  Errors:
  187.  *      Various.
  188.  */
  189. HRESULT OpenRFS(LPTSTR szStoreRoot, LPTSTR szFile, ULONG ulFlags, PRFS *pprfs)
  190. {
  191.     HRESULT hr = hrSuccess;
  192.     HRESULT hrStg = hrSuccess;
  193.     SCODE sc;
  194.     LPTSTR szFullPath = NULL;
  195.     ULONG cRFN = 0L;
  196.     LPSTORAGE lpstg = NULL;
  197.     IStream *lpstream = NULL;
  198.     PRFS prfs = NULL;
  199.  
  200. #ifdef _WIN32
  201.     OLE_CHAR szOle[MAX_PATH];
  202.     int cbOle = 0L;
  203.  
  204. #else
  205.     OLE_CHAR *szOle;
  206.  
  207. #endif
  208.     LARGE_INTEGER liBeg;
  209.  
  210.     /* initial default receive folder settings */
  211.     ULONG cInitRFNs = 1;        /* number of default nodes */
  212.  
  213. #pragma pack(1)
  214.     struct RFN
  215.     {
  216.         ULONG cbNode;
  217.         ULONG cbClass;
  218.         TCHAR szClass;
  219.         ULONG cbFolder;
  220.         TCHAR szFolderPath;
  221.     } DefaultNode =
  222.     {
  223.         (2 * sizeof(ULONG)) + (2 * sizeof(TCHAR)),
  224.         sizeof(TCHAR),
  225.         '\0',
  226.         sizeof(TCHAR),
  227.         '\0'
  228.     };
  229. #pragma pack()
  230.  
  231.     LISet32(liBeg, 0);  /* This is an OLE initializer macro */
  232.  
  233.     AssertSz(szStoreRoot, "Bad szStoreRoot");
  234.     AssertSz(szFile, "Bad szFile");
  235.     AssertSz(pprfs, "Bad pprfs");
  236.  
  237.     hr = HrAppendPath(szStoreRoot, szFile, &szFullPath);
  238.     if (hr != hrSuccess)
  239.         goto exit;
  240.  
  241.     sc = ScAllocZ(sizeof(RFS), &prfs);
  242.     if (sc != S_OK)
  243.     {
  244.         hr = ResultFromScode(sc);
  245.         goto exit;
  246.     }
  247.  
  248.     prfs->szFile = szFullPath;
  249.  
  250. #ifdef _WIN32
  251.     cbOle = 1 + lstrlen(szFullPath);
  252.     Assert(cbOle < MAX_PATH);
  253.     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szFullPath, cbOle, szOle, cbOle);
  254. #else
  255.     szOle = szFullPath;
  256. #endif
  257.  
  258.     if (ulFlags & RFS_CREATE)
  259.     {
  260.         hrStg = StgCreateDocfile(szOle, grfStorageCreate, 0, &lpstg);
  261.  
  262.         if (hrStg != hrSuccess)
  263.             goto stg_err;
  264.  
  265. #ifdef _WIN32
  266.         cbOle = 1 + lstrlen(szRFSStreamName);
  267.         Assert(cbOle < MAX_PATH);
  268.         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szRFSStreamName, cbOle,
  269.                 szOle, cbOle);
  270. #else
  271.         szOle = szRFSStreamName;
  272. #endif
  273.         hrStg = lpstg->lpVtbl->CreateStream(lpstg, szOle, grfStreamCreate,
  274.                 0, 0, &lpstream);
  275.         if (hrStg != hrSuccess)
  276.             goto stg_err;
  277.  
  278.         /* Initialize the newly created stream */
  279.         hrStg = lpstream->lpVtbl->Seek(lpstream, liBeg, STREAM_SEEK_SET, NULL);
  280.         if (hrStg != hrSuccess)
  281.             goto stg_err;
  282.  
  283.         /* add the default RFS setting of the root (path = '\0') */
  284.         hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cInitRFNs,
  285.                 sizeof cInitRFNs, NULL);
  286.         if (hrStg != hrSuccess)
  287.             goto stg_err;
  288.  
  289.         hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &DefaultNode,
  290.                 DefaultNode.cbNode + sizeof(ULONG), NULL);
  291.         if (hrStg != hrSuccess)
  292.             goto stg_err;
  293.  
  294.         /* Commit docfile changes.  If we don't do this now, the file on  */
  295.         /* disk will NOT be a docfile (i.e. OLE2 will not recognize it as */
  296.         /* a docfile) if opened again with no other changes made to it.   */
  297.  
  298.         hrStg = lpstg->lpVtbl->Commit(lpstg, 0);
  299.         if (hrStg != hrSuccess)
  300.             goto stg_err;
  301.     }
  302.     else        /* Open an existing stream */
  303.     {
  304.         hr = OpenRFSStream(prfs, FALSE, &lpstream, &lpstg);
  305.         if (hr != hrSuccess)
  306.             goto exit;
  307.  
  308.         hrStg = lpstream->lpVtbl->Seek(lpstream, liBeg, STREAM_SEEK_SET, NULL);
  309.         if (hrStg != hrSuccess)
  310.             goto stg_err;
  311.  
  312.         hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN,
  313.                 sizeof cRFN, NULL);
  314.  
  315.         /* fall through to error handler */
  316.     }
  317.  
  318. stg_err:
  319.     if (hrStg != hrSuccess)
  320.         hr = ResultFromScode(MapStorageSCode(GetScode(hrStg)));
  321.  
  322. exit:
  323.     if (lpstream)
  324.     {
  325.         CloseRFSStream(lpstream, lpstg);
  326.         lpstg = NULL;
  327.     }
  328.  
  329.     if (hr != hrSuccess)
  330.     {
  331.         FreeNull(szFullPath);
  332.         FreeNull(prfs);
  333.         UlRelease(lpstg);
  334.     }
  335.     else
  336.         *pprfs = prfs;
  337.  
  338.     DebugTraceResult(OpenRFS, hr);
  339.     return hr;
  340. }
  341.  
  342. /*
  343.  *  GetRFN
  344.  *
  345.  *  Purpose:
  346.  *      Returns an RFN containing the receive folder setting
  347.  *      for the message class that is passed in as a parameter.  If
  348.  *      there is not a receive folder setting for this particular
  349.  *      message class, the "best match" is returned, with best
  350.  *      match being defined in GetReceiveFolder (see msgstobj.c).
  351.  *      the way we measure this best match is to have a match index
  352.  *      which is incremented every time a section of the message
  353.  *      class is matched.  These values begin at 2 because 1 is
  354.  *      reserved for the default message class.  For example, the
  355.  *      message class "IPM.Note.Phone" matches against the
  356.  *      following receive folder settings in the following way:
  357.  *
  358.  *          "" (default)        1
  359.  *          "IPM"               2
  360.  *          "IPM.Note"          3
  361.  *          "IPM.Note.Phone"    4
  362.  *          "IPC"               0
  363.  *
  364.  *  Arguments:
  365.  *      prfs            Pointer to the RFS context.
  366.  *      szClassName     Name of the message class for which to
  367.  *                      search for a receive folder setting.
  368.  *      pprfn           Address of location in which to return a
  369.  *                      pointer to an RFN structure containing
  370.  *                      the message class and folder name of the
  371.  *                      "best match" receive folder setting.
  372.  *
  373.  *  Returns:
  374.  *      HRESULT
  375.  *
  376.  *  Side effects:
  377.  *      None.
  378.  *
  379.  *  Errors:
  380.  *      Various.
  381.  *
  382.  *  Notes:
  383.  *      Use FreeRFN() to release the memory of the returned
  384.  *      RFN structure in *pprfn.
  385.  */
  386. HRESULT GetRFN(PRFS prfs, LPTSTR szClassName, PRFN *pprfn)
  387. {
  388.     HRESULT hr = hrSuccess;
  389.     HRESULT hrStg = hrSuccess;
  390.     SCODE sc = S_OK;
  391.     LPSTORAGE lpstg = NULL;
  392.     LPSTREAM lpstream = NULL;
  393.     PRFN prfn = NULL;
  394.     ULONG ibNextNode = 0L;
  395.     ULONG ibMatchNode = 0L;
  396.     ULONG cbNode = 0L;
  397.     ULONG cbClass = 0L;
  398.     ULONG cRFN = 0L;
  399.     UINT uiMatchLvl = 0;
  400.     UINT ui = 0;
  401.     TCHAR rgch[1024];
  402.     LARGE_INTEGER li;
  403.     ULONG cbCls = 0L;
  404.     ULONG cbName = 0L;
  405.  
  406.     AssertSz(prfs, "Bad prfs");
  407.     AssertSz(szClassName, "Bad szClassName");
  408.     AssertSz(pprfn, "Bad pprfn");
  409.  
  410.     hr = OpenRFSStream(prfs, FALSE, &lpstream, &lpstg);
  411.     if (hr != hrSuccess)
  412.         goto exit;
  413.  
  414.     /* Read the count of RFS nodes from the stream. */
  415.  
  416.     LISet32(li, 0);     /* This is an OLE initializer macro */
  417.     hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL);
  418.     if (hrStg != hrSuccess)
  419.         goto stg_err;
  420.  
  421.     hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN,
  422.             sizeof cRFN, NULL);
  423.     if (hrStg != hrSuccess)
  424.         goto stg_err;
  425.  
  426.     /* Loop over nodes, looking for the best message class match */
  427.  
  428.     for (ui = 0, ibNextNode = sizeof cRFN; ui < cRFN; ui++)
  429.     {
  430.         /* Set seek pointer to beginning of this node */
  431.  
  432.         LISet32(li, ibNextNode);
  433.         hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL);
  434.         if (hrStg != hrSuccess)
  435.             goto stg_err;
  436.  
  437.         /* Need to have an absolute index to the NEXT node. */
  438.         /* Remember:  cbNode is not self-inclusive, so the  */
  439.         /* next node is (cbNode + sizeof cbNode) from the   */
  440.         /* current node.                                    */
  441.  
  442.         hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbNode,
  443.                 sizeof cbNode, NULL);
  444.         if (hrStg != hrSuccess)
  445.             goto stg_err;
  446.  
  447.         ibNextNode += cbNode + sizeof cbNode;
  448.  
  449.         /* Get and compare message class */
  450.  
  451.         hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbClass,
  452.                 sizeof cbClass, NULL);
  453.         if (hrStg != hrSuccess)
  454.             goto stg_err;
  455.  
  456.         AssertSz(sizeof rgch >= cbClass, "Message class too big!");
  457.  
  458.         if (cbClass > 0L)       /* If it's not a free node */
  459.         {
  460.             UINT uiMatchCur = 0;
  461.  
  462.             hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) rgch, 
  463.                     cbClass, NULL);
  464.             if (hrStg != hrSuccess)
  465.                 goto stg_err;
  466.  
  467.             if (rgch[0] == '\0')
  468.             {
  469.                 uiMatchCur = 1;
  470.             }
  471.             else
  472.                 /* count the matching message class sections */
  473.             {
  474.                 TCHAR *pch1 = rgch;
  475.                 TCHAR *pch1Ahead = NULL;
  476.                 TCHAR *pch2 = szClassName;
  477.                 TCHAR *pch2Ahead = NULL;
  478.  
  479.                 /* if matching against a class that is less refined than */
  480.                 /* what we are searching for return 0 */
  481.                 if (lstrlen(szClassName) >= lstrlen(rgch))
  482.                 {
  483.                     while (*pch1 && *pch2)
  484.                     {
  485.                         for (pch1Ahead = pch1 + 1; *pch1Ahead &&
  486.                             *pch1Ahead != '.'; pch1Ahead++)
  487.                             ;
  488.                         for (pch2Ahead = pch2 + 1; *pch2Ahead &&
  489.                             *pch2Ahead != '.'; pch2Ahead++)
  490.                             ;
  491.                         if (pch1Ahead - pch1 == pch2Ahead - pch2 &&
  492.                             !memcmp(pch1, pch2, pch1Ahead - pch1))
  493.                         {
  494.                             uiMatchCur++;
  495.                             pch1 = pch1Ahead;
  496.                             pch2 = pch2Ahead;
  497.                         }
  498.                         else
  499.                             break;
  500.                     }
  501.                 }
  502.                 /* We want to match a "real" setting higher than the */
  503.                 /* default, so we increment a real match to be > 1.  */
  504.  
  505.                 if (uiMatchCur > 0)
  506.                 {
  507.                     uiMatchCur++;
  508.                 }
  509.             }
  510.  
  511.             if (uiMatchCur > uiMatchLvl)
  512.             {
  513.                 /* Here we set ibMatchNode to be the absolute index of */
  514.                 /* the cbClass member of the node (NOT the cbNode      */
  515.                 /* member.  When we seek back to this position, we can */
  516.                 /* begin reading the cbClass immediately (see below).  */
  517.  
  518.                 ibMatchNode = ibNextNode - cbNode;
  519.                 uiMatchLvl = uiMatchCur;
  520.             }
  521.         }
  522.     }
  523.  
  524.     if (uiMatchLvl == 0)
  525.     {
  526.         hr = ResultFromScode(MAPI_E_NOT_FOUND);
  527.         goto exit;
  528.     }
  529.  
  530.     /* Set the return variable w/best match */
  531.  
  532.     sc = ScAllocZ(sizeof(RFN), (PPV) &prfn);
  533.     if (sc != S_OK)
  534.         goto sc_err;
  535.  
  536.     /* Goto best match node, but seek pointer will be past cbNode */
  537.  
  538.     LISet32(li, ibMatchNode);
  539.     hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL);
  540.     if (hrStg != hrSuccess)
  541.         goto stg_err;
  542.  
  543.     /* Read in class name */
  544.  
  545.     hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbCls,
  546.             sizeof cbCls, NULL);
  547.     if (hrStg != hrSuccess)
  548.         goto stg_err;
  549.  
  550.     sc = ScAlloc(cbCls, (PPV) &prfn->szClass);
  551.     if (sc != S_OK)
  552.         goto sc_err;
  553.  
  554.     hrStg = lpstream->lpVtbl->Read(lpstream,
  555.             (LPVOID) prfn->szClass, cbCls, NULL);
  556.     if (hrStg != hrSuccess)
  557.         goto stg_err;
  558.  
  559.     /* Read in folder name */
  560.  
  561.     hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbName,
  562.             sizeof cbName, NULL);
  563.     if (hrStg != hrSuccess)
  564.         goto stg_err;
  565.  
  566.     sc = ScAlloc(cbName, (PPV) &prfn->szName);
  567.     if (sc != S_OK)
  568.         goto sc_err;
  569.  
  570.     hrStg = lpstream->lpVtbl->Read(lpstream,
  571.             (LPVOID) prfn->szName, cbName, NULL);
  572.     if (hrStg != hrSuccess)
  573.         goto stg_err;
  574.  
  575. sc_err:
  576.     if (sc != S_OK)
  577.     {
  578.         Assert(hr == hrSuccess);
  579.         Assert(hrStg == hrSuccess);
  580.         hr = ResultFromScode(sc);
  581.     }
  582.  
  583. stg_err:
  584.     if (hrStg != hrSuccess)
  585.     {
  586.         Assert(sc == S_OK);
  587.         Assert(hr == hrSuccess);
  588.         hr = ResultFromScode(MapStorageSCode(GetScode(hrStg)));
  589.     }
  590.  
  591. exit:
  592.     if (lpstream)
  593.         CloseRFSStream(lpstream, lpstg);
  594.  
  595.     if (hr != hrSuccess)
  596.         FreeRFN(prfn);
  597.     else
  598.         *pprfn = prfn;
  599.  
  600.     DebugTraceResult(GetRFN, hr);
  601.     return hr;
  602. }
  603.  
  604. /*
  605.  -  FreeRFN
  606.  -
  607.  *  Release the memory of an RFN allocated and returned
  608.  *  by the GetRFN() procedure.
  609.  */
  610. void FreeRFN(PRFN prfn)
  611. {
  612.     if (prfn)
  613.     {
  614.         (void)FreeNull((LPVOID) prfn->szClass);
  615.         (void)FreeNull((LPVOID) prfn->szName);
  616.         (void)FreeNull((LPVOID) prfn);
  617.     }
  618. }
  619.  
  620. /*
  621.  *  DeleteRFN
  622.  *
  623.  *  Purpose:
  624.  *      Delete the receive folder setting associated with a
  625.  *      particular message class.  We do this by "zeroing out" the
  626.  *      node on disk, rather than actually removing it and
  627.  *      compacting the stream.  We can easily zero out the node
  628.  *      once we've found the right one by setting the length of the
  629.  *      message class string contained in it to be zero (an invalid
  630.  *      value).
  631.  *
  632.  *  Arguments:
  633.  *      prfs            Pointer to the RFS context to use.
  634.  *      szClassName     Buffer containing the name of the message
  635.  *                      class for which to remove the receive
  636.  *                      folder setting.  We do a linear search
  637.  *                      through the stream to find the node on disk
  638.  *                      with a matching message class.
  639.  *
  640.  *  Returns:
  641.  *      HRESULT
  642.  *
  643.  *  Side effects:
  644.  *      None.
  645.  *
  646.  *  Errors:
  647.  *      Various.
  648.  */
  649. HRESULT DeleteRFN(PRFS prfs, LPTSTR szClassName)
  650. {
  651.     HRESULT hr = hrSuccess;
  652.     HRESULT hrStg = hrSuccess;
  653.     LPSTORAGE lpstg = NULL;
  654.     IStream *lpstream = NULL;
  655.     ULONG ibNextNode = 0L;
  656.     ULONG cbNode = 0L;
  657.     ULONG cbClass = 0L;
  658.     ULONG cRFN = 0L;
  659.     UINT ui = 0;
  660.     TCHAR rgch[1024];
  661.     LARGE_INTEGER li;
  662.  
  663.     AssertSz(prfs, "Bad prfs");
  664.     AssertSz(szClassName, "Bad szClassName");
  665.  
  666.     hr = OpenRFSStream(prfs, TRUE, &lpstream, &lpstg);
  667.     if (hr != hrSuccess)
  668.         goto exit;
  669.  
  670.     /* Read the count of RFS nodes from the stream. */
  671.  
  672.     LISet32(li, 0);     /* This is an OLE initializer macro */
  673.     hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL);
  674.     if (hrStg != hrSuccess)
  675.         goto stg_err;
  676.  
  677.     hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN,
  678.             sizeof cRFN, NULL);
  679.     if (hrStg != hrSuccess)
  680.         goto stg_err;
  681.  
  682.     /* Loop over nodes, looking for a message class match */
  683.  
  684.     for (ui = 0, ibNextNode = sizeof cRFN; ui < cRFN; ui++)
  685.     {
  686.         /* Set seek pointer to beginning of first node */
  687.  
  688.         LISet32(li, ibNextNode);
  689.         hrStg = lpstream->lpVtbl->Seek(lpstream, li, STREAM_SEEK_SET, NULL);
  690.         if (hrStg != hrSuccess)
  691.             goto stg_err;
  692.  
  693.         /* Need to have an absolute index to the NEXT node. */
  694.         /* Remember:  cbNode is not self-inclusive, so the  */
  695.         /* next node is (cbNode + sizeof cbNode) from the   */
  696.         /* current node.                                    */
  697.  
  698.         hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbNode,
  699.                 sizeof cbNode, NULL);
  700.         if (hrStg != hrSuccess)
  701.             goto stg_err;
  702.  
  703.         ibNextNode += cbNode + sizeof cbNode;
  704.  
  705.         /* Get and compare message class */
  706.  
  707.         hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cbClass,
  708.                 sizeof cbClass, NULL);
  709.         if (hrStg != hrSuccess)
  710.             goto stg_err;
  711.  
  712.         AssertSz(sizeof rgch >= cbClass * sizeof(TCHAR),
  713.             "Message class too big!");
  714.  
  715.         if (cbClass > 0L)       /* If it's not a free node */
  716.         {
  717.             hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) rgch,
  718.                     cbClass, NULL);
  719.             if (hrStg != hrSuccess)
  720.                 goto stg_err;
  721.  
  722.             if (cbClass == Cbtszsize(szClassName)
  723.                 && !memcmp(szClassName, rgch, (UINT) cbClass))
  724.             {
  725.                 LONG ibClass = 0L;
  726.  
  727.                 /* Seek back to cbClass */
  728.  
  729.                 ibClass -= (LONG) (cbClass + sizeof cbClass);
  730.                 LISet32(li, ibClass);
  731.                 hrStg = lpstream->lpVtbl->Seek(lpstream, li,
  732.                         STREAM_SEEK_CUR, NULL);
  733.                 if (hrStg != hrSuccess)
  734.                     goto stg_err;
  735.  
  736.                 /* Zero out the node */
  737.  
  738.                 cbClass = 0L;
  739.                 hrStg = lpstream->lpVtbl->Write(lpstream,
  740.                         (LPVOID) &cbClass, sizeof cbClass, NULL);
  741.                 if (hrStg != hrSuccess)
  742.                     goto stg_err;
  743.  
  744.                 /* Commit the change */
  745.  
  746.                 hrStg = lpstg->lpVtbl->Commit(lpstg, 0);
  747.                 if (hrStg != hrSuccess)
  748.                     goto stg_err;
  749.  
  750.                 break;
  751.             }
  752.         }
  753.     }
  754.  
  755. stg_err:
  756.     if (hrStg)
  757.         hr = ResultFromScode(MapStorageSCode(GetScode(hrStg)));
  758.  
  759. exit:
  760.     if (lpstream)
  761.         CloseRFSStream(lpstream, lpstg);
  762.  
  763.     DebugTraceResult(DeleteRFN, hr);
  764.     return hr;
  765. }
  766.  
  767. /*
  768.  *  AddRFN
  769.  *
  770.  *  Purpose:
  771.  *      Adds a node (on disk) to the stream that holds receive
  772.  *      folder settings for a message store.  Does this by creating
  773.  *      a new node at the current End-Of-Stream (at the end of all
  774.  *      other nodes).
  775.  *
  776.  *  Arguments:
  777.  *      prfs        Pointer to the receive folder storage context.
  778.  *      prfn    Pointer to the new node to add.
  779.  *
  780.  *  Returns:
  781.  *      HRESULT
  782.  *
  783.  *  Side effects:
  784.  *      None.
  785.  *
  786.  *  Errors:
  787.  *      Various.
  788.  */
  789. HRESULT AddRFN(PRFS prfs, PRFN prfn)
  790. {
  791.     HRESULT hr = hrSuccess;
  792.     HRESULT hrStg = hrSuccess;
  793.     LPSTORAGE lpstg = NULL;
  794.     IStream *lpstream = NULL;
  795.     UINT ui = 0;
  796.     ULONG cb = 0L;
  797.     ULONG cbNode = 0L;
  798.     ULONG cbClass = 0L;
  799.     ULONG cbName = 0L;
  800.     ULONG cRFN = 0L;
  801.     LARGE_INTEGER liEOS;
  802.  
  803.     AssertSz(prfs, "Bad prfs");
  804.     AssertSz(prfn, "Bad prfn");
  805.     AssertSz(prfn->szClass, "Bad prfn->szClass");
  806.     AssertSz(prfn->szName, "Bad prfn->szName");
  807.  
  808.     hr = OpenRFSStream(prfs, TRUE, &lpstream, &lpstg);
  809.     if (hr != hrSuccess)
  810.         goto exit;
  811.  
  812.     /* Find the end of the stream.  Strictly speaking, we can't just seek */
  813.     /* the current End-Of-Stream (what the IStream thinks is it's current */
  814.     /* EOS), because we really want to be at the end of the last node,    */
  815.     /* and there may have been stuff written after (from a failed write). */
  816.     /* First, read the count of RFS nodes from the stream. */
  817.  
  818.     LISet32(liEOS, 0);      /* This is an OLE initializer macro */
  819.     hrStg = lpstream->lpVtbl->Seek(lpstream, liEOS, STREAM_SEEK_SET, NULL);
  820.     if (hrStg != hrSuccess)
  821.         goto stg_err;
  822.  
  823.     hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cRFN,
  824.             sizeof cRFN, NULL);
  825.     if (hrStg != hrSuccess)
  826.         goto stg_err;
  827.  
  828.     for (ui = 0; ui < cRFN; ui++)
  829.     {
  830.         hrStg = lpstream->lpVtbl->Read(lpstream, (LPVOID) &cb,
  831.                 sizeof cb, NULL);
  832.         if (hrStg != hrSuccess)
  833.             goto stg_err;
  834.  
  835.         LISet32(liEOS, (LONG) cb);
  836.  
  837.         hrStg = lpstream->lpVtbl->Seek(lpstream, liEOS, STREAM_SEEK_CUR, NULL);
  838.         if (hrStg != hrSuccess)
  839.             goto stg_err;
  840.     }
  841.  
  842.     /* Write out the node */
  843.  
  844.     /* Size of node: length of 2 strings + 2 NULL characters + 2 times */
  845.     /* the size of the space needed to hold the string lengths.        */
  846.  
  847.     cbClass = Cbtszsize(prfn->szClass);
  848.     cbName = Cbtszsize(prfn->szName);
  849.     cbNode = 2 * sizeof(ULONG) + cbClass + cbName;
  850.  
  851.     hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cbNode,
  852.             sizeof cbNode, NULL);
  853.     if (hrStg != hrSuccess)
  854.         goto stg_err;
  855.  
  856.     hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cbClass,
  857.             sizeof cbClass, NULL);
  858.     if (hrStg != hrSuccess)
  859.         goto stg_err;
  860.  
  861.     hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) prfn->szClass,
  862.             cbClass, NULL);
  863.     if (hrStg != hrSuccess)
  864.         goto stg_err;
  865.  
  866.     hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cbName,
  867.             sizeof cbName, NULL);
  868.     if (hrStg != hrSuccess)
  869.         goto stg_err;
  870.  
  871.     hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) prfn->szName,
  872.             cbName, NULL);
  873.     if (hrStg != hrSuccess)
  874.         goto stg_err;
  875.  
  876.     /* Keep cRFN, the in-memory and on-disk */
  877.     /* copies, in sync with each other.     */
  878.  
  879.     LISet32(liEOS, 0L);
  880.     hrStg = lpstream->lpVtbl->Seek(lpstream, liEOS, STREAM_SEEK_SET, NULL);
  881.     if (hrStg != hrSuccess)
  882.         goto stg_err;
  883.  
  884.     cRFN++;
  885.     hrStg = lpstream->lpVtbl->Write(lpstream, (LPVOID) &cRFN,
  886.             sizeof cRFN, NULL);
  887.     if (hrStg != hrSuccess)
  888.     {
  889.         cRFN--;
  890.         goto stg_err;
  891.     }
  892.  
  893.     /* Commit the change */
  894.  
  895.     hrStg = lpstg->lpVtbl->Commit(lpstg, 0);
  896.     /* if ( hrStg ), fall through to stg_err */
  897.  
  898. stg_err:
  899.     if (hrStg)
  900.         hr = ResultFromScode(MapStorageSCode(GetScode(hrStg)));
  901.  
  902. exit:
  903.     if (lpstream)
  904.         CloseRFSStream(lpstream, lpstg);
  905.  
  906.     DebugTraceResult(AddRFN, hr);
  907.     return hr;
  908. }
  909.  
  910. /*
  911.  *  CloseRFS
  912.  *
  913.  *  Purpose:
  914.  *      Frees and invalidates an open context for accessing receive
  915.  *      folder settings.
  916.  *
  917.  *  Arguments:
  918.  *      prfs        Pointer to the object to close.
  919.  *
  920.  *  Returns:
  921.  *      HRESULT
  922.  *
  923.  *  Side effects:
  924.  *      None.
  925.  *
  926.  *  Errors:
  927.  *      Various.
  928.  */
  929. HRESULT CloseRFS(PRFS prfs)
  930. {
  931.     HRESULT hr = hrSuccess;
  932.  
  933.     AssertSz(prfs, "Bad prfs");
  934.  
  935.     FreeNull(prfs->szFile);
  936.     FreeNull(prfs);
  937.  
  938.     DebugTraceResult(CloseRFS, hr);
  939.     return hr;
  940. }
  941.  
  942. /*
  943.  *  Internal functions
  944.  */
  945.  
  946. /*
  947.  *  OpenRFSStream
  948.  *
  949.  *  Purpose:
  950.  *      Open the stream within a docfile that contains receive
  951.  *      folder settings.
  952.  *
  953.  *  Arguments:
  954.  *      prfs        Receive folder settings context.
  955.  *      fModify     TRUE indicates the caller wants write access.
  956.  *      lppstream   Address in which to return a pointer to the
  957.  *                  newly opened stream.
  958.  *
  959.  *  Returns:
  960.  *      HRESULT
  961.  *
  962.  *  Side effects:
  963.  *      None.
  964.  *
  965.  *  Errors:
  966.  *      Various storage errors.
  967.  */
  968. static HRESULT OpenRFSStream(PRFS prfs, BOOL fModify, IStream **lppstream,
  969.     LPSTORAGE *lppstg)
  970. {
  971.     HRESULT hr = hrSuccess;
  972.     HRESULT hrStg = hrSuccess;
  973.     DWORD grfMode;
  974.  
  975. #ifdef _WIN32
  976.     OLE_CHAR szOle[MAX_PATH];
  977.     int cbOle = 0L;
  978. #else
  979.     OLE_CHAR *szOle;
  980. #endif
  981.  
  982.     LPSTORAGE lpstg = NULL;
  983.     IStream *lpstream = NULL;
  984.  
  985.     AssertSz(prfs, "Bad prfs");
  986.     AssertSz(lppstream, "Bad lppstream");
  987.  
  988. #ifdef _WIN32
  989.     cbOle = 1 + lstrlen(prfs->szFile);
  990.     Assert(cbOle < MAX_PATH);
  991.     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, prfs->szFile, cbOle, szOle, cbOle);
  992. #else
  993.     szOle = prfs->szFile;
  994. #endif
  995.  
  996.     if (fModify)
  997.         grfMode = STGM_SHARE_EXCLUSIVE | STGM_READWRITE;
  998.     else
  999.         grfMode = STGM_SHARE_EXCLUSIVE | STGM_READ;
  1000.  
  1001.     hrStg = StgOpenStorage(szOle, NULL, grfMode | STGM_TRANSACTED, NULL,
  1002.             0, &lpstg);
  1003.     if (hrStg != hrSuccess)
  1004.         goto stg_err;
  1005.  
  1006. #ifdef _WIN32
  1007.     cbOle = 1 + lstrlen(szRFSStreamName);
  1008.     Assert(cbOle < MAX_PATH);
  1009.     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szRFSStreamName, cbOle, szOle, cbOle);
  1010. #else
  1011.     szOle = szRFSStreamName;
  1012. #endif
  1013.  
  1014.     hrStg = lpstg->lpVtbl->OpenStream(lpstg, szOle, NULL, grfMode, 0, &lpstream);
  1015.     if (hrStg != hrSuccess)
  1016.         goto stg_err;
  1017.  
  1018.     /* WARNING:  If any code is added between here and the error handler */
  1019.     /* that can fail, a check in the error handler must be added to free */
  1020.     /* the open stream in the event of an error.                         */
  1021.  
  1022.     *lppstream = lpstream;
  1023.     *lppstg = lpstg;
  1024.  
  1025. stg_err:
  1026.     if (hrStg != hrSuccess)
  1027.         hr = ResultFromScode(MapStorageSCode(GetScode(hrStg)));
  1028.  
  1029.     if (hr != hrSuccess)
  1030.         UlRelease(lpstg);
  1031.  
  1032.     DebugTraceResult(OpenRFSStream, hr);
  1033.     return hr;
  1034. }
  1035.  
  1036. /*
  1037.  *  CloseRFSStream
  1038.  *
  1039.  *  Purpose:
  1040.  *      Close the stream within a docfile that holds receive folder
  1041.  *      settings.
  1042.  *
  1043.  *  Arguments:
  1044.  *      lpstream    Pointer to the stream.
  1045.  *      lpstg       Pointer to the storage instance in which this
  1046.  *                  stream resides.
  1047.  *
  1048.  *  Returns:
  1049.  *      void
  1050.  *
  1051.  *  Side effects:
  1052.  *      None.
  1053.  *
  1054.  *  Errors:
  1055.  *      None.
  1056.  */
  1057. static void CloseRFSStream(IStream *lpstream, LPSTORAGE lpstg)
  1058. {
  1059.     AssertSz(lpstream, "Bad lpstream");
  1060.     AssertSz(lpstg, "Bad lpstg");
  1061.  
  1062.     NFSideAssertSz(UlRelease(lpstream) == 0L, "lpstream not released");
  1063.     NFSideAssertSz(UlRelease(lpstg) == 0L, "lpstg not released");
  1064.  
  1065.     return;
  1066. }
  1067.