home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / sample.ab / abctbl1.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  26.6 KB  |  925 lines

  1. /***********************************************************************
  2.  *
  3.  *  ABCTBL1.C
  4.  *
  5.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  6.  *
  7.  *  Contents Table
  8.  *
  9.  *  The contents table associated with this provider.  It's file based.
  10.  *  The format of the .SAB file is in ABP.H.
  11.  *
  12.  *  This implementation of IMAPITable is retrieved by calling the
  13.  *  GetContentsTable() method of the sample ABP's single directory (see
  14.  *  abcont.c).
  15.  *
  16.  *  There are a few interesting features in this implementation.  First
  17.  *  it's implemented on a flat file (.SAB).  Second, it actually supports
  18.  *  an implementation of ANR (Ambiguous Name Resolution).  And lastly, it
  19.  *  supports FindRow (limited to prefix searching on display names).
  20.  *
  21.  *  This is the minimum set of restrictions that your provider MUST support.
  22.  *  It MUST have an ANR implementation and support prefix (downward) searching
  23.  *  on whatever your table is sorted on (in this case PR_DISPLAY_NAME).
  24.  *
  25.  *  The browsing of a flat file is done a record at a time.  It's never
  26.  *  completely read into memory.  It only reads records from the file when
  27.  *  it absolutely has to (like in QueryRows).  The advantage to this is
  28.  *  a low memory footprint and the ability to browse rather large files
  29.  *  with decent performance.
  30.  */
  31. /*
  32.  *  ANR is also supported in this implementation.  In the code will often
  33.  *  be 'if' statements making two different code paths between the restricted
  34.  *  and unrestricted version.  The restrictions rely on a couple of
  35.  *  bit arrays.  Each bit corresponds (in order) to the records in the .SAB
  36.  *  file.  One bit array, rgChecked, says whether or not a record in the
  37.  *  .SAB file has actually been compared to the restriction.  It's initially
  38.  *  set to all 0s - which means that none of the records have been checked.
  39.  *  The second bit array, rgMatched, says whether or not the particular
  40.  *  record matches the current restriction.  This array is initialized to all
  41.  *  1s - which says that all the records in the .SAB file match the current
  42.  *  restriction.  The reason for this is for the fraction returned in
  43.  *  QueryPosition--By assuming that all the rows match and only updating
  44.  *  the array when something doesn't match, the fraction returned in
  45.  *  QueryPosition will get larger and has the effect of making the thumb-bar
  46.  *  on a listbox implemented on top of this table to scroll down as you go
  47.  *  down the list.
  48.  *
  49.  *  As a row is about to be read it is checked to see if it's been compared
  50.  *  to the current restriction.  If it has then to determine whether or not
  51.  *  to actually read the record and build the row we just look at the matched
  52.  *  bit array.  If it doesn't match we go on to the next record and check
  53.  *  again.  If it does match we read the record and build the row.
  54.  *
  55.  *  Only if it hasn't been checked do we actually go and read the row and
  56.  *  compare it to the current restriction, setting the rgChecked and
  57.  *  rgMatched bits accordingly.
  58.  *
  59.  *  In this implementation the ANR comparison rules are as follows:
  60.  *  (See FNameMatch for the actual code to do this)
  61.  *
  62.  *    A 'word' is defined as any substring separated by separators.
  63.  *    The separators are defined as ',', ' ', and '-'.
  64.  *    A restriction is said to match a display name iff all the words
  65.  *    in the restriction are all prefixes of words in the display name.
  66.  *
  67.  *    For example, a restriction of "b d" will match:
  68.  *     "Barney Donovan"
  69.  *     "Donovan, Barney"
  70.  *     "Danielle Blumenthal"
  71.  *    But will not match:
  72.  *     "Darby Opal"
  73.  *     "Ben Masters"
  74.  *
  75.  *  Other providers can do whatever matching they want with the ANR
  76.  *  restriction.  For example, soundex or additional field comparisons (like
  77.  *  email addresses).  A good (and fast) implementation will differentiate
  78.  *  your provider from others in a positive way.
  79.  *
  80.  *  FindRow's implementation effectively handles prefix searching on display
  81.  *  names (PR_DISPLAY_NAME).  It does this by binary searching the .SAB file.
  82.  *  The only tricky part is knowing when to stop.  Basically you want to stop
  83.  *  on the first name in the list that matches the restriction.  It's easy to
  84.  *  do linearly, but a little more trick with a binary search.  This
  85.  *  implementation is a reasonable example.
  86.  *
  87.  *
  88.  *  This table has only the following columns:
  89.  */
  90.  
  91. #include "abp.h"
  92. #include "abctbl.h"
  93. #include "sampabp.rh"
  94.  
  95. const SizedSPropTagArray(cvalvtMax, tagaivtabcColSet) =
  96. {
  97.     cvalvtMax,
  98.     {
  99.         PR_DISPLAY_NAME_A,
  100.         PR_ENTRYID,
  101.         PR_ADDRTYPE_A,
  102.         PR_EMAIL_ADDRESS_A,
  103.         PR_OBJECT_TYPE,
  104.         PR_DISPLAY_TYPE,
  105.         PR_INSTANCE_KEY
  106.     }
  107. };
  108. LPSPropTagArray ptagaivtabcColSet = (LPSPropTagArray) &tagaivtabcColSet;
  109.  
  110. /*
  111.  *
  112.  *  The following routines are implemented in the files abctbl2.c and abctbl3.c:
  113.  *
  114.  *
  115.  *      IVTABC_Release
  116.  *      IVTABC_GetStatus
  117.  *      IVTABC_SetColumns
  118.  *      IVTABC_QueryColumns
  119.  *      IVTABC_GetRowCount
  120.  *      IVTABC_SeekRow
  121.  *      IVTABC_SeekRowApprox
  122.  *      IVTABC_QueryPosition
  123.  *      IVTABC_FindRow
  124.  *      IVTABC_Restrict
  125.  *      IVTABC_CreateBookmark
  126.  *      IVTABC_FreeBookmark
  127.  *      IVTABC_SortTable
  128.  *      IVTABC_QuerySortOrder
  129.  *      IVTABC_QueryRows
  130.  *      IVTABC_Abort
  131.  *      IVTABC_ExpandRow
  132.  *      IVTABC_CollapseRow
  133.  *      IVTABC_WaitForCompletion
  134.  *      IVTABC_GetCollapseState
  135.  *      IVTABC_SetCollapseState
  136.  *
  137.  *
  138.  *  This file (abctbl1.c) has all the utility functions used throughout this
  139.  *  objects methods.  They are the following:
  140.  *
  141.  *      HrNewIVTAbc()
  142.  *      HrValidateEntry()
  143.  *      FChecked()
  144.  *      FMatched()
  145.  *      FNameMatch()
  146.  *      HrOpenFile()
  147.  *      fIVTAbcIdleRoutine()
  148.  *      FreeANRBitmaps()
  149.  *
  150.  *
  151.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  152.  *
  153.  ***********************************************************************/
  154.  
  155. /*
  156.  *  vtbl filled in here.
  157.  */
  158.  
  159. const IVTABC_Vtbl vtblIVTABC =
  160. {
  161.  
  162.     IVTABC_QueryInterface,
  163.     (IVTABC_AddRef_METHOD *)        ROOT_AddRef,
  164.     IVTABC_Release,
  165.     (IVTABC_GetLastError_METHOD *)  ROOT_GetLastError,
  166.     IVTABC_Advise,
  167.     IVTABC_Unadvise,
  168.     IVTABC_GetStatus,
  169.     IVTABC_SetColumns,
  170.     IVTABC_QueryColumns,
  171.     IVTABC_GetRowCount,
  172.     IVTABC_SeekRow,
  173.     IVTABC_SeekRowApprox,
  174.     IVTABC_QueryPosition,
  175.     IVTABC_FindRow,
  176.     IVTABC_Restrict,
  177.     IVTABC_CreateBookmark,
  178.     IVTABC_FreeBookmark,
  179.     IVTABC_SortTable,
  180.     IVTABC_QuerySortOrder,
  181.     IVTABC_QueryRows,
  182.     IVTABC_Abort,
  183.     IVTABC_ExpandRow,
  184.     IVTABC_CollapseRow,
  185.     IVTABC_WaitForCompletion,
  186.     IVTABC_GetCollapseState,
  187.     IVTABC_SetCollapseState
  188. };
  189.  
  190.  
  191. /* Idle function */
  192. FNIDLE fIVTAbcIdleRoutine;
  193.  
  194. #define     IVTABC_IDLE_INTERVAL        300L
  195. #define     IVTABC_IDLE_PRIORITY        -2
  196. #define     IVTABC_IDLE_FLAGS           FIROINTERVAL
  197.  
  198. /*************************************************************************
  199.  *
  200.  -  NewIVTAbc
  201.  -
  202.  *  Creates a new IMAPITable on the contents of a .SAB file.
  203.  *
  204.  *
  205.  */
  206. HRESULT 
  207. HrNewIVTAbc(LPMAPITABLE *       lppIVTAbc,
  208.             LPABLOGON           lpABLogon,
  209.             LPABCONT            lpABC,
  210.             HINSTANCE           hLibrary,
  211.             LPALLOCATEBUFFER    lpAllocBuff,
  212.             LPALLOCATEMORE      lpAllocMore,
  213.             LPFREEBUFFER        lpFreeBuff,
  214.             LPMALLOC            lpMalloc )
  215. {
  216.     LPIVTABC lpIVTAbc = NULL;
  217.     SCODE scode;
  218.     HRESULT hResult = hrSuccess;
  219.     ULONG ulBK, ulSize, ulSizeHigh;
  220.  
  221.     /*
  222.      *  Allocate space for the IVTABC structure
  223.      */
  224.  
  225.     scode = lpAllocBuff(sizeof(IVTABC), (LPVOID *) &lpIVTAbc);
  226.     if (FAILED(scode))
  227.     {
  228.         hResult = ResultFromScode(scode);
  229.         goto err;
  230.     }
  231.  
  232.     lpIVTAbc->lpVtbl = &vtblIVTABC;
  233.     lpIVTAbc->lcInit = 1;
  234.     lpIVTAbc->hResult = hrSuccess;
  235.     lpIVTAbc->idsLastError = 0;
  236.  
  237.     lpIVTAbc->hLibrary = hLibrary;
  238.     lpIVTAbc->lpAllocBuff = lpAllocBuff;
  239.     lpIVTAbc->lpAllocMore = lpAllocMore;
  240.     lpIVTAbc->lpFreeBuff = lpFreeBuff;
  241.     lpIVTAbc->lpMalloc = lpMalloc;
  242.     lpIVTAbc->lpABLogon = lpABLogon;
  243.  
  244.     lpIVTAbc->lpszFileName = NULL;
  245.     hResult = HrLpszGetCurrentFileName(lpABLogon, &(lpIVTAbc->lpszFileName));
  246.     if (HR_FAILED(hResult))
  247.     {
  248.         goto err;
  249.     }
  250.  
  251.     lpIVTAbc->lpPTAColSet = (LPSPropTagArray) &tagaivtabcColSet;
  252.  
  253.     /*
  254.      *  Open the .SAB file associated with this logged in session.
  255.      *  Currently it's opened with FILE_SHARED_READ.  This has an
  256.      *  unpleasant side-effect of not being able to modify the .SAB
  257.      *  file while this table is active.
  258.      *
  259.      *  It should be OF_SHARE_DENY_NONE.  I don't have the code to
  260.      *  handle this in this version.
  261.      */
  262.     if ((lpIVTAbc->hFile = CreateFile(
  263.                 lpIVTAbc->lpszFileName,
  264.                 GENERIC_READ,
  265.                 FILE_SHARE_READ|FILE_SHARE_WRITE,
  266.                 NULL,
  267.                 OPEN_EXISTING,
  268.                 FILE_ATTRIBUTE_NORMAL,
  269.                 NULL)) == INVALID_HANDLE_VALUE)
  270.     {
  271.         /*
  272.          *  The file didn't open...
  273.          */
  274.  
  275.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  276.         SetErrorIDS(lpABC, hResult, IDS_CANT_OPEN_SAB);
  277.  
  278.         goto err;
  279.     }
  280.  
  281.     /*
  282.      *  Get the time and date stamp
  283.      *
  284.      */
  285.     (void)GetFileTime(lpIVTAbc->hFile, NULL, NULL, &(lpIVTAbc->filetime));
  286.  
  287.     /*  Get the size of the file */
  288.     if ((ulSize = GetFileSize(lpIVTAbc->hFile, &ulSizeHigh)) == 0xFFFFFFFF)
  289.     {
  290.         /*
  291.          *  MAYBE I have an error
  292.          */
  293.         if (GetLastError() != NO_ERROR)
  294.         {
  295.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  296.             SetErrorIDS(lpABC, hResult, IDS_SAB_FILE_ATTRIB);
  297.             goto err;
  298.         }
  299.  
  300.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  301.         SetErrorIDS(lpABC, hResult, IDS_SAB_TOO_LARGE);
  302.         goto err;
  303.     }
  304.  
  305.     /*
  306.      *  Actual number of valid positions
  307.      */
  308.     lpIVTAbc->ulMaxPos = (ulSize / sizeof(ABCREC));
  309.  
  310.     /*
  311.      *  Check to see if it's an exact multiple of sizeof(ABCREC)
  312.      */
  313.     if (lpIVTAbc->ulMaxPos * sizeof(ABCREC) != ulSize)
  314.     {
  315.         /*
  316.          *  Nope.
  317.          */
  318.  
  319.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  320.         SetErrorIDS(lpABC, hResult, IDS_SAB_CORRUPT);
  321.         goto err;
  322.     }
  323.  
  324.     lpIVTAbc->ulPosition = 0;
  325.  
  326.     /*  Setup bookmarks  */
  327.     for (ulBK = 0; ulBK < MAX_BOOKMARKS; lpIVTAbc->rglpABCBK[ulBK++] = NULL);
  328.  
  329.     /*
  330.      *  Restriction stuff
  331.      */
  332.  
  333.     lpIVTAbc->rgChecked = NULL;
  334.     lpIVTAbc->rgMatched = NULL;
  335.     lpIVTAbc->lpszPartialName = NULL;
  336.  
  337.     /*
  338.      *  Registered notification stuff
  339.      */
  340.  
  341.     lpIVTAbc->cAdvise = 0;
  342.     lpIVTAbc->parglpAdvise = NULL;
  343.     lpIVTAbc->ulConnectMic = (ULONG) lpIVTAbc & 0xffffff;
  344.     InitializeCriticalSection(&lpIVTAbc->cs);
  345.  
  346.     /*
  347.      *  Register the idle function
  348.      */
  349.  
  350.     lpIVTAbc->ftg = FtgRegisterIdleRoutine(
  351.         fIVTAbcIdleRoutine,
  352.         lpIVTAbc,
  353.         IVTABC_IDLE_PRIORITY,
  354.         IVTABC_IDLE_INTERVAL,
  355.         IVTABC_IDLE_FLAGS);
  356.  
  357.     InitializeCriticalSection(&lpIVTAbc->cs);
  358.  
  359.     /*  We must AddRef the lpABLogon object since we will be using it
  360.      */
  361.     lpABLogon->lpVtbl->AddRef(lpABLogon);
  362.  
  363.     *lppIVTAbc = (LPVOID) lpIVTAbc;
  364.  
  365. out:
  366.  
  367.         
  368.     DebugTraceResult(HrNewIVTAbc, hResult);
  369.     return hResult;
  370.  
  371. err:
  372.     if (lpIVTAbc)
  373.     {
  374.         if (lpIVTAbc->hFile != INVALID_HANDLE_VALUE)
  375.         {
  376.             /*
  377.              *  Must've opened the file
  378.              */
  379.             CloseHandle(lpIVTAbc->hFile);
  380.         }
  381.  
  382.         /*  Free the current .SAB file name */
  383.         lpFreeBuff(lpIVTAbc->lpszFileName);
  384.  
  385.         /*  Free the mem */
  386.         lpFreeBuff( lpIVTAbc );
  387.     }
  388.  
  389.     goto out;
  390.  
  391. }
  392.  
  393. /*************************************************************************
  394.  *
  395.  -  HrValidateEntry
  396.  -
  397.  *
  398.  *  Used in restricted lists
  399.  *  Checks to see if a given entry at a particular location matches
  400.  *  the active restriction.  It always modifies rgChecked, and may
  401.  *  modify rgMatched.
  402.  */
  403. HRESULT 
  404. HrValidateEntry(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  405. {
  406.     ABCREC abcrec;
  407.     ULONG cbRead;
  408.     HRESULT hResult;
  409.  
  410.     /*
  411.      *  Open the file
  412.      */
  413.     hResult = HrOpenFile(lpIVTAbc);
  414.     if (HR_FAILED(hResult))
  415.     {
  416.         DebugTraceResult(HrValidateEntry, hResult);
  417.         return hResult;
  418.     }
  419.  
  420.     /*
  421.      *  Set the file position to lNewPos
  422.      */
  423.  
  424.     (void) SetFilePointer(lpIVTAbc->hFile, ulNewPos * sizeof(ABCREC),
  425.                           NULL, FILE_BEGIN);
  426.  
  427.     /*
  428.      *  Read in the record from the file
  429.      */
  430.     if (!ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
  431.             sizeof(ABCREC), &cbRead, NULL))
  432.     {
  433.         DebugTraceSc(HrValidateEntry, MAPI_E_DISK_ERROR);
  434.         return ResultFromScode(MAPI_E_DISK_ERROR);
  435.     }
  436.  
  437.     /*  Second check  */
  438.     if (cbRead != sizeof(ABCREC))
  439.     {
  440.         DebugTraceSc(HrValidateEntry, MAPI_E_DISK_ERROR);
  441.         return ResultFromScode(MAPI_E_DISK_ERROR);
  442.     }
  443.  
  444.     /*
  445.      *  Always set the Checked flag
  446.      */
  447.     lpIVTAbc->rgChecked[ulNewPos / 8] |= (((unsigned char)0x80) >> (ulNewPos % 8));
  448.  
  449.     /*
  450.      *  See if the rgchDisplayName matches the restriction
  451.      */
  452.     if (!FNameMatch(lpIVTAbc, abcrec.rgchDisplayName))
  453.     {
  454.         /*
  455.          *  Apparently not.  Reset the Matched flag
  456.          */
  457.  
  458.         lpIVTAbc->ulRstrDenom--;
  459.  
  460.         lpIVTAbc->rgMatched[ulNewPos / 8] &= ~(((unsigned char)0x80) >> (ulNewPos % 8));
  461.     }
  462.  
  463.     return hrSuccess;
  464. }
  465.  
  466. /*************************************************************************
  467.  *
  468.  -  FChecked
  469.  -
  470.  *
  471.  *  Returns whether or not an entry has ever been checked
  472.  *  Just looks at the bit in the rgChecked array that corresponds with
  473.  *  lNewPos
  474.  *
  475.  */
  476. BOOL
  477. FChecked(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  478. {
  479.     ULONG ulT = (ULONG) (lpIVTAbc->rgChecked[ulNewPos / 8] & (((unsigned char)0x80) >> (ulNewPos % 8)));
  480.  
  481.     return (BOOL) !!ulT;
  482. }
  483.  
  484. /*************************************************************************
  485.  *
  486.  -  FMatched
  487.  -
  488.  *
  489.  *  Returns whether or not an entry has been matched
  490.  *  Just checks the bit in the rgMatched array corresponding with
  491.  *  lNewPos
  492.  *
  493.  */
  494. BOOL
  495. FMatched(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  496. {
  497.     ULONG ulT = (lpIVTAbc->rgMatched[ulNewPos / 8] & (((unsigned char)0x80) >> (ulNewPos % 8)));
  498.  
  499.     return (BOOL) !!ulT;
  500.  
  501. }
  502.  
  503. /*************************************************************************
  504.  *
  505.  -  FNameMatch
  506.  -
  507.  *  Tries to match the rgchDisplayName with the partial name of the
  508.  *  restriction.
  509.  *  It tries to prefix match each partial name component (i.e. word) with
  510.  *  each rgchDisplayName name component (i.e. word).  Only if all of them
  511.  *  match (from the partial name) does this return TRUE.
  512.  */
  513. BOOL
  514. FCharInString(LPSTR lpsz, CHAR ch);
  515.  
  516.  
  517. BOOL
  518. FNameMatch(LPIVTABC lpIVTAbc, LPSTR rgchDisplayName)
  519. {
  520.     LPSTR szANRSep = ", -";
  521.     LPSTR szANR = lpIVTAbc->lpszPartialName;
  522.     LPSTR pchEndSzANR = szANR + lstrlenA(szANR);
  523.     ULONG cchANRName;
  524.     ULONG cchFullNameName;
  525.     LPSTR szFullNameT;
  526.     LPSTR szT;
  527.  
  528.     /*  If someone tries to match more than an iwMOMax-part name, the function
  529.      *  will return fFalse.  But then if someone is trying to match a name
  530.      *  with iwMOMax parts, chances are they weren't going to get it right
  531.      *  anyway....
  532.      */
  533.  
  534. #define iwMOMax 50
  535.  
  536.     WORD rgwMO[iwMOMax];
  537.     int iwMOMac = 0;
  538.  
  539.     while (TRUE)
  540.     {
  541.         /*  Find the end of the partial name we're pointing at  */
  542.  
  543.         szT = szANR;
  544.         while (!FCharInString(szANRSep, *szT))
  545.             ++szT;
  546.         cchANRName = szT - szANR;
  547.  
  548.         /*  Check if it matches any name in the full name  */
  549.  
  550.         szFullNameT = rgchDisplayName;
  551.         while (TRUE)
  552.         {
  553.             szT = szFullNameT;
  554.  
  555.             /*  Find the length of the name in the full name  */
  556.             /*  we're checking against.                       */
  557.  
  558.             while (!FCharInString(szANRSep, *szT))
  559.                 ++szT;
  560.             cchFullNameName = szT - szFullNameT;
  561.  
  562.             if (cchANRName <= cchFullNameName &&
  563.                 CompareStringA( lcidUser,
  564.                                 NORM_IGNORECASE,
  565.                                 szANR,
  566.                                 (int) cchANRName,
  567.                                 szFullNameT,
  568.                                 (int) cchANRName) == 2 )
  569.             {
  570.                 int iwMO;
  571.  
  572.                 for (iwMO = 0; iwMO < iwMOMac; iwMO++)
  573.                     if (rgwMO[iwMO] == (WORD) (szFullNameT - rgchDisplayName))
  574.                         break;
  575.  
  576.                 /*  We found the partial name so check the next ANR part */
  577.                 if (iwMO == iwMOMac)
  578.                 {
  579.                     if (iwMOMac == iwMOMax - 1)
  580.                     {
  581.                         /*  If some user wants to match an iwMOMax part
  582.                          *  name, chances are it wasn't going to match
  583.                          *  anyway...
  584.                          */
  585.                         return FALSE;
  586.                     }
  587.                     rgwMO[iwMOMac++] = szFullNameT - rgchDisplayName;
  588.                     break;
  589.                 }
  590.             }
  591.  
  592.             /*  We didn't find the partial name this time around, so
  593.              *  try to check the next name in the full name.
  594.              */
  595.  
  596.             szFullNameT += cchFullNameName;
  597.  
  598.             while (*szFullNameT && FCharInString(szANRSep, *szFullNameT))
  599.                 ++szFullNameT;
  600.  
  601.             if (*szFullNameT == '\0')
  602.                 return FALSE;   /*  We never found the partial name. */
  603.         }
  604.  
  605.         /* We found the partial name, so check the next ANR part */
  606.  
  607.         szANR += cchANRName;
  608.         while (*szANR && FCharInString(szANRSep, *szANR))
  609.             ++szANR;
  610.  
  611.         if (*szANR == '\0')
  612.             return TRUE;        /* No more ANR to check, so we found `em all  */
  613.     }
  614.  
  615.     /*  Not reached (we hope...)  */
  616.     return FALSE;
  617. }
  618.  
  619. /*
  620.  -  HrOpenFile
  621.  -
  622.  *  Opens the .SAB file associated with the table and
  623.  *  checks whether the .SAB file has changed.
  624.  *  If it has changed, the table bookmarks and ANR bitmaps
  625.  *  are updated and everyone on the advise list is notified.
  626.  */
  627. HRESULT 
  628. HrOpenFile(LPIVTABC lpIVTAbc)
  629. {
  630.     HRESULT hResult = hrSuccess;
  631.     FILETIME filetime;
  632.     ULONG ulSize, ulSizeHigh, ulMaxPos;
  633.     LPSTR lpszFileName = NULL;
  634.  
  635.     /*
  636.      *  If file is not open, open it
  637.      */
  638.     if (lpIVTAbc->hFile == INVALID_HANDLE_VALUE)
  639.     {
  640.  
  641.         if (!FEqualSABFiles(lpIVTAbc->lpABLogon, lpIVTAbc->lpszFileName))
  642.         {
  643.             /*
  644.              *  Get the new file name
  645.              */
  646.             hResult = HrLpszGetCurrentFileName(lpIVTAbc->lpABLogon, &lpszFileName);
  647.             if (HR_FAILED(hResult))
  648.             {
  649.                 goto err;
  650.             }
  651.  
  652.             /*
  653.              *  Replace the old one with this
  654.              */
  655.             lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszFileName);
  656.             lpIVTAbc->lpszFileName = lpszFileName;
  657.  
  658.             lpszFileName = NULL;
  659.         }
  660.  
  661.         
  662.         /*
  663.          *  File is not open so lets try to open it
  664.          */
  665.         lpIVTAbc->hFile = CreateFile(
  666.             lpIVTAbc->lpszFileName,
  667.             GENERIC_READ,
  668.             FILE_SHARE_READ|FILE_SHARE_WRITE,
  669.             NULL,
  670.             OPEN_EXISTING,
  671.             FILE_ATTRIBUTE_NORMAL,
  672.             NULL);
  673.  
  674.         if (lpIVTAbc->hFile == INVALID_HANDLE_VALUE)
  675.         {
  676.             /*
  677.              *  The file didn't open...
  678.              */
  679.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  680.             SetErrorIDS(lpIVTAbc, hResult, IDS_CANT_OPEN_SAB_FILE);
  681.             goto err;
  682.         }
  683.     }
  684.  
  685.     /*
  686.      *  Get the time and date stamp
  687.      */
  688.     if (!GetFileTime(lpIVTAbc->hFile, NULL, NULL, &filetime))
  689.     {
  690.         if (GetLastError() != NO_ERROR)
  691.         {
  692.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  693.             SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_FILE_ATTRIB);
  694.         }
  695.  
  696.         goto err;
  697.     }
  698.  
  699.     /*  Get the size of the file */
  700.     if ((ulSize = GetFileSize(lpIVTAbc->hFile, &ulSizeHigh)) == 0xFFFFFFFF)
  701.     {
  702.         /*
  703.          *  MAYBE I have an error
  704.          */
  705.         if (GetLastError() != NO_ERROR)
  706.         {
  707.             hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  708.             SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_FILE_ATTRIB);
  709.             goto err;
  710.         }
  711.  
  712.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  713.         SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_TOO_LARGE);
  714.         goto err;
  715.     }
  716.  
  717.     /*
  718.      *  Actual number of valid positions
  719.      */
  720.     ulMaxPos = (ulSize / sizeof(ABCREC));
  721.  
  722.     /*
  723.      *  Check to see if it's an exact multiple of sizeof(ABCREC)
  724.      */
  725.     if (ulMaxPos * sizeof(ABCREC) != ulSize)
  726.     {
  727.         hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  728.         SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_CORRUPT);
  729.         goto err;
  730.     }
  731.  
  732.     /*
  733.      *  if the file has changed set new position, reset bookmarks etc and
  734.      *  notify everybody one the advise list
  735.      */
  736.     if (CompareFileTime(&filetime, &lpIVTAbc->filetime) || ulMaxPos != lpIVTAbc->ulMaxPos)
  737.     {
  738.         ULONG ulBK;
  739.         ABCREC abcrec;
  740.         ULONG cbT;
  741.         LPMAPIADVISESINK *ppadvise;
  742.         ULONG cAdvises;
  743.         NOTIFICATION notif;
  744.  
  745.         /* save new max position and filetime */
  746.         lpIVTAbc->filetime = filetime;
  747.         lpIVTAbc->ulMaxPos = ulMaxPos;
  748.  
  749.         /* if current position is past the end of file set it to the end */
  750.         if (lpIVTAbc->ulPosition > lpIVTAbc->ulMaxPos)
  751.             lpIVTAbc->ulPosition = lpIVTAbc->ulMaxPos;
  752.  
  753.         if (ulMaxPos)
  754.         {
  755.             SetFilePointer(lpIVTAbc->hFile, (ulMaxPos - 1)*sizeof(ABCREC), NULL, FILE_BEGIN);
  756.  
  757.             /*  Read in the record at that location  */
  758.             if (!ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
  759.                     sizeof(ABCREC), &cbT, NULL))
  760.             {
  761.                 hResult = ResultFromScode(MAPI_E_DISK_ERROR);
  762.                 SetErrorIDS(lpIVTAbc, hResult, IDS_SAB_NO_READ);
  763.  
  764.                 goto err;
  765.             }
  766.  
  767.             /* if any of the bookmarks are past the end of file
  768.              * set the file time to current file time, the position to last
  769.              * record and record to last record
  770.              */
  771.             for (ulBK = 0; ulBK < MAX_BOOKMARKS; ulBK++)
  772.                 if (lpIVTAbc->rglpABCBK[ulBK] &&
  773.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition > lpIVTAbc->ulMaxPos)
  774.                 {
  775.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition = ulMaxPos - 1;
  776.                     lpIVTAbc->rglpABCBK[ulBK]->filetime = filetime;
  777.                     lpIVTAbc->rglpABCBK[ulBK]->abcrec = abcrec;
  778.                 }
  779.  
  780.             /*
  781.             *  Reallocate the checked&matched arrays
  782.             */
  783.  
  784.             cbT = (lpIVTAbc->ulMaxPos) / 8 + 1; /* Number of bytes in both arrays */
  785.  
  786.             /* Reallocate ANR bitmaps */
  787.             if (lpIVTAbc->rgChecked)
  788.             {
  789.                 lpIVTAbc->rgChecked = lpIVTAbc->lpMalloc->lpVtbl->Realloc(
  790.                     lpIVTAbc->lpMalloc,
  791.                     lpIVTAbc->rgChecked,
  792.                     cbT);
  793.             }
  794.  
  795.             if (lpIVTAbc->rgMatched)
  796.             {
  797.                 lpIVTAbc->rgMatched = lpIVTAbc->lpMalloc->lpVtbl->Realloc(
  798.                     lpIVTAbc->lpMalloc,
  799.                     lpIVTAbc->rgMatched,
  800.                     cbT);
  801.             }
  802.         }
  803.         else
  804.         {
  805.             /* if any of the bookmarks are past the end of file
  806.              * set the file time to current file time, the position to the
  807.              * beginning of the file.
  808.              */
  809.             for (ulBK = 0; ulBK < MAX_BOOKMARKS; ulBK++)
  810.                 if (lpIVTAbc->rglpABCBK[ulBK] &&
  811.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition > lpIVTAbc->ulMaxPos)
  812.                 {
  813.                     lpIVTAbc->rglpABCBK[ulBK]->ulPosition = 0;
  814.                     lpIVTAbc->rglpABCBK[ulBK]->filetime = filetime;
  815.                 }
  816.  
  817.             /* free the ANR bitmaps */
  818.             FreeANRBitmaps(lpIVTAbc);
  819.         }
  820.  
  821.         /* initialize the notification */
  822.         ZeroMemory(¬if, sizeof(NOTIFICATION));
  823.         notif.ulEventType = fnevTableModified;
  824.         notif.info.tab.ulTableEvent = TABLE_RELOAD;
  825.  
  826.         /* notify everyone that the table has changed */
  827.         for (ppadvise = lpIVTAbc->parglpAdvise, cAdvises = 0;
  828.             cAdvises < lpIVTAbc->cAdvise;
  829.             ++ppadvise, ++cAdvises)
  830.         {
  831.             Assert(*ppadvise);
  832.             if (ppadvise)
  833.                 (void)(*ppadvise)->lpVtbl->OnNotify(*ppadvise, 1, ¬if);
  834.         }
  835.     }
  836.  
  837. out:
  838.  
  839.     DebugTraceResult(NewIVTAbc, hResult);
  840.     return hResult;
  841. err:
  842.     lpIVTAbc->lpFreeBuff(lpszFileName);
  843.  
  844.     goto out;
  845.  
  846. }
  847.  
  848. /*************************************************************************
  849.  *
  850.  -  fIVTAbcIdleRoutine
  851.  -
  852.  *  This function called during idle time closes the .SAB file and notifies
  853.  *  everyone on the advise list if the file name has changed
  854.  *
  855.  */
  856.  
  857. BOOL STDAPICALLTYPE
  858. fIVTAbcIdleRoutine(LPVOID lpv)
  859. {
  860.     LPIVTABC lpIVTAbc = (LPIVTABC) lpv;
  861.  
  862.     Assert(lpv);
  863.  
  864.     /* if file is open close it  */
  865.     if (lpIVTAbc->hFile != INVALID_HANDLE_VALUE)
  866.     {
  867.         CloseHandle(lpIVTAbc->hFile);
  868.         lpIVTAbc->hFile = INVALID_HANDLE_VALUE;
  869.     }
  870.  
  871.     /* has file name has changed? */
  872.     if (!FEqualSABFiles(lpIVTAbc->lpABLogon, lpIVTAbc->lpszFileName))
  873.     {
  874.         /* file name has changed so call HrOpenFile to reset bookmarks etc */
  875.         if (!HR_FAILED(HrOpenFile(lpIVTAbc)))
  876.         {
  877.             /* close the file */
  878.             CloseHandle(lpIVTAbc->hFile);
  879.             lpIVTAbc->hFile = INVALID_HANDLE_VALUE;
  880.         }
  881.     }
  882.     return TRUE;
  883. }
  884.  
  885. /*************************************************************************
  886.  *
  887.  -  FreeANRBitmaps
  888.  -
  889.  *  Frees the two ANR bitmaps associated with this table
  890.  *
  891.  *
  892.  */
  893. void 
  894. FreeANRBitmaps(LPIVTABC lpIVTAbc)
  895. {
  896.     if (lpIVTAbc->rgChecked)
  897.     {
  898.         lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgChecked);
  899.         lpIVTAbc->rgChecked = NULL;
  900.     }
  901.  
  902.     if (lpIVTAbc->rgMatched)
  903.     {
  904.         lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgMatched);
  905.         lpIVTAbc->rgMatched = NULL;
  906.     }
  907. }
  908.  
  909.  
  910.  
  911. /*
  912.  *  FCharInString
  913.  *
  914.  *  Finds a character in a string
  915.  */
  916. BOOL
  917. FCharInString(LPSTR lpsz, CHAR ch)
  918. {
  919.  
  920.     while (*lpsz && *lpsz != ch)
  921.         lpsz++;
  922.  
  923.     return (*lpsz == ch);
  924. }
  925.