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

  1. /*
  2.  *  M S P T B L . C
  3.  *
  4.  *  Functions to manage a hierarchy and contents tables cached on disk.
  5.  *
  6.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  7.  */
  8.  
  9. #include "msp.h"
  10.  
  11. static SCODE ScCreateFile(LPTSTR szFile, ULONG ulAccess, ULONG ulShare,
  12.     ULONG ulCreate, HANDLE * lphFile);
  13. static HRESULT HrOpenTblFileRetry(LPTSTR szFile, ULONG ulAccess, ULONG ulShare,
  14.     ULONG ulCreate, HANDLE * lphFile);
  15. static HRESULT HrNewCounts(PIFLD pifld, LPTABLEDATA lptbl);
  16. static HRESULT HrReadBytes(HANDLE hFile, LPVOID lpBuffer, ULONG cbToRead,
  17.     BOOL * pfEOF);
  18. static HRESULT HrWriteBytes(HANDLE hFile, LPVOID lpBuffer, ULONG cbToWrite);
  19. static HRESULT HrWriteRow(HANDLE hFile, LPSRow prw);
  20. static VOID TranslateFileError(BOOL fSuccess, ULONG cbIn, ULONG cbOut,
  21.     BOOL * pfEOF, SCODE * pscFile);
  22. static HRESULT HrGetTime(LPSRow prw, FILETIME * pfiletime);
  23. static HRESULT HrRemoveBadTableRows(LPTABLEDATA lptbl, PIFLD pifldParent,
  24.     PIMS pims, BOOL * pfTableChanged);
  25. static HRESULT HrAddMissingTableRows(LPTABLEDATA lptbl, PIFLD pifld,
  26.     LPSTR szTemplate, LPSPropTagArray ptaga, BOOL * pfTableChanged);
  27.  
  28.  
  29.  
  30. /* format of a row of table data on disk (DRW) */
  31. typedef struct _DRW
  32. {
  33.     ULONG   cbRow;
  34.     SRow    rw;
  35.     BYTE    ab[MAPI_DIM];
  36. } DRW, *PDRW;
  37.  
  38. #define CbNewDRW(_cb)       (offsetof(DRW,ab) + (_cb))
  39. #define CbDRW(_pdrw)        (offsetof(DRW,ab) + (UINT)((_pdrw)->cbRow))
  40.  
  41.  
  42. /****************************************************************************
  43.  * ScCreateFile
  44.  *
  45.  * Purpose      Open or create a file
  46.  *
  47.  * Parameters
  48.  *  szFile          name of the file
  49.  *  ulAccess        read/write access desired see CreateFile
  50.  *  ulShare         sharing desired see CreateFile
  51.  *  ulCreate        creation disposition see CreateFile
  52.  *  lphFile         returns handle of open file, undefined if call fails
  53.  *
  54.  */
  55. static SCODE 
  56. ScCreateFile(LPTSTR szFile, ULONG ulAccess, ULONG ulShare,
  57.     ULONG ulCreate, HANDLE * lphFile)
  58. {
  59.     HANDLE hFile;
  60.     SCODE sc = S_OK;
  61.  
  62.     hFile = CreateFile(szFile, ulAccess, ulShare, NULL,
  63.         ulCreate, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  64.  
  65.     if (hFile == INVALID_HANDLE_VALUE)
  66.     {
  67.         switch (GetLastError())
  68.         {
  69.         case ERROR_FILE_NOT_FOUND:
  70.             sc = MAPI_E_NOT_FOUND;
  71.             break;
  72.  
  73.         case ERROR_SHARING_VIOLATION:
  74.         case ERROR_LOCK_VIOLATION:
  75.             sc = MAPI_E_BUSY;
  76.             break;
  77.  
  78.         default:
  79.             sc = MAPI_E_NO_ACCESS;
  80.             break;
  81.         }
  82.     }
  83.     else
  84.     {
  85.         AssertSz(!IsBadWritePtr(lphFile, sizeof(HANDLE)), "Bad parameter"
  86.             " (lphFile) given to ScCreateFile");
  87.  
  88.         *lphFile = hFile;
  89.     }
  90.  
  91.     DebugTraceSc(ScCreateFile, sc);
  92.     return sc;
  93. }
  94.  
  95. /*
  96.  * HrOpenTblFileRetry
  97.  *
  98.  * Purpose      Open or create a file, but retry if it is busy
  99.  *
  100.  * Parameters
  101.  *  szFile          name of the file
  102.  *  ulAccess        read/write access desired see CreateFile
  103.  *  ulShare         sharing desired see CreateFile
  104.  *  ulCreate        creation disposition see CreateFile
  105.  *  lphFile         returns handle of open file, undefined if call fails
  106.  *
  107.  */
  108. static HRESULT HrOpenTblFileRetry(LPTSTR szFile, ULONG ulAccess, ULONG ulShare,
  109.     ULONG ulCreate, HANDLE * lphFile)
  110. {
  111.     UINT iRetry;
  112.     SCODE sc = S_OK;
  113.     HANDLE hFile;
  114.  
  115.     iRetry = 0;
  116.     while (TRUE)
  117.     {
  118.         sc = ScCreateFile(szFile, ulAccess, ulShare, ulCreate, &hFile);
  119.  
  120.         if (sc != MAPI_E_BUSY || ++iRetry >= NUM_RETRIES)
  121.             break;
  122.  
  123.         Sleep(500);
  124.     }
  125.  
  126.     if (sc == S_OK)
  127.         *lphFile = hFile;
  128.  
  129.     #ifdef DEBUG
  130.     if (iRetry >= NUM_RETRIES)
  131.         TraceSz("HrOpenTblFileRetry: Failing open. Too many tries.");
  132.     #endif
  133.  
  134.     DebugTraceSc(HrOpenTblFileRetry, sc);
  135.     return ResultFromScode(sc);
  136. }
  137.  
  138. /*
  139.  * HrWriteCounts
  140.  *
  141.  *  Purpose
  142.  *      This function writes the counts of messages and unread messages
  143.  *      in a folder to the folder's property file. After the code 
  144.  *      validates what the counts should be, this function writes the
  145.  *      counts so that they are correct.
  146.  *
  147.  *  Parameters
  148.  *      pifld: A pointer to the folder object to update.
  149.  *      ulMsgs: The number of messages in the folder.
  150.  *      ulUnread: The number of unread messages in the folder.
  151.  *
  152.  */
  153. static HRESULT HrWriteCounts(PIFLD pifld, ULONG ulMsgs, ULONG ulUnread)
  154. {
  155.     HRESULT hr;
  156.     LPMESSAGE lpmsg = NULL;
  157.     PLMR plmr = &pifld->pims->lmr;
  158.     ULONG ulMsgsCopy = ulMsgs;      /* workaround - MS C code-gen bug */
  159.  
  160.     hr = HrOpenPropertyMessageRetry(pifld->peid, pifld->pims, TRUE, &lpmsg);
  161.     if (hr != hrSuccess)
  162.         goto exit;
  163.  
  164.     hr = HrSetOneROProp(lpmsg, plmr, PR_CONTENT_COUNT, &ulMsgsCopy);
  165.     if (hr != hrSuccess)
  166.         goto exit;
  167.  
  168.     hr = HrSetOneROProp(lpmsg, plmr, PR_CONTENT_UNREAD, &ulUnread);
  169.     if (hr != hrSuccess)
  170.         goto exit;
  171.  
  172.     hr = lpmsg->lpVtbl->SaveChanges(lpmsg, FORCE_SAVE);
  173.  
  174. exit:
  175.     UlRelease(lpmsg);
  176.  
  177.     DebugTraceResult(HrWriteCounts, hr);
  178.     return hr;
  179. }
  180.  
  181. /*
  182.  * HrNewCounts
  183.  *
  184.  * Purpose  Make the contents count and unread count of the folder in pifld
  185.  *          agree with the data in the contents table lptbl
  186.  *
  187.  * Parameters
  188.  *      pifld       the folder
  189.  *      lptbl       the table
  190.  */
  191. static HRESULT HrNewCounts(PIFLD pifld, LPTABLEDATA lptbl)
  192. {
  193.     HRESULT hr = hrSuccess;
  194.     ULONG cMessages = 0;
  195.     ULONG cUnread = 0;
  196.     LPSRow lpsRow = NULL;
  197.     ULONG ulRow = 0;
  198.     PLMR plmr = &pifld->pims->lmr;
  199.     LPMESSAGE lpmsg = NULL;
  200.  
  201.     /* check each row in the table to see if the message has been read */
  202.  
  203.     while (TRUE)
  204.     {
  205.         LPSPropValue pval;
  206.         LPSPropValue pvalMax;
  207.  
  208.         hr = lptbl->lpVtbl->HrEnumRow(lptbl, ulRow++, &lpsRow);
  209.         if (hr != hrSuccess)
  210.             goto exit;
  211.  
  212.         if (lpsRow == NULL)
  213.             break;
  214.  
  215.         cMessages++;
  216.  
  217.         pval = lpsRow->lpProps;
  218.         pvalMax = pval + lpsRow->cValues;
  219.  
  220.         /* check PR_MESSAGE_FLAGS to see if this message is unread */
  221.         while (pval < pvalMax)
  222.         {
  223.             if (pval->ulPropTag == PR_MESSAGE_FLAGS)
  224.             {
  225.                 if (!(pval->Value.l & MSGFLAG_READ))
  226.                     cUnread++;
  227.                 break;
  228.             }
  229.             pval++;
  230.         }
  231.  
  232.         LMFree(&pifld->pims->lmr, lpsRow);
  233.         lpsRow = NULL;
  234.     }
  235.  
  236.     hr = HrWriteCounts(pifld, cMessages, cUnread);
  237.  
  238. exit:
  239.     LMFree(&pifld->pims->lmr, lpsRow);
  240.  
  241.     DebugTraceResult(HrNewCounts, hr);
  242.     return hr;
  243. }
  244.  
  245. /**********************************************************************
  246.  * HrGetTableName
  247.  *
  248.  * Purpose
  249.  *      Given an open object and the kind of table
  250.  *      returns the full path name of the file that caches that table's data.
  251.  *      Must be freed with FreeNull.
  252.  *
  253.  * Parameters
  254.  *  pobj        object whose table is needed
  255.  *  szEIDPath   EID path (may be NULL)
  256.  *  szFileName  Name of the file that holds the table.
  257.  *  lppszTable  pointer to storage for the path name of the table file
  258.  */
  259. HRESULT HrGetTableName(POBJ pobj, LPSTR szEIDPath, LPSTR szFileName,
  260.     LPSTR *pszTable)
  261. {
  262.     LPTSTR szDir = NULL;        /* Full Path name of directory containing table */
  263.     HRESULT hr = hrSuccess;
  264.     PIMS pims = pobj->pims;
  265.  
  266.     Assert(!IsBadWritePtr(pszTable, sizeof(LPTSTR)));
  267.  
  268.     hr = HrFullPathName(pims->szStorePath, szEIDPath, NULL, &szDir);
  269.     if (hr != hrSuccess)
  270.         goto exit;
  271.  
  272.     /* get memory to hold the filename that contains the table. */
  273.     /* Add 1 char for the backslash before the filename. */
  274.  
  275.     hr = HrAlloc(CCH_NAME * sizeof(TCHAR)
  276.         + ((lstrlen(szDir) + 1) * sizeof(TCHAR)), (PPV) pszTable);
  277.     if (hr != hrSuccess)
  278.         goto exit;
  279.  
  280.     lstrcpy(*pszTable, szDir);
  281.     lstrcat(*pszTable, "\\");
  282.     lstrcat(*pszTable, szFileName);
  283.  
  284. exit:
  285.     FreeNull(szDir);
  286.     DebugTraceResult(HrGetTableName, hr);
  287.     return hr;
  288. }
  289.  
  290. /**********************************************************************
  291.  * HrReadBytes
  292.  *
  293.  * Read a number of bytes from a file into a buffer, checking for errors and
  294.  * end-of-file.
  295.  *
  296.  * Parameters:
  297.  *
  298.  * hFile:       File handle to read from.
  299.  * lpBuffer:    Pointer to a buffer to read into. The buffer should be big
  300.  *              enough to hold the incoming data.
  301.  * cbToRead:    The number of bytes to read. Should be <= UINT_MAX.
  302.  * pfEOF:       A pointer to the location to return a boolean specifying
  303.  *              whether the read encountered an end-of-file. If it did, the
  304.  *              buffer will return with no data in it. This pointer may be NULL,
  305.  *              in which case end-of-file will be treated as an error.
  306.  *
  307.  * Returns:
  308.  *  HRESULT     (either a file read error, or MAPI_E_CALL_FAILED when either
  309.  *              EOF is encountered and pfEOF is NULL, or when the number of
  310.  *              bytes read is less than the number requested).
  311.  */
  312. static HRESULT HrReadBytes(HANDLE hFile, LPVOID lpBuffer, ULONG cbToRead,
  313.     BOOL *pfEOF)
  314. {
  315.     SCODE sc = S_OK;
  316.     ULONG cbRead = 0;
  317.     BOOL fSuccess;
  318.  
  319.     AssertSz(cbToRead <= UINT_MAX && !IsBadWritePtr(lpBuffer, (UINT) cbToRead),
  320.         "Bad buffer");
  321.  
  322.     fSuccess = ReadFile(hFile, lpBuffer, cbToRead, &cbRead, NULL);
  323.  
  324.     TranslateFileError(fSuccess, cbToRead, cbRead, pfEOF, &sc);
  325.  
  326.     DebugTraceSc(HrReadBytes, sc);
  327.     return ResultFromScode(sc);
  328. }
  329.  
  330. /**********************************************************************
  331.  * HrWriteBytes
  332.  *
  333.  * Write a number of bytes from a buffer into a file, checking for errors.
  334.  *
  335.  * Parameters:
  336.  *
  337.  * hFile:       File handle to write into.
  338.  * lpBuffer:    Pointer to a buffer containing the data to write.
  339.  * cbToRead:    The number of bytes to write. Should be <= UINT_MAX.
  340.  *
  341.  * Returns:
  342.  *  HRESULT     (either a file write error, or MAPI_E_CALL_FAILED when the
  343.  *              number of bytes written is less than the number requested).
  344.  */
  345. static HRESULT HrWriteBytes(HANDLE hFile, LPVOID lpBuffer, ULONG cbToWrite)
  346. {
  347.     SCODE sc = S_OK;
  348.     ULONG cbWritten = 0;
  349.     BOOL fSuccess;
  350.  
  351.     AssertSz(cbToWrite <= UINT_MAX && !IsBadReadPtr(lpBuffer, (UINT) cbToWrite),
  352.         "Bad buffer");
  353.  
  354.     fSuccess = WriteFile(hFile, lpBuffer, cbToWrite, &cbWritten, NULL);
  355.  
  356.     TranslateFileError(fSuccess, cbToWrite, cbWritten, NULL, &sc);
  357.  
  358.     DebugTraceSc(HrWriteBytes, sc);
  359.     return ResultFromScode(sc);
  360. }
  361.  
  362. /**********************************************************************
  363.  * TranslateFileError
  364.  *
  365.  * Checks for errors from a windows ReadFile or WriteFile call, translating
  366.  * windows error codes to MAPI errors, and, if requested, checking for end-of-
  367.  * file. Also checks to make sure the number of bytes read or written equals
  368.  * the number given as input.
  369.  *
  370.  * Parameters:
  371.  *
  372.  * fSuccess:    The return value from ReadFile or WriteFile.
  373.  * cbIn:        The number of bytes given as input to ReadFile or WriteFile.
  374.  * cbOut:       The number of bytes returned from ReadFile or WriteFile.
  375.  * pfEOF:       A pointer to the location to return a BOOL specifying
  376.  *              whether cbOut was 0 and fSuccess was TRUE. This condition
  377.  *              indicates end-of-file when reading, and should not be treated
  378.  *              as an error. The pointer may be NULL, in which case, this
  379.  *              condition is treated as an error, and *psc is returned with
  380.  *              MAPI_E_CALL_FAILED.
  381.  * psc:         A pointer to the location to return an SCODE indicating
  382.  *              the success or failure of the ReadFile or WriteFile.
  383.  *
  384.  * Returns: VOID
  385.  */
  386. static VOID TranslateFileError(BOOL fSuccess, ULONG cbIn, ULONG cbOut,
  387.     BOOL *pfEOF, SCODE *pscFile)
  388. {
  389.     SCODE sc = S_OK;
  390.     BOOL fEOF = FALSE;
  391.     BOOL fCheckEOF = (pfEOF != NULL);
  392.  
  393.     if (!fSuccess)
  394.     {
  395.         DWORD dwError = GetLastError();
  396.  
  397.         if (dwError != 0)
  398.  
  399.             switch (dwError)
  400.             {
  401.             case 0:
  402.                 TraceSz("SampleMS: ScTranslateFileError found unexpected "
  403.                     "success\n");
  404.                 break;
  405.  
  406.             case ERROR_SHARING_VIOLATION:
  407.             case ERROR_LOCK_VIOLATION:
  408.                 sc = MAPI_E_BUSY;
  409.                 break;
  410.  
  411.             case ERROR_FILE_NOT_FOUND:
  412.                 sc = MAPI_E_NOT_FOUND;
  413.                 break;
  414.  
  415.             case ERROR_TOO_MANY_OPEN_FILES:
  416.                 sc = MAPI_E_NOT_ENOUGH_RESOURCES;
  417.                 break;
  418.  
  419.             case ERROR_ACCESS_DENIED:
  420.             case ERROR_INVALID_ACCESS:
  421.             case ERROR_INVALID_DRIVE:
  422.                 sc = MAPI_E_NO_ACCESS;
  423.                 break;
  424.  
  425.             default:
  426.                 sc = MAPI_E_CALL_FAILED;
  427.                 break;
  428.             }
  429.     }
  430.     else if (cbOut != cbIn)
  431.     {
  432.         sc = MAPI_E_CALL_FAILED;
  433.  
  434.         /* If the caller wants to discriminate end-of-file from other */
  435.         /* errors, and the file call returned zero bytes, return EOF, */
  436.         /* and don't return an error. */
  437.  
  438.         if (fCheckEOF && cbOut == 0)
  439.         {
  440.             sc = S_OK;
  441.             fEOF = TRUE;
  442.         }
  443.     }
  444.  
  445.     AssertSz(!IsBadWritePtr(pscFile, sizeof(SCODE)), "Bad parameter (pscFile) "
  446.         "given to TranslateFileError");
  447.  
  448.     *pscFile = sc;
  449.  
  450.     if (pfEOF)
  451.     {
  452.         AssertSz(!IsBadWritePtr(pfEOF, sizeof(BOOL)), "Bad parameter (pfEOF) "
  453.             "given to TranslateFileError");
  454.         *pfEOF = fEOF;
  455.     }
  456.  
  457.     return;
  458. }
  459.  
  460. /*
  461.  * HrWriteRow
  462.  *
  463.  *  Purpose
  464.  *      Writes one row of data to the file handle given.
  465.  *
  466.  *  Arguments
  467.  *      hFile: File handle to update.
  468.  *      prw: Pointer to the row to write.
  469.  *
  470.  *  RETURNS: HRESULT
  471.  */
  472. static HRESULT HrWriteRow(HANDLE hFile, LPSRow prw)
  473. {
  474.     HRESULT hr = hrSuccess;
  475.     SCODE sc;
  476.     ULONG cb;
  477.     PDRW pdrw = NULL;
  478.  
  479.     sc = ScCountProps((UINT) prw->cValues, prw->lpProps, &cb);
  480.     if (sc != S_OK)
  481.     {
  482.         hr = ResultFromScode(sc);
  483.         goto exit;
  484.     }
  485.  
  486.     /* Allocate space for the disk row */
  487.     hr = HrAlloc(CbNewDRW(cb), &pdrw);
  488.     if (hr != hrSuccess)
  489.         goto exit;
  490.  
  491.     /* fill in the disk row structure */
  492.  
  493.     pdrw->cbRow = cb;
  494.     pdrw->rw.cValues = prw->cValues;
  495.  
  496.     /* The lpProps field of the row we write should be point at the */
  497.     /* row we are writing (i.e., pdrw->ab). We will use that pointer */
  498.     /* value when we read the data off disk to fixup the pointers via */
  499.     /* ScRelocProps. */
  500.     pdrw->rw.lpProps = (LPSPropValue) pdrw->ab;
  501.  
  502.     sc = ScCopyProps((UINT) prw->cValues, prw->lpProps, &(pdrw->ab), NULL);
  503.     if (sc != S_OK)
  504.     {
  505.         hr = ResultFromScode(sc);
  506.         goto exit;
  507.     }
  508.  
  509.     /* write the row */
  510.     hr = HrWriteBytes(hFile, pdrw, CbDRW(pdrw));
  511.     if (hr != hrSuccess)
  512.         goto exit;
  513.  
  514. exit:
  515.     FreeNull(pdrw);
  516.     
  517.     DebugTraceResult(HrWriteRow, hr);
  518.     return hr;
  519. }
  520.  
  521. /*************************************************************************
  522.  * HrWriteTableOnDisk
  523.  *
  524.  * Purpose
  525.  *      write the table in lptbl to the disk image of lpvObjects table
  526.  *      of type ulType
  527.  *
  528.  *  lptbl       Table to be written
  529.  *  pobj        object whose table is to be written
  530.  *  szEIDPath   Pathname of the EID of the folder, or NULL
  531.  *  szFileName  Name of the file that holds this table type.
  532.  *
  533.  */
  534. HRESULT HrWriteTableOnDisk(LPTABLEDATA lptbl, POBJ pobj, LPSTR szEIDPath,
  535.     LPSTR szFileName)
  536. {
  537.     LPTSTR szFile = NULL;
  538.     HANDLE hFile = INVALID_HANDLE_VALUE; /* handle to open file */
  539.     LPSRow lpsRow = NULL;               /* next row to be written */
  540.     ULONG ulRowNumber;          /* number of the row being written */
  541.     HRESULT hr = hrSuccess;
  542.     PLMR plmr = &pobj->pims->lmr;
  543.  
  544.     /* get the name of the file holding the table */
  545.     hr = HrGetTableName(pobj, szEIDPath, szFileName, &szFile);
  546.     if (hr != hrSuccess)
  547.         goto exit;
  548.  
  549.     /* open the file with exclusive access */
  550.  
  551.     hr = HrOpenTblFileRetry(szFile, GENERIC_WRITE, 0L, CREATE_ALWAYS, &hFile);
  552.     if (hr != hrSuccess)
  553.         goto exit;
  554.  
  555.     /* write the table data to the file */
  556.     ulRowNumber = 0;
  557.     while (TRUE)
  558.     {
  559.         hr = lptbl->lpVtbl->HrEnumRow(lptbl, ulRowNumber, &lpsRow);
  560.         if (hr != hrSuccess)
  561.             goto exit;
  562.         if (lpsRow == NULL)
  563.             break;
  564.  
  565.         hr = HrWriteRow(hFile, lpsRow);
  566.         if (hr != hrSuccess)
  567.             goto exit;
  568.  
  569.         LMFree(plmr, lpsRow);
  570.         lpsRow = NULL;
  571.         ulRowNumber++;
  572.     }
  573.  
  574. exit:
  575.     AssertSz(GetScode(hr) != MAPI_W_ERRORS_RETURNED,
  576.         "Unexpected warning return");
  577.  
  578.     /* Set the end of file marker, in case we shrank the file. */
  579.     if (hr == hrSuccess && SetEndOfFile(hFile) == FALSE)
  580.         hr = ResultFromScode(MAPI_E_DISK_ERROR);
  581.  
  582.     if (hFile != INVALID_HANDLE_VALUE)
  583.         CloseHandle(hFile);
  584.  
  585.     /* erase the file if in error */
  586.     if (hr != hrSuccess && szFile)
  587.         DeleteFile(szFile);
  588.  
  589.     FreeNull(szFile);
  590.     LMFree(plmr, lpsRow);
  591.  
  592.     DebugTraceResult(HrWriteTableOnDisk, hr);
  593.     return hr;
  594. }
  595.  
  596. /*************************************************************************
  597.  * HrReadTableFromDisk
  598.  *
  599.  * Purpose
  600.  *      Read a table from this disk cache into the table lptbl
  601.  *
  602.  *  lptbl       the table to be built
  603.  *  pobj        object whose table is to be read
  604.  *  szEIDPath   Pathname of the EID of the folder, or NULL
  605.  *  cCols       Number of columns in this type of table
  606.  *  szFileName  Name of the file that holds this table type.
  607.  */
  608. HRESULT HrReadTableFromDisk(LPTABLEDATA lptbl, POBJ pobj, LPSTR szEIDPath,
  609.     ULONG cCols, LPSTR szFileName)
  610. {
  611.     HRESULT hr;
  612.     PLMR    plmr;
  613.     BOOL    fBadTableData   = FALSE;
  614.     LPTSTR  szFile          = NULL;
  615.     HANDLE  hFile           = INVALID_HANDLE_VALUE;
  616.     PDRW    pdrw            = NULL;
  617.  
  618.     plmr = &pobj->pims->lmr;
  619.  
  620.     /* get the name of the file holding the table */
  621.     hr = HrGetTableName(pobj, szEIDPath, szFileName, &szFile);
  622.     if (hr != hrSuccess)
  623.         goto exit;
  624.  
  625.     /* open the file */
  626.     hr = HrOpenTblFileRetry(szFile, GENERIC_READ, FILE_SHARE_READ,
  627.         OPEN_ALWAYS, &hFile);
  628.     if (hr != hrSuccess)
  629.     {
  630.         /* If the file wasn't found, then simply leave the table empty, */
  631.         /* and return success. */
  632.  
  633.         if (GetScode(hr) == MAPI_E_NOT_FOUND)
  634.             hr = hrSuccess;
  635.  
  636.         goto exit;
  637.     }
  638.  
  639.     while (TRUE)
  640.     {
  641.         BOOL fEOF;
  642.         DRW drwTemp;
  643.         ULONG cbOut;
  644.         SCODE sc;
  645.  
  646.         /* Read the beginning of the disk row. */
  647.         hr = HrReadBytes(hFile, &drwTemp, CbNewDRW(0), &fEOF);
  648.         if (hr != hrSuccess)
  649.             goto exit;
  650.  
  651.         if (fEOF)
  652.             break;
  653.  
  654.         /* Sanity check for bad data. */
  655.         /* Note that the number of columns in the disk version of the */
  656.         /* table can be less than the number of columns in the in-memory */
  657.         /* table due to properties that were missing from the message */
  658.         /* when it was written to disk. */
  659.  
  660.         if (    drwTemp.rw.cValues > cCols
  661.             ||  drwTemp.rw.cValues == 0
  662.             ||  drwTemp.cbRow < drwTemp.rw.cValues * sizeof(SPropValue))
  663.         {
  664.             hr = ResultFromScode(MAPI_E_CORRUPT_DATA);
  665.             fBadTableData = TRUE;
  666.             goto exit;
  667.         }
  668.  
  669.         hr = HrAlloc(CbNewDRW(drwTemp.cbRow), &pdrw);
  670.         if (hr != hrSuccess)
  671.             goto exit;
  672.  
  673.         memcpy(pdrw, &drwTemp, CbNewDRW(0));
  674.  
  675.         /* read the rest of the row */
  676.         hr = HrReadBytes(hFile, &pdrw->ab, pdrw->cbRow, NULL);
  677.         if (hr != hrSuccess)
  678.         {
  679.             fBadTableData = TRUE;
  680.             goto exit;
  681.         }
  682.  
  683.         sc = ScRelocProps((UINT) pdrw->rw.cValues, (LPSPropValue) pdrw->ab,
  684.             pdrw->rw.lpProps, &pdrw->ab, &cbOut);
  685.         if (sc != S_OK || cbOut != pdrw->cbRow)
  686.         {
  687.             hr = ResultFromScode(MAPI_E_CORRUPT_DATA);
  688.             fBadTableData = TRUE;
  689.             goto exit;
  690.         }
  691.  
  692.         pdrw->rw.lpProps = (LPSPropValue) pdrw->ab;
  693.  
  694.         /* add this row to the table */
  695.         hr = lptbl->lpVtbl->HrModifyRow(lptbl, &pdrw->rw);
  696.         if (hr != hrSuccess)
  697.             goto exit;
  698.  
  699.         FreeNull(pdrw);
  700.         pdrw = NULL;
  701.     }
  702.  
  703. exit:
  704.     if (hFile != INVALID_HANDLE_VALUE)
  705.         CloseHandle(hFile);
  706.  
  707.     /* erase the file if it is bogus. We will regenerate it if we can. */
  708.     if (fBadTableData)
  709.         DeleteFile(szFile);
  710.  
  711.     FreeNull(szFile);
  712.     FreeNull(pdrw);
  713.  
  714.     DebugTraceResult(HrReadTableFromDisk, hr);
  715.     return hr;
  716. }
  717.  
  718. /************************************************************************
  719.  * HrGetTime
  720.  *
  721.  * Purpose  return the value of PR_LAST_MODIFICATION_TIME from the properties
  722.  *          in the given property array
  723.  *
  724.  * Parameters
  725.  *  prw         pointer to the table row to search
  726.  *  pfiletime   pointer to last modification time
  727.  */
  728. static HRESULT HrGetTime(LPSRow prw, FILETIME *pfiletime)
  729. {
  730.     LPSPropValue pvalT;
  731.     LPSPropValue pvalMax;
  732.     SCODE sc = MAPI_E_NOT_FOUND;
  733.  
  734.     Assert(!IsBadReadPtr(prw, sizeof(SRow)));
  735.     Assert(prw->cValues <= UINT_MAX / sizeof(SPropValue));
  736.     Assert(!IsBadReadPtr(prw->lpProps, ((UINT) prw->cValues) * sizeof(SPropValue)));
  737.     Assert(!IsBadWritePtr(pfiletime, sizeof(FILETIME)));
  738.  
  739.     pvalT = prw->lpProps;
  740.     pvalMax = pvalT + prw->cValues;
  741.  
  742.     while (pvalT < pvalMax)
  743.     {
  744.         if (pvalT->ulPropTag == PR_LAST_MODIFICATION_TIME)
  745.         {
  746.             sc = S_OK;
  747.             *pfiletime = pvalT->Value.ft;
  748.             break;
  749.         }
  750.         pvalT++;
  751.     }
  752.  
  753.     DebugTraceSc(HrGetTime, sc);
  754.     return ResultFromScode(sc);
  755. }
  756.  
  757. /*************************************************************************
  758.  * HrSyncOutgoingTable
  759.  *
  760.  * Purpose
  761.  *      Verifies that every table row in memory actually corresponds to
  762.  *      a message on disk. If bad rows are found, removes them and
  763.  *      rewrites the disk version of the table data. Note that this code
  764.  *      does not verify that the message on disk actually is in the queue,
  765.  *      nor does it check for messages that are somehow missing from the table.
  766.  *
  767.  *  lptbl           pointer to the outgoing table data object.
  768.  *  pims            a pointer to the message store object.
  769.  *
  770.  */
  771. HRESULT HrSyncOutgoingTable(LPTABLEDATA lptbl, PIMS pims)
  772. {
  773.     HRESULT hr;
  774.     BOOL fRowsRemoved;
  775.  
  776.     hr = HrRemoveBadTableRows(lptbl, NULL, pims, &fRowsRemoved);
  777.  
  778.     if (hr == hrSuccess && fRowsRemoved)
  779.         hr = HrWriteTableOnDisk(lptbl, (POBJ) pims, NULL, szOutgoingFileName);
  780.  
  781.     DebugTraceResult(HrSyncOutgoingTable, hr);
  782.     return hr;
  783. }
  784.  
  785. /*************************************************************************
  786.  * HrSyncContentsTable
  787.  *
  788.  * Purpose
  789.  *      Verifies that the table in memory agrees with what's on disk.
  790.  *      If discrepancies are found, fixes them, and rewrites the disk
  791.  *      version of the table data.
  792.  *
  793.  *  pifld           the parent folder of the contents table.
  794.  *  fWriteTable     if TRUE, write the disk version of the table if the
  795.  *                  in-memory table is out of sync.
  796.  *
  797.  */
  798. HRESULT HrSyncContentsTable(PIFLD pifld, BOOL fWriteTable)
  799. {
  800.     HRESULT hr;
  801.     BOOL fRowsRemoved;
  802.     BOOL fRowsAdded;
  803.     LPTABLEDATA lptbl;
  804.  
  805.     lptbl = pifld->lptblContents;
  806.  
  807.     hr = HrRemoveBadTableRows(lptbl, pifld, pifld->pims, &fRowsRemoved);
  808.     if (hr != hrSuccess)
  809.         goto exit;
  810.  
  811.     hr = HrAddMissingTableRows(lptbl, pifld, szMessageTemplate,
  812.         (LPSPropTagArray) &sPropTagsContents, &fRowsAdded);
  813.     if (hr != hrSuccess)
  814.         goto exit;
  815.  
  816.     /* update folder's content count and unread count if */
  817.     /* the table was out of ssync */
  818.  
  819.     if (fRowsRemoved || fRowsAdded)
  820.     {
  821.         hr = HrNewCounts(pifld, lptbl);
  822.         if (hr != hrSuccess)
  823.             goto exit;
  824.  
  825.         if (fWriteTable)
  826.         {
  827.             hr = HrWriteTableOnDisk(lptbl, (POBJ) pifld, pifld->peid->szPath,
  828.                 szContentsFileName);
  829.             if (hr != hrSuccess)
  830.                 goto exit;
  831.         }
  832.     }
  833.     else
  834.     {
  835.         /* If there aren't any rows in the table, we still need to
  836.          * update the folder's count of messages, because the counts 
  837.          * may not be zero, even though there aren't any messages on disk.
  838.          */
  839.         LPSRow lpSRow = NULL;
  840.  
  841.         hr = lptbl->lpVtbl->HrEnumRow(lptbl, 0, &lpSRow);
  842.         if (hr != hrSuccess)
  843.             goto exit;
  844.  
  845.         if (lpSRow == NULL)
  846.             hr = HrWriteCounts(pifld, 0, 0);
  847.  
  848.         LMFree(&pifld->pims->lmr, lpSRow);
  849.     }
  850.  
  851. exit:
  852.     DebugTraceResult(HrSyncContentsTable, hr);
  853.     return hr;
  854. }
  855.  
  856. /*************************************************************************
  857.  * HrRemoveBadTableRows
  858.  *
  859.  * Purpose
  860.  *      Verifies that every row in the table given has a corresponding
  861.  *      file on disk. If the file on disk does not exist, the routine
  862.  *      removes the row from the table.
  863.  *
  864.  *  lptbl           pointer to table
  865.  *  pifldParent     the folder that all objects should be in. May be NULL.
  866.  *  pims            the message store object
  867.  *  pfTableChanged  Pointer to a location to return a BOOL that, when TRUE,
  868.  *                  indicates that a bad row was found, and the table was changed
  869.  *
  870.  */
  871. static HRESULT HrRemoveBadTableRows(LPTABLEDATA lptbl, PIFLD pifldParent,
  872.     PIMS pims, BOOL *pfTableChanged)
  873. {
  874.     HRESULT hr = hrSuccess;
  875.     LPSRow lpSRow = NULL;
  876.     BOOL fTableChanged = FALSE; /* TRUE if lptbl was changed */
  877.     ULONG iRow = 0;
  878.  
  879.     while (TRUE)
  880.     {
  881.         LPSTR szPath;
  882.         BOOL fIsParent = TRUE;
  883.         PEID peid;
  884.  
  885.         hr = lptbl->lpVtbl->HrEnumRow(lptbl, iRow++, &lpSRow);
  886.  
  887.         if (hr != hrSuccess || lpSRow == NULL)
  888.             break;
  889.  
  890.         peid = (PEID) lpSRow->lpProps->Value.bin.lpb;
  891.  
  892.         /* If the caller gave us a parent folder, verify that the message */
  893.         /* is in that folder. For example, if the caller is checking the */
  894.         /* contents table, we can check that all messages are in the folder */
  895.         /* they should be, but for the outgoing queue table, we can't. */
  896.  
  897.         if (pifldParent)
  898.         {
  899.             hr = HrIsParent(pifldParent->peid, peid, &fIsParent);
  900.             if (hr != hrSuccess)
  901.                 goto exit;
  902.         }
  903.  
  904.         hr = HrFullPathName(pims->szStorePath, peid->szPath, NULL, &szPath);
  905.         if (hr != hrSuccess)
  906.             goto exit;
  907.  
  908.         /* Delete the row if it no longer exists on disk */
  909.         /* or isn't in the correct folder */
  910.         if ((GetFileAttributes(szPath) == -1) || !fIsParent)
  911.         {
  912.             HRESULT hrT;
  913.  
  914.             hrT = lptbl->lpVtbl->HrDeleteRow(lptbl, lpSRow->lpProps);
  915.  
  916.             if (hrT != hrSuccess)
  917.             {
  918.                 TraceSz1("SampleMS: HrSyncTableWithDisk: error %s "
  919.                     "deleting out-of-date table entry\n",
  920.                     SzDecodeScode(GetScode(hrT)));
  921.             }
  922.  
  923.             fTableChanged = TRUE;
  924.         }
  925.  
  926.         LMFree(&pims->lmr, lpSRow);
  927.         lpSRow = NULL;
  928.         FreeNull(szPath);
  929.     }
  930.  
  931.     AssertSz(!IsBadWritePtr(pfTableChanged, sizeof(BOOL)), "Bad parameter"
  932.         " (pfTableChanged) given to HrRemoveBadTableRows");
  933.     *pfTableChanged = fTableChanged;
  934.  
  935. exit:
  936.     LMFree(&pims->lmr, lpSRow);
  937.     DebugTraceResult(HrRemoveBadTableRows, hr);
  938.     return hr;
  939. }
  940.  
  941. /*************************************************************************
  942.  * HrAddMissingTableRows
  943.  *
  944.  * Purpose
  945.  *      Searches a folder for objects of the specified type, and verifies
  946.  *      that each object exists in the table given, and is up-to-date.
  947.  *      If the object does not exist or is out-of-date, the routine
  948.  *      adds or updates the table row with current information from the object.
  949.  *      This routine only works for contents tables currently.
  950.  *
  951.  *  lptbl           pointer to table
  952.  *  pifld           the folder that all objects should be in.
  953.  *  szTemplate      type of files to search for for this table type
  954.  *  ptaga           list of proptags for this type of table
  955.  *  pfTableChanged  Pointer to a location to return a BOOL that, when TRUE,
  956.  *                  indicates that the table was changed to match the disk
  957.  *
  958.  */
  959. static HRESULT HrAddMissingTableRows(LPTABLEDATA lptbl, PIFLD pifld,
  960.     LPSTR szTemplate, LPSPropTagArray ptaga, BOOL * pfTableChanged)
  961. {
  962.     HRESULT hr;
  963.     WIN32_FIND_DATA ffd;
  964.     HANDLE hFindFile = INVALID_HANDLE_VALUE;
  965.     ULONG ulOffset;
  966.     LPTSTR szFile = NULL;       /* full path name of next file in the table */
  967.     PEID peid = NULL;
  968.     BOOL fTableChanged = FALSE;
  969.     LPSRow lpSRow = NULL;
  970.     SPropValue pvInstKey;
  971.     PLMR plmr = &pifld->pims->lmr;
  972.  
  973.     /* for each file of the right kind ( message or folder ) on disk */
  974.     /* make sure its entry in the cache is up to date */
  975.  
  976.     pvInstKey.ulPropTag = PR_INSTANCE_KEY;
  977.  
  978.     /* get the next file */
  979.     hr = HrFindFirstID(pifld, szTemplate, &ulOffset, &szFile, &hFindFile,
  980.         &ffd, &peid);
  981.  
  982.     while (hr == hrSuccess)
  983.     {
  984.         FILETIME ftTableTime;   /* modify time of szFile in lptbl */
  985.  
  986.         /* get its row in the table */
  987.         pvInstKey.Value.bin.cb = CbEID(peid);
  988.         pvInstKey.Value.bin.lpb = (LPBYTE) peid;
  989.  
  990.         hr = lptbl->lpVtbl->HrQueryRow(lptbl, &pvInstKey, &lpSRow, NULL);
  991.  
  992.         if (GetScode(hr) == MAPI_E_NOT_ENOUGH_MEMORY)
  993.             goto exit;
  994.  
  995.         /* update the row if necessary */
  996.         /* store in ftTableTime, the modify time of this object */
  997.         /* as stored in the lptbl */
  998.         if (hr == hrSuccess && lpSRow)
  999.             hr = HrGetTime(lpSRow, &ftTableTime);
  1000.  
  1001.         if (hr != hrSuccess
  1002.             || lpSRow == NULL
  1003.             || CompareFileTime(&ftTableTime, &(ffd.ftLastWriteTime)) == -1)
  1004.         {
  1005.             fTableChanged = TRUE;
  1006.  
  1007.             HrUpdateRow(pifld->pims, lptbl, peid, ptaga, &(ffd.ftLastWriteTime),
  1008.                 MAPI_MESSAGE);
  1009.         }
  1010.  
  1011.         LMFree(plmr, peid);
  1012.         peid = NULL;
  1013.         LMFree(plmr, lpSRow);
  1014.         lpSRow = NULL;
  1015.  
  1016.         hr = HrFindNextID(pifld, ulOffset, szFile, hFindFile, &ffd, &peid);
  1017.     }
  1018.  
  1019.     if (GetScode(hr) == MAPI_E_NOT_FOUND)
  1020.         hr = hrSuccess;
  1021.  
  1022.     if (hr == hrSuccess)
  1023.     {
  1024.         AssertSz(!IsBadWritePtr(pfTableChanged, sizeof(BOOL)), "Bad parameter"
  1025.             " (pfTableChanged) given to HrAddMissingTableRows");
  1026.         *pfTableChanged = fTableChanged;
  1027.     }
  1028.  
  1029. exit:
  1030.     CloseIDSearch(&hFindFile, &szFile);
  1031.     LMFree(plmr, lpSRow);
  1032.     LMFree(plmr, peid);
  1033.  
  1034.     DebugTraceResult(HrAddMissingTableRows, hr);
  1035.     return hr;
  1036. }
  1037.  
  1038.