/***********************************************************************
 *
 *  ABCTBL2.C
 *
 *  Contents Table - Part 2.
 *
 *
 *  The following routines are implemented in this file.
 *
 *
 *      IVTABC_SeekRow
 *      IVTABC_SeekRowApprox
 *      IVTABC_GetRowCount
 *      IVTABC_QueryPosition
 *      IVTABC_FindRow
 *      IVTABC_Restrict
 *      IVTABC_QueryRows
 *
 *
 *
 *  Copyright 1992, 1993, 1994 Microsoft Corporation.  All Rights Reserved.
 *
 * Revision History:
 *
 *      When            Who                                     What
 *      --------        ------------------  ---------------------------------------
 *      1.1.94          MAPI                            Original source from MAPI sample AB Provider
 *      3.7.94          Yoram Yaacovi           Update to MAPI build 154
 *      3.11.94         Yoram Yaacovi           Update to use Fax AB include files
 *      8.1.94          Yoram Yaacovi           Update to MAPI 304
 *      11.7.94         Yoram Yaacovi           Update to MAPI 318 (PR_INSTANCE_KEY)
 *
 ***********************************************************************/

#include "faxab.h"


/*************************************************************************
 *
 -  IVTABC_SeekRow
 -
 *
 *  Tries to seek an appropriate number of rows.
 *
 *
 */
STDMETHODIMP
IVTABC_SeekRow( LPIVTABC lpIVTAbc,
                BOOKMARK bkOrigin,
                LONG lRowCount,
                LONG * lplRowsSought
               )
{
    LONG lNewPos;
    LONG lMoved;
    LONG lDelta;
    LONG lLast;
    HRESULT hResult = hrSuccess;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_SeekRow, hResult);
        return hResult;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_SeekRow, hResult);
        return hResult;
    }

    /*
     *  Check the out parameter for writability (if requested)
     */
    if (lplRowsSought &&
        IsBadWritePtr(lplRowsSought, SIZEOF(LONG)))
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_SeekRow, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);



    if (bkOrigin == BOOKMARK_BEGINNING)
    {
        lNewPos = 0;

    }
    else if (bkOrigin == BOOKMARK_CURRENT)
    {
        lNewPos = lpIVTAbc->ulPosition;

    }
    else if (bkOrigin == BOOKMARK_END)
    {
        lNewPos = lpIVTAbc->ulMaxPos;

    }
    else
    {
        ULONG ulBK = (ULONG) bkOrigin - 3;
        LPABCBK lpABCBK = NULL;

        /*
         *  See if it's out of range
         */
        if (ulBK < 0 || ulBK >= MAX_BOOKMARKS)
        {
            /*
             *  bad book mark, it's an error, so...
             */
            hResult = ResultFromScode(E_INVALIDARG);

            goto out;
        }

        if (!(lpABCBK = lpIVTAbc->rglpABCBK[ulBK]))
        {
            /*
             *  bookmark has not been allocated
             */
            hResult = ResultFromScode(MAPI_E_INVALID_BOOKMARK);

            goto out;
        }

        /* Not validating existing bookmark  */
        lNewPos = lpABCBK->ulPosition;
    }

    /*
     *  Figure out what endpoint to use and what direction to go to
     *  get there.
     */
    if (lRowCount < 0)
    {
        lLast = 0;
        lDelta = -1;
    }
    else
    {
        lLast = lpIVTAbc->ulMaxPos;
        lDelta = 1;
    }

    /*
     *  While there's rows to seek ...
     */
    lMoved = 0;
    while( (lNewPos != lLast) && (lMoved != lRowCount) )
    {
        lNewPos += lDelta;

        /*
         *  Unrestricted list is easy: just seek.  Also, if the next
         *  'row' is the end of the table, just go there; don't want
         *  to check it against any restriction.
         */
        if ( (!lpIVTAbc->lpszPartialName) || (lNewPos == (LONG) lpIVTAbc->ulMaxPos))
        {
            lMoved += lDelta;
        }

        /*
         *  Otherwise, deal with the restricted list:  only count
         *  the row if it's in the restriction.
         */
        else
        {
            if (!FChecked(lpIVTAbc, (ULONG) lNewPos))
            {
                hResult = HrValidateEntry(lpIVTAbc, (ULONG) lNewPos);

                if (HR_FAILED(hResult))
                {
                    goto out;
                }
            }

            if (FMatched(lpIVTAbc, (ULONG) lNewPos))
            {
                lMoved += lDelta;
            }
        }
    }

    if (lplRowsSought)
        *lplRowsSought = lMoved;

    lpIVTAbc->ulPosition = lNewPos;

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_SeekRow, hResult);
    return hResult;

}

/*************************************************************************
 *
 -  IVTABC_SeekRowApprox
 -
 *  Tries to set the position of the table according to the approximate
 *  position passed in.
 *
 *
 */
STDMETHODIMP
IVTABC_SeekRowApprox( LPIVTABC lpIVTAbc,
                      ULONG ulNumerator,
                      ULONG ulDenominator
                     )
{
    HRESULT hResult = hrSuccess;
    ULONG iByte;
    BYTE bCount;
    ULONG ulPos = 0;
    ULONG ulCount = 0;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_SeekRowApprox, hResult);
        return hResult;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_SeekRowApprox, hResult);
        return hResult;
    }

    /*
     *  0 denominator is not allowed
     */
    if (!ulDenominator)
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_SeekRowApprox, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);


    if (ulNumerator >= ulDenominator)
    {
        /*  We're at the end of the list */
        lpIVTAbc->ulPosition = lpIVTAbc->ulMaxPos;

        hResult = hrSuccess;
        goto out;
    }

    /*
     *  Since I'm using muldiv() which takes ints/longs, I should shift right
     *  so that I don't incorrectly call it.
     *  I'm really just checking to see if the sign bit is set...
     */

    if (((long)ulNumerator < 0) || ((long)ulDenominator < 0))
    {
        ulNumerator >>= 1;
        ulDenominator >>= 1;
    }

    if (!lpIVTAbc->lpszPartialName)
    {
        /*
         *  The NON-Restriction method
         */

        lpIVTAbc->ulPosition = MULDIV(lpIVTAbc->ulMaxPos, ulNumerator, ulDenominator);

        hResult = hrSuccess;
        goto out;
    }

    /*
     *  Restriction method
     */

    /*  Figure out % of which corresponds with numerator. */
    ulCount = MULDIV(lpIVTAbc->ulRstrDenom, ulNumerator, ulDenominator);

    /*  Count bits in rgMatched until I match numerator */

    for (iByte = 0; iByte < (lpIVTAbc->ulMaxPos / 8); iByte++)
    {
        CBitsB(lpIVTAbc->rgMatched[iByte], bCount); /* <-- MACRO  */
        ulPos += (ULONG) bCount;

        if (ulPos >= ulCount)
        {
            ulPos -= bCount;    /* Go back a byte */
            break;
        }
    }

    /*  My current position is there. */
    lpIVTAbc->ulPosition = iByte * 8;


out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_SeekRowApprox, hResult);
    return hResult;

}

/*************************************************************************
 *
 -  IVTABC_GetRowCount
 -
 *
 *  If there's a restriction applied, I don't necessarily know how many
 *  rows there are...
 */

STDMETHODIMP
IVTABC_GetRowCount( LPIVTABC lpIVTAbc,
                    ULONG ulFlags,
                    ULONG * lpulCount
                   )
{
    HRESULT hResult;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_GetRowCount, hResult);
        return hResult;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_GetRowCount, hResult);
        return hResult;
    }

    /*
     *  Check the out parameters for writability
     */
    if (IsBadWritePtr(lpulCount, sizeof(ULONG)))
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_GetRowCount, hResult);
        return hResult;
    }

    if (ulFlags & ~TBL_NOWAIT)
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);

        DebugTraceResult(IVTABC_GetRowCount, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);

    /*
     *  If there's no restriction, you can actually calculate this..
     */
    if (!lpIVTAbc->lpszPartialName)
    {

        /*
         *  Number of actual rows
         */
        *lpulCount = lpIVTAbc->ulMaxPos;
        hResult = hrSuccess;
        goto out;
    }

    /*
     *  There's no way that I can tell how many actual entries there
     *  are without counting them.  That takes way too long, so we give
     *  the client our best guess.
     */

    *lpulCount = lpIVTAbc->ulRstrDenom;

    /*
     *  Then we warn the client that this count may not be accurate
     */

    hResult = ResultFromScode(MAPI_W_APPROX_COUNT);

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_GetRowCount, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  IVTABC_QueryPosition
 -
 *  Figures out the current fractional position
 *
 *
 *
 */
STDMETHODIMP
IVTABC_QueryPosition( LPIVTABC lpIVTAbc,
                      ULONG * lpulRow,
                      ULONG * lpulNumerator,
                      ULONG * lpulDenominator
                     )
{
    HRESULT hResult = hrSuccess;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryPosition, hResult);
        return hResult;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryPosition, hResult);
        return hResult;
    }

    /*
     *  Check the out parameters for writability
     */
    if ( IsBadWritePtr(lpulRow,           SIZEOF(ULONG))
        || IsBadWritePtr(lpulNumerator,   SIZEOF(ULONG))
        || IsBadWritePtr(lpulDenominator, SIZEOF(ULONG))
       )
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryPosition, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);


    /*  ...I don't have a restriction  */
    if (!lpIVTAbc->lpszPartialName)
    {
        *lpulRow = lpIVTAbc->ulPosition;

        *lpulNumerator = lpIVTAbc->ulPosition;
        *lpulDenominator = lpIVTAbc->ulMaxPos;
    }
    else
    {
        BYTE bCount = 0;
        BYTE bFrag;
        ULONG iByte;

        /*
         *  Zero out fraction
         */
        *lpulNumerator = 0;

        /*
         *  Set denominator that we've been keeping track of.
         */
        *lpulDenominator = (lpIVTAbc->ulRstrDenom ? lpIVTAbc->ulRstrDenom : 1);

        /*
         *  Handle corner case - we're at the beginning of the list...
         */
        if (lpIVTAbc->ulPosition == 0)
        {
            *lpulRow = 0;
            goto out;
        }

        /*
         *  Calculate Numerator
         *  We use the rgMatched bit array and count the bits up to
         *  our current position (lpIVTAbc->ulPosition).
         *
         */
        for (iByte = 0; iByte < (lpIVTAbc->ulPosition / 8); iByte++)
        {
            CBitsB(lpIVTAbc->rgMatched[iByte], bCount); /* <-- MACRO  */
            *lpulNumerator += (ULONG) bCount;
        }
        /*  Count the fragment  */
        bFrag = lpIVTAbc->rgMatched[iByte];
        bFrag = bFrag >> (8 - (lpIVTAbc->ulPosition % 8));

        CBitsB(bFrag, bCount);
        *lpulNumerator += (ULONG) bCount;

        /*
         *  Good guess here...
         */
        *lpulRow = *lpulNumerator;

    }

out:

    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_QueryPosition, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  IVTABC_FindRow
 -
 *
 *  Prefix searches to find a row
 *
 *
 */
STDMETHODIMP
IVTABC_FindRow( LPIVTABC lpIVTAbc,
                LPSRestriction lpRestriction,
                BOOKMARK bkOrigin,
                ULONG ulFlags
               )
{
    HRESULT hResult = hrSuccess;

    ULONG cbRead;
    ABCREC abcrec;
    LONG lpos;
    ULONG fFound = FALSE;
    LPTSTR szPrefix;
    int nCmpResult;

    ULONG ulCurMin;
    ULONG ulCurMac;
    ULONG ulPosT;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_FindRow, hResult);
        return hResult;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_FindRow, hResult);
        return hResult;
    }

    /*
     *  Check flags
     */
    if (ulFlags & ~DIR_BACKWARD)
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);

        DebugTraceResult(IVTABC_FindRow, hResult);
        return hResult;
    }

    /*
     *  I don't go backwards, yet.
     */
    if (ulFlags & DIR_BACKWARD)
    {
        hResult = ResultFromScode(MAPI_E_NO_SUPPORT);

        DebugTraceResult(IVTABC_FindRow, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);

    /*
     *  Open the file
     */
    hResult = HrOpenFile(lpIVTAbc);
    if (HR_FAILED(hResult))
    {
        goto out;
    }

    /*
     *  initialize
     */
    ulCurMin = lpIVTAbc->ulPosition;
    ulCurMac = lpIVTAbc->ulMaxPos;

    /*
     *  I handle two type of restrictions:
     *              prefix searching on Display Names
     *              Finding a row based on it's instance key
     */

    /*
     *  The restriction looks like:
     *
     *  +-----------------
     *  | RES_PROPERTY
     *  +-----------------
     *  | RELOP_GE
     *  +-----------------
     *  | PR_DISPLAY_NAME
     *  +-----------------
     *  | LPSPropVal   -----+
     *  +-----------------  |
     *                      |   +-------------------------
     *                      +-->| PR_DISPLAY_NAME
     *                          +-------------------------
     *                          | 0 <-- for alignment (don't care)
     *                          +-------------------------
     *                          | lpszA <-- prefix for display name
     *                          +-------------------------
     *
     *
     *                              -OR-
     *
     *  Find a row based on it's instance key
     *
     *  +-----------------
     *  | RES_PROPERTY
     *  +-----------------
     *  | RELOP_EQ
     *  +-----------------
     *  | PR_INSTANCE_KEY
     *  +-----------------
     *  | LPSPropVal   -----+
     *  +-----------------  |
     *                      |   +-------------------------
     *                      +-->| PR_INSTANCE_KEY
     *                          +-------------------------
     *                          |     | cbInstanceKey
     *                          + bin +-------------------
     *                          |     | lpbInstanceKey
     *                          +-------------------------
     *
     *
     *  If it doesn't look like one of these, return MAPI_E_TOO_COMPLEX.
     */

    /*
     *  Both restrictions require this one
     */
    if (lpRestriction->rt != RES_PROPERTY)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    /*
     *  Look for Instance Key first - it's easiest to handle
     */
    if (lpRestriction->res.resProperty.relop == RELOP_EQ)
    {
        LPABCRecInstance lpABCRecInstance;

        if (lpRestriction->res.resProperty.ulPropTag != PR_INSTANCE_KEY)
        {
            /*
             *  Clearly something we don't recognize
             */
            hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

            goto out;
        }

        /*
         *  Crack the bin part of this restriction and
         *  see if we can still find our way back to this
         *  record - quickly...
         */
        lpABCRecInstance = (LPABCRecInstance) lpRestriction->res.resProperty.lpProp->Value.bin.lpb;

        /*
         *  First check to see that we're browsing the same file
         */
        if (lstrcmp(lpABCRecInstance->rgchzFileName, lpIVTAbc->lpszFileName))
        {
            /*
             *  Nope, different names, return not found and leave our position alone...
             */
            hResult = ResultFromScode(MAPI_E_NOT_FOUND);
            goto out;
        }

        /*
         *  Ok, so we think we're browsing the same file.  Has it been modified since the
         *  last time we looked?
         */
        if (memcmp(&(lpABCRecInstance->filetime), &(lpIVTAbc->filetime), SIZEOF(FILETIME)))
        {
            /*
             *  Nope, they're different, so no guarantees here...
             */
            hResult = ResultFromScode(MAPI_E_NOT_FOUND);
            goto out;
        }

        /*
         *  Now, I feel pretty confident about this instance key.  Just set my current position
         *  and go for it.
         */
        lpIVTAbc->ulPosition = lpABCRecInstance->ulRecordPosition;

        /* Done */
        goto out;

    }

    /*
     *  Now we're looking for prefix searching on display name
     */
    if (lpRestriction->res.resProperty.relop != RELOP_GE)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    if (lpRestriction->res.resProperty.ulPropTag != PR_DISPLAY_NAME_A)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    szPrefix = lpRestriction->res.resProperty.lpProp->Value.LPSZ;

    if (bkOrigin == BOOKMARK_BEGINNING)
    {
        ulCurMin = 0;

    }
    else if (bkOrigin == BOOKMARK_END)
    {
        ulCurMin = lpIVTAbc->ulMaxPos;

    }
    else if (bkOrigin != BOOKMARK_CURRENT)
    {
        ULONG ulBK = (ULONG) bkOrigin - 3;
        LPABCBK lpABCBK = NULL;

        /*
         *  See if it's out of range
         */
        if (ulBK < 0 || ulBK >= MAX_BOOKMARKS)
        {
            /*
             *  bad book mark, it's an error, so...
             */
            hResult = ResultFromScode(E_INVALIDARG);

            goto out;
        }

        if (!(lpABCBK = lpIVTAbc->rglpABCBK[ulBK]))
        {
            /*
             *  bookmark has not been allocated
             */
            hResult = ResultFromScode(MAPI_E_INVALID_BOOKMARK);

            goto out;
        }

        /*  Not validating existing bookmark  */
        ulCurMin = lpABCBK->ulPosition;
    }

    while (ulCurMin < ulCurMac)
    {
        /*
         *  Look for a row which matches the table restriction (if any).
         */
        ulPosT = (ulCurMin + ulCurMac) / 2;

        lpos = (long)((long)ulPosT * (long)SIZEOF(ABCREC));

        SetFilePointer(lpIVTAbc->hFile, lpos, NULL, FILE_BEGIN);

        /*  Read in the record at that location  */
        if ( !ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
                       SIZEOF(ABCREC), &cbRead, NULL)
           )
        {
            hResult = ResultFromScode(MAPI_E_DISK_ERROR);
            SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_NO_READ);

            goto out;
        }

        /*
         *  I want case insensitive comparisons here...
         */
        nCmpResult = lstrcmpi(szPrefix, abcrec.rgchDisplayName);

        if (nCmpResult > 0)
        {
            ulCurMin = ulPosT + 1;
        }
        else
        {
            ulCurMac = ulPosT;
            if ( !lpIVTAbc->lpszPartialName ||
                 FNameMatch(lpIVTAbc, abcrec.rgchDisplayName)
               )
            {
                fFound = TRUE;
            }
        }
    }

    /*
     *  If I didn't find a row, return MAPI_E_NOT_FOUND.
     */
    if (!fFound)
    {
        hResult = ResultFromScode(MAPI_E_NOT_FOUND);

        goto out;
    }

    /*
     *  Otherwise, set the current position to the row found.
     */
    lpIVTAbc->ulPosition = ulCurMac;

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_FindRow, hResult);
    return hResult;
}

/*************************************************************************
 *
 -  IVTABC_Restrict
 -
 *
 *      Should just support ANR type restrictions...
 */
STDMETHODIMP
IVTABC_Restrict( LPIVTABC lpIVTAbc,
                 LPSRestriction lpRestriction,
                 ULONG ulFlags
                )
{
    LPTSTR szT = NULL;
    LPTSTR lpszTNew = NULL;
    ULONG cbTB = 0;
    BYTE bFilter = 0;
    SCODE scode;
    HRESULT hResult = hrSuccess;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        goto out;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        goto out;
    }

    /*
     *  Check flags
     */
    if (ulFlags & ~(TBL_NOWAIT|TBL_BATCH))
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);

        goto out;
    }

    /*
     *  I only handle ANR type restrictions
     */

    /*
     *  Check to see if they're resetting the restrictions
     */
    if (!lpRestriction)
    {
        EnterCriticalSection(&lpIVTAbc->cs);

        if (lpIVTAbc->lpszPartialName)
            (*(lpIVTAbc->lpFreeBuff)) (lpIVTAbc->lpszPartialName);
        lpIVTAbc->lpszPartialName = NULL;

        FreeANRBitmaps(lpIVTAbc);

        LeaveCriticalSection(&lpIVTAbc->cs);

        return hrSuccess;
    }

    /*
     *  The restriction must look like:
     *
     *
     *  +--------------
     *  | RES_PROPERTY
     *  +--------------
     *  | RELOP_EQ
     *  +--------------
     *  | PR_ANR
     *  +--------------
     *  | LPSPropVal ---+
     *  +-------------- |
     *                  |   +-------------------------
     *                  +-->| PR_ANR
     *                      +-------------------------
     *                      | 0 <-- for alignment (don't care)
     *                      +-------------------------
     *                      | lpszA <-- string to ANR on
     *                      +-------------------------
     *
     *
     *
     *  If it doesn't look like this, return MAPI_E_TOO_COMPLEX.
     *
     */

    if (lpRestriction->rt != RES_PROPERTY)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    if (lpRestriction->res.resProperty.relop != RELOP_EQ)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    if (lpRestriction->res.resProperty.ulPropTag != PR_ANR)
    {
        hResult = ResultFromScode(MAPI_E_TOO_COMPLEX);

        goto out;
    }

    /*
     *  NULL string is not defined - it's a bad restriction
     */
    if (!lpRestriction->res.resProperty.lpProp->Value.LPSZ)
    {
        hResult = ResultFromScode(E_INVALIDARG);

        goto out;
    }


    szT = lpRestriction->res.resProperty.lpProp->Value.LPSZ;

    /*
     *  Skip over leading spaces
     */

    while (*szT == TEXT(' '))
        szT++;

    /*
     *  Empty string is not defined - it's a bad restriction
     */
    if (*szT == TEXT('\0'))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto out;
    }


    /*
     *  Copy the string for the partial name
     */

    scode = lpIVTAbc->lpAllocBuff((lstrlen(szT) + 1)*SIZEOF(TCHAR), (LPVOID *) &lpszTNew);
    if (FAILED(scode))
    {
        /*
         *  Memory error
         */

        hResult = ResultFromScode(scode);
        goto out;
    }
    lstrcpy(lpszTNew, szT);


    EnterCriticalSection(&lpIVTAbc->cs);


    /*
     *  Clear up any old restriction
     */

    if (lpIVTAbc->lpszPartialName)
        lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszPartialName);

    lpIVTAbc->lpszPartialName = lpszTNew;

    FreeANRBitmaps(lpIVTAbc);


    /*
     *  Allocate enough bits for the checked&matched arrays
     */
    cbTB = (lpIVTAbc->ulMaxPos) / 8 + 1;    /* Number of bytes in both arrays */

    lpIVTAbc->rgChecked = lpIVTAbc->lpMalloc->lpVtbl->Alloc(
        lpIVTAbc->lpMalloc,
        cbTB);

    if (lpIVTAbc->rgChecked == NULL)
    {
        lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszPartialName);
        lpIVTAbc->lpszPartialName = NULL;

        hResult = ResultFromScode(scode);

        LeaveCriticalSection(&lpIVTAbc->cs);
        goto out;
    }

    lpIVTAbc->rgMatched = lpIVTAbc->lpMalloc->lpVtbl->Alloc(
        lpIVTAbc->lpMalloc,
        cbTB);

    if (lpIVTAbc->rgMatched == NULL)
    {
        (*(lpIVTAbc->lpFreeBuff)) (lpIVTAbc->lpszPartialName);
        lpIVTAbc->lpszPartialName = NULL;

        lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgChecked);
        lpIVTAbc->rgChecked = NULL;

        hResult = ResultFromScode(scode);

        LeaveCriticalSection(&lpIVTAbc->cs);
        goto out;
    }

    /*
     *  Initialize the checked array with 0's
     */

    FillMemory(lpIVTAbc->rgChecked, (UINT) cbTB, 0x00);

    /*
     *  Initialize the matched array with 1's
     *  [we assume that if you haven't checked it, it matches]
     */

    FillMemory(lpIVTAbc->rgMatched, (UINT) cbTB, 0xFF);

    /*
     *  Fill in the end bits so we don't have to worry about them
     *  later
     */
    bFilter = (0xFF >> (lpIVTAbc->ulMaxPos % 8));

    /*  Checked end bits should be 1 */
    lpIVTAbc->rgChecked[cbTB - 1] = bFilter;

    /*  Matched end bits should be 0 */
    lpIVTAbc->rgMatched[cbTB - 1] = ~bFilter;

    /*
     *  Set the currenly known total number of rows
     *  that match the restriction.
     */

    lpIVTAbc->ulRstrDenom = lpIVTAbc->ulMaxPos;

    LeaveCriticalSection(&lpIVTAbc->cs);

out:

    DebugTraceResult(IVTABC_Restrict, hResult);
    return hResult;

}



/*************************************************************************
 *
 -  IVTABC_QueryRows
 -
 *  Attempts to retrieve ulRowCount rows from the .FAB file.  Even in the
 *  restricted case.
 *
 *
 */
STDMETHODIMP
IVTABC_QueryRows( LPIVTABC lpIVTAbc,
                  LONG lRowCount,
                  ULONG ulFlags,
                  LPSRowSet * lppRows
                 )
{
    SCODE scode;
    HRESULT hResult = hrSuccess;
    LPSRowSet lpRowSet = NULL;
    LPSPropValue lpRow = NULL;
    int cbSizeOfRow;
    int cRows, iRow;
    int cCols;
    DWORD cbRead;
    ABCREC abcrec;
    ULONG ulOrigPosition;

    /*
     *  Validate parameters
     */

    /*
     *  Check to see if it's large enough to hold this object
     */
    if (IsBadReadPtr(lpIVTAbc, SIZEOF(IVTABC)))
    {
        /*
         *  Not large enough
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }

    /*
     *  Check to see that it's the correct vtbl
     */
    if (lpIVTAbc->lpVtbl != &vtblIVTABC)
    {
        /*
         *  Not my vtbl
         */
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }

    /*
     *  Check the out parameter for writability
     */
    if (IsBadWritePtr(lppRows, SIZEOF(LPSRowSet)))
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }

    /*
     *  Never valid to ask for 0 rows
     */
    if (!lRowCount)
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }

    /*  //$ MM2 Hack!  Won't Read backwards for TR2 */
    if (lRowCount < 0)
    {
        hResult = ResultFromScode(E_INVALIDARG);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }

    /*
     *  Check the flags
     */
    if (ulFlags & ~TBL_NOADVANCE)
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);

        DebugTraceResult(IVTABC_QueryRows, hResult);
        return hResult;
    }


    EnterCriticalSection(&lpIVTAbc->cs);

    /*
     *  Open the file
     */
    hResult = HrOpenFile(lpIVTAbc);
    if (HR_FAILED(hResult))
    {
        goto out;
    }

    ulOrigPosition = lpIVTAbc->ulPosition;

    /*
     *  Calculate # of rows that will be read.
     */
    cRows = (int)lpIVTAbc->ulMaxPos - (int)lpIVTAbc->ulPosition;
    cRows = (cRows < (int)lRowCount ? cRows : (int)lRowCount);

    /*
     *  Allocate array for SRowSet
     */

    scode = lpIVTAbc->lpAllocBuff(sizeof(ULONG) + cRows * SIZEOF(SRow),
                                        (LPVOID *) &lpRowSet);
    if (FAILED(scode))
    {
        hResult = ResultFromScode(scode);

        goto out;
    }

    /*
     *  Initialize - so we can clean up later if we have to...
     */
    lpRowSet->cRows = cRows;

    for( iRow = 0; iRow < cRows; iRow++ )
    {
        lpRowSet->aRow[iRow].lpProps = NULL;
    }



    /*
     *  Seek to correct position in file
     */
    (void) SetFilePointer( lpIVTAbc->hFile,
                           lpIVTAbc->ulPosition * SIZEOF(ABCREC),
                           NULL,
                           FILE_BEGIN
                          );

    /*
     *  Read each row from the file
     */
    for (iRow = 0; iRow < cRows; iRow++)
    {

        /*  The only properties available are:
         *
         *  PR_DISPLAY_NAME, PR_ENTRYID, PR_ADDRTYPE, PR_EMAIL_ADDRESS,
         *  PR_OBJECT_TYPE, PR_DISPLAY_TYPE
         */

        /*
         *  Handle restricted lists
         */
        if (lpIVTAbc->lpszPartialName)
        {
            ULONG ulPos = lpIVTAbc->ulPosition;

next:
            if (ulPos == lpIVTAbc->ulMaxPos)
            {
                break;
            }

            if (!FChecked(lpIVTAbc, ulPos))
            {
                hResult = HrValidateEntry(lpIVTAbc, ulPos);
                if (HR_FAILED(hResult))
                {
                    goto err;
                }
            }

            if (!FMatched(lpIVTAbc, ulPos))
            {
                ulPos++;
                goto next;
            }

            lpIVTAbc->ulPosition = ulPos;
            (void) SetFilePointer( lpIVTAbc->hFile,
                                   lpIVTAbc->ulPosition * SIZEOF(ABCREC),
                                   NULL,
                                   FILE_BEGIN
                                  );

        }

        lpIVTAbc->ulPosition++;

        /*
         *  Read in the record from the file
         */
        if ( !ReadFile( lpIVTAbc->hFile, (LPVOID) &abcrec,
                        SIZEOF(ABCREC), &cbRead, NULL)
            )
        {
            hResult = ResultFromScode(MAPI_E_DISK_ERROR);
            SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_NO_READ);

            goto err;
        }
        /*  Second check  */
        if ((UINT) cbRead != SIZEOF(ABCREC))
        {
            /*
             *  Should never get here.
             */
            hResult = ResultFromScode(MAPI_E_DISK_ERROR);
            SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_NO_READ);

            goto err;
        }

        /*  Allocate memory to start a row.
         */
        cbSizeOfRow = SIZEOF(ULONG) +
            (int)lpIVTAbc->lpPTAColSet->cValues * SIZEOF(SPropValue);

        scode = lpIVTAbc->lpAllocBuff(cbSizeOfRow, (LPVOID *) &lpRow);
        if (FAILED(scode))
        {
            hResult = ResultFromScode(scode);

            goto err;
        }

        /*
         *  Get all the data
         */
        for (cCols = 0; cCols < (int)lpIVTAbc->lpPTAColSet->cValues; cCols++)
        {
            switch (lpIVTAbc->lpPTAColSet->aulPropTag[cCols])
            {
            case PR_DISPLAY_NAME_A:
            {

                INT cch = lstrlen(abcrec.rgchDisplayName) + 1;

                lpRow[cCols].ulPropTag = PR_DISPLAY_NAME_A;
                scode = lpIVTAbc->lpAllocMore( cch,
                                               lpRow,
                                               (LPVOID *) &(lpRow[cCols].Value.lpszA)
                                              );

                if (FAILED(scode))
                {
                    lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                        PROP_ID(PR_DISPLAY_NAME_A));
                    lpRow[cCols].Value.err = scode;
                }
                else
                {
#ifdef UNICODE
                    lpRow[cCols].Value.lpszA[0] = 0;
                    WideCharToMultiByte( CP_ACP, 0,
                                         abcrec.rgchDisplayName, -1,
                                         lpRow[cCols].Value.lpszA, cch,
                                         NULL, NULL
                                        );
#else
                    lstrcpy(lpRow[cCols].Value.LPSZ, abcrec.rgchDisplayName);
#endif
                }

            }
            break;

            case PR_EMAIL_ADDRESS_A:
            {

                INT cch = lstrlen(abcrec.rgchEmailAddress) + 1;

                lpRow[cCols].ulPropTag = PR_EMAIL_ADDRESS_A;
                scode = lpIVTAbc->lpAllocMore( cch,
                                               lpRow,
                                               (LPVOID *) &(lpRow[cCols].Value.lpszA)
                                              );

                if (FAILED(scode))
                {
                    lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                        PROP_ID(PR_EMAIL_ADDRESS_A));
                    lpRow[cCols].Value.err = scode;
                }
                else
                {
#ifdef UNICODE
                    lpRow[cCols].Value.lpszA[0] = 0;
                    WideCharToMultiByte( CP_ACP, 0,
                                         abcrec.rgchEmailAddress, -1,
                                         lpRow[cCols].Value.lpszA, cch,
                                         NULL, NULL
                                        );
#else
                    lstrcpy(lpRow[cCols].Value.LPSZ, abcrec.rgchEmailAddress);
#endif
                }

            }
            break;


            case PR_ADDRTYPE_A:
            {
                /*
                 *  AddrType is always "MSPEER" for the FAB
                 */

                INT cch = lstrlen(lpszEMT) + 1;

                lpRow[cCols].ulPropTag = PR_ADDRTYPE_A;
                scode = lpIVTAbc->lpAllocMore( cch,
                                               lpRow,
                                               (LPVOID *) &(lpRow[cCols].Value.lpszA)
                                              );

                if (FAILED(scode))
                {
                    lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                        PROP_ID(PR_ADDRTYPE_A));
                    lpRow[cCols].Value.err = scode;
                }
                else
                {
#ifdef UNICODE
                    lpRow[cCols].Value.lpszA[0] = 0;
                    WideCharToMultiByte( CP_ACP, 0,
                                         lpszEMT, -1,
                                         lpRow[cCols].Value.lpszA, cch,
                                         NULL, NULL
                                        );
#else
                    lstrcpy(lpRow[cCols].Value.LPSZ, lpszEMT);
#endif
                }

            }
            break;

            case PR_ENTRYID:
            {
                /*
                 *  Fixed sized entryid.  Basically just the .FAB file record
                 */
                LPUSR_ENTRYID lpUsrEid;

                lpRow[cCols].ulPropTag = PR_ENTRYID;
                scode = lpIVTAbc->lpAllocMore (SIZEOF(USR_ENTRYID), lpRow,
                            (LPVOID *) &(lpRow[cCols].Value.bin.lpb));

                if (FAILED(scode))
                {
                    lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                        PROP_ID(PR_ENTRYID));
                    lpRow[cCols].Value.err = scode;
                }
                else
                {
                    lpUsrEid = (LPUSR_ENTRYID) lpRow[cCols].Value.bin.lpb;

                    RtlZeroMemory(lpUsrEid, SIZEOF(USR_ENTRYID));

                    /*  Size of entryid */
                    lpRow[cCols].Value.bin.cb = SIZEOF(USR_ENTRYID);

                    lpUsrEid->abFlags[0] = 0;   /*  long-term, recipient */
                    lpUsrEid->abFlags[1] = 0;
                    lpUsrEid->abFlags[2] = 0;
                    lpUsrEid->abFlags[3] = 0;
                    lpUsrEid->muid = muidABMAWF;
                    lpUsrEid->ulVersion = MAWF_VERSION;
                    lpUsrEid->ulType = MAWF_USER;
                    lpUsrEid->abcrec = abcrec;
                }

            }
            break;

            case PR_OBJECT_TYPE:
            {
                /*
                 *  MailUser
                 */

                lpRow[cCols].ulPropTag = PR_OBJECT_TYPE;
                lpRow[cCols].Value.ul = MAPI_MAILUSER;
            }
            break;

            case PR_DISPLAY_TYPE:
            {
                /*
                 *  MailUser
                 */

                lpRow[cCols].ulPropTag = PR_DISPLAY_TYPE;
                lpRow[cCols].Value.ul = DT_MAILUSER;

            }
            break;

            case PR_INSTANCE_KEY:
            {
                LPABCRecInstance lpABCRecInstance;
                UINT cbRecInstance;
                /*
                 *  Instance keys are made up of:
                 *              ulRecordPosition - current position in the file
                 *              filetime                 - current date and time stamp of file
                 *              lpszFileName     - current file that we're browsing
                 */
                lpRow[cCols].ulPropTag = PR_INSTANCE_KEY;

                cbRecInstance = SIZEOF(ABCRecInstance)+(lstrlen(lpIVTAbc->lpszFileName)+1)*SIZEOF(TCHAR);
                scode = lpIVTAbc->lpAllocMore(cbRecInstance,
                                lpRow,
                                (LPVOID) &(lpRow[cCols].Value.bin.lpb));

                if (FAILED(scode))
                {
                    lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_INSTANCE_KEY));
                    lpRow[cCols].Value.err = scode;
                }
                else
                {
                    lpABCRecInstance = (LPABCRecInstance) lpRow[cCols].Value.bin.lpb;

                    ZeroMemory(lpABCRecInstance, cbRecInstance);

                    lpRow[cCols].Value.bin.cb = (ULONG) cbRecInstance;

                    lpABCRecInstance->ulRecordPosition = lpIVTAbc->ulPosition;
                    lpABCRecInstance->filetime = lpIVTAbc->filetime;
                    lstrcpy(lpABCRecInstance->rgchzFileName, lpIVTAbc->lpszFileName);

                }
            }
            break;

            default:
            {
                lpRow[cCols].ulPropTag = PROP_TAG(PT_ERROR,
                    PROP_ID(lpIVTAbc->lpPTAColSet->aulPropTag[cCols]));
                lpRow[cCols].Value.err = MAPI_E_NOT_FOUND;
            }
            break;
            }
        }

        /*  # of columns  */
        lpRowSet->aRow[iRow].cValues = lpIVTAbc->lpPTAColSet->cValues;

        /*  Actual row of data */
        lpRowSet->aRow[iRow].lpProps = lpRow;

        lpRow = NULL;

    }

    /*
     *  it's always iRow.
     */
    lpRowSet->cRows = iRow;

    /*
     *  Handle Seeked position stuff
     */
    if (ulFlags & TBL_NOADVANCE)
    {
        /*
         *  Set it back to it's original position
         */
        lpIVTAbc->ulPosition = ulOrigPosition;
    }


    *lppRows = lpRowSet;

out:
    LeaveCriticalSection(&lpIVTAbc->cs);

    DebugTraceResult(IVTABC_QueryRows, hResult);
    return hResult;

err:
    /*
     *  Clean up memory...
     */

    /*  Free the row  */
    lpIVTAbc->lpFreeBuff(lpRow);

    /*  Clean up the rest of the rows  */
    FreeProws(lpRowSet);

    *lppRows = NULL;

    goto out;

}